Many hyperlinks are disabled.
Use anonymous login
to enable hyperlinks.
Overview
Comment: | Merge in all changes up to the 3.7.0.1 release. |
---|---|
Downloads: | Tarball | ZIP archive |
Timelines: | family | ancestors | descendants | both | apple-osx |
Files: | files | file ages | folders |
SHA1: |
f88c6367d2f96cc93846ba38e6b963a1 |
User & Date: | drh 2010-08-07 11:46:07.000 |
References
2011-04-05
| ||
18:34 | Remove dead code from the OP_JournalMode opcode in the VDBE. This code seems to have been useless since [f88c6367d2] on [2010-08-07]. (check-in: a89f24e2c9 user: drh tags: trunk) | |
Context
2010-08-18
| ||
00:09 | Updating apple-osx with minor source fixes, database truncate and replace private calls and a bunch of conditionalization for tests running in different environments (check-in: 5e2ee7db0f user: adam tags: apple-osx) | |
2010-08-07
| ||
11:46 | Merge in all changes up to the 3.7.0.1 release. (check-in: f88c6367d2 user: drh tags: apple-osx) | |
2010-08-04
| ||
12:31 | Version 3.7.0.1 (Leaf check-in: 042a1abb03 user: drh tags: release, branch-3.7.0) | |
2010-06-16
| ||
19:48 | Merge in changes up to and including the 3.6.23.1 release. (check-in: 21ca87f691 user: drh tags: apple-osx) | |
Changes
Changes to Makefile.in.
︙ | ︙ | |||
156 157 158 159 160 161 162 163 164 165 | LTINSTALL = $(LIBTOOL) --mode=install $(INSTALL) # nawk compatible awk. NAWK = @AWK@ # You should not have to change anything below this line ############################################################################### # Object files for the SQLite library (non-amalgamation). # | > > | > | > > | | | | | | | | | | | | | < | | 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 | LTINSTALL = $(LIBTOOL) --mode=install $(INSTALL) # nawk compatible awk. NAWK = @AWK@ # You should not have to change anything below this line ############################################################################### USE_AMALGAMATION = @USE_AMALGAMATION@ # Object files for the SQLite library (non-amalgamation). # LIBOBJS0 = alter.lo analyze.lo attach.lo auth.lo \ backup.lo bitvec.lo btmutex.lo btree.lo build.lo \ callback.lo complete.lo ctime.lo date.lo delete.lo expr.lo fault.lo fkey.lo \ fts3.lo fts3_expr.lo fts3_hash.lo fts3_icu.lo fts3_porter.lo \ fts3_snippet.lo fts3_tokenizer.lo fts3_tokenizer1.lo fts3_write.lo \ func.lo global.lo hash.lo \ icu.lo insert.lo journal.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_os2.lo mutex_unix.lo mutex_w32.lo \ notify.lo opcodes.lo os.lo os_os2.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 select.lo status.lo \ table.lo tokenize.lo trigger.lo \ update.lo util.lo vacuum.lo \ vdbe.lo vdbeapi.lo vdbeaux.lo vdbeblob.lo vdbemem.lo vdbetrace.lo \ wal.lo walker.lo where.lo utf.o vtab.lo # Object files for the amalgamation. # LIBOBJS1 = sqlite3.lo # Determine the real value of LIBOBJ based on the 'configure' script # LIBOBJ = $(LIBOBJS$(USE_AMALGAMATION)) # All of the source code files. # SRC = \ $(TOP)/src/alter.c \ $(TOP)/src/analyze.c \ |
︙ | ︙ | |||
272 273 274 275 276 277 278 279 280 281 | $(TOP)/src/vdbeapi.c \ $(TOP)/src/vdbeaux.c \ $(TOP)/src/vdbeblob.c \ $(TOP)/src/vdbemem.c \ $(TOP)/src/vdbetrace.c \ $(TOP)/src/vdbeInt.h \ $(TOP)/src/vtab.c \ $(TOP)/src/walker.c \ $(TOP)/src/where.c | > > < < < < < < < < < < < | 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 | $(TOP)/src/vdbeapi.c \ $(TOP)/src/vdbeaux.c \ $(TOP)/src/vdbeblob.c \ $(TOP)/src/vdbemem.c \ $(TOP)/src/vdbetrace.c \ $(TOP)/src/vdbeInt.h \ $(TOP)/src/vtab.c \ $(TOP)/src/wal.c \ $(TOP)/src/wal.h \ $(TOP)/src/walker.c \ $(TOP)/src/where.c # Source code for extensions # SRC += \ $(TOP)/ext/fts1/fts1.c \ $(TOP)/ext/fts1/fts1.h \ $(TOP)/ext/fts1/fts1_hash.c \ $(TOP)/ext/fts1/fts1_hash.h \ |
︙ | ︙ | |||
330 331 332 333 334 335 336 | SRC += \ $(TOP)/ext/rtree/rtree.h \ $(TOP)/ext/rtree/rtree.c SRC += \ $(TOP)/ext/sqlrr/sqlrr.h \ $(TOP)/ext/sqlrr/sqlrr.c | | > | | < < < < < < < < < < | < < < < | < < < < < < < < < | < < < < < | > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > < | > > > > > < | | | | | | < | 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 | SRC += \ $(TOP)/ext/rtree/rtree.h \ $(TOP)/ext/rtree/rtree.c SRC += \ $(TOP)/ext/sqlrr/sqlrr.h \ $(TOP)/ext/sqlrr/sqlrr.c # Generated source code files # SRC += \ keywordhash.h \ opcodes.c \ opcodes.h \ parse.c \ parse.h \ config.h \ sqlite3.h # Source code to the test files. # TESTSRC = \ $(TOP)/src/test1.c \ $(TOP)/src/test2.c \ $(TOP)/src/test3.c \ $(TOP)/src/test4.c \ $(TOP)/src/test5.c \ $(TOP)/src/test6.c \ $(TOP)/src/test7.c \ $(TOP)/src/test8.c \ $(TOP)/src/test9.c \ $(TOP)/src/test_autoext.c \ $(TOP)/src/test_async.c \ $(TOP)/src/test_backup.c \ $(TOP)/src/test_btree.c \ $(TOP)/src/test_config.c \ $(TOP)/src/test_demovfs.c \ $(TOP)/src/test_devsym.c \ $(TOP)/src/test_func.c \ $(TOP)/src/test_hexio.c \ $(TOP)/src/test_init.c \ $(TOP)/src/test_intarray.c \ $(TOP)/src/test_journal.c \ $(TOP)/src/test_malloc.c \ $(TOP)/src/test_mutex.c \ $(TOP)/src/test_onefile.c \ $(TOP)/src/test_osinst.c \ $(TOP)/src/test_pcache.c \ $(TOP)/src/test_schema.c \ $(TOP)/src/test_server.c \ $(TOP)/src/test_stat.c \ $(TOP)/src/test_tclvar.c \ $(TOP)/src/test_thread.c \ $(TOP)/src/test_vfs.c \ $(TOP)/src/test_wsd.c # Source code to the library files needed by the test fixture # TESTSRC2 = \ $(TOP)/src/attach.c \ $(TOP)/src/backup.c \ $(TOP)/src/bitvec.c \ $(TOP)/src/btree.c \ $(TOP)/src/build.c \ $(TOP)/src/ctime.c \ $(TOP)/src/date.c \ $(TOP)/src/expr.c \ $(TOP)/src/func.c \ $(TOP)/src/insert.c \ $(TOP)/src/wal.c \ $(TOP)/src/mem5.c \ $(TOP)/src/os.c \ $(TOP)/src/os_os2.c \ $(TOP)/src/os_unix.c \ $(TOP)/src/os_win.c \ $(TOP)/src/pager.c \ $(TOP)/src/pragma.c \ $(TOP)/src/prepare.c \ $(TOP)/src/printf.c \ $(TOP)/src/random.c \ $(TOP)/src/pcache.c \ $(TOP)/src/pcache1.c \ $(TOP)/src/select.c \ $(TOP)/src/tokenize.c \ $(TOP)/src/utf.c \ $(TOP)/src/util.c \ $(TOP)/src/vdbeapi.c \ $(TOP)/src/vdbeaux.c \ $(TOP)/src/vdbe.c \ $(TOP)/src/vdbemem.c \ $(TOP)/src/vdbetrace.c \ $(TOP)/src/where.c \ parse.c \ $(TOP)/ext/fts3/fts3.c \ $(TOP)/ext/fts3/fts3_expr.c \ $(TOP)/ext/fts3/fts3_tokenizer.c \ $(TOP)/ext/fts3/fts3_write.c \ $(TOP)/ext/async/sqlite3async.c # Header files used by all library source files. # HDR = \ $(TOP)/src/btree.h \ $(TOP)/src/btreeInt.h \ $(TOP)/src/hash.h \ $(TOP)/src/hwtime.h \ keywordhash.h \ $(TOP)/src/mutex.h \ opcodes.h \ $(TOP)/src/os.h \ $(TOP)/src/os_common.h \ $(TOP)/src/pager.h \ $(TOP)/src/pcache.h \ parse.h \ sqlite3.h \ $(TOP)/src/sqlite3ext.h \ $(TOP)/src/sqliteInt.h \ $(TOP)/src/sqliteLimit.h \ $(TOP)/src/vdbe.h \ $(TOP)/src/vdbeInt.h \ config.h # Header files used by extensions # EXTHDR += \ $(TOP)/ext/fts1/fts1.h \ $(TOP)/ext/fts1/fts1_hash.h \ $(TOP)/ext/fts1/fts1_tokenizer.h EXTHDR += \ $(TOP)/ext/fts2/fts2.h \ $(TOP)/ext/fts2/fts2_hash.h \ $(TOP)/ext/fts2/fts2_tokenizer.h EXTHDR += \ $(TOP)/ext/fts3/fts3.h \ $(TOP)/ext/fts3/fts3Int.h \ $(TOP)/ext/fts3/fts3_hash.h \ $(TOP)/ext/fts3/fts3_tokenizer.h EXTHDR += \ $(TOP)/ext/rtree/rtree.h EXTHDR += \ $(TOP)/ext/icu/sqliteicu.h EXTHDR += \ $(TOP)/ext/sqlrr/sqlrr.h # If using the amalgamation, use sqlite3.c directly to build the test # fixture. Otherwise link against libsqlite3.la. (This distinction is # necessary because the test fixture requires non-API symbols which are # hidden when the library is built via the amalgamation). # TESTFIXTURE_SRC0 = $(TESTSRC2) libsqlite3.la TESTFIXTURE_SRC1 = sqlite3.c TESTFIXTURE_SRC = $(TESTSRC) $(TOP)/src/tclsqlite.c $(TESTFIXTURE_SRC$(USE_AMALGAMATION)) # 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 |
︙ | ︙ | |||
486 487 488 489 490 491 492 | # This target creates a directory named "tsrc" and fills it with # copies of all of the C source code and header files needed to # build on the target system. Some of the C source code and header # files are automatically generated. This target takes care of # all that automatic generation. # | | | | > > > > > > > | < > > > > | > | | | 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 | # This target creates a directory named "tsrc" and fills it with # copies of all of the C source code and header files needed to # build on the target system. Some of the C source code and header # files are automatically generated. This target takes care of # all that automatic generation. # .target_source: $(SRC) $(TOP)/tool/vdbe-compress.tcl rm -rf tsrc mkdir tsrc cp -f $(SRC) tsrc rm tsrc/sqlite.h.in tsrc/parse.y $(TCLSH_CMD) $(TOP)/tool/vdbe-compress.tcl <tsrc/vdbe.c >vdbe.new mv vdbe.new tsrc/vdbe.c touch .target_source sqlite3.c: .target_source $(TOP)/tool/mksqlite3c.tcl $(TCLSH_CMD) $(TOP)/tool/mksqlite3c.tcl # Rule to build the amalgamation # sqlite3.lo: sqlite3.c $(LTCOMPILE) $(TEMP_STORE) -c sqlite3.c # Rules to build the LEMON compiler generator # lemon$(BEXE): $(TOP)/tool/lemon.c $(TOP)/src/lempar.c $(BCC) -o $@ $(TOP)/tool/lemon.c cp $(TOP)/src/lempar.c . # Rules to build individual *.o files from generated *.c files. This # applies to: # # parse.o # opcodes.o # parse.lo: parse.c $(HDR) $(LTCOMPILE) $(TEMP_STORE) -c parse.c opcodes.lo: opcodes.c $(LTCOMPILE) $(TEMP_STORE) -c opcodes.c # Rules to build individual *.o files from files in the src directory. # alter.lo: $(TOP)/src/alter.c $(HDR) $(LTCOMPILE) $(TEMP_STORE) -c $(TOP)/src/alter.c analyze.lo: $(TOP)/src/analyze.c $(HDR) $(LTCOMPILE) $(TEMP_STORE) -c $(TOP)/src/analyze.c |
︙ | ︙ | |||
635 636 637 638 639 640 641 | pcache.lo: $(TOP)/src/pcache.c $(HDR) $(TOP)/src/pcache.h $(LTCOMPILE) $(TEMP_STORE) -c $(TOP)/src/pcache.c pcache1.lo: $(TOP)/src/pcache1.c $(HDR) $(TOP)/src/pcache.h $(LTCOMPILE) $(TEMP_STORE) -c $(TOP)/src/pcache1.c | < < < < < < < < < < < < < < < < < < < < | 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 | pcache.lo: $(TOP)/src/pcache.c $(HDR) $(TOP)/src/pcache.h $(LTCOMPILE) $(TEMP_STORE) -c $(TOP)/src/pcache.c pcache1.lo: $(TOP)/src/pcache1.c $(HDR) $(TOP)/src/pcache.h $(LTCOMPILE) $(TEMP_STORE) -c $(TOP)/src/pcache1.c os.lo: $(TOP)/src/os.c $(HDR) $(LTCOMPILE) $(TEMP_STORE) -c $(TOP)/src/os.c os_unix.lo: $(TOP)/src/os_unix.c $(HDR) $(LTCOMPILE) $(TEMP_STORE) -c $(TOP)/src/os_unix.c os_win.lo: $(TOP)/src/os_win.c $(HDR) $(LTCOMPILE) $(TEMP_STORE) -c $(TOP)/src/os_win.c os_os2.lo: $(TOP)/src/os_os2.c $(HDR) $(LTCOMPILE) $(TEMP_STORE) -c $(TOP)/src/os_os2.c pragma.lo: $(TOP)/src/pragma.c $(HDR) $(LTCOMPILE) $(TEMP_STORE) -c $(TOP)/src/pragma.c prepare.lo: $(TOP)/src/prepare.c $(HDR) $(LTCOMPILE) $(TEMP_STORE) -c $(TOP)/src/prepare.c printf.lo: $(TOP)/src/printf.c $(HDR) |
︙ | ︙ | |||
691 692 693 694 695 696 697 | select.lo: $(TOP)/src/select.c $(HDR) $(LTCOMPILE) $(TEMP_STORE) -c $(TOP)/src/select.c status.lo: $(TOP)/src/status.c $(HDR) $(LTCOMPILE) $(TEMP_STORE) -c $(TOP)/src/status.c | < < < < < < < < < < | 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 | select.lo: $(TOP)/src/select.c $(HDR) $(LTCOMPILE) $(TEMP_STORE) -c $(TOP)/src/select.c status.lo: $(TOP)/src/status.c $(HDR) $(LTCOMPILE) $(TEMP_STORE) -c $(TOP)/src/status.c table.lo: $(TOP)/src/table.c $(HDR) $(LTCOMPILE) $(TEMP_STORE) -c $(TOP)/src/table.c tokenize.lo: $(TOP)/src/tokenize.c keywordhash.h $(HDR) $(LTCOMPILE) $(TEMP_STORE) -c $(TOP)/src/tokenize.c trigger.lo: $(TOP)/src/trigger.c $(HDR) $(LTCOMPILE) $(TEMP_STORE) -c $(TOP)/src/trigger.c update.lo: $(TOP)/src/update.c $(HDR) $(LTCOMPILE) $(TEMP_STORE) -c $(TOP)/src/update.c utf.lo: $(TOP)/src/utf.c $(HDR) |
︙ | ︙ | |||
743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 | vdbetrace.lo: $(TOP)/src/vdbetrace.c $(HDR) $(LTCOMPILE) $(TEMP_STORE) -c $(TOP)/src/vdbetrace.c vtab.lo: $(TOP)/src/vtab.c $(HDR) $(LTCOMPILE) $(TEMP_STORE) -c $(TOP)/src/vtab.c walker.lo: $(TOP)/src/walker.c $(HDR) $(LTCOMPILE) $(TEMP_STORE) -c $(TOP)/src/walker.c where.lo: $(TOP)/src/where.c $(HDR) $(LTCOMPILE) $(TEMP_STORE) -c $(TOP)/src/where.c tclsqlite-shell.lo: $(TOP)/src/tclsqlite.c $(HDR) $(LTCOMPILE) -DTCLSH=1 -o $@ -c $(TOP)/src/tclsqlite.c tclsqlite-stubs.lo: $(TOP)/src/tclsqlite.c $(HDR) $(LTCOMPILE) -DTCL_USE_STUBS=1 -o $@ -c $(TOP)/src/tclsqlite.c tclsqlite3$(TEXE): tclsqlite-shell.lo libsqlite3.la $(LTLINK) -o $@ tclsqlite-shell.lo \ libsqlite3.la $(LIBTCL) testfixture$(TEXE): $(TESTFIXTURE_SRC) | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | < < > > > | > > | 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 | vdbetrace.lo: $(TOP)/src/vdbetrace.c $(HDR) $(LTCOMPILE) $(TEMP_STORE) -c $(TOP)/src/vdbetrace.c vtab.lo: $(TOP)/src/vtab.c $(HDR) $(LTCOMPILE) $(TEMP_STORE) -c $(TOP)/src/vtab.c wal.lo: $(TOP)/src/wal.c $(HDR) $(LTCOMPILE) $(TEMP_STORE) -c $(TOP)/src/wal.c walker.lo: $(TOP)/src/walker.c $(HDR) $(LTCOMPILE) $(TEMP_STORE) -c $(TOP)/src/walker.c where.lo: $(TOP)/src/where.c $(HDR) $(LTCOMPILE) $(TEMP_STORE) -c $(TOP)/src/where.c tclsqlite.lo: $(TOP)/src/tclsqlite.c $(HDR) $(LTCOMPILE) -DUSE_TCL_STUBS=1 -c $(TOP)/src/tclsqlite.c tclsqlite-shell.lo: $(TOP)/src/tclsqlite.c $(HDR) $(LTCOMPILE) -DTCLSH=1 -o $@ -c $(TOP)/src/tclsqlite.c tclsqlite-stubs.lo: $(TOP)/src/tclsqlite.c $(HDR) $(LTCOMPILE) -DTCL_USE_STUBS=1 -o $@ -c $(TOP)/src/tclsqlite.c tclsqlite3$(TEXE): tclsqlite-shell.lo libsqlite3.la $(LTLINK) -o $@ tclsqlite-shell.lo \ libsqlite3.la $(LIBTCL) # Rules to build opcodes.c and opcodes.h # opcodes.c: opcodes.h $(TOP)/mkopcodec.awk sort -n -b -k 3 opcodes.h | $(NAWK) -f $(TOP)/mkopcodec.awk >opcodes.c opcodes.h: parse.h $(TOP)/src/vdbe.c $(TOP)/mkopcodeh.awk cat parse.h $(TOP)/src/vdbe.c | $(NAWK) -f $(TOP)/mkopcodeh.awk >opcodes.h # Rules to build parse.c and parse.h - the outputs of lemon. # parse.h: parse.c parse.c: $(TOP)/src/parse.y lemon$(BEXE) $(TOP)/addopcodes.awk cp $(TOP)/src/parse.y . rm -f parse.h ./lemon$(BEXE) $(OPT_FEATURE_FLAGS) $(OPTS) parse.y mv parse.h parse.h.temp $(NAWK) -f $(TOP)/addopcodes.awk parse.h.temp >parse.h sqlite3.h: $(TOP)/src/sqlite.h.in $(TOP)/manifest.uuid $(TOP)/VERSION tclsh $(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 # Rules to build the extension objects. # icu.lo: $(TOP)/ext/icu/icu.c $(HDR) $(EXTHDR) $(LTCOMPILE) -DSQLITE_CORE -c $(TOP)/ext/icu/icu.c fts2.lo: $(TOP)/ext/fts2/fts2.c $(HDR) $(EXTHDR) $(LTCOMPILE) -DSQLITE_CORE -c $(TOP)/ext/fts2/fts2.c fts2_hash.lo: $(TOP)/ext/fts2/fts2_hash.c $(HDR) $(EXTHDR) $(LTCOMPILE) -DSQLITE_CORE -c $(TOP)/ext/fts2/fts2_hash.c fts2_icu.lo: $(TOP)/ext/fts2/fts2_icu.c $(HDR) $(EXTHDR) $(LTCOMPILE) -DSQLITE_CORE -c $(TOP)/ext/fts2/fts2_icu.c fts2_porter.lo: $(TOP)/ext/fts2/fts2_porter.c $(HDR) $(EXTHDR) $(LTCOMPILE) -DSQLITE_CORE -c $(TOP)/ext/fts2/fts2_porter.c fts2_tokenizer.lo: $(TOP)/ext/fts2/fts2_tokenizer.c $(HDR) $(EXTHDR) $(LTCOMPILE) -DSQLITE_CORE -c $(TOP)/ext/fts2/fts2_tokenizer.c fts2_tokenizer1.lo: $(TOP)/ext/fts2/fts2_tokenizer1.c $(HDR) $(EXTHDR) $(LTCOMPILE) -DSQLITE_CORE -c $(TOP)/ext/fts2/fts2_tokenizer1.c fts3.lo: $(TOP)/ext/fts3/fts3.c $(HDR) $(EXTHDR) $(LTCOMPILE) -DSQLITE_CORE -c $(TOP)/ext/fts3/fts3.c fts3_expr.lo: $(TOP)/ext/fts3/fts3_expr.c $(HDR) $(EXTHDR) $(LTCOMPILE) -DSQLITE_CORE -c $(TOP)/ext/fts3/fts3_expr.c fts3_hash.lo: $(TOP)/ext/fts3/fts3_hash.c $(HDR) $(EXTHDR) $(LTCOMPILE) -DSQLITE_CORE -c $(TOP)/ext/fts3/fts3_hash.c fts3_icu.lo: $(TOP)/ext/fts3/fts3_icu.c $(HDR) $(EXTHDR) $(LTCOMPILE) -DSQLITE_CORE -c $(TOP)/ext/fts3/fts3_icu.c fts3_snippet.lo: $(TOP)/ext/fts3/fts3_snippet.c $(HDR) $(EXTHDR) $(LTCOMPILE) -DSQLITE_CORE -c $(TOP)/ext/fts3/fts3_snippet.c fts3_porter.lo: $(TOP)/ext/fts3/fts3_porter.c $(HDR) $(EXTHDR) $(LTCOMPILE) -DSQLITE_CORE -c $(TOP)/ext/fts3/fts3_porter.c fts3_tokenizer.lo: $(TOP)/ext/fts3/fts3_tokenizer.c $(HDR) $(EXTHDR) $(LTCOMPILE) -DSQLITE_CORE -c $(TOP)/ext/fts3/fts3_tokenizer.c fts3_tokenizer1.lo: $(TOP)/ext/fts3/fts3_tokenizer1.c $(HDR) $(EXTHDR) $(LTCOMPILE) -DSQLITE_CORE -c $(TOP)/ext/fts3/fts3_tokenizer1.c fts3_write.lo: $(TOP)/ext/fts3/fts3_write.c $(HDR) $(EXTHDR) $(LTCOMPILE) -DSQLITE_CORE -c $(TOP)/ext/fts3/fts3_write.c rtree.lo: $(TOP)/ext/rtree/rtree.c $(HDR) $(EXTHDR) $(LTCOMPILE) -DSQLITE_CORE -c $(TOP)/ext/rtree/rtree.c # Rules to build the 'testfixture' application. # # If using the amalgamation, use sqlite3.c directly to build the test # fixture. Otherwise link against libsqlite3.la. (This distinction is # necessary because the test fixture requires non-API symbols which are # 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_SRC0 = $(TESTSRC2) libsqlite3.la TESTFIXTURE_SRC1 = sqlite3.c TESTFIXTURE_SRC = $(TESTSRC) $(TOP)/src/tclsqlite.c $(TESTFIXTURE_SRC$(USE_AMALGAMATION)) testfixture$(TEXE): $(TESTFIXTURE_SRC) $(LTLINK) -DSQLITE_NO_SYNC=1 $(TEMP_STORE) $(TESTFIXTURE_FLAGS) \ -o $@ $(TESTFIXTURE_SRC) $(LIBTCL) $(TLIBS) fulltest: testfixture$(TEXE) sqlite3$(TEXE) ./testfixture$(TEXE) $(TOP)/test/all.test soaktest: testfixture$(TEXE) sqlite3$(TEXE) ./testfixture$(TEXE) $(TOP)/test/all.test -soak=1 test: testfixture$(TEXE) sqlite3$(TEXE) ./testfixture$(TEXE) $(TOP)/test/veryquick.test sqlite3_analyzer$(TEXE): $(TESTFIXTURE_SRC) $(TOP)/tool/spaceanal.tcl sed \ -e '/^#/d' \ -e 's,\\,\\\\,g' \ -e 's,",\\",g' \ -e 's,^,",' \ -e 's,$$,\\n",' \ $(TOP)/tool/spaceanal.tcl >spaceanal_tcl.h $(LTLINK) -DTCLSH=2 -DSQLITE_TEST=1 -DSQLITE_CRASH_TEST=1 \ -DSQLITE_SERVER=1 -DSQLITE_PRIVATE="" -DSQLITE_CORE \ $(TEMP_STORE) -o $@ $(TESTFIXTURE_SRC) $(LIBTCL) $(TLIBS) # Standard install and cleanup targets # lib_install: libsqlite3.la $(INSTALL) -d $(DESTDIR)$(libdir) $(LTINSTALL) libsqlite3.la $(DESTDIR)$(libdir) install: sqlite3$(BEXE) lib_install sqlite3.h sqlite3.pc ${HAVE_TCL:1=tcl_install} $(INSTALL) -d $(DESTDIR)$(bindir) $(LTINSTALL) sqlite3$(BEXE) $(DESTDIR)$(bindir) |
︙ | ︙ | |||
809 810 811 812 813 814 815 | $(LTINSTALL) libtclsqlite3.la $(DESTDIR)$(TCLLIBDIR) rm -f $(DESTDIR)$(TCLLIBDIR)/libtclsqlite3.la $(DESTDIR)$(TCLLIBDIR)/libtclsqlite3.a $(INSTALL) -m 0644 pkgIndex.tcl $(DESTDIR)$(TCLLIBDIR) clean: rm -f *.lo *.la *.o sqlite3$(TEXE) libsqlite3.la rm -f sqlite3.h opcodes.* | | > < | | 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 | $(LTINSTALL) libtclsqlite3.la $(DESTDIR)$(TCLLIBDIR) rm -f $(DESTDIR)$(TCLLIBDIR)/libtclsqlite3.la $(DESTDIR)$(TCLLIBDIR)/libtclsqlite3.a $(INSTALL) -m 0644 pkgIndex.tcl $(DESTDIR)$(TCLLIBDIR) clean: rm -f *.lo *.la *.o sqlite3$(TEXE) libsqlite3.la rm -f sqlite3.h opcodes.* rm -rf .libs .deps rm -f lemon$(BEXE) lempar.c parse.* sqlite*.tar.gz rm -f mkkeywordhash$(BEXE) keywordhash.h rm -f $(PUBLISH) rm -f *.da *.bb *.bbg gmon.out rm -rf tsrc .target_source rm -f testfixture$(TEXE) test.db rm -f sqlite3.dll sqlite3.lib sqlite3.def rm -f sqlite3.c distclean: clean rm -f config.log config.status libtool Makefile sqlite3.pc # # Windows section # |
︙ | ︙ |
Changes to Makefile.vxworks.
︙ | ︙ | |||
382 383 384 385 386 387 388 389 390 391 392 393 394 395 | $(TOP)/src/test_onefile.c \ $(TOP)/src/test_osinst.c \ $(TOP)/src/test_pcache.c \ $(TOP)/src/test_schema.c \ $(TOP)/src/test_server.c \ $(TOP)/src/test_tclvar.c \ $(TOP)/src/test_thread.c \ $(TOP)/src/test_wsd.c \ #TESTSRC += $(TOP)/ext/fts2/fts2_tokenizer.c #TESTSRC += $(TOP)/ext/fts3/fts3_tokenizer.c TESTSRC2 = \ $(TOP)/src/attach.c $(TOP)/src/backup.c $(TOP)/src/btree.c \ | > | 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 | $(TOP)/src/test_onefile.c \ $(TOP)/src/test_osinst.c \ $(TOP)/src/test_pcache.c \ $(TOP)/src/test_schema.c \ $(TOP)/src/test_server.c \ $(TOP)/src/test_tclvar.c \ $(TOP)/src/test_thread.c \ $(TOP)/src/test_vfs.c \ $(TOP)/src/test_wsd.c \ #TESTSRC += $(TOP)/ext/fts2/fts2_tokenizer.c #TESTSRC += $(TOP)/ext/fts3/fts3_tokenizer.c TESTSRC2 = \ $(TOP)/src/attach.c $(TOP)/src/backup.c $(TOP)/src/btree.c \ |
︙ | ︙ | |||
620 621 622 623 624 625 626 | $(TESTSRC) $(TOP)/src/tclsqlite.c sqlite3.c fts3amal.c \ -o testfixture$(EXE) $(LIBTCL) $(THREADLIB) fulltest: testfixture$(EXE) sqlite3$(EXE) ./testfixture$(EXE) $(TOP)/test/all.test soaktest: testfixture$(EXE) sqlite3$(EXE) | | | 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 | $(TESTSRC) $(TOP)/src/tclsqlite.c sqlite3.c fts3amal.c \ -o testfixture$(EXE) $(LIBTCL) $(THREADLIB) fulltest: testfixture$(EXE) sqlite3$(EXE) ./testfixture$(EXE) $(TOP)/test/all.test soaktest: testfixture$(EXE) sqlite3$(EXE) ./testfixture$(EXE) $(TOP)/test/all.test -soak=1 test: testfixture$(EXE) sqlite3$(EXE) ./testfixture$(EXE) $(TOP)/test/veryquick.test sqlite3_analyzer$(EXE): $(TOP)/src/tclsqlite.c sqlite3.c $(TESTSRC) \ $(TOP)/tool/spaceanal.tcl sed \ |
︙ | ︙ |
Changes to VERSION.
|
| | | 1 | 3.7.0.1 |
Added art/sqlite370.eps.
cannot compute difference between binary files
Added art/sqlite370.jpg.
cannot compute difference between binary files
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.62 for sqlite 3.7.0.1. # # Copyright (C) 1992, 1993, 1994, 1995, 1996, 1998, 1999, 2000, 2001, # 2002, 2003, 2004, 2005, 2006, 2007, 2008 Free Software Foundation, Inc. # This configure script is free software; the Free Software Foundation # gives unlimited permission to copy, distribute and modify it. ## --------------------- ## ## M4sh Initialization. ## |
︙ | ︙ | |||
739 740 741 742 743 744 745 | MFLAGS= MAKEFLAGS= SHELL=${CONFIG_SHELL-/bin/sh} # Identity of this package. PACKAGE_NAME='sqlite' PACKAGE_TARNAME='sqlite' | | | | 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 | MFLAGS= MAKEFLAGS= SHELL=${CONFIG_SHELL-/bin/sh} # Identity of this package. PACKAGE_NAME='sqlite' PACKAGE_TARNAME='sqlite' PACKAGE_VERSION='3.7.0.1' PACKAGE_STRING='sqlite 3.7.0.1' PACKAGE_BUGREPORT='' # Factoring default headers for most tests. ac_includes_default="\ #include <stdio.h> #ifdef HAVE_SYS_TYPES_H # include <sys/types.h> |
︙ | ︙ | |||
1483 1484 1485 1486 1487 1488 1489 | # # 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 | | | 1483 1484 1485 1486 1487 1488 1489 1490 1491 1492 1493 1494 1495 1496 1497 | # # 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.7.0.1 to adapt to many kinds of systems. Usage: $0 [OPTION]... [VAR=VALUE]... To assign environment variables (e.g., CC, CFLAGS...), specify them as VAR=VALUE. See below for descriptions of some of the useful variables. Defaults for the options are specified in brackets. |
︙ | ︙ | |||
1548 1549 1550 1551 1552 1553 1554 | --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 | | | 1548 1549 1550 1551 1552 1553 1554 1555 1556 1557 1558 1559 1560 1561 1562 | --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.7.0.1:";; 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] |
︙ | ︙ | |||
1666 1667 1668 1669 1670 1671 1672 | cd "$ac_pwd" || { ac_status=$?; break; } done fi test -n "$ac_init_help" && exit $ac_status if $ac_init_version; then cat <<\_ACEOF | | | | 1666 1667 1668 1669 1670 1671 1672 1673 1674 1675 1676 1677 1678 1679 1680 1681 1682 1683 1684 1685 1686 1687 1688 1689 1690 1691 1692 1693 1694 | 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.7.0.1 generated by GNU Autoconf 2.62 Copyright (C) 1992, 1993, 1994, 1995, 1996, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 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 fi 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.7.0.1, which was generated by GNU Autoconf 2.62. Invocation command line was $ $0 $@ _ACEOF exec 5>>config.log { |
︙ | ︙ | |||
13968 13969 13970 13971 13972 13973 13974 | exec 6>&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=" | | | 13968 13969 13970 13971 13972 13973 13974 13975 13976 13977 13978 13979 13980 13981 13982 | exec 6>&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.7.0.1, which was generated by GNU Autoconf 2.62. Invocation command line was CONFIG_FILES = $CONFIG_FILES CONFIG_HEADERS = $CONFIG_HEADERS CONFIG_LINKS = $CONFIG_LINKS CONFIG_COMMANDS = $CONFIG_COMMANDS $ $0 $@ |
︙ | ︙ | |||
14021 14022 14023 14024 14025 14026 14027 | $config_commands Report bugs to <bug-autoconf@gnu.org>." _ACEOF cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 ac_cs_version="\\ | | | 14021 14022 14023 14024 14025 14026 14027 14028 14029 14030 14031 14032 14033 14034 14035 | $config_commands Report bugs to <bug-autoconf@gnu.org>." _ACEOF cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 ac_cs_version="\\ sqlite config.status 3.7.0.1 configured by $0, generated by GNU Autoconf 2.62, with options \\"`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`\\" Copyright (C) 2008 Free Software Foundation, Inc. This config.status script is free software; the Free Software Foundation gives unlimited permission to copy, distribute and modify it." |
︙ | ︙ |
Added doc/pager-invariants.txt.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 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 | *** Throughout this document, a page is deemed to have been synced automatically as soon as it is written when PRAGMA synchronous=OFF. Otherwise, the page is not synced until the xSync method of the VFS is called successfully on the file containing the page. *** Definition: A page of the database file is said to be "overwriteable" if one or more of the following are true about the page: (a) The original content of the page as it was at the beginning of the transaction has been written into the rollback journal and synced. (b) The page was a freelist leaf page at the start of the transaction. (c) The page number is greater than the largest page that existed in the database file at the start of the transaction. (1) A page of the database file is never overwritten unless one of the following are true: (a) The page and all other pages on the same sector are overwriteable. (b) The atomic page write optimization is enabled, and the entire transaction other than the update of the transaction sequence number consists of a single page change. (2) The content of a page written into the rollback journal exactly matches both the content in the database when the rollback journal was written and the content in the database at the beginning of the current transaction. (3) Writes to the database file are an integer multiple of the page size in length and are aligned to a page boundary. (4) Reads from the database file are either aligned on a page boundary and an integer multiple of the page size in length or are taken from the first 100 bytes of the database file. (5) All writes to the database file are synced prior to the rollback journal being deleted, truncated, or zeroed. (6) If a master journal file is used, then all writes to the database file are synced prior to the master journal being deleted. *** Definition: Two databases (or the same database at two points it time) are said to be "logically equivalent" if they give the same answer to all queries. Note in particular the the content of freelist leaf pages can be changed arbitarily without effecting the logical equivalence of the database. (7) At any time, if any subset, including the empty set and the total set, of the unsynced changes to a rollback journal are removed and the journal is rolled back, the resulting database file will be logical equivalent to the database file at the beginning of the transaction. (8) When a transaction is rolled back, the xTruncate method of the VFS is called to restore the database file to the same size it was at the beginning of the transaction. (In some VFSes, the xTruncate method is a no-op, but that does not change the fact the SQLite will invoke it.) (9) Whenever the database file is modified, at least one bit in the range of bytes from 24 through 39 inclusive will be changed prior to releasing the EXCLUSIVE lock. (10) The pattern of bits in bytes 24 through 39 shall not repeat in less than one billion transactions. (11) A database file is well-formed at the beginning and at the conclusion of every transaction. (12) An EXCLUSIVE lock must be held on the database file before making any changes to the database file. (13) A SHARED lock must be held on the database file before reading any content out of the database file. |
Added doc/vfs-shm.txt.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 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 | The 5 states of an historical rollback lock as implemented by the xLock, xUnlock, and xCheckReservedLock methods of the sqlite3_io_methods objec are: UNLOCKED SHARED RESERVED PENDING EXCLUSIVE The wal-index file has a similar locking hierarchy implemented using the xShmLock method of the sqlite3_vfs object, but with 7 states. Each connection to a wal-index file must be in one of the following 7 states: UNLOCKED READ READ_FULL WRITE PENDING CHECKPOINT RECOVER These roughly correspond to the 5 states of a rollback lock except that SHARED is split out into 2 states: READ and READ_FULL and there is an extra RECOVER state used for wal-index reconstruction. The meanings of the various wal-index locking states is as follows: UNLOCKED - The wal-index is not in use. READ - Some prefix of the wal-index is being read. Additional wal-index information can be appended at any time. The newly appended content will be ignored by the holder of the READ lock. READ_FULL - The entire wal-index is being read. No new information can be added to the wal-index. The holder of a READ_FULL lock promises never to read pages from the database file that are available anywhere in the wal-index. WRITE - It is OK to append to the wal-index file and to adjust the header to indicate the new "last valid frame". PENDING - Waiting on all READ locks to clear so that a CHECKPOINT lock can be acquired. CHECKPOINT - It is OK to write any WAL data into the database file and zero the last valid frame field of the wal-index header. The wal-index file itself may not be changed other than to zero the last valid frame field in the header. RECOVER - Held during wal-index recovery. Used to prevent a race if multiple clients try to recover a wal-index at the same time. A particular lock manager implementation may coalesce one or more of the wal-index locking states, though with a reduction in concurrency. For example, an implemention might implement only exclusive locking, in which case all states would be equivalent to CHECKPOINT, meaning that only one reader or one writer or one checkpointer could be active at a time. Or, an implementation might combine READ and READ_FULL into a single state equivalent to READ, meaning that a writer could coexist with a reader, but no reader or writers could coexist with a checkpointer. The lock manager must obey the following rules: (1) A READ cannot coexist with CHECKPOINT. (2) A READ_FULL cannot coexist with WRITE. (3) None of WRITE, PENDING, CHECKPOINT, or RECOVER can coexist. The SQLite core will obey the next set of rules. These rules are assertions on the behavior of the SQLite core which might be verified during testing using an instrumented lock manager. (5) No part of the wal-index will be read without holding either some kind of SHM lock or an EXCLUSIVE lock on the original database. The original database is the file named in the 2nd parameter to the xShmOpen method. (6) A holder of a READ_FULL will never read any page of the database file that is contained anywhere in the wal-index. (7) No part of the wal-index other than the header will be written nor will the size of the wal-index grow without holding a WRITE or an EXCLUSIVE on the original database file. (8) The wal-index header will not be written without holding one of WRITE, CHECKPOINT, or RECOVER on the wal-index or an EXCLUSIVE on the original database files. (9) A CHECKPOINT or RECOVER must be held on the wal-index, or an EXCLUSIVE on the original database file, in order to reset the last valid frame counter in the header of the wal-index back to zero. (10) A WRITE can only increase the last valid frame pointer in the header. The SQLite core will only ever send requests for UNLOCK, READ, WRITE, CHECKPOINT, or RECOVER to the lock manager. The SQLite core will never request a READ_FULL or PENDING lock though the lock manager may deliver those locking states in response to READ and CHECKPOINT requests, respectively, if and only if the requested READ or CHECKPOINT cannot be delivered. The following are the allowed lock transitions: Original-State Request New-State -------------- ---------- ---------- (11a) UNLOCK READ READ (11b) UNLOCK READ READ_FULL (11c) UNLOCK CHECKPOINT PENDING (11d) UNLOCK CHECKPOINT CHECKPOINT (11e) READ UNLOCK UNLOCK (11f) READ WRITE WRITE (11g) READ RECOVER RECOVER (11h) READ_FULL UNLOCK UNLOCK (11i) READ_FULL WRITE WRITE (11j) READ_FULL RECOVER RECOVER (11k) WRITE READ READ (11l) PENDING UNLOCK UNLOCK (11m) PENDING CHECKPOINT CHECKPOINT (11n) CHECKPOINT UNLOCK UNLOCK (11o) RECOVER READ READ These 15 transitions are all that needs to be supported. The lock manager implementation can assert that fact. The other 27 possible transitions among the 7 locking states will never occur. |
Changes to ext/async/sqlite3async.c.
︙ | ︙ | |||
663 664 665 666 667 668 669 | sqlite3_file *pFile, void *zOut, int iAmt, sqlite3_int64 iOffset ){ AsyncFileData *p = ((AsyncFile *)pFile)->pData; int rc = SQLITE_OK; | | | 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 | sqlite3_file *pFile, void *zOut, int iAmt, sqlite3_int64 iOffset ){ AsyncFileData *p = ((AsyncFile *)pFile)->pData; int rc = SQLITE_OK; sqlite3_int64 filesize = 0; sqlite3_file *pBase = p->pBaseRead; sqlite3_int64 iAmt64 = (sqlite3_int64)iAmt; /* Grab the write queue mutex for the duration of the call */ async_mutex_enter(ASYNC_MUTEX_QUEUE); /* If an I/O error has previously occurred in this virtual file |
︙ | ︙ | |||
686 687 688 689 690 691 692 | sqlite3_int64 nRead; rc = pBase->pMethods->xFileSize(pBase, &filesize); if( rc!=SQLITE_OK ){ goto asyncread_out; } nRead = MIN(filesize - iOffset, iAmt64); if( nRead>0 ){ | | | 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 | sqlite3_int64 nRead; rc = pBase->pMethods->xFileSize(pBase, &filesize); if( rc!=SQLITE_OK ){ goto asyncread_out; } nRead = MIN(filesize - iOffset, iAmt64); if( nRead>0 ){ rc = pBase->pMethods->xRead(pBase, zOut, (int)nRead, iOffset); ASYNC_TRACE(("READ %s %d bytes at %d\n", p->zName, nRead, iOffset)); } } if( rc==SQLITE_OK ){ AsyncWrite *pWrite; char *zName = p->zName; |
︙ | ︙ | |||
713 714 715 716 717 718 719 720 721 | ** these offsets is a negative number, set them to 0. */ sqlite3_int64 iBeginOut = (pWrite->iOffset-iOffset); sqlite3_int64 iBeginIn = -iBeginOut; if( iBeginIn<0 ) iBeginIn = 0; if( iBeginOut<0 ) iBeginOut = 0; nCopy = MIN(nByte64-iBeginIn, iAmt64-iBeginOut); if( nCopy>0 ){ | > > | > > > | 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 | ** these offsets is a negative number, set them to 0. */ sqlite3_int64 iBeginOut = (pWrite->iOffset-iOffset); sqlite3_int64 iBeginIn = -iBeginOut; if( iBeginIn<0 ) iBeginIn = 0; if( iBeginOut<0 ) iBeginOut = 0; filesize = MAX(filesize, pWrite->iOffset+nByte64); nCopy = MIN(nByte64-iBeginIn, iAmt64-iBeginOut); if( nCopy>0 ){ memcpy(&((char *)zOut)[iBeginOut], &pWrite->zBuf[iBeginIn], (size_t)nCopy); ASYNC_TRACE(("OVERREAD %d bytes at %d\n", nCopy, iBeginOut+iOffset)); } } } } asyncread_out: async_mutex_leave(ASYNC_MUTEX_QUEUE); if( rc==SQLITE_OK && filesize<(iOffset+iAmt) ){ rc = SQLITE_IOERR_SHORT_READ; } return rc; } /* ** Truncate the file to nByte bytes in length. This just adds an entry to ** the write-op list, no IO actually takes place. */ |
︙ | ︙ | |||
1134 1135 1136 1137 1138 1139 1140 | } sqlite3_free(pData); } async_mutex_leave(ASYNC_MUTEX_LOCK); if( rc==SQLITE_OK ){ | < > > > | 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 | } sqlite3_free(pData); } async_mutex_leave(ASYNC_MUTEX_LOCK); if( rc==SQLITE_OK ){ pData->pLock = pLock; } if( rc==SQLITE_OK && isAsyncOpen ){ rc = addNewAsyncWrite(pData, ASYNC_OPENEXCLUSIVE, (sqlite3_int64)flags,0,0); if( rc==SQLITE_OK ){ if( pOutFlags ) *pOutFlags = flags; }else{ async_mutex_enter(ASYNC_MUTEX_LOCK); unlinkAsyncFile(pData); async_mutex_leave(ASYNC_MUTEX_LOCK); sqlite3_free(pData); } } if( rc!=SQLITE_OK ){ p->pMethod = 0; }else{ incrOpenFileCount(); } return rc; } /* ** Implementation of sqlite3OsDelete. Add an entry to the end of the ** write-op queue to perform the delete. */ |
︙ | ︙ | |||
1228 1229 1230 1231 1232 1233 1234 | /* Because of the way intra-process file locking works, this backend ** needs to return a canonical path. The following block assumes the ** file-system uses unix style paths. */ if( rc==SQLITE_OK ){ int i, j; char *z = zPathOut; | | | 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 | /* Because of the way intra-process file locking works, this backend ** needs to return a canonical path. The following block assumes the ** file-system uses unix style paths. */ if( rc==SQLITE_OK ){ int i, j; char *z = zPathOut; int n = (int)strlen(z); while( n>1 && z[n-1]=='/' ){ n--; } for(i=j=0; i<n; i++){ if( z[i]=='/' ){ if( z[i+1]=='/' ) continue; if( z[i+1]=='.' && i+2<n && z[i+2]=='/' ){ i += 1; continue; |
︙ | ︙ |
Changes to ext/fts3/fts3.c.
︙ | ︙ | |||
44 45 46 47 48 49 50 | ** 14 bits - BA ** 21 bits - BBA ** and so on. ** ** This is similar in concept to how sqlite encodes "varints" but ** the encoding is not the same. SQLite varints are big-endian ** are are limited to 9 bytes in length whereas FTS3 varints are | | | | | | | | | | | | | > > < < | | | 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 | ** 14 bits - BA ** 21 bits - BBA ** and so on. ** ** This is similar in concept to how sqlite encodes "varints" but ** the encoding is not the same. SQLite varints are big-endian ** are are limited to 9 bytes in length whereas FTS3 varints are ** little-endian and can be up to 10 bytes in length (in theory). ** ** Example encodings: ** ** 1: 0x01 ** 127: 0x7f ** 128: 0x81 0x00 ** ** **** Document lists **** ** A doclist (document list) holds a docid-sorted list of hits for a ** given term. Doclists hold docids and associated token positions. ** A docid is the unique integer identifier for a single document. ** A position is the index of a word within the document. The first ** word of the document has a position of 0. ** ** FTS3 used to optionally store character offsets using a compile-time ** option. But that functionality is no longer supported. ** ** A doclist is stored like this: ** ** array { ** varint docid; ** array { (position list for column 0) ** varint position; (2 more than the delta from previous position) ** } ** array { ** varint POS_COLUMN; (marks start of position list for new column) ** varint column; (index of new column) ** array { ** varint position; (2 more than the delta from previous position) ** } ** } ** varint POS_END; (marks end of positions for this document. ** } ** ** Here, array { X } means zero or more occurrences of X, adjacent in ** memory. A "position" is an index of a token in the token stream ** generated by the tokenizer. Note that POS_END and POS_COLUMN occur ** in the same logical place as the position element, and act as sentinals ** ending a position list array. POS_END is 0. POS_COLUMN is 1. ** The positions numbers are not stored literally but rather as two more ** than the difference from the prior position, or the just the position plus ** 2 for the first position. Example: ** ** label: A B C D E F G H I J K ** value: 123 5 9 1 1 14 35 0 234 72 0 ** ** The 123 value is the first docid. For column zero in this document ** there are two matches at positions 3 and 10 (5-2 and 9-2+3). The 1 ** at D signals the start of a new column; the 1 at E indicates that the ** new column is column number 1. There are two positions at 12 and 45 ** (14-2 and 35-2+12). The 0 at H indicate the end-of-document. The ** 234 at I is the next docid. It has one position 72 (72-2) and then ** terminates with the 0 at K. ** ** A "position-list" is the list of positions for multiple columns for ** a single docid. A "column-list" is the set of positions for a single ** column. Hence, a position-list consists of one or more column-lists, ** a document record consists of a docid followed by a position-list and ** a doclist consists of one or more document records. ** ** A bare doclist omits the position information, becoming an ** array of varint-encoded docids. ** **** Segment leaf nodes **** ** Segment leaf nodes store terms and doclists, ordered by term. Leaf ** nodes are written using LeafWriter, and read using LeafReader (to ** iterate through a single leaf node's data) and LeavesReader (to ** iterate through a segment's entire leaf layer). Leaf nodes have ** the format: |
︙ | ︙ | |||
355 356 357 358 359 360 361 | sqlite_int64 i; int ret = sqlite3Fts3GetVarint(p, &i); *pi = (int) i; return ret; } /* | | < | 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 | sqlite_int64 i; int ret = sqlite3Fts3GetVarint(p, &i); *pi = (int) i; return ret; } /* ** Return the number of bytes required to encode v as a varint */ int sqlite3Fts3VarintLen(sqlite3_uint64 v){ int i = 0; do{ i++; v >>= 7; }while( v!=0 ); |
︙ | ︙ | |||
407 408 409 410 411 412 413 | } z[iOut] = '\0'; } } /* ** Read a single varint from the doclist at *pp and advance *pp to point | | | 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 | } z[iOut] = '\0'; } } /* ** Read a single varint from the doclist at *pp and advance *pp to point ** to the first byte past the end of the varint. Add the value of the varint ** to *pVal. */ static void fts3GetDeltaVarint(char **pp, sqlite3_int64 *pVal){ sqlite3_int64 iVal; *pp += sqlite3Fts3GetVarint(*pp, &iVal); *pVal += iVal; } |
︙ | ︙ | |||
463 464 465 466 467 468 469 | /* ** Construct one or more SQL statements from the format string given ** and then evaluate those statements. The success code is writting ** into *pRc. ** ** If *pRc is initially non-zero then this routine is a no-op. */ | | | 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 | /* ** Construct one or more SQL statements from the format string given ** and then evaluate those statements. The success code is writting ** into *pRc. ** ** If *pRc is initially non-zero then this routine is a no-op. */ static void fts3DbExec( int *pRc, /* Success code */ sqlite3 *db, /* Database in which to run SQL */ const char *zFormat, /* Format string for SQL */ ... /* Arguments to the format string */ ){ va_list ap; char *zSql; |
︙ | ︙ | |||
543 544 545 546 547 548 549 550 551 552 553 554 555 556 | return rc; } /* ** Create the backing store tables (%_content, %_segments and %_segdir) ** required by the FTS3 table passed as the only argument. This is done ** as part of the vtab xCreate() method. */ static int fts3CreateTables(Fts3Table *p){ int rc = SQLITE_OK; /* Return code */ int i; /* Iterator variable */ char *zContentCols; /* Columns of %_content table */ sqlite3 *db = p->db; /* The database connection */ | > > > > | 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 | return rc; } /* ** Create the backing store tables (%_content, %_segments and %_segdir) ** required by the FTS3 table passed as the only argument. This is done ** as part of the vtab xCreate() method. ** ** If the p->bHasDocsize boolean is true (indicating that this is an ** FTS4 table, not an FTS3 table) then also create the %_docsize and ** %_stat tables required by FTS4. */ static int fts3CreateTables(Fts3Table *p){ int rc = SQLITE_OK; /* Return code */ int i; /* Iterator variable */ char *zContentCols; /* Columns of %_content table */ sqlite3 *db = p->db; /* The database connection */ |
︙ | ︙ | |||
600 601 602 603 604 605 606 607 608 609 610 611 612 613 | return rc; } /* ** An sqlite3_exec() callback for fts3TableExists. */ static int fts3TableExistsCallback(void *pArg, int n, char **pp1, char **pp2){ *(int*)pArg = 1; return 1; } /* ** Determine if a table currently exists in the database. */ | > > > | 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 | return rc; } /* ** An sqlite3_exec() callback for fts3TableExists. */ static int fts3TableExistsCallback(void *pArg, int n, char **pp1, char **pp2){ UNUSED_PARAMETER(n); UNUSED_PARAMETER(pp1); UNUSED_PARAMETER(pp2); *(int*)pArg = 1; return 1; } /* ** Determine if a table currently exists in the database. */ |
︙ | ︙ | |||
625 626 627 628 629 630 631 | if( *pRc ) return; zSql = sqlite3_mprintf( "SELECT 1 FROM %Q.sqlite_master WHERE name='%q%s'", zDb, zName, zSuffix ); rc = sqlite3_exec(db, zSql, fts3TableExistsCallback, &res, 0); sqlite3_free(zSql); | | | | | | | | | | 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 | if( *pRc ) return; zSql = sqlite3_mprintf( "SELECT 1 FROM %Q.sqlite_master WHERE name='%q%s'", zDb, zName, zSuffix ); rc = sqlite3_exec(db, zSql, fts3TableExistsCallback, &res, 0); sqlite3_free(zSql); *pResult = (u8)(res & 0xff); if( rc!=SQLITE_ABORT ) *pRc = rc; } /* ** This function is the implementation of both the xConnect and xCreate ** methods of the FTS3 virtual table. ** ** The argv[] array contains the following: ** ** argv[0] -> module name ("fts3" or "fts4") ** argv[1] -> database name ** argv[2] -> table name ** argv[...] -> "column name" and other module argument fields. */ static int fts3InitVtab( int isCreate, /* True for xCreate, false for xConnect */ sqlite3 *db, /* The SQLite database connection */ void *pAux, /* Hash table containing tokenizers */ 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 */ ){ Fts3Hash *pHash = (Fts3Hash *)pAux; Fts3Table *p; /* Pointer to allocated vtab */ int rc; /* Return code */ int i; /* Iterator variable */ int nByte; /* Size of allocation used for *p */ int iCol; /* Column index */ int nString = 0; /* Bytes required to hold all column names */ int nCol = 0; /* Number of columns in the FTS table */ char *zCsr; /* Space for holding column names */ int nDb; /* Bytes required to hold database name */ int nName; /* Bytes required to hold table name */ const char *zTokenizer = 0; /* Name of tokenizer to use */ sqlite3_tokenizer *pTokenizer = 0; /* Tokenizer for this table */ nDb = (int)strlen(argv[1]) + 1; nName = (int)strlen(argv[2]) + 1; for(i=3; i<argc; i++){ |
︙ | ︙ | |||
889 890 891 892 893 894 895 896 897 898 899 900 901 902 | sqlite3Fts3ExprFree(pCsr->pExpr); sqlite3_free(pCsr->aDoclist); sqlite3_free(pCsr->aMatchinfo); sqlite3_free(pCsr); return SQLITE_OK; } static int fts3CursorSeek(sqlite3_context *pContext, Fts3Cursor *pCsr){ if( pCsr->isRequireSeek ){ pCsr->isRequireSeek = 0; sqlite3_bind_int64(pCsr->pStmt, 1, pCsr->iPrevId); if( SQLITE_ROW==sqlite3_step(pCsr->pStmt) ){ return SQLITE_OK; }else{ | > > > > > | 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 | sqlite3Fts3ExprFree(pCsr->pExpr); sqlite3_free(pCsr->aDoclist); sqlite3_free(pCsr->aMatchinfo); sqlite3_free(pCsr); return SQLITE_OK; } /* ** 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){ if( pCsr->isRequireSeek ){ pCsr->isRequireSeek = 0; sqlite3_bind_int64(pCsr->pStmt, 1, pCsr->iPrevId); if( SQLITE_ROW==sqlite3_step(pCsr->pStmt) ){ return SQLITE_OK; }else{ |
︙ | ︙ | |||
915 916 917 918 919 920 921 922 923 924 925 926 927 928 | return rc; } }else{ return SQLITE_OK; } } static int fts3NextMethod(sqlite3_vtab_cursor *pCursor){ int rc = SQLITE_OK; /* Return code */ Fts3Cursor *pCsr = (Fts3Cursor *)pCursor; if( pCsr->aDoclist==0 ){ if( SQLITE_ROW!=sqlite3_step(pCsr->pStmt) ){ pCsr->isEof = 1; | > > > > > > > > > > > | 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 | return rc; } }else{ return SQLITE_OK; } } /* ** Advance the cursor to the next row in the %_content table that ** matches the search criteria. For a MATCH search, this will be ** the next row that matches. For a full-table scan, this will be ** simply the next row in the %_content table. For a docid lookup, ** this routine simply sets the EOF flag. ** ** Return SQLITE_OK if nothing goes wrong. SQLITE_OK is returned ** even if we reach end-of-file. The fts3EofMethod() will be called ** subsequently to determine whether or not an EOF was hit. */ static int fts3NextMethod(sqlite3_vtab_cursor *pCursor){ int rc = SQLITE_OK; /* Return code */ Fts3Cursor *pCsr = (Fts3Cursor *)pCursor; if( pCsr->aDoclist==0 ){ if( SQLITE_ROW!=sqlite3_step(pCsr->pStmt) ){ pCsr->isEof = 1; |
︙ | ︙ | |||
1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 | *piPrev = iVal; } /* ** When this function is called, *ppPoslist is assumed to point to the ** start of a position-list. After it returns, *ppPoslist points to the ** first byte after the position-list. ** ** If pp is not NULL, then the contents of the position list are copied ** to *pp. *pp is set to point to the first byte past the last byte copied ** before this function returns. */ static void fts3PoslistCopy(char **pp, char **ppPoslist){ char *pEnd = *ppPoslist; char c = 0; /* The end of a position list is marked by a zero encoded as an FTS3 | > > > > > | | | | > > > > > > > > > > > > > > > > > > > > | > > | > > > | | | | | | > | | > > > > > > | | | | | | > | > > > > | | | | | | | | | > | | | | | | | | 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 | *piPrev = iVal; } /* ** When this function is called, *ppPoslist is assumed to point to the ** start of a position-list. After it returns, *ppPoslist points to the ** first byte after the position-list. ** ** A position list is list of positions (delta encoded) and columns for ** a single document record of a doclist. So, in other words, this ** routine advances *ppPoslist so that it points to the next docid in ** the doclist, or to the first byte past the end of the doclist. ** ** If pp is not NULL, then the contents of the position list are copied ** to *pp. *pp is set to point to the first byte past the last byte copied ** before this function returns. */ static void fts3PoslistCopy(char **pp, char **ppPoslist){ char *pEnd = *ppPoslist; char c = 0; /* The end of a position list is marked by a zero encoded as an FTS3 ** varint. A single POS_END (0) byte. Except, if the 0 byte is preceded by ** a byte with the 0x80 bit set, then it is not a varint 0, but the tail ** of some other, multi-byte, value. ** ** The following while-loop moves pEnd to point to the first byte that is not ** immediately preceded by a byte with the 0x80 bit set. Then increments ** pEnd once more so that it points to the byte immediately following the ** last byte in the position-list. */ while( *pEnd | c ){ c = *pEnd++ & 0x80; testcase( c!=0 && (*pEnd)==0 ); } pEnd++; /* Advance past the POS_END terminator byte */ if( pp ){ int n = (int)(pEnd - *ppPoslist); char *p = *pp; memcpy(p, *ppPoslist, n); p += n; *pp = p; } *ppPoslist = pEnd; } /* ** When this function is called, *ppPoslist is assumed to point to the ** start of a column-list. After it returns, *ppPoslist points to the ** to the terminator (POS_COLUMN or POS_END) byte of the column-list. ** ** A column-list is list of delta-encoded positions for a single column ** within a single document within a doclist. ** ** The column-list is terminated either by a POS_COLUMN varint (1) or ** a POS_END varint (0). This routine leaves *ppPoslist pointing to ** the POS_COLUMN or POS_END that terminates the column-list. ** ** If pp is not NULL, then the contents of the column-list are copied ** to *pp. *pp is set to point to the first byte past the last byte copied ** before this function returns. The POS_COLUMN or POS_END terminator ** is not copied into *pp. */ static void fts3ColumnlistCopy(char **pp, char **ppPoslist){ char *pEnd = *ppPoslist; char c = 0; /* A column-list is terminated by either a 0x01 or 0x00 byte that is ** not part of a multi-byte varint. */ while( 0xFE & (*pEnd | c) ){ c = *pEnd++ & 0x80; testcase( c!=0 && ((*pEnd)&0xfe)==0 ); } if( pp ){ int n = (int)(pEnd - *ppPoslist); char *p = *pp; memcpy(p, *ppPoslist, n); p += n; *pp = p; } *ppPoslist = pEnd; } /* ** Value used to signify the end of an position-list. This is safe because ** it is not possible to have a document with 2^31 terms. */ #define POSITION_LIST_END 0x7fffffff /* ** This function is used to help parse position-lists. When this function is ** called, *pp may point to the start of the next varint in the position-list ** being parsed, or it may point to 1 byte past the end of the position-list ** (in which case **pp will be a terminator bytes POS_END (0) or ** (1)). ** ** If *pp points past the end of the current position-list, set *pi to ** POSITION_LIST_END and return. Otherwise, read the next varint from *pp, ** increment the current value of *pi by the value read, and set *pp to ** point to the next value before returning. ** ** Before calling this routine *pi must be initialized to the value of ** the previous position, or zero if we are reading the first position ** in the position-list. Because positions are delta-encoded, the value ** of the previous position is needed in order to compute the value of ** the next position. */ static void fts3ReadNextPos( char **pp, /* IN/OUT: Pointer into position-list buffer */ sqlite3_int64 *pi /* IN/OUT: Value read from position-list */ ){ if( (**pp)&0xFE ){ fts3GetDeltaVarint(pp, pi); *pi -= 2; }else{ *pi = POSITION_LIST_END; } } /* ** If parameter iCol is not 0, write an POS_COLUMN (1) byte followed by ** the value of iCol encoded as a varint to *pp. This will start a new ** column list. ** ** Set *pp to point to the byte just after the last byte written before ** returning (do not modify it if iCol==0). Return the total number of bytes ** written (0 if iCol==0). */ static int fts3PutColNumber(char **pp, int iCol){ int n = 0; /* Number of bytes written */ if( iCol ){ char *p = *pp; /* Output pointer */ n = 1 + sqlite3Fts3PutVarint(&p[1], iCol); *p = 0x01; *pp = &p[n]; } return n; } /* ** Compute the union of two position lists. The output written ** into *pp contains all positions of both *pp1 and *pp2 in sorted ** order and with any duplicates removed. All pointers are ** updated appropriately. The caller is responsible for insuring ** that there is enough space in *pp to hold the complete output. */ static void fts3PoslistMerge( char **pp, /* Output buffer */ char **pp1, /* Left input list */ char **pp2 /* Right input list */ ){ char *p = *pp; char *p1 = *pp1; char *p2 = *pp2; while( *p1 || *p2 ){ int iCol1; /* The current column index in pp1 */ int iCol2; /* The current column index in pp2 */ if( *p1==POS_COLUMN ) sqlite3Fts3GetVarint32(&p1[1], &iCol1); else if( *p1==POS_END ) iCol1 = POSITION_LIST_END; else iCol1 = 0; if( *p2==POS_COLUMN ) sqlite3Fts3GetVarint32(&p2[1], &iCol2); else if( *p2==POS_END ) iCol2 = POSITION_LIST_END; else iCol2 = 0; if( iCol1==iCol2 ){ sqlite3_int64 i1 = 0; /* Last position from pp1 */ sqlite3_int64 i2 = 0; /* Last position from pp2 */ sqlite3_int64 iPrev = 0; int n = fts3PutColNumber(&p, iCol1); p1 += n; p2 += n; /* At this point, both p1 and p2 point to the start of column-lists ** for the same column (the column with index iCol1 and iCol2). ** A column-list is a list of non-negative delta-encoded varints, each ** incremented by 2 before being stored. Each list is terminated by a ** POS_END (0) or POS_COLUMN (1). The following block merges the two lists ** and writes the results to buffer p. p is left pointing to the byte ** after the list written. No terminator (POS_END or POS_COLUMN) is ** written to the output. */ fts3GetDeltaVarint(&p1, &i1); fts3GetDeltaVarint(&p2, &i2); do { fts3PutDeltaVarint(&p, &iPrev, (i1<i2) ? i1 : i2); iPrev -= 2; if( i1==i2 ){ fts3ReadNextPos(&p1, &i1); fts3ReadNextPos(&p2, &i2); }else if( i1<i2 ){ fts3ReadNextPos(&p1, &i1); }else{ fts3ReadNextPos(&p2, &i2); } }while( i1!=POSITION_LIST_END || i2!=POSITION_LIST_END ); }else if( iCol1<iCol2 ){ p1 += fts3PutColNumber(&p, iCol1); fts3ColumnlistCopy(&p, &p1); }else{ p2 += fts3PutColNumber(&p, iCol2); fts3ColumnlistCopy(&p, &p2); } } *p++ = POS_END; *pp = p; *pp1 = p1 + 1; *pp2 = p2 + 1; } /* ** nToken==1 searches for adjacent positions. |
︙ | ︙ | |||
1232 1233 1234 1235 1236 1237 1238 | char *p = (pp ? *pp : 0); char *p1 = *pp1; char *p2 = *pp2; int iCol1 = 0; int iCol2 = 0; assert( *p1!=0 && *p2!=0 ); | | | | | > | 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 | char *p = (pp ? *pp : 0); char *p1 = *pp1; char *p2 = *pp2; int iCol1 = 0; int iCol2 = 0; assert( *p1!=0 && *p2!=0 ); if( *p1==POS_COLUMN ){ p1++; p1 += sqlite3Fts3GetVarint32(p1, &iCol1); } if( *p2==POS_COLUMN ){ p2++; p2 += sqlite3Fts3GetVarint32(p2, &iCol2); } while( 1 ){ if( iCol1==iCol2 ){ char *pSave = p; sqlite3_int64 iPrev = 0; sqlite3_int64 iPos1 = 0; sqlite3_int64 iPos2 = 0; if( pp && iCol1 ){ *p++ = POS_COLUMN; p += sqlite3Fts3PutVarint(p, iCol1); } assert( *p1!=POS_END && *p1!=POS_COLUMN ); assert( *p2!=POS_END && *p2!=POS_COLUMN ); fts3GetDeltaVarint(&p1, &iPos1); iPos1 -= 2; fts3GetDeltaVarint(&p2, &iPos2); iPos2 -= 2; while( 1 ){ if( iPos2>iPos1 && iPos2<=iPos1+nToken ){ sqlite3_int64 iSave; if( !pp ){ |
︙ | ︙ | |||
1505 1506 1507 1508 1509 1510 1511 1512 1513 1514 1515 1516 1517 1518 | } break; } default: assert( mergetype==MERGE_POS_NEAR || mergetype==MERGE_NEAR ); { char *aTmp = 0; char **ppPos = 0; if( mergetype==MERGE_POS_NEAR ){ ppPos = &p; aTmp = sqlite3_malloc(2*(n1+n2+1)); if( !aTmp ){ return SQLITE_NOMEM; } } | > | 1571 1572 1573 1574 1575 1576 1577 1578 1579 1580 1581 1582 1583 1584 1585 | } break; } default: assert( mergetype==MERGE_POS_NEAR || mergetype==MERGE_NEAR ); { char *aTmp = 0; char **ppPos = 0; if( mergetype==MERGE_POS_NEAR ){ ppPos = &p; aTmp = sqlite3_malloc(2*(n1+n2+1)); if( !aTmp ){ return SQLITE_NOMEM; } } |
︙ | ︙ | |||
1550 1551 1552 1553 1554 1555 1556 | /* ** A pointer to an instance of this structure is used as the context ** argument to sqlite3Fts3SegReaderIterate() */ typedef struct TermSelect TermSelect; struct TermSelect { int isReqPos; | | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > < < < < < < | > > > | | < < | < > > > > > > > > > > > > > > > > > > > > > > > > | | | | | | > > | | > > > | > > | | | | 1617 1618 1619 1620 1621 1622 1623 1624 1625 1626 1627 1628 1629 1630 1631 1632 1633 1634 1635 1636 1637 1638 1639 1640 1641 1642 1643 1644 1645 1646 1647 1648 1649 1650 1651 1652 1653 1654 1655 1656 1657 1658 1659 1660 1661 1662 1663 1664 1665 1666 1667 1668 1669 1670 1671 1672 1673 1674 1675 1676 1677 1678 1679 1680 1681 1682 1683 1684 1685 1686 1687 1688 1689 1690 1691 1692 1693 1694 1695 1696 1697 1698 1699 1700 1701 1702 1703 1704 1705 1706 1707 1708 1709 1710 1711 1712 1713 1714 1715 1716 1717 1718 1719 1720 1721 1722 1723 1724 1725 1726 1727 1728 1729 1730 1731 1732 1733 1734 1735 1736 1737 1738 1739 1740 1741 1742 1743 1744 1745 1746 1747 1748 1749 1750 1751 1752 1753 1754 1755 1756 1757 1758 1759 1760 1761 1762 1763 1764 1765 | /* ** A pointer to an instance of this structure is used as the context ** argument to sqlite3Fts3SegReaderIterate() */ typedef struct TermSelect TermSelect; struct TermSelect { int isReqPos; char *aaOutput[16]; /* Malloc'd output buffer */ int anOutput[16]; /* Size of output in bytes */ }; /* ** Merge all doclists in the TermSelect.aaOutput[] array into a single ** doclist stored in TermSelect.aaOutput[0]. If successful, delete all ** other doclists (except the aaOutput[0] one) and return SQLITE_OK. ** ** If an OOM error occurs, return SQLITE_NOMEM. In this case it is ** the responsibility of the caller to free any doclists left in the ** TermSelect.aaOutput[] array. */ static int fts3TermSelectMerge(TermSelect *pTS){ int mergetype = (pTS->isReqPos ? MERGE_POS_OR : MERGE_OR); char *aOut = 0; int nOut = 0; int i; /* Loop through the doclists in the aaOutput[] array. Merge them all ** into a single doclist. */ for(i=0; i<SizeofArray(pTS->aaOutput); i++){ if( pTS->aaOutput[i] ){ if( !aOut ){ aOut = pTS->aaOutput[i]; nOut = pTS->anOutput[i]; pTS->aaOutput[0] = 0; }else{ int nNew = nOut + pTS->anOutput[i]; char *aNew = sqlite3_malloc(nNew); if( !aNew ){ sqlite3_free(aOut); return SQLITE_NOMEM; } fts3DoclistMerge(mergetype, 0, 0, aNew, &nNew, pTS->aaOutput[i], pTS->anOutput[i], aOut, nOut ); sqlite3_free(pTS->aaOutput[i]); sqlite3_free(aOut); pTS->aaOutput[i] = 0; aOut = aNew; nOut = nNew; } } } pTS->aaOutput[0] = aOut; pTS->anOutput[0] = nOut; return SQLITE_OK; } /* ** This function is used as the sqlite3Fts3SegReaderIterate() callback when ** querying the full-text index for a doclist associated with a term or ** term-prefix. */ static int fts3TermSelectCb( Fts3Table *p, /* Virtual table object */ void *pContext, /* Pointer to TermSelect structure */ char *zTerm, int nTerm, char *aDoclist, int nDoclist ){ TermSelect *pTS = (TermSelect *)pContext; UNUSED_PARAMETER(p); UNUSED_PARAMETER(zTerm); UNUSED_PARAMETER(nTerm); if( pTS->aaOutput[0]==0 ){ /* If this is the first term selected, copy the doclist to the output ** buffer using memcpy(). TODO: Add a way to transfer control of the ** aDoclist buffer from the caller so as to avoid the memcpy(). */ pTS->aaOutput[0] = sqlite3_malloc(nDoclist); pTS->anOutput[0] = nDoclist; if( pTS->aaOutput[0] ){ memcpy(pTS->aaOutput[0], aDoclist, nDoclist); }else{ return SQLITE_NOMEM; } }else{ int mergetype = (pTS->isReqPos ? MERGE_POS_OR : MERGE_OR); char *aMerge = aDoclist; int nMerge = nDoclist; int iOut; for(iOut=0; iOut<SizeofArray(pTS->aaOutput); iOut++){ char *aNew; int nNew; if( pTS->aaOutput[iOut]==0 ){ assert( iOut>0 ); pTS->aaOutput[iOut] = aMerge; pTS->anOutput[iOut] = nMerge; break; } nNew = nMerge + pTS->anOutput[iOut]; aNew = sqlite3_malloc(nNew); if( !aNew ){ if( aMerge!=aDoclist ){ sqlite3_free(aMerge); } return SQLITE_NOMEM; } fts3DoclistMerge(mergetype, 0, 0, aNew, &nNew, pTS->aaOutput[iOut], pTS->anOutput[iOut], aMerge, nMerge ); if( iOut>0 ) sqlite3_free(aMerge); sqlite3_free(pTS->aaOutput[iOut]); pTS->aaOutput[iOut] = 0; aMerge = aNew; nMerge = nNew; if( (iOut+1)==SizeofArray(pTS->aaOutput) ){ pTS->aaOutput[iOut] = aMerge; pTS->anOutput[iOut] = nMerge; } } } return SQLITE_OK; } /* ** This function retreives the doclist for the specified term (or term ** prefix) from the database. ** ** The returned doclist may be in one of two formats, depending on the ** value of parameter isReqPos. If isReqPos is zero, then the doclist is ** a sorted list of delta-compressed docids (a bare doclist). If isReqPos ** is non-zero, then the returned list is in the same format as is stored ** in the database without the found length specifier at the start of on-disk ** doclists. */ static int fts3TermSelect( Fts3Table *p, /* Virtual table handle */ int iColumn, /* Column to query (or -ve for all columns) */ const char *zTerm, /* Term to query for */ int nTerm, /* Size of zTerm in bytes */ |
︙ | ︙ | |||
1723 1724 1725 1726 1727 1728 1729 | filter.iCol = iColumn; filter.zTerm = zTerm; filter.nTerm = nTerm; rc = sqlite3Fts3SegReaderIterate(p, apSegment, nSegment, &filter, fts3TermSelectCb, (void *)&tsc ); | > > | > | | > | > | 1863 1864 1865 1866 1867 1868 1869 1870 1871 1872 1873 1874 1875 1876 1877 1878 1879 1880 1881 1882 1883 1884 1885 1886 1887 | filter.iCol = iColumn; filter.zTerm = zTerm; filter.nTerm = nTerm; rc = sqlite3Fts3SegReaderIterate(p, apSegment, nSegment, &filter, fts3TermSelectCb, (void *)&tsc ); if( rc==SQLITE_OK ){ rc = fts3TermSelectMerge(&tsc); } if( rc==SQLITE_OK ){ *ppOut = tsc.aaOutput[0]; *pnOut = tsc.anOutput[0]; }else{ for(i=0; i<SizeofArray(tsc.aaOutput); i++){ sqlite3_free(tsc.aaOutput[i]); } } finished: sqlite3_reset(pStmt); for(i=0; i<nSegment; i++){ sqlite3Fts3SegReaderFree(p, apSegment[i]); } |
︙ | ︙ | |||
1871 1872 1873 1874 1875 1876 1877 | pLeft->nDoclist = nOut; } return rc; } /* ** Evaluate the full-text expression pExpr against fts3 table pTab. Store | | > > | 2016 2017 2018 2019 2020 2021 2022 2023 2024 2025 2026 2027 2028 2029 2030 2031 2032 | pLeft->nDoclist = nOut; } return rc; } /* ** Evaluate the full-text expression pExpr against fts3 table pTab. Store ** the resulting doclist in *paOut and *pnOut. This routine mallocs for ** the space needed to store the output. The caller is responsible for ** freeing the space when it has finished. */ static int evalFts3Expr( Fts3Table *p, /* Virtual table handle */ Fts3Expr *pExpr, /* Parsed fts3 expression */ char **paOut, /* OUT: Pointer to malloc'd result buffer */ int *pnOut, /* OUT: Size of buffer at *paOut */ int isReqPos /* Require positions in output buffer */ |
︙ | ︙ |
Changes to ext/fts3/fts3Int.h.
︙ | ︙ | |||
49 50 51 52 53 54 55 56 57 58 59 60 61 62 | /* ** Maximum length of a varint encoded integer. The varint format is different ** from that used by SQLite, so the maximum length is 10, not 9. */ #define FTS3_VARINT_MAX 10 /* ** This section provides definitions to allow the ** FTS3 extension to be compiled outside of the ** amalgamation. */ #ifndef SQLITE_AMALGAMATION /* | > > > > > > > > > > > > > > | 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 | /* ** Maximum length of a varint encoded integer. The varint format is different ** from that used by SQLite, so the maximum length is 10, not 9. */ #define FTS3_VARINT_MAX 10 /* ** The testcase() macro is only used by the amalgamation. If undefined, ** make it a no-op. */ #ifndef testcase # define testcase(X) #endif /* ** Terminator values for position-lists and column-lists. */ #define POS_COLUMN (1) /* Column-list terminator */ #define POS_END (0) /* Position-list terminator */ /* ** This section provides definitions to allow the ** FTS3 extension to be compiled outside of the ** amalgamation. */ #ifndef SQLITE_AMALGAMATION /* |
︙ | ︙ |
Changes to ext/fts3/fts3_snippet.c.
︙ | ︙ | |||
567 568 569 570 571 572 573 | ** ....X.....X.... ** ** This is done as part of extracting the snippet text, not when selecting ** the snippet. Snippet selection is done based on doclists only, so there ** is no way for fts3BestSnippet() to know whether or not the document ** actually contains terms that follow the final highlighted term. */ | | | 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 | ** ....X.....X.... ** ** This is done as part of extracting the snippet text, not when selecting ** the snippet. Snippet selection is done based on doclists only, so there ** is no way for fts3BestSnippet() to know whether or not the document ** actually contains terms that follow the final highlighted term. */ static int fts3SnippetShift( Fts3Table *pTab, /* FTS3 table snippet comes from */ int nSnippet, /* Number of tokens desired for snippet */ const char *zDoc, /* Document text to extract snippet from */ int nDoc, /* Size of buffer zDoc in bytes */ int *piPos, /* IN/OUT: First token of snippet */ u64 *pHlmask /* IN/OUT: Mask of tokens to highlight */ ){ |
︙ | ︙ |
Changes to ext/rtree/rtree.c.
︙ | ︙ | |||
2480 2481 2482 2483 2484 2485 2486 2487 2488 2489 2490 2491 2492 2493 | if( SQLITE_ROW==sqlite3_step(pRtree->pReadRowid) ){ sqlite3_reset(pRtree->pReadRowid); rc = SQLITE_CONSTRAINT; goto constraint; } rc = sqlite3_reset(pRtree->pReadRowid); } if( rc==SQLITE_OK ){ rc = ChooseLeaf(pRtree, &cell, 0, &pLeaf); } if( rc==SQLITE_OK ){ int rc2; pRtree->iReinsertHeight = -1; | > | 2480 2481 2482 2483 2484 2485 2486 2487 2488 2489 2490 2491 2492 2493 2494 | if( SQLITE_ROW==sqlite3_step(pRtree->pReadRowid) ){ sqlite3_reset(pRtree->pReadRowid); rc = SQLITE_CONSTRAINT; goto constraint; } rc = sqlite3_reset(pRtree->pReadRowid); } *pRowid = cell.iRowid; if( rc==SQLITE_OK ){ rc = ChooseLeaf(pRtree, &cell, 0, &pLeaf); } if( rc==SQLITE_OK ){ int rc2; pRtree->iReinsertHeight = -1; |
︙ | ︙ |
Changes to ext/rtree/rtree1.test.
︙ | ︙ | |||
394 395 396 397 398 399 400 401 402 | #------------------------------------------------------------------------- # Ticket #3970: Check that the error message is meaningful when a # keyword is used as a column name. # do_test rtree-10.1 { catchsql { CREATE VIRTUAL TABLE t7 USING rtree(index, x1, y1, x2, y2) } } {1 {near "index": syntax error}} finish_test | > > > > > > > > > > > > > > > > > | 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 | #------------------------------------------------------------------------- # Ticket #3970: Check that the error message is meaningful when a # keyword is used as a column name. # do_test rtree-10.1 { catchsql { CREATE VIRTUAL TABLE t7 USING rtree(index, x1, y1, x2, y2) } } {1 {near "index": syntax error}} #------------------------------------------------------------------------- # Test last_insert_rowid(). # do_test rtree-11.1 { execsql { CREATE VIRTUAL TABLE t8 USING rtree(idx, x1, x2, y1, y2); INSERT INTO t8 VALUES(1, 1.0, 1.0, 2.0, 2.0); SELECT last_insert_rowid(); } } {1} do_test rtree-11.2 { execsql { INSERT INTO t8 VALUES(NULL, 1.0, 1.0, 2.0, 2.0); SELECT last_insert_rowid(); } } {2} finish_test |
Changes to main.mk.
︙ | ︙ | |||
62 63 64 65 66 67 68 | mutex.o mutex_noop.o mutex_os2.o mutex_unix.o mutex_w32.o \ notify.o opcodes.o os.o os_os2.o os_unix.o os_win.o \ pager.o parse.o pcache.o pcache1.o pragma.o prepare.o printf.o \ random.o resolve.o rowset.o rtree.o select.o status.o \ table.o tokenize.o trigger.o \ update.o util.o vacuum.o \ vdbe.o vdbeapi.o vdbeaux.o vdbeblob.o vdbemem.o vdbetrace.o \ | | | 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 | mutex.o mutex_noop.o mutex_os2.o mutex_unix.o mutex_w32.o \ notify.o opcodes.o os.o os_os2.o os_unix.o os_win.o \ pager.o parse.o pcache.o pcache1.o pragma.o prepare.o printf.o \ random.o resolve.o rowset.o rtree.o select.o status.o \ table.o tokenize.o trigger.o \ update.o util.o vacuum.o \ vdbe.o vdbeapi.o vdbeaux.o vdbeblob.o vdbemem.o vdbetrace.o \ wal.o walker.o where.o utf.o vtab.o # All of the source code files. # SRC = \ $(TOP)/src/alter.c \ |
︙ | ︙ | |||
154 155 156 157 158 159 160 161 162 163 164 165 166 167 | $(TOP)/src/vdbeapi.c \ $(TOP)/src/vdbeaux.c \ $(TOP)/src/vdbeblob.c \ $(TOP)/src/vdbemem.c \ $(TOP)/src/vdbetrace.c \ $(TOP)/src/vdbeInt.h \ $(TOP)/src/vtab.c \ $(TOP)/src/walker.c \ $(TOP)/src/where.c # Source code for extensions # SRC += \ $(TOP)/ext/fts1/fts1.c \ | > > | 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 | $(TOP)/src/vdbeapi.c \ $(TOP)/src/vdbeaux.c \ $(TOP)/src/vdbeblob.c \ $(TOP)/src/vdbemem.c \ $(TOP)/src/vdbetrace.c \ $(TOP)/src/vdbeInt.h \ $(TOP)/src/vtab.c \ $(TOP)/src/wal.c \ $(TOP)/src/wal.h \ $(TOP)/src/walker.c \ $(TOP)/src/where.c # Source code for extensions # SRC += \ $(TOP)/ext/fts1/fts1.c \ |
︙ | ︙ | |||
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 | $(TOP)/src/test8.c \ $(TOP)/src/test9.c \ $(TOP)/src/test_autoext.c \ $(TOP)/src/test_async.c \ $(TOP)/src/test_backup.c \ $(TOP)/src/test_btree.c \ $(TOP)/src/test_config.c \ $(TOP)/src/test_devsym.c \ $(TOP)/src/test_func.c \ $(TOP)/src/test_hexio.c \ $(TOP)/src/test_init.c \ $(TOP)/src/test_intarray.c \ $(TOP)/src/test_journal.c \ $(TOP)/src/test_malloc.c \ $(TOP)/src/test_mutex.c \ $(TOP)/src/test_onefile.c \ $(TOP)/src/test_osinst.c \ $(TOP)/src/test_pcache.c \ $(TOP)/src/test_schema.c \ $(TOP)/src/test_server.c \ $(TOP)/src/test_tclvar.c \ $(TOP)/src/test_thread.c \ $(TOP)/src/test_wsd.c #TESTSRC += $(TOP)/ext/fts2/fts2_tokenizer.c #TESTSRC += $(TOP)/ext/fts3/fts3_tokenizer.c TESTSRC2 = \ | > > > | > > | > | > > > > | | > > | > > | > > | > > | > > > | > > > | > | > | 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 | $(TOP)/src/test8.c \ $(TOP)/src/test9.c \ $(TOP)/src/test_autoext.c \ $(TOP)/src/test_async.c \ $(TOP)/src/test_backup.c \ $(TOP)/src/test_btree.c \ $(TOP)/src/test_config.c \ $(TOP)/src/test_demovfs.c \ $(TOP)/src/test_devsym.c \ $(TOP)/src/test_func.c \ $(TOP)/src/test_hexio.c \ $(TOP)/src/test_init.c \ $(TOP)/src/test_intarray.c \ $(TOP)/src/test_journal.c \ $(TOP)/src/test_malloc.c \ $(TOP)/src/test_mutex.c \ $(TOP)/src/test_onefile.c \ $(TOP)/src/test_osinst.c \ $(TOP)/src/test_pcache.c \ $(TOP)/src/test_schema.c \ $(TOP)/src/test_server.c \ $(TOP)/src/test_stat.c \ $(TOP)/src/test_tclvar.c \ $(TOP)/src/test_thread.c \ $(TOP)/src/test_vfs.c \ $(TOP)/src/test_wsd.c #TESTSRC += $(TOP)/ext/fts2/fts2_tokenizer.c #TESTSRC += $(TOP)/ext/fts3/fts3_tokenizer.c TESTSRC2 = \ $(TOP)/src/attach.c \ $(TOP)/src/backup.c \ $(TOP)/src/btree.c \ $(TOP)/src/build.c \ $(TOP)/src/date.c \ $(TOP)/src/expr.c \ $(TOP)/src/func.c \ $(TOP)/src/insert.c \ $(TOP)/src/wal.c \ $(TOP)/src/mem5.c \ $(TOP)/src/os.c \ $(TOP)/src/os_os2.c \ $(TOP)/src/os_unix.c \ $(TOP)/src/os_win.c \ $(TOP)/src/pager.c \ $(TOP)/src/pragma.c \ $(TOP)/src/prepare.c \ $(TOP)/src/printf.c \ $(TOP)/src/random.c \ $(TOP)/src/pcache.c \ $(TOP)/src/pcache1.c \ $(TOP)/src/select.c \ $(TOP)/src/tokenize.c \ $(TOP)/src/utf.c \ $(TOP)/src/util.c \ $(TOP)/src/vdbeapi.c \ $(TOP)/src/vdbeaux.c \ $(TOP)/src/vdbe.c \ $(TOP)/src/vdbemem.c \ $(TOP)/src/where.c \ parse.c \ $(TOP)/ext/fts3/fts3.c \ $(TOP)/ext/fts3/fts3_expr.c \ $(TOP)/ext/fts3/fts3_tokenizer.c \ $(TOP)/ext/fts3/fts3_write.c \ $(TOP)/ext/async/sqlite3async.c # Header files used by all library source files. # HDR = \ $(TOP)/src/btree.h \ $(TOP)/src/btreeInt.h \ |
︙ | ︙ | |||
322 323 324 325 326 327 328 | $(RANLIB) libsqlite3.a sqlite3$(EXE): $(TOP)/src/shell.c libsqlite3.a sqlite3.h $(TCCX) $(READLINE_FLAGS) -o sqlite3$(EXE) \ $(TOP)/src/shell.c \ libsqlite3.a $(LIBREADLINE) $(TLIBS) $(THREADLIB) | < < | 350 351 352 353 354 355 356 357 358 359 360 361 362 363 | $(RANLIB) libsqlite3.a sqlite3$(EXE): $(TOP)/src/shell.c libsqlite3.a sqlite3.h $(TCCX) $(READLINE_FLAGS) -o sqlite3$(EXE) \ $(TOP)/src/shell.c \ libsqlite3.a $(LIBREADLINE) $(TLIBS) $(THREADLIB) # This target creates a directory named "tsrc" and fills it with # copies of all of the C source code and header files needed to # build on the target system. Some of the C source code and header # files are automatically generated. This target takes care of # all that automatic generation. # target_source: $(SRC) $(EXTHDR) $(TOP)/tool/vdbe-compress.tcl |
︙ | ︙ | |||
393 394 395 396 397 398 399 | parse.h: parse.c parse.c: $(TOP)/src/parse.y lemon $(TOP)/addopcodes.awk cp $(TOP)/src/parse.y . rm -f parse.h ./lemon $(OPTS) parse.y mv parse.h parse.h.temp | | | 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 | parse.h: parse.c parse.c: $(TOP)/src/parse.y lemon $(TOP)/addopcodes.awk cp $(TOP)/src/parse.y . rm -f parse.h ./lemon $(OPTS) parse.y mv parse.h parse.h.temp $(NAWK) -f $(TOP)/addopcodes.awk parse.h.temp >parse.h sqlite3.h: $(TOP)/src/sqlite.h.in $(TOP)/manifest.uuid $(TOP)/VERSION tclsh $(TOP)/tool/mksqlite3h.tcl $(TOP) >sqlite3.h keywordhash.h: $(TOP)/tool/mkkeywordhash.c $(BCC) -o mkkeywordhash $(OPTS) $(TOP)/tool/mkkeywordhash.c ./mkkeywordhash >keywordhash.h |
︙ | ︙ | |||
467 468 469 470 471 472 473 | tclsqlite3: $(TOP)/src/tclsqlite.c libsqlite3.a $(TCCX) $(TCL_FLAGS) -DTCLSH=1 -o tclsqlite3 \ $(TOP)/src/tclsqlite.c libsqlite3.a $(LIBTCL) $(THREADLIB) # Rules to build the 'testfixture' application. # | | | | | | | | | 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 | tclsqlite3: $(TOP)/src/tclsqlite.c libsqlite3.a $(TCCX) $(TCL_FLAGS) -DTCLSH=1 -o tclsqlite3 \ $(TOP)/src/tclsqlite.c libsqlite3.a $(LIBTCL) $(THREADLIB) # Rules to build the 'testfixture' application. # TESTFIXTURE_FLAGS = -DSQLITE_TEST=1 -DSQLITE_CRASH_TEST=1 TESTFIXTURE_FLAGS += -DSQLITE_SERVER=1 -DSQLITE_PRIVATE="" -DSQLITE_CORE testfixture$(EXE): $(TESTSRC2) libsqlite3.a $(TESTSRC) $(TOP)/src/tclsqlite.c $(TCCX) $(TCL_FLAGS) -DTCLSH=1 $(TESTFIXTURE_FLAGS) \ $(TESTSRC) $(TESTSRC2) $(TOP)/src/tclsqlite.c \ -o testfixture$(EXE) $(LIBTCL) $(THREADLIB) libsqlite3.a amalgamation-testfixture$(EXE): sqlite3.c $(TESTSRC) $(TOP)/src/tclsqlite.c $(TCCX) $(TCL_FLAGS) -DTCLSH=1 $(TESTFIXTURE_FLAGS) \ $(TESTSRC) $(TOP)/src/tclsqlite.c sqlite3.c \ -o testfixture$(EXE) $(LIBTCL) $(THREADLIB) fts3-testfixture$(EXE): sqlite3.c fts3amal.c $(TESTSRC) $(TOP)/src/tclsqlite.c $(TCCX) $(TCL_FLAGS) -DTCLSH=1 $(TESTFIXTURE_FLAGS) \ -DSQLITE_ENABLE_FTS3=1 \ $(TESTSRC) $(TOP)/src/tclsqlite.c sqlite3.c fts3amal.c \ -o testfixture$(EXE) $(LIBTCL) $(THREADLIB) fulltest: testfixture$(EXE) sqlite3$(EXE) ./testfixture$(EXE) $(TOP)/test/all.test soaktest: testfixture$(EXE) sqlite3$(EXE) ./testfixture$(EXE) $(TOP)/test/all.test -soak=1 test: testfixture$(EXE) sqlite3$(EXE) ./testfixture$(EXE) $(TOP)/test/veryquick.test sqlite3_analyzer$(EXE): $(TOP)/src/tclsqlite.c sqlite3.c $(TESTSRC) \ $(TOP)/tool/spaceanal.tcl sed \ -e '/^#/d' \ -e 's,\\,\\\\,g' \ -e 's,",\\",g' \ -e 's,^,",' \ -e 's,$$,\\n",' \ $(TOP)/tool/spaceanal.tcl >spaceanal_tcl.h $(TCCX) $(TCL_FLAGS) -DTCLSH=2 $(TESTFIXTURE_FLAGS) \ -DSQLITE_TEST=1 -DSQLITE_PRIVATE="" \ $(TESTSRC) $(TOP)/src/tclsqlite.c sqlite3.c \ -o sqlite3_analyzer$(EXE) \ $(LIBTCL) $(THREADLIB) TEST_EXTENSION = $(SHPREFIX)testloadext.$(SO) $(TEST_EXTENSION): $(TOP)/src/test_loadext.c $(MKSHLIB) $(TOP)/src/test_loadext.c -o $(TEST_EXTENSION) |
︙ | ︙ |
Changes to src/alter.c.
︙ | ︙ | |||
222 223 224 225 226 227 228 | } } #endif /* !SQLITE_OMIT_TRIGGER */ /* ** Register built-in functions used to help implement ALTER TABLE */ | | | | | < | < > > > > > > > > | 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 | } } #endif /* !SQLITE_OMIT_TRIGGER */ /* ** Register built-in functions used to help implement ALTER TABLE */ void sqlite3AlterFunctions(void){ static SQLITE_WSD FuncDef aAlterTableFuncs[] = { FUNCTION(sqlite_rename_table, 2, 0, 0, renameTableFunc), #ifndef SQLITE_OMIT_TRIGGER FUNCTION(sqlite_rename_trigger, 2, 0, 0, renameTriggerFunc), #endif #ifndef SQLITE_OMIT_FOREIGN_KEY FUNCTION(sqlite_rename_parent, 3, 0, 0, renameParentFunc), #endif }; int i; FuncDefHash *pHash = &GLOBAL(FuncDefHash, sqlite3GlobalFunctions); FuncDef *aFunc = (FuncDef*)&GLOBAL(FuncDef, aAlterTableFuncs); for(i=0; i<ArraySize(aAlterTableFuncs); i++){ sqlite3FuncDefInsert(pHash, &aFunc[i]); } } /* ** This function is used to create the text of expressions of the form: ** ** name=<constant1> OR name=<constant2> OR ... ** |
︙ | ︙ | |||
376 377 378 379 380 381 382 | int nTabName; /* Number of UTF-8 characters in zTabName */ const char *zTabName; /* Original name of the table */ Vdbe *v; #ifndef SQLITE_OMIT_TRIGGER char *zWhere = 0; /* Where clause to locate temp triggers */ #endif VTable *pVTab = 0; /* Non-zero if this is a v-tab with an xRename() */ | > | > > | 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 | int nTabName; /* Number of UTF-8 characters in zTabName */ const char *zTabName; /* Original name of the table */ Vdbe *v; #ifndef SQLITE_OMIT_TRIGGER char *zWhere = 0; /* Where clause to locate temp triggers */ #endif VTable *pVTab = 0; /* Non-zero if this is a v-tab with an xRename() */ int savedDbFlags; /* Saved value of db->flags */ savedDbFlags = db->flags; if( NEVER(db->mallocFailed) ) goto exit_rename_table; assert( pSrc->nSrc==1 ); assert( sqlite3BtreeHoldsAllMutexes(pParse->db) ); pTab = sqlite3LocateTable(pParse, 0, pSrc->a[0].zName, pSrc->a[0].zDatabase); if( !pTab ) goto exit_rename_table; iDb = sqlite3SchemaToIndex(pParse->db, pTab->pSchema); zDb = db->aDb[iDb].zName; db->flags |= SQLITE_PreferBuiltin; /* Get a NULL terminated version of the new table name. */ zName = sqlite3NameFromToken(db, pName); if( !zName ) goto exit_rename_table; /* Check that a table or index named 'zName' does not already exist ** in database iDb. If so, this is an error. |
︙ | ︙ | |||
552 553 554 555 556 557 558 559 560 561 562 563 564 565 | /* Drop and reload the internal table schema. */ reloadTableSchema(pParse, pTab, zName); exit_rename_table: sqlite3SrcListDelete(db, pSrc); sqlite3DbFree(db, zName); } /* ** Generate code to make sure the file format number is at least minFormat. ** The generated code will increase the file format number if necessary. */ | > | 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 | /* Drop and reload the internal table schema. */ reloadTableSchema(pParse, pTab, zName); exit_rename_table: sqlite3SrcListDelete(db, pSrc); sqlite3DbFree(db, zName); db->flags = savedDbFlags; } /* ** Generate code to make sure the file format number is at least minFormat. ** The generated code will increase the file format number if necessary. */ |
︙ | ︙ | |||
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 | sqlite3ValueFree(pVal); } /* Modify the CREATE TABLE statement. */ zCol = sqlite3DbStrNDup(db, (char*)pColDef->z, pColDef->n); if( zCol ){ char *zEnd = &zCol[pColDef->n-1]; while( zEnd>zCol && (*zEnd==';' || sqlite3Isspace(*zEnd)) ){ *zEnd-- = '\0'; } sqlite3NestedParse(pParse, "UPDATE \"%w\".%s SET " "sql = substr(sql,1,%d) || ', ' || %Q || substr(sql,%d) " "WHERE type = 'table' AND name = %Q", zDb, SCHEMA_TABLE(iDb), pNew->addColOffset, zCol, pNew->addColOffset+1, zTab ); sqlite3DbFree(db, zCol); } /* If the default value of the new column is NULL, then set the file ** format to 2. If the default value of the new column is not NULL, ** the file format becomes 3. */ sqlite3MinimumFileFormat(pParse, iDb, pDflt ? 3 : 2); | > > > | 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 | sqlite3ValueFree(pVal); } /* Modify the CREATE TABLE statement. */ zCol = sqlite3DbStrNDup(db, (char*)pColDef->z, pColDef->n); if( zCol ){ char *zEnd = &zCol[pColDef->n-1]; int savedDbFlags = db->flags; while( zEnd>zCol && (*zEnd==';' || sqlite3Isspace(*zEnd)) ){ *zEnd-- = '\0'; } db->flags |= SQLITE_PreferBuiltin; sqlite3NestedParse(pParse, "UPDATE \"%w\".%s SET " "sql = substr(sql,1,%d) || ', ' || %Q || substr(sql,%d) " "WHERE type = 'table' AND name = %Q", zDb, SCHEMA_TABLE(iDb), pNew->addColOffset, zCol, pNew->addColOffset+1, zTab ); sqlite3DbFree(db, zCol); db->flags = savedDbFlags; } /* If the default value of the new column is NULL, then set the file ** format to 2. If the default value of the new column is not NULL, ** the file format becomes 3. */ sqlite3MinimumFileFormat(pParse, iDb, pDflt ? 3 : 2); |
︙ | ︙ |
Changes to src/analyze.c.
︙ | ︙ | |||
32 33 34 35 36 37 38 | */ static void openStatTable( Parse *pParse, /* Parsing context */ int iDb, /* The database we are looking in */ int iStatCur, /* Open the sqlite_stat1 table on this cursor */ const char *zWhere /* Delete entries associated with this table */ ){ | | | 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 | */ static void openStatTable( Parse *pParse, /* Parsing context */ int iDb, /* The database we are looking in */ int iStatCur, /* Open the sqlite_stat1 table on this cursor */ const char *zWhere /* Delete entries associated with this table */ ){ static const struct { const char *zName; const char *zCols; } aTable[] = { { "sqlite_stat1", "tbl,idx,stat" }, #ifdef SQLITE_ENABLE_STAT2 { "sqlite_stat2", "tbl,idx,sampleno,sample" }, #endif |
︙ | ︙ | |||
614 615 616 617 618 619 620 | sqlite3_column_text(pStmt, 2) ); int n = sqlite3_column_bytes(pStmt, 2); if( n>24 ){ n = 24; } pSample->nByte = (u8)n; | > > > | | | | | | > | 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 | sqlite3_column_text(pStmt, 2) ); int n = sqlite3_column_bytes(pStmt, 2); if( n>24 ){ n = 24; } pSample->nByte = (u8)n; if( n < 1){ pSample->u.z = 0; }else{ pSample->u.z = sqlite3DbMallocRaw(dbMem, n); if( pSample->u.z ){ memcpy(pSample->u.z, z, n); }else{ db->mallocFailed = 1; break; } } } } } } } rc = sqlite3_finalize(pStmt); |
︙ | ︙ |
Changes to src/attach.c.
︙ | ︙ | |||
139 140 141 142 143 144 145 | }else if( aNew->pSchema->file_format && aNew->pSchema->enc!=ENC(db) ){ zErrDyn = sqlite3MPrintf(db, "attached databases must use the same text encoding as main database"); rc = SQLITE_ERROR; } pPager = sqlite3BtreePager(aNew->pBt); sqlite3PagerLockingMode(pPager, db->dfltLockMode); | < | 139 140 141 142 143 144 145 146 147 148 149 150 151 152 | }else if( aNew->pSchema->file_format && aNew->pSchema->enc!=ENC(db) ){ zErrDyn = sqlite3MPrintf(db, "attached databases must use the same text encoding as main database"); rc = SQLITE_ERROR; } pPager = sqlite3BtreePager(aNew->pBt); sqlite3PagerLockingMode(pPager, db->dfltLockMode); sqlite3BtreeSecureDelete(aNew->pBt, sqlite3BtreeSecureDelete(db->aDb[0].pBt,-1) ); } aNew->safety_level = 3; aNew->zName = sqlite3DbStrDup(db, zName); if( rc==SQLITE_OK && aNew->zName==0 ){ rc = SQLITE_NOMEM; |
︙ | ︙ | |||
284 285 286 287 288 289 290 | /* ** This procedure generates VDBE code for a single invocation of either the ** sqlite_detach() or sqlite_attach() SQL user functions. */ static void codeAttach( Parse *pParse, /* The parser context */ int type, /* Either SQLITE_ATTACH or SQLITE_DETACH */ | | | 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 | /* ** This procedure generates VDBE code for a single invocation of either the ** sqlite_detach() or sqlite_attach() SQL user functions. */ static void codeAttach( Parse *pParse, /* The parser context */ int type, /* Either SQLITE_ATTACH or SQLITE_DETACH */ FuncDef const *pFunc,/* FuncDef wrapper for detachFunc() or attachFunc() */ Expr *pAuthArg, /* Expression to pass to authorization callback */ Expr *pFilename, /* Name of database file */ Expr *pDbname, /* Name of the database to use internally */ Expr *pKey /* Database key for encryption extension */ ){ int rc; NameContext sName; |
︙ | ︙ | |||
354 355 356 357 358 359 360 | /* ** Called by the parser to compile a DETACH statement. ** ** DETACH pDbname */ void sqlite3Detach(Parse *pParse, Expr *pDbname){ | | | | 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 | /* ** Called by the parser to compile a DETACH statement. ** ** DETACH pDbname */ void sqlite3Detach(Parse *pParse, Expr *pDbname){ static const FuncDef detach_func = { 1, /* nArg */ SQLITE_UTF8, /* iPrefEnc */ 0, /* flags */ 0, /* pUserData */ 0, /* pNext */ detachFunc, /* xFunc */ 0, /* xStep */ 0, /* xFinalize */ "sqlite_detach", /* zName */ 0 /* pHash */ }; codeAttach(pParse, SQLITE_DETACH, &detach_func, pDbname, 0, 0, pDbname); } /* ** Called by the parser to compile an ATTACH statement. ** ** ATTACH p AS pDbname KEY pKey */ void sqlite3Attach(Parse *pParse, Expr *p, Expr *pDbname, Expr *pKey){ static const FuncDef attach_func = { 3, /* nArg */ SQLITE_UTF8, /* iPrefEnc */ 0, /* flags */ 0, /* pUserData */ 0, /* pNext */ attachFunc, /* xFunc */ 0, /* xStep */ |
︙ | ︙ |
Changes to src/backup.c.
︙ | ︙ | |||
213 214 215 216 217 218 219 | assert( !isFatalError(p->rc) ); assert( iSrcPg!=PENDING_BYTE_PAGE(p->pSrc->pBt) ); assert( zSrcData ); /* Catch the case where the destination is an in-memory database and the ** page sizes of the source and destination differ. */ | | | 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 | assert( !isFatalError(p->rc) ); assert( iSrcPg!=PENDING_BYTE_PAGE(p->pSrc->pBt) ); assert( zSrcData ); /* Catch the case where the destination is an in-memory database and the ** page sizes of the source and destination differ. */ if( nSrcPgsz!=nDestPgsz && sqlite3PagerIsMemdb(pDestPager) ){ rc = SQLITE_READONLY; } /* This loop runs once for each destination page spanned by the source ** page. For each iteration, variable iOff is set to the byte offset ** of the destination page. */ |
︙ | ︙ | |||
283 284 285 286 287 288 289 290 291 292 293 294 295 296 | } /* ** Copy nPage pages from the source b-tree to the destination. */ int sqlite3_backup_step(sqlite3_backup *p, int nPage){ int rc; sqlite3_mutex_enter(p->pSrcDb->mutex); sqlite3BtreeEnter(p->pSrc); if( p->pDestDb ){ sqlite3_mutex_enter(p->pDestDb->mutex); } | > > > | 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 | } /* ** Copy nPage pages from the source b-tree to the destination. */ int sqlite3_backup_step(sqlite3_backup *p, int nPage){ int rc; int destMode; /* Destination journal mode */ int pgszSrc = 0; /* Source page size */ int pgszDest = 0; /* Destination page size */ sqlite3_mutex_enter(p->pSrcDb->mutex); sqlite3BtreeEnter(p->pSrc); if( p->pDestDb ){ sqlite3_mutex_enter(p->pDestDb->mutex); } |
︙ | ︙ | |||
323 324 325 326 327 328 329 330 331 332 333 | ** one now. If a transaction is opened here, then it will be closed ** before this function exits. */ if( rc==SQLITE_OK && 0==sqlite3BtreeIsInReadTrans(p->pSrc) ){ rc = sqlite3BtreeBeginTrans(p->pSrc, 0); bCloseTrans = 1; } /* Now that there is a read-lock on the source database, query the ** source pager for the number of pages in the database. */ | > > > > > > > > > | | < | 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 | ** one now. If a transaction is opened here, then it will be closed ** before this function exits. */ if( rc==SQLITE_OK && 0==sqlite3BtreeIsInReadTrans(p->pSrc) ){ rc = sqlite3BtreeBeginTrans(p->pSrc, 0); bCloseTrans = 1; } /* Do not allow backup if the destination database is in WAL mode ** and the page sizes are different between source and destination */ pgszSrc = sqlite3BtreeGetPageSize(p->pSrc); pgszDest = sqlite3BtreeGetPageSize(p->pDest); destMode = sqlite3PagerGetJournalMode(sqlite3BtreePager(p->pDest)); if( SQLITE_OK==rc && destMode==PAGER_JOURNALMODE_WAL && pgszSrc!=pgszDest ){ rc = SQLITE_READONLY; } /* Now that there is a read-lock on the source database, query the ** source pager for the number of pages in the database. */ nSrcPage = (int)sqlite3BtreeLastPage(p->pSrc); assert( nSrcPage>=0 ); for(ii=0; (nPage<0 || ii<nPage) && p->iNext<=(Pgno)nSrcPage && !rc; ii++){ const Pgno iSrcPg = p->iNext; /* Source page number */ if( iSrcPg!=PENDING_BYTE_PAGE(p->pSrc->pBt) ){ DbPage *pSrcPg; /* Source page object */ rc = sqlite3PagerGet(pSrcPager, iSrcPg, &pSrcPg); if( rc==SQLITE_OK ){ rc = backupOnePage(p, iSrcPg, sqlite3PagerGetData(pSrcPg)); |
︙ | ︙ | |||
360 361 362 363 364 365 366 | ** is to make sure that the schema-version really does change in ** the case where the source and destination databases have the ** same schema version. */ if( rc==SQLITE_DONE && (rc = sqlite3BtreeUpdateMeta(p->pDest,1,p->iDestSchema+1))==SQLITE_OK ){ | < < > > | | | | | | | | | | | | | 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 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 | ** is to make sure that the schema-version really does change in ** the case where the source and destination databases have the ** same schema version. */ if( rc==SQLITE_DONE && (rc = sqlite3BtreeUpdateMeta(p->pDest,1,p->iDestSchema+1))==SQLITE_OK ){ int nDestTruncate; if( p->pDestDb ){ sqlite3ResetInternalSchema(p->pDestDb, 0); } /* Set nDestTruncate to the final number of pages in the destination ** database. The complication here is that the destination page ** size may be different to the source page size. ** ** If the source page size is smaller than the destination page size, ** round up. In this case the call to sqlite3OsTruncate() below will ** fix the size of the file. However it is important to call ** sqlite3PagerTruncateImage() here so that any pages in the ** destination file that lie beyond the nDestTruncate page mark are ** journalled by PagerCommitPhaseOne() before they are destroyed ** by the file truncation. */ assert( pgszSrc==sqlite3BtreeGetPageSize(p->pSrc) ); assert( pgszDest==sqlite3BtreeGetPageSize(p->pDest) ); if( pgszSrc<pgszDest ){ int ratio = pgszDest/pgszSrc; nDestTruncate = (nSrcPage+ratio-1)/ratio; if( nDestTruncate==(int)PENDING_BYTE_PAGE(p->pDest->pBt) ){ nDestTruncate--; } }else{ nDestTruncate = nSrcPage * (pgszSrc/pgszDest); } sqlite3PagerTruncateImage(pDestPager, nDestTruncate); if( pgszSrc<pgszDest ){ /* If the source page-size is smaller than the destination page-size, ** two extra things may need to happen: ** ** * The destination may need to be truncated, and ** ** * Data stored on the pages immediately following the ** pending-byte page in the source database may need to be ** copied into the destination database. */ const i64 iSize = (i64)pgszSrc * (i64)nSrcPage; sqlite3_file * const pFile = sqlite3PagerFile(pDestPager); assert( pFile ); assert( (i64)nDestTruncate*(i64)pgszDest >= iSize || ( nDestTruncate==(int)(PENDING_BYTE_PAGE(p->pDest->pBt)-1) && iSize>=PENDING_BYTE && iSize<=PENDING_BYTE+pgszDest )); if( SQLITE_OK==(rc = sqlite3PagerCommitPhaseOne(pDestPager, 0, 1)) && SQLITE_OK==(rc = backupTruncateFile(pFile, iSize)) && SQLITE_OK==(rc = sqlite3PagerSync(pDestPager)) ){ i64 iOff; i64 iEnd = MIN(PENDING_BYTE + pgszDest, iSize); for( iOff=PENDING_BYTE+pgszSrc; rc==SQLITE_OK && iOff<iEnd; iOff+=pgszSrc ){ PgHdr *pSrcPg = 0; const Pgno iSrcPg = (Pgno)((iOff/pgszSrc)+1); rc = sqlite3PagerGet(pSrcPager, iSrcPg, &pSrcPg); if( rc==SQLITE_OK ){ u8 *zData = sqlite3PagerGetData(pSrcPg); rc = sqlite3OsWrite(pFile, zData, pgszSrc, iOff); } sqlite3PagerUnref(pSrcPg); } } }else{ rc = sqlite3PagerCommitPhaseOne(pDestPager, 0, 0); } |
︙ | ︙ | |||
454 455 456 457 458 459 460 461 462 463 464 465 466 467 | if( bCloseTrans ){ TESTONLY( int rc2 ); TESTONLY( rc2 = ) sqlite3BtreeCommitPhaseOne(p->pSrc, 0); TESTONLY( rc2 |= ) sqlite3BtreeCommitPhaseTwo(p->pSrc); assert( rc2==SQLITE_OK ); } p->rc = rc; } if( p->pDestDb ){ sqlite3_mutex_leave(p->pDestDb->mutex); } sqlite3BtreeLeave(p->pSrc); sqlite3_mutex_leave(p->pSrcDb->mutex); | > > > | 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 | if( bCloseTrans ){ TESTONLY( int rc2 ); TESTONLY( rc2 = ) sqlite3BtreeCommitPhaseOne(p->pSrc, 0); TESTONLY( rc2 |= ) sqlite3BtreeCommitPhaseTwo(p->pSrc); assert( rc2==SQLITE_OK ); } if( rc==SQLITE_IOERR_NOMEM ){ rc = SQLITE_NOMEM; } p->rc = rc; } if( p->pDestDb ){ sqlite3_mutex_leave(p->pDestDb->mutex); } sqlite3BtreeLeave(p->pSrc); sqlite3_mutex_leave(p->pSrcDb->mutex); |
︙ | ︙ |
Changes to src/btree.c.
︙ | ︙ | |||
512 513 514 515 516 517 518 | ** optimization 2 above is omitted if the corresponding bit is already ** set in BtShared.pHasContent. The contents of the bitvec are cleared ** at the end of every transaction. */ static int btreeSetHasContent(BtShared *pBt, Pgno pgno){ int rc = SQLITE_OK; if( !pBt->pHasContent ){ | | < < < | | 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 | ** optimization 2 above is omitted if the corresponding bit is already ** set in BtShared.pHasContent. The contents of the bitvec are cleared ** at the end of every transaction. */ static int btreeSetHasContent(BtShared *pBt, Pgno pgno){ int rc = SQLITE_OK; if( !pBt->pHasContent ){ assert( pgno<=pBt->nPage ); pBt->pHasContent = sqlite3BitvecCreate(pBt->nPage); if( !pBt->pHasContent ){ rc = SQLITE_NOMEM; } } if( rc==SQLITE_OK && pgno<=sqlite3BitvecSize(pBt->pHasContent) ){ rc = sqlite3BitvecSet(pBt->pHasContent, pgno); } |
︙ | ︙ | |||
1559 1560 1561 1562 1563 1564 1565 | return 0; } /* ** Return the size of the database file in pages. If there is any kind of ** error, return ((unsigned int)-1). */ | | | < > | | | | < > > > | | | | | > < < < < < < | | 1556 1557 1558 1559 1560 1561 1562 1563 1564 1565 1566 1567 1568 1569 1570 1571 1572 1573 1574 1575 1576 1577 1578 1579 1580 1581 1582 1583 1584 1585 1586 1587 1588 1589 1590 1591 1592 1593 1594 1595 1596 1597 1598 1599 1600 1601 1602 1603 1604 1605 1606 1607 1608 | return 0; } /* ** Return the size of the database file in pages. If there is any kind of ** error, return ((unsigned int)-1). */ static Pgno btreePagecount(BtShared *pBt){ return pBt->nPage; } u32 sqlite3BtreeLastPage(Btree *p){ assert( sqlite3BtreeHoldsMutex(p) ); assert( ((p->pBt->nPage)&0x8000000)==0 ); return (int)btreePagecount(p->pBt); } /* ** Get a page from the pager and initialize it. This routine is just a ** convenience wrapper around separate calls to btreeGetPage() and ** btreeInitPage(). ** ** If an error occurs, then the value *ppPage is set to is undefined. It ** may remain unchanged, or it may be set to an invalid value. */ static int getAndInitPage( BtShared *pBt, /* The database file */ Pgno pgno, /* Number of the page to get */ MemPage **ppPage /* Write the page pointer here */ ){ int rc; assert( sqlite3_mutex_held(pBt->mutex) ); if( pgno>btreePagecount(pBt) ){ rc = SQLITE_CORRUPT_BKPT; }else{ rc = btreeGetPage(pBt, pgno, ppPage, 0); if( rc==SQLITE_OK ){ rc = btreeInitPage(*ppPage); if( rc!=SQLITE_OK ){ releasePage(*ppPage); } } } testcase( pgno==0 ); assert( pgno!=0 || rc==SQLITE_CORRUPT ); return rc; } /* ** Release a MemPage. This should be called once for each prior ** call to btreeGetPage. */ |
︙ | ︙ | |||
2236 2237 2238 2239 2240 2241 2242 | ** ** SQLITE_OK is returned on success. If the file is not a ** well-formed database file, then SQLITE_CORRUPT is returned. ** SQLITE_BUSY is returned if the database is locked. SQLITE_NOMEM ** is returned if we run out of memory. */ static int lockBtree(BtShared *pBt){ | | | | > > > | < > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 2230 2231 2232 2233 2234 2235 2236 2237 2238 2239 2240 2241 2242 2243 2244 2245 2246 2247 2248 2249 2250 2251 2252 2253 2254 2255 2256 2257 2258 2259 2260 2261 2262 2263 2264 2265 2266 2267 2268 2269 2270 2271 2272 2273 2274 2275 2276 2277 2278 2279 2280 2281 2282 2283 2284 2285 2286 2287 2288 2289 2290 2291 2292 2293 2294 2295 2296 2297 2298 2299 2300 2301 2302 2303 2304 2305 2306 2307 2308 2309 2310 | ** ** SQLITE_OK is returned on success. If the file is not a ** well-formed database file, then SQLITE_CORRUPT is returned. ** SQLITE_BUSY is returned if the database is locked. SQLITE_NOMEM ** is returned if we run out of memory. */ static int lockBtree(BtShared *pBt){ int rc; /* Result code from subfunctions */ MemPage *pPage1; /* Page 1 of the database file */ int nPage; /* Number of pages in the database */ int nPageFile = 0; /* Number of pages in the database file */ int nPageHeader; /* Number of pages in the database according to hdr */ assert( sqlite3_mutex_held(pBt->mutex) ); assert( pBt->pPage1==0 ); rc = sqlite3PagerSharedLock(pBt->pPager); if( rc!=SQLITE_OK ) return rc; rc = btreeGetPage(pBt, 1, &pPage1, 0); if( rc!=SQLITE_OK ) return rc; /* Do some checking to help insure the file we opened really is ** a valid database file. */ nPage = nPageHeader = get4byte(28+(u8*)pPage1->aData); if( (rc = sqlite3PagerPagecount(pBt->pPager, &nPageFile))!=SQLITE_OK ){; goto page1_init_failed; } if( nPage==0 || memcmp(24+(u8*)pPage1->aData, 92+(u8*)pPage1->aData,4)!=0 ){ nPage = nPageFile; } if( nPage>0 ){ int pageSize; int usableSize; u8 *page1 = pPage1->aData; rc = SQLITE_NOTADB; if( memcmp(page1, zMagicHeader, 16)!=0 ){ goto page1_init_failed; } #ifdef SQLITE_OMIT_WAL if( page1[18]>1 ){ pBt->readOnly = 1; } if( page1[19]>1 ){ goto page1_init_failed; } #else if( page1[18]>2 ){ pBt->readOnly = 1; } if( page1[19]>2 ){ goto page1_init_failed; } /* If the write version is set to 2, this database should be accessed ** in WAL mode. If the log is not already open, open it now. Then ** return SQLITE_OK and return without populating BtShared.pPage1. ** The caller detects this and calls this function again. This is ** required as the version of page 1 currently in the page1 buffer ** may not be the latest version - there may be a newer one in the log ** file. */ if( page1[19]==2 && pBt->doNotUseWAL==0 ){ int isOpen = 0; rc = sqlite3PagerOpenWal(pBt->pPager, &isOpen); if( rc!=SQLITE_OK ){ goto page1_init_failed; }else if( isOpen==0 ){ releasePage(pPage1); return SQLITE_OK; } rc = SQLITE_NOTADB; } #endif /* The maximum embedded fraction must be exactly 25%. And the minimum ** embedded fraction must be 12.5% for both leaf-data and non-leaf-data. ** The original design allowed these amounts to vary, but as of ** version 3.6.0, we require them to be fixed. */ if( memcmp(&page1[21], "\100\040\040",3)!=0 ){ |
︙ | ︙ | |||
2299 2300 2301 2302 2303 2304 2305 2306 2307 2308 2309 2310 2311 2312 | pBt->usableSize = (u16)usableSize; pBt->pageSize = (u16)pageSize; freeTempSpace(pBt); rc = sqlite3PagerSetPagesize(pBt->pPager, &pBt->pageSize, pageSize-usableSize); return rc; } if( usableSize<480 ){ goto page1_init_failed; } pBt->pageSize = (u16)pageSize; pBt->usableSize = (u16)usableSize; #ifndef SQLITE_OMIT_AUTOVACUUM pBt->autoVacuum = (get4byte(&page1[36 + 4*4])?1:0); | > > > > | 2329 2330 2331 2332 2333 2334 2335 2336 2337 2338 2339 2340 2341 2342 2343 2344 2345 2346 | pBt->usableSize = (u16)usableSize; pBt->pageSize = (u16)pageSize; freeTempSpace(pBt); rc = sqlite3PagerSetPagesize(pBt->pPager, &pBt->pageSize, pageSize-usableSize); return rc; } if( nPageHeader>nPageFile ){ rc = SQLITE_CORRUPT_BKPT; goto page1_init_failed; } if( usableSize<480 ){ goto page1_init_failed; } pBt->pageSize = (u16)pageSize; pBt->usableSize = (u16)usableSize; #ifndef SQLITE_OMIT_AUTOVACUUM pBt->autoVacuum = (get4byte(&page1[36 + 4*4])?1:0); |
︙ | ︙ | |||
2329 2330 2331 2332 2333 2334 2335 2336 2337 2338 2339 2340 2341 2342 | */ pBt->maxLocal = (pBt->usableSize-12)*64/255 - 23; pBt->minLocal = (pBt->usableSize-12)*32/255 - 23; pBt->maxLeaf = pBt->usableSize - 35; pBt->minLeaf = (pBt->usableSize-12)*32/255 - 23; assert( pBt->maxLeaf + 23 <= MX_CELL_SIZE(pBt) ); pBt->pPage1 = pPage1; return SQLITE_OK; page1_init_failed: releasePage(pPage1); pBt->pPage1 = 0; return rc; } | > | 2363 2364 2365 2366 2367 2368 2369 2370 2371 2372 2373 2374 2375 2376 2377 | */ pBt->maxLocal = (pBt->usableSize-12)*64/255 - 23; pBt->minLocal = (pBt->usableSize-12)*32/255 - 23; pBt->maxLeaf = pBt->usableSize - 35; pBt->minLeaf = (pBt->usableSize-12)*32/255 - 23; assert( pBt->maxLeaf + 23 <= MX_CELL_SIZE(pBt) ); pBt->pPage1 = pPage1; pBt->nPage = nPage; return SQLITE_OK; page1_init_failed: releasePage(pPage1); pBt->pPage1 = 0; return rc; } |
︙ | ︙ | |||
2366 2367 2368 2369 2370 2371 2372 | ** into a new empty database by initializing the first page of ** the database. */ static int newDatabase(BtShared *pBt){ MemPage *pP1; unsigned char *data; int rc; | < < | | | 2401 2402 2403 2404 2405 2406 2407 2408 2409 2410 2411 2412 2413 2414 2415 2416 2417 2418 | ** into a new empty database by initializing the first page of ** the database. */ static int newDatabase(BtShared *pBt){ MemPage *pP1; unsigned char *data; int rc; assert( sqlite3_mutex_held(pBt->mutex) ); if( pBt->nPage>0 ){ return SQLITE_OK; } pP1 = pBt->pPage1; assert( pP1!=0 ); data = pP1->aData; rc = sqlite3PagerWrite(pP1->pDbPage); if( rc ) return rc; memcpy(data, zMagicHeader, sizeof(zMagicHeader)); |
︙ | ︙ | |||
2397 2398 2399 2400 2401 2402 2403 2404 2405 2406 2407 2408 2409 2410 | pBt->pageSizeFixed = 1; #ifndef SQLITE_OMIT_AUTOVACUUM assert( pBt->autoVacuum==1 || pBt->autoVacuum==0 ); assert( pBt->incrVacuum==1 || pBt->incrVacuum==0 ); put4byte(&data[36 + 4*4], pBt->autoVacuum); put4byte(&data[36 + 7*4], pBt->incrVacuum); #endif return SQLITE_OK; } /* ** Attempt to start a new transaction. A write-transaction ** is started if the second argument is nonzero, otherwise a read- ** transaction. If the second argument is 2 or more and exclusive | > > | 2430 2431 2432 2433 2434 2435 2436 2437 2438 2439 2440 2441 2442 2443 2444 2445 | pBt->pageSizeFixed = 1; #ifndef SQLITE_OMIT_AUTOVACUUM assert( pBt->autoVacuum==1 || pBt->autoVacuum==0 ); assert( pBt->incrVacuum==1 || pBt->incrVacuum==0 ); put4byte(&data[36 + 4*4], pBt->autoVacuum); put4byte(&data[36 + 7*4], pBt->incrVacuum); #endif pBt->nPage = 1; data[31] = 1; return SQLITE_OK; } /* ** Attempt to start a new transaction. A write-transaction ** is started if the second argument is nonzero, otherwise a read- ** transaction. If the second argument is 2 or more and exclusive |
︙ | ︙ | |||
2486 2487 2488 2489 2490 2491 2492 2493 2494 2495 2496 2497 2498 2499 | /* Any read-only or read-write transaction implies a read-lock on ** page 1. So if some other shared-cache client already has a write-lock ** on page 1, the transaction cannot be opened. */ rc = querySharedCacheTableLock(p, MASTER_ROOT, READ_LOCK); if( SQLITE_OK!=rc ) goto trans_begun; do { /* Call lockBtree() until either pBt->pPage1 is populated or ** lockBtree() returns something other than SQLITE_OK. lockBtree() ** may return SQLITE_OK but leave pBt->pPage1 set to 0 if after ** reading page 1 it discovers that the page-size of the database ** file is not pBt->pageSize. In this case lockBtree() will update ** pBt->pageSize to the page-size of the file on disk. | > | 2521 2522 2523 2524 2525 2526 2527 2528 2529 2530 2531 2532 2533 2534 2535 | /* Any read-only or read-write transaction implies a read-lock on ** page 1. So if some other shared-cache client already has a write-lock ** on page 1, the transaction cannot be opened. */ rc = querySharedCacheTableLock(p, MASTER_ROOT, READ_LOCK); if( SQLITE_OK!=rc ) goto trans_begun; pBt->initiallyEmpty = (u8)(pBt->nPage==0); do { /* Call lockBtree() until either pBt->pPage1 is populated or ** lockBtree() returns something other than SQLITE_OK. lockBtree() ** may return SQLITE_OK but leave pBt->pPage1 set to 0 if after ** reading page 1 it discovers that the page-size of the database ** file is not pBt->pageSize. In this case lockBtree() will update ** pBt->pageSize to the page-size of the file on disk. |
︙ | ︙ | |||
2510 2511 2512 2513 2514 2515 2516 | } } } if( rc!=SQLITE_OK ){ unlockBtreeIfUnused(pBt); } | | < > > > | > > > > > > > > | > > > > | 2546 2547 2548 2549 2550 2551 2552 2553 2554 2555 2556 2557 2558 2559 2560 2561 2562 2563 2564 2565 2566 2567 2568 2569 2570 2571 2572 2573 2574 2575 2576 2577 2578 2579 2580 2581 2582 2583 2584 2585 2586 2587 2588 2589 2590 2591 2592 2593 2594 2595 2596 2597 2598 2599 | } } } if( rc!=SQLITE_OK ){ unlockBtreeIfUnused(pBt); } }while( (rc&0xFF)==SQLITE_BUSY && pBt->inTransaction==TRANS_NONE && btreeInvokeBusyHandler(pBt) ); if( rc==SQLITE_OK ){ if( p->inTrans==TRANS_NONE ){ pBt->nTransaction++; #ifndef SQLITE_OMIT_SHARED_CACHE if( p->sharable ){ assert( p->lock.pBtree==p && p->lock.iTable==1 ); p->lock.eLock = READ_LOCK; p->lock.pNext = pBt->pLock; pBt->pLock = &p->lock; } #endif } p->inTrans = (wrflag?TRANS_WRITE:TRANS_READ); if( p->inTrans>pBt->inTransaction ){ pBt->inTransaction = p->inTrans; } if( wrflag ){ MemPage *pPage1 = pBt->pPage1; #ifndef SQLITE_OMIT_SHARED_CACHE assert( !pBt->pWriter ); pBt->pWriter = p; pBt->isExclusive = (u8)(wrflag>1); #endif /* If the db-size header field is incorrect (as it may be if an old ** client has been writing the database file), update it now. Doing ** this sooner rather than later means the database size can safely ** re-read the database size from page 1 if a savepoint or transaction ** rollback occurs within the transaction. */ if( pBt->nPage!=get4byte(&pPage1->aData[28]) ){ rc = sqlite3PagerWrite(pPage1->pDbPage); if( rc==SQLITE_OK ){ put4byte(&pPage1->aData[28], pBt->nPage); } } } } trans_begun: if( rc==SQLITE_OK && wrflag ){ /* This call makes sure that the pager has the correct number of ** open savepoints. If the second parameter is greater than 0 and |
︙ | ︙ | |||
2765 2766 2767 2768 2769 2770 2771 2772 2773 2774 2775 2776 | ** process is complete. If nFin is zero, it is assumed that ** incrVacuumStep() will be called a finite amount of times ** which may or may not empty the freelist. A full autovacuum ** has nFin>0. A "PRAGMA incremental_vacuum" has nFin==0. */ static int incrVacuumStep(BtShared *pBt, Pgno nFin, Pgno iLastPg){ Pgno nFreeList; /* Number of pages still on the free-list */ assert( sqlite3_mutex_held(pBt->mutex) ); assert( iLastPg>nFin ); if( !PTRMAP_ISPAGE(pBt, iLastPg) && iLastPg!=PENDING_BYTE_PAGE(pBt) ){ | > < | 2815 2816 2817 2818 2819 2820 2821 2822 2823 2824 2825 2826 2827 2828 2829 2830 2831 2832 2833 2834 | ** process is complete. If nFin is zero, it is assumed that ** incrVacuumStep() will be called a finite amount of times ** which may or may not empty the freelist. A full autovacuum ** has nFin>0. A "PRAGMA incremental_vacuum" has nFin==0. */ static int incrVacuumStep(BtShared *pBt, Pgno nFin, Pgno iLastPg){ Pgno nFreeList; /* Number of pages still on the free-list */ int rc; assert( sqlite3_mutex_held(pBt->mutex) ); assert( iLastPg>nFin ); if( !PTRMAP_ISPAGE(pBt, iLastPg) && iLastPg!=PENDING_BYTE_PAGE(pBt) ){ u8 eType; Pgno iPtrPage; nFreeList = get4byte(&pBt->pPage1->aData[36]); if( nFreeList==0 ){ return SQLITE_DONE; } |
︙ | ︙ | |||
2846 2847 2848 2849 2850 2851 2852 | } if( nFin==0 ){ iLastPg--; while( iLastPg==PENDING_BYTE_PAGE(pBt)||PTRMAP_ISPAGE(pBt, iLastPg) ){ if( PTRMAP_ISPAGE(pBt, iLastPg) ){ MemPage *pPg; | | > | 2896 2897 2898 2899 2900 2901 2902 2903 2904 2905 2906 2907 2908 2909 2910 2911 2912 2913 2914 2915 2916 2917 2918 2919 2920 2921 2922 2923 | } if( nFin==0 ){ iLastPg--; while( iLastPg==PENDING_BYTE_PAGE(pBt)||PTRMAP_ISPAGE(pBt, iLastPg) ){ if( PTRMAP_ISPAGE(pBt, iLastPg) ){ MemPage *pPg; rc = btreeGetPage(pBt, iLastPg, &pPg, 0); if( rc!=SQLITE_OK ){ return rc; } rc = sqlite3PagerWrite(pPg->pDbPage); releasePage(pPg); if( rc!=SQLITE_OK ){ return rc; } } iLastPg--; } sqlite3PagerTruncateImage(pBt->pPager, iLastPg); pBt->nPage = iLastPg; } return SQLITE_OK; } /* ** A write-transaction must be opened before calling this function. ** It performs a single unit of work towards an incremental vacuum. |
︙ | ︙ | |||
2881 2882 2883 2884 2885 2886 2887 | sqlite3BtreeEnter(p); assert( pBt->inTransaction==TRANS_WRITE && p->inTrans==TRANS_WRITE ); if( !pBt->autoVacuum ){ rc = SQLITE_DONE; }else{ invalidateAllOverflowCache(pBt); | | > > > > | 2932 2933 2934 2935 2936 2937 2938 2939 2940 2941 2942 2943 2944 2945 2946 2947 2948 2949 2950 | sqlite3BtreeEnter(p); assert( pBt->inTransaction==TRANS_WRITE && p->inTrans==TRANS_WRITE ); if( !pBt->autoVacuum ){ rc = SQLITE_DONE; }else{ invalidateAllOverflowCache(pBt); rc = incrVacuumStep(pBt, 0, btreePagecount(pBt)); if( rc==SQLITE_OK ){ rc = sqlite3PagerWrite(pBt->pPage1->pDbPage); put4byte(&pBt->pPage1->aData[28], pBt->nPage); } } sqlite3BtreeLeave(p); return rc; } /* ** This routine is called prior to sqlite3PagerCommit when a transaction |
︙ | ︙ | |||
2912 2913 2914 2915 2916 2917 2918 | Pgno nFin; /* Number of pages in database after autovacuuming */ Pgno nFree; /* Number of pages on the freelist initially */ Pgno nPtrmap; /* Number of PtrMap pages to be freed */ Pgno iFree; /* The next page to be freed */ int nEntry; /* Number of entries on one ptrmap page */ Pgno nOrig; /* Database size before freeing */ | | | 2967 2968 2969 2970 2971 2972 2973 2974 2975 2976 2977 2978 2979 2980 2981 | Pgno nFin; /* Number of pages in database after autovacuuming */ Pgno nFree; /* Number of pages on the freelist initially */ Pgno nPtrmap; /* Number of PtrMap pages to be freed */ Pgno iFree; /* The next page to be freed */ int nEntry; /* Number of entries on one ptrmap page */ Pgno nOrig; /* Database size before freeing */ nOrig = btreePagecount(pBt); if( PTRMAP_ISPAGE(pBt, nOrig) || nOrig==PENDING_BYTE_PAGE(pBt) ){ /* It is not possible to create a database for which the final page ** is either a pointer-map page or the pending-byte page. If one ** is encountered, this indicates corruption. */ return SQLITE_CORRUPT_BKPT; } |
︙ | ︙ | |||
2937 2938 2939 2940 2941 2942 2943 | } if( nFin>nOrig ) return SQLITE_CORRUPT_BKPT; for(iFree=nOrig; iFree>nFin && rc==SQLITE_OK; iFree--){ rc = incrVacuumStep(pBt, nFin, iFree); } if( (rc==SQLITE_DONE || rc==SQLITE_OK) && nFree>0 ){ | < > > | 2992 2993 2994 2995 2996 2997 2998 2999 3000 3001 3002 3003 3004 3005 3006 3007 3008 3009 3010 3011 | } if( nFin>nOrig ) return SQLITE_CORRUPT_BKPT; for(iFree=nOrig; iFree>nFin && rc==SQLITE_OK; iFree--){ rc = incrVacuumStep(pBt, nFin, iFree); } if( (rc==SQLITE_DONE || rc==SQLITE_OK) && nFree>0 ){ rc = sqlite3PagerWrite(pBt->pPage1->pDbPage); put4byte(&pBt->pPage1->aData[32], 0); put4byte(&pBt->pPage1->aData[36], 0); put4byte(&pBt->pPage1->aData[28], nFin); sqlite3PagerTruncateImage(pBt->pPager, nFin); pBt->nPage = nFin; } if( rc!=SQLITE_OK ){ sqlite3PagerRollback(pPager); } } assert( nRef==sqlite3PagerRefcount(pPager) ); |
︙ | ︙ | |||
3191 3192 3193 3194 3195 3196 3197 3198 3199 3200 3201 3202 3203 3204 | rc = rc2; } /* The rollback may have destroyed the pPage1->aData value. So ** call btreeGetPage() on page 1 again to make ** sure pPage1->aData is set correctly. */ if( btreeGetPage(pBt, 1, &pPage1, 0)==SQLITE_OK ){ releasePage(pPage1); } assert( countWriteCursors(pBt)==0 ); pBt->inTransaction = TRANS_READ; } btreeEndTransaction(p); | > > > > > | 3247 3248 3249 3250 3251 3252 3253 3254 3255 3256 3257 3258 3259 3260 3261 3262 3263 3264 3265 | rc = rc2; } /* The rollback may have destroyed the pPage1->aData value. So ** call btreeGetPage() on page 1 again to make ** sure pPage1->aData is set correctly. */ if( btreeGetPage(pBt, 1, &pPage1, 0)==SQLITE_OK ){ int nPage = get4byte(28+(u8*)pPage1->aData); testcase( nPage==0 ); if( nPage==0 ) sqlite3PagerPagecount(pBt->pPager, &nPage); testcase( pBt->nPage!=nPage ); pBt->nPage = nPage; releasePage(pPage1); } assert( countWriteCursors(pBt)==0 ); pBt->inTransaction = TRANS_READ; } btreeEndTransaction(p); |
︙ | ︙ | |||
3228 3229 3230 3231 3232 3233 3234 | int rc; BtShared *pBt = p->pBt; sqlite3BtreeEnter(p); assert( p->inTrans==TRANS_WRITE ); assert( pBt->readOnly==0 ); assert( iStatement>0 ); assert( iStatement>p->db->nSavepoint ); | < < < | | | | | | | < | 3289 3290 3291 3292 3293 3294 3295 3296 3297 3298 3299 3300 3301 3302 3303 3304 3305 3306 3307 3308 3309 | int rc; BtShared *pBt = p->pBt; sqlite3BtreeEnter(p); assert( p->inTrans==TRANS_WRITE ); assert( pBt->readOnly==0 ); assert( iStatement>0 ); assert( iStatement>p->db->nSavepoint ); assert( pBt->inTransaction==TRANS_WRITE ); /* At the pager level, a statement transaction is a savepoint with ** an index greater than all savepoints created explicitly using ** SQL statements. It is illegal to open, release or rollback any ** such savepoints while the statement transaction savepoint is active. */ rc = sqlite3PagerOpenSavepoint(pBt->pPager, iStatement); sqlite3BtreeLeave(p); return rc; } /* ** The second argument to this function, op, is always SAVEPOINT_ROLLBACK ** or SAVEPOINT_RELEASE. This function either releases or rolls back the |
︙ | ︙ | |||
3264 3265 3266 3267 3268 3269 3270 3271 3272 3273 3274 3275 3276 3277 3278 | if( p && p->inTrans==TRANS_WRITE ){ BtShared *pBt = p->pBt; assert( op==SAVEPOINT_RELEASE || op==SAVEPOINT_ROLLBACK ); assert( iSavepoint>=0 || (iSavepoint==-1 && op==SAVEPOINT_ROLLBACK) ); sqlite3BtreeEnter(p); rc = sqlite3PagerSavepoint(pBt->pPager, op, iSavepoint); if( rc==SQLITE_OK ){ rc = newDatabase(pBt); } sqlite3BtreeLeave(p); } return rc; } /* | > > > > > | 3321 3322 3323 3324 3325 3326 3327 3328 3329 3330 3331 3332 3333 3334 3335 3336 3337 3338 3339 3340 | if( p && p->inTrans==TRANS_WRITE ){ BtShared *pBt = p->pBt; assert( op==SAVEPOINT_RELEASE || op==SAVEPOINT_ROLLBACK ); assert( iSavepoint>=0 || (iSavepoint==-1 && op==SAVEPOINT_ROLLBACK) ); sqlite3BtreeEnter(p); rc = sqlite3PagerSavepoint(pBt->pPager, op, iSavepoint); if( rc==SQLITE_OK ){ if( iSavepoint<0 && pBt->initiallyEmpty ) pBt->nPage = 0; rc = newDatabase(pBt); pBt->nPage = get4byte(28 + pBt->pPage1->aData); if( pBt->nPage==0 ){ sqlite3PagerPagecount(pBt->pPager, (int*)&pBt->nPage); } } sqlite3BtreeLeave(p); } return rc; } /* |
︙ | ︙ | |||
3330 3331 3332 3333 3334 3335 3336 | assert( p->inTrans>TRANS_NONE ); assert( wrFlag==0 || p->inTrans==TRANS_WRITE ); assert( pBt->pPage1 && pBt->pPage1->aData ); if( NEVER(wrFlag && pBt->readOnly) ){ return SQLITE_READONLY; } | | | 3392 3393 3394 3395 3396 3397 3398 3399 3400 3401 3402 3403 3404 3405 3406 | assert( p->inTrans>TRANS_NONE ); assert( wrFlag==0 || p->inTrans==TRANS_WRITE ); assert( pBt->pPage1 && pBt->pPage1->aData ); if( NEVER(wrFlag && pBt->readOnly) ){ return SQLITE_READONLY; } if( iTable==1 && btreePagecount(pBt)==0 ){ return SQLITE_EMPTY; } /* Now that no other errors can occur, finish filling in the BtCursor ** variables and link the cursor into the BtShared list. */ pCur->pgnoRoot = (Pgno)iTable; pCur->iPage = -1; |
︙ | ︙ | |||
3601 3602 3603 3604 3605 3606 3607 | Pgno iGuess = ovfl+1; u8 eType; while( PTRMAP_ISPAGE(pBt, iGuess) || iGuess==PENDING_BYTE_PAGE(pBt) ){ iGuess++; } | | | 3663 3664 3665 3666 3667 3668 3669 3670 3671 3672 3673 3674 3675 3676 3677 | Pgno iGuess = ovfl+1; u8 eType; while( PTRMAP_ISPAGE(pBt, iGuess) || iGuess==PENDING_BYTE_PAGE(pBt) ){ iGuess++; } if( iGuess<=btreePagecount(pBt) ){ rc = ptrmapGet(pBt, iGuess, &eType, &pgno); if( rc==SQLITE_OK && eType==PTRMAP_OVERFLOW2 && pgno==ovfl ){ next = iGuess; rc = SQLITE_DONE; } } } |
︙ | ︙ | |||
4196 4197 4198 4199 4200 4201 4202 | assert( cursorHoldsMutex(pCur) ); assert( sqlite3_mutex_held(pCur->pBtree->db->mutex) ); rc = moveToRoot(pCur); if( rc==SQLITE_OK ){ if( pCur->eState==CURSOR_INVALID ){ assert( pCur->apPage[pCur->iPage]->nCell==0 ); *pRes = 1; | < | 4258 4259 4260 4261 4262 4263 4264 4265 4266 4267 4268 4269 4270 4271 | assert( cursorHoldsMutex(pCur) ); assert( sqlite3_mutex_held(pCur->pBtree->db->mutex) ); rc = moveToRoot(pCur); if( rc==SQLITE_OK ){ if( pCur->eState==CURSOR_INVALID ){ assert( pCur->apPage[pCur->iPage]->nCell==0 ); *pRes = 1; }else{ assert( pCur->apPage[pCur->iPage]->nCell>0 ); *pRes = 0; rc = moveToLeftmost(pCur); } } return rc; |
︙ | ︙ | |||
4633 4634 4635 4636 4637 4638 4639 | u32 k; /* Number of leaves on the trunk of the freelist */ MemPage *pTrunk = 0; MemPage *pPrevTrunk = 0; Pgno mxPage; /* Total size of the database file */ assert( sqlite3_mutex_held(pBt->mutex) ); pPage1 = pBt->pPage1; | | | 4694 4695 4696 4697 4698 4699 4700 4701 4702 4703 4704 4705 4706 4707 4708 | u32 k; /* Number of leaves on the trunk of the freelist */ MemPage *pTrunk = 0; MemPage *pPrevTrunk = 0; Pgno mxPage; /* Total size of the database file */ assert( sqlite3_mutex_held(pBt->mutex) ); pPage1 = pBt->pPage1; mxPage = btreePagecount(pBt); n = get4byte(&pPage1->aData[36]); testcase( n==mxPage-1 ); if( n>=mxPage ){ return SQLITE_CORRUPT_BKPT; } if( n>0 ){ /* There are pages on the freelist. Reuse one of those pages. */ |
︙ | ︙ | |||
4829 4830 4831 4832 4833 4834 4835 | } releasePage(pPrevTrunk); pPrevTrunk = 0; }while( searchList ); }else{ /* There are no pages on the freelist, so create a new page at the ** end of the file */ | | | | | < | < | | | | | | > > | | 4890 4891 4892 4893 4894 4895 4896 4897 4898 4899 4900 4901 4902 4903 4904 4905 4906 4907 4908 4909 4910 4911 4912 4913 4914 4915 4916 4917 4918 4919 4920 4921 4922 4923 4924 4925 4926 4927 4928 4929 4930 4931 4932 | } releasePage(pPrevTrunk); pPrevTrunk = 0; }while( searchList ); }else{ /* There are no pages on the freelist, so create a new page at the ** end of the file */ rc = sqlite3PagerWrite(pBt->pPage1->pDbPage); if( rc ) return rc; pBt->nPage++; if( pBt->nPage==PENDING_BYTE_PAGE(pBt) ) pBt->nPage++; #ifndef SQLITE_OMIT_AUTOVACUUM if( pBt->autoVacuum && PTRMAP_ISPAGE(pBt, pBt->nPage) ){ /* If *pPgno refers to a pointer-map page, allocate two new pages ** at the end of the file instead of one. The first allocated page ** becomes a new pointer-map page, the second is used by the caller. */ MemPage *pPg = 0; TRACE(("ALLOCATE: %d from end of file (pointer-map page)\n", pBt->nPage)); assert( pBt->nPage!=PENDING_BYTE_PAGE(pBt) ); rc = btreeGetPage(pBt, pBt->nPage, &pPg, 1); if( rc==SQLITE_OK ){ rc = sqlite3PagerWrite(pPg->pDbPage); releasePage(pPg); } if( rc ) return rc; pBt->nPage++; if( pBt->nPage==PENDING_BYTE_PAGE(pBt) ){ pBt->nPage++; } } #endif put4byte(28 + (u8*)pBt->pPage1->aData, pBt->nPage); *pPgno = pBt->nPage; assert( *pPgno!=PENDING_BYTE_PAGE(pBt) ); rc = btreeGetPage(pBt, *pPgno, ppPage, 1); if( rc ) return rc; rc = sqlite3PagerWrite((*ppPage)->pDbPage); if( rc!=SQLITE_OK ){ releasePage(*ppPage); } TRACE(("ALLOCATE: %d from end of file\n", *pPgno)); } |
︙ | ︙ | |||
5047 5048 5049 5050 5051 5052 5053 | assert( pBt->usableSize > 4 ); ovflPageSize = pBt->usableSize - 4; nOvfl = (info.nPayload - info.nLocal + ovflPageSize - 1)/ovflPageSize; assert( ovflPgno==0 || nOvfl>0 ); while( nOvfl-- ){ Pgno iNext = 0; MemPage *pOvfl = 0; | | | 5108 5109 5110 5111 5112 5113 5114 5115 5116 5117 5118 5119 5120 5121 5122 | assert( pBt->usableSize > 4 ); ovflPageSize = pBt->usableSize - 4; nOvfl = (info.nPayload - info.nLocal + ovflPageSize - 1)/ovflPageSize; assert( ovflPgno==0 || nOvfl>0 ); while( nOvfl-- ){ Pgno iNext = 0; MemPage *pOvfl = 0; if( ovflPgno<2 || ovflPgno>btreePagecount(pBt) ){ /* 0 is not a legal page number and page 1 cannot be an ** overflow page. Therefore if ovflPgno<2 or past the end of the ** file the database must be corrupt. */ return SQLITE_CORRUPT_BKPT; } if( nOvfl ){ rc = getOverflowPage(pBt, ovflPgno, &pOvfl, &iNext); |
︙ | ︙ | |||
6879 6880 6881 6882 6883 6884 6885 6886 | /* Update the pointer-map and meta-data with the new root-page number. */ ptrmapPut(pBt, pgnoRoot, PTRMAP_ROOTPAGE, 0, &rc); if( rc ){ releasePage(pRoot); return rc; } rc = sqlite3BtreeUpdateMeta(p, 4, pgnoRoot); | > > > > > > | | 6940 6941 6942 6943 6944 6945 6946 6947 6948 6949 6950 6951 6952 6953 6954 6955 6956 6957 6958 6959 6960 6961 | /* Update the pointer-map and meta-data with the new root-page number. */ ptrmapPut(pBt, pgnoRoot, PTRMAP_ROOTPAGE, 0, &rc); if( rc ){ releasePage(pRoot); return rc; } /* When the new root page was allocated, page 1 was made writable in ** order either to increase the database filesize, or to decrement the ** freelist count. Hence, the sqlite3BtreeUpdateMeta() call cannot fail. */ assert( sqlite3PagerIswriteable(pBt->pPage1->pDbPage) ); rc = sqlite3BtreeUpdateMeta(p, 4, pgnoRoot); if( NEVER(rc) ){ releasePage(pRoot); return rc; } }else{ rc = allocateBtreePage(pBt, &pRoot, &pgnoRoot, 1, 0); if( rc ) return rc; |
︙ | ︙ | |||
6920 6921 6922 6923 6924 6925 6926 | ){ MemPage *pPage; int rc; unsigned char *pCell; int i; assert( sqlite3_mutex_held(pBt->mutex) ); | | | 6987 6988 6989 6990 6991 6992 6993 6994 6995 6996 6997 6998 6999 7000 7001 | ){ MemPage *pPage; int rc; unsigned char *pCell; int i; assert( sqlite3_mutex_held(pBt->mutex) ); if( pgno>btreePagecount(pBt) ){ return SQLITE_CORRUPT_BKPT; } rc = getAndInitPage(pBt, pgno, &pPage); if( rc ) return rc; for(i=0; i<pPage->nCell; i++){ pCell = findCell(pPage, i); |
︙ | ︙ | |||
7671 7672 7673 7674 7675 7676 7677 | char zErr[100]; sqlite3BtreeEnter(p); assert( p->inTrans>TRANS_NONE && pBt->inTransaction>TRANS_NONE ); nRef = sqlite3PagerRefcount(pBt->pPager); sCheck.pBt = pBt; sCheck.pPager = pBt->pPager; | | | 7738 7739 7740 7741 7742 7743 7744 7745 7746 7747 7748 7749 7750 7751 7752 | char zErr[100]; sqlite3BtreeEnter(p); assert( p->inTrans>TRANS_NONE && pBt->inTransaction>TRANS_NONE ); nRef = sqlite3PagerRefcount(pBt->pPager); sCheck.pBt = pBt; sCheck.pPager = pBt->pPager; sCheck.nPage = btreePagecount(sCheck.pBt); sCheck.mxErr = mxErr; sCheck.nErr = 0; sCheck.mallocFailed = 0; *pnErr = 0; if( sCheck.nPage==0 ){ sqlite3BtreeLeave(p); return 0; |
︙ | ︙ | |||
7939 7940 7941 7942 7943 7944 7945 | assert( cursorHoldsMutex(pCur) ); assert( sqlite3_mutex_held(pCur->pBtree->db->mutex) ); assert(!pCur->isIncrblobHandle); assert(!pCur->aOverflow); pCur->isIncrblobHandle = 1; } #endif | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 8006 8007 8008 8009 8010 8011 8012 8013 8014 8015 8016 8017 8018 8019 8020 8021 8022 8023 8024 8025 8026 8027 8028 8029 8030 8031 8032 8033 8034 8035 8036 8037 8038 8039 8040 8041 8042 8043 8044 8045 8046 8047 8048 | assert( cursorHoldsMutex(pCur) ); assert( sqlite3_mutex_held(pCur->pBtree->db->mutex) ); assert(!pCur->isIncrblobHandle); assert(!pCur->aOverflow); pCur->isIncrblobHandle = 1; } #endif /* ** Set both the "read version" (single byte at byte offset 18) and ** "write version" (single byte at byte offset 19) fields in the database ** header to iVersion. */ int sqlite3BtreeSetVersion(Btree *pBtree, int iVersion){ BtShared *pBt = pBtree->pBt; int rc; /* Return code */ assert( pBtree->inTrans==TRANS_NONE ); assert( iVersion==1 || iVersion==2 ); /* If setting the version fields to 1, do not automatically open the ** WAL connection, even if the version fields are currently set to 2. */ pBt->doNotUseWAL = (u8)(iVersion==1); rc = sqlite3BtreeBeginTrans(pBtree, 0); if( rc==SQLITE_OK ){ u8 *aData = pBt->pPage1->aData; if( aData[18]!=(u8)iVersion || aData[19]!=(u8)iVersion ){ rc = sqlite3BtreeBeginTrans(pBtree, 2); if( rc==SQLITE_OK ){ rc = sqlite3PagerWrite(pBt->pPage1->pDbPage); if( rc==SQLITE_OK ){ aData[18] = (u8)iVersion; aData[19] = (u8)iVersion; } } } } pBt->doNotUseWAL = 0; return rc; } |
Changes to src/btree.h.
︙ | ︙ | |||
77 78 79 80 81 82 83 84 85 86 87 88 89 90 | int sqlite3BtreeClose(Btree*); int sqlite3BtreeSetCacheSize(Btree*,int); int sqlite3BtreeSetSafetyLevel(Btree*,int,int); int sqlite3BtreeSyncDisabled(Btree*); int sqlite3BtreeSetPageSize(Btree *p, int nPagesize, int nReserve, int eFix); int sqlite3BtreeGetPageSize(Btree*); int sqlite3BtreeMaxPageCount(Btree*,int); int sqlite3BtreeSecureDelete(Btree*,int); int sqlite3BtreeGetReserve(Btree*); int sqlite3BtreeSetAutoVacuum(Btree *, int); int sqlite3BtreeGetAutoVacuum(Btree *); int sqlite3BtreeBeginTrans(Btree*,int); int sqlite3BtreeCommitPhaseOne(Btree*, const char *zMaster); int sqlite3BtreeCommitPhaseTwo(Btree*); | > | 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 | int sqlite3BtreeClose(Btree*); int sqlite3BtreeSetCacheSize(Btree*,int); int sqlite3BtreeSetSafetyLevel(Btree*,int,int); int sqlite3BtreeSyncDisabled(Btree*); int sqlite3BtreeSetPageSize(Btree *p, int nPagesize, int nReserve, int eFix); int sqlite3BtreeGetPageSize(Btree*); int sqlite3BtreeMaxPageCount(Btree*,int); u32 sqlite3BtreeLastPage(Btree*); int sqlite3BtreeSecureDelete(Btree*,int); int sqlite3BtreeGetReserve(Btree*); int sqlite3BtreeSetAutoVacuum(Btree *, int); int sqlite3BtreeGetAutoVacuum(Btree *); int sqlite3BtreeBeginTrans(Btree*,int); int sqlite3BtreeCommitPhaseOne(Btree*, const char *zMaster); int sqlite3BtreeCommitPhaseTwo(Btree*); |
︙ | ︙ | |||
180 181 182 183 184 185 186 187 188 189 190 191 192 193 | char *sqlite3BtreeIntegrityCheck(Btree*, int *aRoot, int nRoot, int, int*); struct Pager *sqlite3BtreePager(Btree*); int sqlite3BtreePutData(BtCursor*, u32 offset, u32 amt, void*); void sqlite3BtreeCacheOverflow(BtCursor *); void sqlite3BtreeClearCursor(BtCursor *); #ifndef NDEBUG int sqlite3BtreeCursorIsValid(BtCursor*); #endif #ifndef SQLITE_OMIT_BTREECOUNT int sqlite3BtreeCount(BtCursor *, i64 *); | > > | 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 | char *sqlite3BtreeIntegrityCheck(Btree*, int *aRoot, int nRoot, int, int*); struct Pager *sqlite3BtreePager(Btree*); int sqlite3BtreePutData(BtCursor*, u32 offset, u32 amt, void*); void sqlite3BtreeCacheOverflow(BtCursor *); void sqlite3BtreeClearCursor(BtCursor *); int sqlite3BtreeSetVersion(Btree *pBt, int iVersion); #ifndef NDEBUG int sqlite3BtreeCursorIsValid(BtCursor*); #endif #ifndef SQLITE_OMIT_BTREECOUNT int sqlite3BtreeCount(BtCursor *, i64 *); |
︙ | ︙ |
Changes to src/btreeInt.h.
︙ | ︙ | |||
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 | Pager *pPager; /* The page cache */ sqlite3 *db; /* Database connection currently using this Btree */ BtCursor *pCursor; /* A list of all open cursors */ MemPage *pPage1; /* First page of the database */ u8 readOnly; /* True if the underlying file is readonly */ u8 pageSizeFixed; /* True if the page size can no longer be changed */ u8 secureDelete; /* True if secure_delete is enabled */ #ifndef SQLITE_OMIT_AUTOVACUUM u8 autoVacuum; /* True if auto-vacuum is enabled */ u8 incrVacuum; /* True if incr-vacuum is enabled */ #endif u16 pageSize; /* Total number of bytes on a page */ u16 usableSize; /* Number of usable bytes on each page */ u16 maxLocal; /* Maximum local payload in non-LEAFDATA tables */ u16 minLocal; /* Minimum local payload in non-LEAFDATA tables */ u16 maxLeaf; /* Maximum local payload in a LEAFDATA table */ u16 minLeaf; /* Minimum local payload in a LEAFDATA table */ u8 inTransaction; /* Transaction state */ int nTransaction; /* Number of open transactions (read + write) */ void *pSchema; /* Pointer to space allocated by sqlite3BtreeSchema() */ void (*xFreeSchema)(void*); /* Destructor for BtShared.pSchema */ sqlite3_mutex *mutex; /* Non-recursive mutex required to access this struct */ Bitvec *pHasContent; /* Set of pages moved to free-list this transaction */ #ifndef SQLITE_OMIT_SHARED_CACHE int nRef; /* Number of references to this structure */ BtShared *pNext; /* Next on a list of sharable BtShared structs */ | > > > | 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 | Pager *pPager; /* The page cache */ sqlite3 *db; /* Database connection currently using this Btree */ BtCursor *pCursor; /* A list of all open cursors */ MemPage *pPage1; /* First page of the database */ u8 readOnly; /* True if the underlying file is readonly */ u8 pageSizeFixed; /* True if the page size can no longer be changed */ u8 secureDelete; /* True if secure_delete is enabled */ u8 initiallyEmpty; /* Database is empty at start of transaction */ #ifndef SQLITE_OMIT_AUTOVACUUM u8 autoVacuum; /* True if auto-vacuum is enabled */ u8 incrVacuum; /* True if incr-vacuum is enabled */ #endif u16 pageSize; /* Total number of bytes on a page */ u16 usableSize; /* Number of usable bytes on each page */ u16 maxLocal; /* Maximum local payload in non-LEAFDATA tables */ u16 minLocal; /* Minimum local payload in non-LEAFDATA tables */ u16 maxLeaf; /* Maximum local payload in a LEAFDATA table */ u16 minLeaf; /* Minimum local payload in a LEAFDATA table */ u8 inTransaction; /* Transaction state */ u8 doNotUseWAL; /* If true, do not open write-ahead-log file */ int nTransaction; /* Number of open transactions (read + write) */ u32 nPage; /* Number of pages in the database */ void *pSchema; /* Pointer to space allocated by sqlite3BtreeSchema() */ void (*xFreeSchema)(void*); /* Destructor for BtShared.pSchema */ sqlite3_mutex *mutex; /* Non-recursive mutex required to access this struct */ Bitvec *pHasContent; /* Set of pages moved to free-list this transaction */ #ifndef SQLITE_OMIT_SHARED_CACHE int nRef; /* Number of references to this structure */ BtShared *pNext; /* Next on a list of sharable BtShared structs */ |
︙ | ︙ |
Changes to src/build.c.
︙ | ︙ | |||
2610 2611 2612 2613 2614 2615 2616 2617 2618 2619 2620 2621 2622 2623 | for(j=0, pTabCol=pTab->aCol; j<pTab->nCol; j++, pTabCol++){ if( sqlite3StrICmp(zColName, pTabCol->zName)==0 ) break; } if( j>=pTab->nCol ){ sqlite3ErrorMsg(pParse, "table %s has no column named %s", pTab->zName, zColName); goto exit_create_index; } pIndex->aiColumn[i] = j; /* Justification of the ALWAYS(pListItem->pExpr->pColl): Because of ** the way the "idxlist" non-terminal is constructed by the parser, ** if pListItem->pExpr is not null then either pListItem->pExpr->pColl ** must exist or else there must have been an OOM error. But if there | > | 2610 2611 2612 2613 2614 2615 2616 2617 2618 2619 2620 2621 2622 2623 2624 | for(j=0, pTabCol=pTab->aCol; j<pTab->nCol; j++, pTabCol++){ if( sqlite3StrICmp(zColName, pTabCol->zName)==0 ) break; } if( j>=pTab->nCol ){ sqlite3ErrorMsg(pParse, "table %s has no column named %s", pTab->zName, zColName); pParse->checkSchema = 1; goto exit_create_index; } pIndex->aiColumn[i] = j; /* Justification of the ALWAYS(pListItem->pExpr->pColl): Because of ** the way the "idxlist" non-terminal is constructed by the parser, ** if pListItem->pExpr is not null then either pListItem->pExpr->pColl ** must exist or else there must have been an OOM error. But if there |
︙ | ︙ | |||
3378 3379 3380 3381 3382 3383 3384 | ** release or rollback an SQL savepoint. */ void sqlite3Savepoint(Parse *pParse, int op, Token *pName){ char *zName = sqlite3NameFromToken(pParse->db, pName); if( zName ){ Vdbe *v = sqlite3GetVdbe(pParse); #ifndef SQLITE_OMIT_AUTHORIZATION | | | 3379 3380 3381 3382 3383 3384 3385 3386 3387 3388 3389 3390 3391 3392 3393 | ** release or rollback an SQL savepoint. */ void sqlite3Savepoint(Parse *pParse, int op, Token *pName){ char *zName = sqlite3NameFromToken(pParse->db, pName); if( zName ){ Vdbe *v = sqlite3GetVdbe(pParse); #ifndef SQLITE_OMIT_AUTHORIZATION static const char * const az[] = { "BEGIN", "RELEASE", "ROLLBACK" }; assert( !SAVEPOINT_BEGIN && SAVEPOINT_RELEASE==1 && SAVEPOINT_ROLLBACK==2 ); #endif if( !v || sqlite3AuthCheck(pParse, SQLITE_SAVEPOINT, az[op], zName, 0) ){ sqlite3DbFree(pParse->db, zName); return; } sqlite3VdbeAddOp4(v, OP_Savepoint, op, 0, 0, zName, P4_DYNAMIC); |
︙ | ︙ | |||
3418 3419 3420 3421 3422 3423 3424 | } db->aDb[1].pBt = pBt; assert( db->aDb[1].pSchema ); if( SQLITE_NOMEM==sqlite3BtreeSetPageSize(pBt, db->nextPagesize, -1, 0) ){ db->mallocFailed = 1; return 1; } | < | 3419 3420 3421 3422 3423 3424 3425 3426 3427 3428 3429 3430 3431 3432 | } db->aDb[1].pBt = pBt; assert( db->aDb[1].pSchema ); if( SQLITE_NOMEM==sqlite3BtreeSetPageSize(pBt, db->nextPagesize, -1, 0) ){ db->mallocFailed = 1; return 1; } } return 0; } /* ** Generate VDBE code that will verify the schema cookie and start ** a read-transaction for all named database files. |
︙ | ︙ |
Changes to src/callback.c.
︙ | ︙ | |||
348 349 350 351 352 353 354 355 356 357 358 359 360 361 | pBest = p; bestScore = score; } p = p->pNext; } /* If no match is found, search the built-in functions. ** ** Except, if createFlag is true, that means that we are trying to ** install a new function. Whatever FuncDef structure is returned will ** have fields overwritten with new information appropriate for the ** new function. But the FuncDefs for built-in functions are read-only. ** So we must not search for built-ins when creating a new function. */ | > > > > | > | 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 | pBest = p; bestScore = score; } p = p->pNext; } /* If no match is found, search the built-in functions. ** ** If the SQLITE_PreferBuiltin flag is set, then search the built-in ** functions even if a prior app-defined function was found. And give ** priority to built-in functions. ** ** Except, if createFlag is true, that means that we are trying to ** install a new function. Whatever FuncDef structure is returned will ** have fields overwritten with new information appropriate for the ** new function. But the FuncDefs for built-in functions are read-only. ** So we must not search for built-ins when creating a new function. */ if( !createFlag && (pBest==0 || (db->flags & SQLITE_PreferBuiltin)!=0) ){ FuncDefHash *pHash = &GLOBAL(FuncDefHash, sqlite3GlobalFunctions); bestScore = 0; p = functionSearch(pHash, h, zName, nName); while( p ){ int score = matchQuality(p, nArg, enc); if( score>bestScore ){ pBest = p; bestScore = score; } |
︙ | ︙ |
Changes to src/ctime.c.
︙ | ︙ | |||
166 167 168 169 170 171 172 173 174 175 176 177 178 179 | "OMIT_AUTHORIZATION", #endif #ifdef SQLITE_OMIT_AUTOINCREMENT "OMIT_AUTOINCREMENT", #endif #ifdef SQLITE_OMIT_AUTOINIT "OMIT_AUTOINIT", #endif #ifdef SQLITE_OMIT_AUTOVACUUM "OMIT_AUTOVACUUM", #endif #ifdef SQLITE_OMIT_BETWEEN_OPTIMIZATION "OMIT_BETWEEN_OPTIMIZATION", #endif | > > > | 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 | "OMIT_AUTHORIZATION", #endif #ifdef SQLITE_OMIT_AUTOINCREMENT "OMIT_AUTOINCREMENT", #endif #ifdef SQLITE_OMIT_AUTOINIT "OMIT_AUTOINIT", #endif #ifdef SQLITE_OMIT_AUTOMATIC_INDEX "OMIT_AUTOMATIC_INDEX", #endif #ifdef SQLITE_OMIT_AUTOVACUUM "OMIT_AUTOVACUUM", #endif #ifdef SQLITE_OMIT_BETWEEN_OPTIMIZATION "OMIT_BETWEEN_OPTIMIZATION", #endif |
︙ | ︙ | |||
188 189 190 191 192 193 194 | #endif #ifdef SQLITE_OMIT_CAST "OMIT_CAST", #endif #ifdef SQLITE_OMIT_CHECK "OMIT_CHECK", #endif | > | | | > | 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 | #endif #ifdef SQLITE_OMIT_CAST "OMIT_CAST", #endif #ifdef SQLITE_OMIT_CHECK "OMIT_CHECK", #endif /* // redundant ** #ifdef SQLITE_OMIT_COMPILEOPTION_DIAGS ** "OMIT_COMPILEOPTION_DIAGS", ** #endif */ #ifdef SQLITE_OMIT_COMPLETE "OMIT_COMPLETE", #endif #ifdef SQLITE_OMIT_COMPOUND_SELECT "OMIT_COMPOUND_SELECT", #endif #ifdef SQLITE_OMIT_DATETIME_FUNCS |
︙ | ︙ | |||
224 225 226 227 228 229 230 | #endif #ifdef SQLITE_OMIT_FOREIGN_KEY "OMIT_FOREIGN_KEY", #endif #ifdef SQLITE_OMIT_GET_TABLE "OMIT_GET_TABLE", #endif | < < < | 229 230 231 232 233 234 235 236 237 238 239 240 241 242 | #endif #ifdef SQLITE_OMIT_FOREIGN_KEY "OMIT_FOREIGN_KEY", #endif #ifdef SQLITE_OMIT_GET_TABLE "OMIT_GET_TABLE", #endif #ifdef SQLITE_OMIT_INCRBLOB "OMIT_INCRBLOB", #endif #ifdef SQLITE_OMIT_INTEGRITY_CHECK "OMIT_INTEGRITY_CHECK", #endif #ifdef SQLITE_OMIT_LIKE_OPTIMIZATION |
︙ | ︙ | |||
304 305 306 307 308 309 310 311 312 313 314 315 316 317 | "OMIT_VACUUM", #endif #ifdef SQLITE_OMIT_VIEW "OMIT_VIEW", #endif #ifdef SQLITE_OMIT_VIRTUALTABLE "OMIT_VIRTUALTABLE", #endif #ifdef SQLITE_OMIT_WSD "OMIT_WSD", #endif #ifdef SQLITE_OMIT_XFER_OPT "OMIT_XFER_OPT", #endif | > > > | 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 | "OMIT_VACUUM", #endif #ifdef SQLITE_OMIT_VIEW "OMIT_VIEW", #endif #ifdef SQLITE_OMIT_VIRTUALTABLE "OMIT_VIRTUALTABLE", #endif #ifdef SQLITE_OMIT_WAL "OMIT_WAL", #endif #ifdef SQLITE_OMIT_WSD "OMIT_WSD", #endif #ifdef SQLITE_OMIT_XFER_OPT "OMIT_XFER_OPT", #endif |
︙ | ︙ |
Changes to src/date.c.
︙ | ︙ | |||
310 311 312 313 314 315 316 | return 0; } /* ** Set the time to the current time reported by the VFS */ static void setDateTimeToCurrent(sqlite3_context *context, DateTime *p){ | < | < | 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 | return 0; } /* ** Set the time to the current time reported by the VFS */ static void setDateTimeToCurrent(sqlite3_context *context, DateTime *p){ sqlite3 *db = sqlite3_context_db_handle(context); sqlite3OsCurrentTimeInt64(db->pVfs, &p->iJD); p->validJD = 1; } /* ** Attempt to parse the given string into a Julian Day Number. Return ** the number of errors. ** |
︙ | ︙ | |||
1034 1035 1036 1037 1038 1039 1040 | sqlite3_context *context, int argc, sqlite3_value **argv ){ time_t t; char *zFormat = (char *)sqlite3_user_data(context); sqlite3 *db; | | | < | < < < < < < | 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 | sqlite3_context *context, int argc, sqlite3_value **argv ){ time_t t; char *zFormat = (char *)sqlite3_user_data(context); sqlite3 *db; sqlite3_int64 iT; char zBuf[20]; UNUSED_PARAMETER(argc); UNUSED_PARAMETER(argv); db = sqlite3_context_db_handle(context); sqlite3OsCurrentTimeInt64(db->pVfs, &iT); t = iT/1000 - 10000*(sqlite3_int64)21086676; #ifdef HAVE_GMTIME_R { struct tm sNow; gmtime_r(&t, &sNow); strftime(zBuf, 20, zFormat, &sNow); } #else |
︙ | ︙ |
Changes to src/delete.c.
︙ | ︙ | |||
504 505 506 507 508 509 510 | pParse->nMem += (1 + pTab->nCol); /* Populate the OLD.* pseudo-table register array. These values will be ** used by any BEFORE and AFTER triggers that exist. */ sqlite3VdbeAddOp2(v, OP_Copy, iRowid, iOld); for(iCol=0; iCol<pTab->nCol; iCol++){ if( mask==0xffffffff || mask&(1<<iCol) ){ | < < | | 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 | pParse->nMem += (1 + pTab->nCol); /* Populate the OLD.* pseudo-table register array. These values will be ** used by any BEFORE and AFTER triggers that exist. */ sqlite3VdbeAddOp2(v, OP_Copy, iRowid, iOld); for(iCol=0; iCol<pTab->nCol; iCol++){ if( mask==0xffffffff || mask&(1<<iCol) ){ sqlite3ExprCodeGetColumnOfTable(v, pTab, iCur, iCol, iOld+iCol+1); } } /* Invoke BEFORE DELETE trigger programs. */ sqlite3CodeRowTrigger(pParse, pTrigger, TK_DELETE, 0, TRIGGER_BEFORE, pTab, iOld, onconf, iLabel ); |
︙ | ︙ |
Changes to src/expr.c.
︙ | ︙ | |||
1501 1502 1503 1504 1505 1506 1507 1508 1509 1510 1511 | } } if( eType==0 ){ /* Could not found an existing table or index to use as the RHS b-tree. ** We will have to generate an ephemeral table to do the job. */ int rMayHaveNull = 0; eType = IN_INDEX_EPH; if( prNotFound ){ *prNotFound = rMayHaveNull = ++pParse->nMem; | > > > > | | | > > | 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 | } } if( eType==0 ){ /* Could not found an existing table or index to use as the RHS b-tree. ** We will have to generate an ephemeral table to do the job. */ double savedNQueryLoop = pParse->nQueryLoop; int rMayHaveNull = 0; eType = IN_INDEX_EPH; if( prNotFound ){ *prNotFound = rMayHaveNull = ++pParse->nMem; }else{ testcase( pParse->nQueryLoop>(double)1 ); pParse->nQueryLoop = (double)1; if( pX->pLeft->iColumn<0 && !ExprHasAnyProperty(pX, EP_xIsSelect) ){ eType = IN_INDEX_ROWID; } } sqlite3CodeSubselect(pParse, pX, rMayHaveNull, eType==IN_INDEX_ROWID); pParse->nQueryLoop = savedNQueryLoop; }else{ pX->iTable = iTab; } return eType; } #endif |
︙ | ︙ | |||
1631 1632 1633 1634 1635 1636 1637 | return 0; } pEList = pExpr->x.pSelect->pEList; if( ALWAYS(pEList!=0 && pEList->nExpr>0) ){ keyInfo.aColl[0] = sqlite3BinaryCompareCollSeq(pParse, pExpr->pLeft, pEList->a[0].pExpr); } | | | 1637 1638 1639 1640 1641 1642 1643 1644 1645 1646 1647 1648 1649 1650 1651 | return 0; } pEList = pExpr->x.pSelect->pEList; if( ALWAYS(pEList!=0 && pEList->nExpr>0) ){ keyInfo.aColl[0] = sqlite3BinaryCompareCollSeq(pParse, pExpr->pLeft, pEList->a[0].pExpr); } }else if( ALWAYS(pExpr->x.pList!=0) ){ /* Case 2: expr IN (exprlist) ** ** For each expression, build an index key from the evaluation and ** store it in the temporary table. If <expr> is a column, then use ** that columns affinity when building index keys. If <expr> is not ** a column, use numeric affinity. */ |
︙ | ︙ | |||
1701 1702 1703 1704 1705 1706 1707 | default: { /* If this has to be a scalar SELECT. Generate code to put the ** value of this select in a memory cell and record the number ** of the memory cell in iColumn. If this is an EXISTS, write ** an integer 0 (not exists) or 1 (exists) into a memory cell ** and record that memory cell in iColumn. */ | < | > | 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 | default: { /* If this has to be a scalar SELECT. Generate code to put the ** value of this select in a memory cell and record the number ** of the memory cell in iColumn. If this is an EXISTS, write ** an integer 0 (not exists) or 1 (exists) into a memory cell ** and record that memory cell in iColumn. */ Select *pSel; /* SELECT statement to encode */ SelectDest dest; /* How to deal with SELECt result */ testcase( pExpr->op==TK_EXISTS ); testcase( pExpr->op==TK_SELECT ); assert( pExpr->op==TK_EXISTS || pExpr->op==TK_SELECT ); assert( ExprHasProperty(pExpr, EP_xIsSelect) ); pSel = pExpr->x.pSelect; sqlite3SelectDestInit(&dest, 0, ++pParse->nMem); if( pExpr->op==TK_SELECT ){ dest.eDest = SRT_Mem; sqlite3VdbeAddOp2(v, OP_Null, 0, dest.iParm); VdbeComment((v, "Init subquery result")); }else{ dest.eDest = SRT_Exists; sqlite3VdbeAddOp2(v, OP_Integer, 0, dest.iParm); VdbeComment((v, "Init EXISTS result")); } sqlite3ExprDelete(pParse->db, pSel->pLimit); pSel->pLimit = sqlite3PExpr(pParse, TK_INTEGER, 0, 0, &sqlite3IntTokens[1]); if( sqlite3Select(pParse, pSel, &dest) ){ return 0; } rReg = dest.iParm; ExprSetIrreducible(pExpr); break; } |
︙ | ︙ | |||
1790 1791 1792 1793 1794 1795 1796 | affinity = comparisonAffinity(pExpr); /* Code the LHS, the <expr> from "<expr> IN (...)". */ sqlite3ExprCachePush(pParse); r1 = sqlite3GetTempReg(pParse); sqlite3ExprCode(pParse, pExpr->pLeft, r1); | > > > > > > > | > > > > > | | 1796 1797 1798 1799 1800 1801 1802 1803 1804 1805 1806 1807 1808 1809 1810 1811 1812 1813 1814 1815 1816 1817 1818 1819 1820 1821 1822 1823 | affinity = comparisonAffinity(pExpr); /* Code the LHS, the <expr> from "<expr> IN (...)". */ sqlite3ExprCachePush(pParse); r1 = sqlite3GetTempReg(pParse); sqlite3ExprCode(pParse, pExpr->pLeft, r1); /* If the LHS is NULL, then the result is either false or NULL depending ** on whether the RHS is empty or not, respectively. */ if( destIfNull==destIfFalse ){ /* Shortcut for the common case where the false and NULL outcomes are ** the same. */ sqlite3VdbeAddOp2(v, OP_IsNull, r1, destIfNull); }else{ int addr1 = sqlite3VdbeAddOp1(v, OP_NotNull, r1); sqlite3VdbeAddOp2(v, OP_Rewind, pExpr->iTable, destIfFalse); sqlite3VdbeAddOp2(v, OP_Goto, 0, destIfNull); sqlite3VdbeJumpHere(v, addr1); } if( eType==IN_INDEX_ROWID ){ /* In this case, the RHS is the ROWID of table b-tree */ sqlite3VdbeAddOp2(v, OP_MustBeInt, r1, destIfFalse); sqlite3VdbeAddOp3(v, OP_NotExists, pExpr->iTable, destIfFalse, r1); }else{ |
︙ | ︙ | |||
2077 2078 2079 2080 2081 2082 2083 2084 2085 2086 2087 2088 2089 2090 | struct yColCache *p; for(i=0, p=pParse->aColCache; i<SQLITE_N_COLCACHE; i++, p++){ if( p->iReg==iReg ){ p->tempReg = 0; } } } /* ** Generate code that will extract the iColumn-th column from ** table pTab and store the column value in a register. An effort ** is made to store the column value in register iReg, but this is ** not guaranteed. The location of the column value is returned. ** | > > > > > > > > > > > > > > > > > > > > > | 2095 2096 2097 2098 2099 2100 2101 2102 2103 2104 2105 2106 2107 2108 2109 2110 2111 2112 2113 2114 2115 2116 2117 2118 2119 2120 2121 2122 2123 2124 2125 2126 2127 2128 2129 | struct yColCache *p; for(i=0, p=pParse->aColCache; i<SQLITE_N_COLCACHE; i++, p++){ if( p->iReg==iReg ){ p->tempReg = 0; } } } /* ** Generate code to extract the value of the iCol-th column of a table. */ void sqlite3ExprCodeGetColumnOfTable( Vdbe *v, /* The VDBE under construction */ Table *pTab, /* The table containing the value */ int iTabCur, /* The cursor for this table */ int iCol, /* Index of the column to extract */ int regOut /* Extract the valud into this register */ ){ if( iCol<0 || iCol==pTab->iPKey ){ sqlite3VdbeAddOp2(v, OP_Rowid, iTabCur, regOut); }else{ int op = IsVirtual(pTab) ? OP_VColumn : OP_Column; sqlite3VdbeAddOp3(v, op, iTabCur, iCol, regOut); } if( iCol>=0 ){ sqlite3ColumnDefault(v, pTab, iCol, regOut); } } /* ** Generate code that will extract the iColumn-th column from ** table pTab and store the column value in a register. An effort ** is made to store the column value in register iReg, but this is ** not guaranteed. The location of the column value is returned. ** |
︙ | ︙ | |||
2106 2107 2108 2109 2110 2111 2112 | if( p->iReg>0 && p->iTable==iTable && p->iColumn==iColumn ){ p->lru = pParse->iCacheCnt++; sqlite3ExprCachePinRegister(pParse, p->iReg); return p->iReg; } } assert( v!=0 ); | < < < < < | < | 2145 2146 2147 2148 2149 2150 2151 2152 2153 2154 2155 2156 2157 2158 2159 | if( p->iReg>0 && p->iTable==iTable && p->iColumn==iColumn ){ p->lru = pParse->iCacheCnt++; sqlite3ExprCachePinRegister(pParse, p->iReg); return p->iReg; } } assert( v!=0 ); sqlite3ExprCodeGetColumnOfTable(v, pTab, iTable, iColumn, iReg); sqlite3ExprCacheStore(pParse, iTable, iColumn, iReg); return iReg; } /* ** Clear all column cache entries. */ |
︙ | ︙ | |||
2349 2350 2351 2352 2353 2354 2355 | assert( z[n]=='\'' ); zBlob = sqlite3HexToBlob(sqlite3VdbeDb(v), z, n); sqlite3VdbeAddOp4(v, OP_Blob, n/2, target, 0, zBlob, P4_DYNAMIC); break; } #endif case TK_VARIABLE: { | < < < < < < < < < < < < < < | | | < | 2382 2383 2384 2385 2386 2387 2388 2389 2390 2391 2392 2393 2394 2395 2396 2397 2398 2399 2400 2401 | assert( z[n]=='\'' ); zBlob = sqlite3HexToBlob(sqlite3VdbeDb(v), z, n); sqlite3VdbeAddOp4(v, OP_Blob, n/2, target, 0, zBlob, P4_DYNAMIC); break; } #endif case TK_VARIABLE: { assert( !ExprHasProperty(pExpr, EP_IntValue) ); assert( pExpr->u.zToken!=0 ); assert( pExpr->u.zToken[0]!=0 ); sqlite3VdbeAddOp2(v, OP_Variable, pExpr->iColumn, target); if( pExpr->u.zToken[1]!=0 ){ sqlite3VdbeChangeP4(v, -1, pExpr->u.zToken, 0); } break; } case TK_REGISTER: { inReg = pExpr->iTable; break; } |
︙ | ︙ | |||
3436 3437 3438 3439 3440 3441 3442 | ** expressions are the same. But if you get a 0 or 1 return, then you ** can be sure the expressions are the same. In the places where ** this routine is used, it does not hurt to get an extra 2 - that ** just might result in some slightly slower code. But returning ** an incorrect 0 or 1 could lead to a malfunction. */ int sqlite3ExprCompare(Expr *pA, Expr *pB){ | < | < < < < < < < < < < < > > > > > > > > > > > > > > > > > > > > > > > > > | 3454 3455 3456 3457 3458 3459 3460 3461 3462 3463 3464 3465 3466 3467 3468 3469 3470 3471 3472 3473 3474 3475 3476 3477 3478 3479 3480 3481 3482 3483 3484 3485 3486 3487 3488 3489 3490 3491 3492 3493 3494 3495 3496 3497 3498 3499 3500 3501 3502 3503 3504 3505 3506 3507 3508 3509 3510 3511 3512 3513 3514 3515 3516 3517 3518 3519 3520 3521 | ** expressions are the same. But if you get a 0 or 1 return, then you ** can be sure the expressions are the same. In the places where ** this routine is used, it does not hurt to get an extra 2 - that ** just might result in some slightly slower code. But returning ** an incorrect 0 or 1 could lead to a malfunction. */ int sqlite3ExprCompare(Expr *pA, Expr *pB){ if( pA==0||pB==0 ){ return pB==pA ? 0 : 2; } assert( !ExprHasAnyProperty(pA, EP_TokenOnly|EP_Reduced) ); assert( !ExprHasAnyProperty(pB, EP_TokenOnly|EP_Reduced) ); if( ExprHasProperty(pA, EP_xIsSelect) || ExprHasProperty(pB, EP_xIsSelect) ){ return 2; } if( (pA->flags & EP_Distinct)!=(pB->flags & EP_Distinct) ) return 2; if( pA->op!=pB->op ) return 2; if( sqlite3ExprCompare(pA->pLeft, pB->pLeft) ) return 2; if( sqlite3ExprCompare(pA->pRight, pB->pRight) ) return 2; if( sqlite3ExprListCompare(pA->x.pList, pB->x.pList) ) return 2; if( pA->iTable!=pB->iTable || pA->iColumn!=pB->iColumn ) return 2; if( ExprHasProperty(pA, EP_IntValue) ){ if( !ExprHasProperty(pB, EP_IntValue) || pA->u.iValue!=pB->u.iValue ){ return 2; } }else if( pA->op!=TK_COLUMN && pA->u.zToken ){ if( ExprHasProperty(pB, EP_IntValue) || NEVER(pB->u.zToken==0) ) return 2; if( sqlite3StrICmp(pA->u.zToken,pB->u.zToken)!=0 ){ return 2; } } if( (pA->flags & EP_ExpCollate)!=(pB->flags & EP_ExpCollate) ) return 1; if( (pA->flags & EP_ExpCollate)!=0 && pA->pColl!=pB->pColl ) return 2; return 0; } /* ** Compare two ExprList objects. Return 0 if they are identical and ** non-zero if they differ in any way. ** ** This routine might return non-zero for equivalent ExprLists. The ** only consequence will be disabled optimizations. But this routine ** must never return 0 if the two ExprList objects are different, or ** a malfunction will result. ** ** Two NULL pointers are considered to be the same. But a NULL pointer ** always differs from a non-NULL pointer. */ int sqlite3ExprListCompare(ExprList *pA, ExprList *pB){ int i; if( pA==0 && pB==0 ) return 0; if( pA==0 || pB==0 ) return 1; if( pA->nExpr!=pB->nExpr ) return 1; for(i=0; i<pA->nExpr; i++){ Expr *pExprA = pA->a[i].pExpr; Expr *pExprB = pB->a[i].pExpr; if( pA->a[i].sortOrder!=pB->a[i].sortOrder ) return 1; if( sqlite3ExprCompare(pExprA, pExprB) ) return 1; } return 0; } /* ** Add a new element to the pAggInfo->aCol[] array. Return the index of ** the new element. Return a negative number if malloc fails. */ static int addAggInfoColumn(sqlite3 *db, AggInfo *pInfo){ int i; |
︙ | ︙ |
Changes to src/func.c.
︙ | ︙ | |||
1410 1411 1412 1413 1414 1415 1416 | sqlite3_result_text(context, sqlite3StrAccumFinish(pAccum), -1, sqlite3_free); } } } /* | | | | < < < < | | | | < | 1410 1411 1412 1413 1414 1415 1416 1417 1418 1419 1420 1421 1422 1423 1424 1425 1426 1427 1428 1429 1430 1431 1432 | sqlite3_result_text(context, sqlite3StrAccumFinish(pAccum), -1, sqlite3_free); } } } /* ** This routine does per-connection function registration. Most ** of the built-in functions above are part of the global function set. ** This routine only deals with those that are not global. */ void sqlite3RegisterBuiltinFunctions(sqlite3 *db){ int rc = sqlite3_overload_function(db, "MATCH", 2); assert( rc==SQLITE_NOMEM || rc==SQLITE_OK ); if( rc==SQLITE_NOMEM ){ db->mallocFailed = 1; } } /* ** Set the LIKEOPT flag on the 2-argument function with the given name. */ static void setLikeOptFlag(sqlite3 *db, const char *zName, u8 flagVal){ |
︙ | ︙ | |||
1591 1592 1593 1594 1595 1596 1597 1598 | FuncDefHash *pHash = &GLOBAL(FuncDefHash, sqlite3GlobalFunctions); FuncDef *aFunc = (FuncDef*)&GLOBAL(FuncDef, aBuiltinFunc); for(i=0; i<ArraySize(aBuiltinFunc); i++){ sqlite3FuncDefInsert(pHash, &aFunc[i]); } sqlite3RegisterDateTimeFunctions(); } | > > > | 1586 1587 1588 1589 1590 1591 1592 1593 1594 1595 1596 | FuncDefHash *pHash = &GLOBAL(FuncDefHash, sqlite3GlobalFunctions); FuncDef *aFunc = (FuncDef*)&GLOBAL(FuncDef, aBuiltinFunc); for(i=0; i<ArraySize(aBuiltinFunc); i++){ sqlite3FuncDefInsert(pHash, &aFunc[i]); } sqlite3RegisterDateTimeFunctions(); #ifndef SQLITE_OMIT_ALTERTABLE sqlite3AlterFunctions(); #endif } |
Changes to src/global.c.
︙ | ︙ | |||
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 | /* ** Hash table for global functions - functions common to all ** database connections. After initialization, this table is ** read-only. */ SQLITE_WSD FuncDefHash sqlite3GlobalFunctions; /* ** The value of the "pending" byte must be 0x40000000 (1 byte past the ** 1-gibabyte boundary) in a compatible database. SQLite never uses ** the database page that contains the pending byte. It never attempts ** to read or write that page. The pending byte page is set assign ** for use by the VFS layers as space for managing file locks. ** ** During testing, it is often desirable to move the pending byte to ** a different position in the file. This allows code that has to ** deal with the pending byte to run on files that are much smaller ** than 1 GiB. The sqlite3_test_control() interface can be used to ** move the pending byte. ** ** IMPORTANT: Changing the pending byte to any value other than ** 0x40000000 results in an incompatible database file format! ** Changing the pending byte during operating results in undefined ** and dileterious behavior. */ int sqlite3PendingByte = 0x40000000; #include "opcodes.h" /* ** Properties of opcodes. The OPFLG_INITIALIZER macro is ** created by mkopcodeh.awk during compilation. Data is obtained ** from the comments following the "case OP_xxxx:" statements in ** the vdbe.c file. */ const unsigned char sqlite3OpcodeProperty[] = OPFLG_INITIALIZER; | > > > > > > > > > > > | 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 | /* ** Hash table for global functions - functions common to all ** database connections. After initialization, this table is ** read-only. */ SQLITE_WSD FuncDefHash sqlite3GlobalFunctions; /* ** Constant tokens for values 0 and 1. */ const Token sqlite3IntTokens[] = { { "0", 1 }, { "1", 1 } }; /* ** The value of the "pending" byte must be 0x40000000 (1 byte past the ** 1-gibabyte boundary) in a compatible database. SQLite never uses ** the database page that contains the pending byte. It never attempts ** to read or write that page. The pending byte page is set assign ** for use by the VFS layers as space for managing file locks. ** ** During testing, it is often desirable to move the pending byte to ** a different position in the file. This allows code that has to ** deal with the pending byte to run on files that are much smaller ** than 1 GiB. The sqlite3_test_control() interface can be used to ** move the pending byte. ** ** IMPORTANT: Changing the pending byte to any value other than ** 0x40000000 results in an incompatible database file format! ** Changing the pending byte during operating results in undefined ** and dileterious behavior. */ #ifndef SQLITE_OMIT_WSD int sqlite3PendingByte = 0x40000000; #endif #include "opcodes.h" /* ** Properties of opcodes. The OPFLG_INITIALIZER macro is ** created by mkopcodeh.awk during compilation. Data is obtained ** from the comments following the "case OP_xxxx:" statements in ** the vdbe.c file. */ const unsigned char sqlite3OpcodeProperty[] = OPFLG_INITIALIZER; |
Changes to src/insert.c.
︙ | ︙ | |||
723 724 725 726 727 728 729 | } if( j>=pTab->nCol ){ if( sqlite3IsRowid(pColumn->a[i].zName) ){ keyColumn = i; }else{ sqlite3ErrorMsg(pParse, "table %S has no column named %s", pTabList, 0, pColumn->a[i].zName); | | | 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 | } if( j>=pTab->nCol ){ if( sqlite3IsRowid(pColumn->a[i].zName) ){ keyColumn = i; }else{ sqlite3ErrorMsg(pParse, "table %S has no column named %s", pTabList, 0, pColumn->a[i].zName); pParse->checkSchema = 1; goto insert_cleanup; } } } } /* If there is no IDLIST term but the table has an integer primary |
︙ | ︙ | |||
842 843 844 845 846 847 848 | if( pColumn==0 ){ j = i; }else{ for(j=0; j<pColumn->nId; j++){ if( pColumn->a[j].idx==i ) break; } } | | | 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 | if( pColumn==0 ){ j = i; }else{ for(j=0; j<pColumn->nId; j++){ if( pColumn->a[j].idx==i ) break; } } if( (!useTempTable && !pList) || (pColumn && j>=pColumn->nId) ){ sqlite3ExprCode(pParse, pTab->aCol[i].pDflt, regCols+i+1); }else if( useTempTable ){ sqlite3VdbeAddOp3(v, OP_Column, srcTab, j, regCols+i+1); }else{ assert( pSelect==0 ); /* Otherwise useTempTable is true */ sqlite3ExprCodeAndCache(pParse, pList->a[j].pExpr, regCols+i+1); } |
︙ | ︙ |
Changes to src/journal.c.
︙ | ︙ | |||
178 179 180 181 182 183 184 | jrnlSync, /* xSync */ jrnlFileSize, /* xFileSize */ 0, /* xLock */ 0, /* xUnlock */ 0, /* xCheckReservedLock */ 0, /* xFileControl */ 0, /* xSectorSize */ | | > > > > | 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 | jrnlSync, /* xSync */ jrnlFileSize, /* xFileSize */ 0, /* xLock */ 0, /* xUnlock */ 0, /* xCheckReservedLock */ 0, /* xFileControl */ 0, /* xSectorSize */ 0, /* xDeviceCharacteristics */ 0, /* xShmMap */ 0, /* xShmLock */ 0, /* xShmBarrier */ 0 /* xShmUnmap */ }; /* ** Open a journal file. */ int sqlite3JournalOpen( sqlite3_vfs *pVfs, /* The VFS to use for actual file I/O */ |
︙ | ︙ |
Changes to src/main.c.
︙ | ︙ | |||
779 780 781 782 783 784 785 | /* SQLITE_READONLY */ "attempt to write a readonly database", /* SQLITE_INTERRUPT */ "interrupted", /* SQLITE_IOERR */ "disk I/O error", /* SQLITE_CORRUPT */ "database disk image is malformed", /* SQLITE_NOTFOUND */ 0, /* SQLITE_FULL */ "database or disk is full", /* SQLITE_CANTOPEN */ "unable to open database file", | | | 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 | /* SQLITE_READONLY */ "attempt to write a readonly database", /* SQLITE_INTERRUPT */ "interrupted", /* SQLITE_IOERR */ "disk I/O error", /* SQLITE_CORRUPT */ "database disk image is malformed", /* SQLITE_NOTFOUND */ 0, /* SQLITE_FULL */ "database or disk is full", /* SQLITE_CANTOPEN */ "unable to open database file", /* SQLITE_PROTOCOL */ "locking protocol", /* SQLITE_EMPTY */ "table contains no data", /* SQLITE_SCHEMA */ "database schema has changed", /* SQLITE_TOOBIG */ "string or blob too big", /* SQLITE_CONSTRAINT */ "constraint failed", /* SQLITE_MISMATCH */ "datatype mismatch", /* SQLITE_MISUSE */ "library routine called out of sequence", /* SQLITE_NOLFS */ "large file support is disabled", |
︙ | ︙ | |||
1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 | pRet = db->pRollbackArg; db->xRollbackCallback = xCallback; db->pRollbackArg = pArg; sqlite3_mutex_leave(db->mutex); return pRet; } /* ** This function returns true if main-memory should be used instead of ** a temporary file for transient pager files and statement journals. ** The value returned depends on the value of db->temp_store (runtime ** parameter) and the compile time value of SQLITE_TEMP_STORE. The ** following table describes the relationship between these two values ** and this functions return value. | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | pRet = db->pRollbackArg; db->xRollbackCallback = xCallback; db->pRollbackArg = pArg; sqlite3_mutex_leave(db->mutex); return pRet; } #ifndef SQLITE_OMIT_WAL /* ** The sqlite3_wal_hook() callback registered by sqlite3_wal_autocheckpoint(). ** Invoke sqlite3_wal_checkpoint if the number of frames in the log file ** is greater than sqlite3.pWalArg cast to an integer (the value configured by ** wal_autocheckpoint()). */ int sqlite3WalDefaultHook( void *pClientData, /* Argument */ sqlite3 *db, /* Connection */ const char *zDb, /* Database */ int nFrame /* Size of WAL */ ){ if( nFrame>=SQLITE_PTR_TO_INT(pClientData) ){ sqlite3BeginBenignMalloc(); sqlite3_wal_checkpoint(db, zDb); sqlite3EndBenignMalloc(); } return SQLITE_OK; } #endif /* SQLITE_OMIT_WAL */ /* ** Configure an sqlite3_wal_hook() callback to automatically checkpoint ** a database after committing a transaction if there are nFrame or ** more frames in the log file. Passing zero or a negative value as the ** nFrame parameter disables automatic checkpoints entirely. ** ** The callback registered by this function replaces any existing callback ** registered using sqlite3_wal_hook(). Likewise, registering a callback ** using sqlite3_wal_hook() disables the automatic checkpoint mechanism ** configured by this function. */ int sqlite3_wal_autocheckpoint(sqlite3 *db, int nFrame){ #ifndef SQLITE_OMIT_WAL if( nFrame>0 ){ sqlite3_wal_hook(db, sqlite3WalDefaultHook, SQLITE_INT_TO_PTR(nFrame)); }else{ sqlite3_wal_hook(db, 0, 0); } #endif return SQLITE_OK; } /* ** Register a callback to be invoked each time a transaction is written ** into the write-ahead-log by this database connection. */ void *sqlite3_wal_hook( sqlite3 *db, /* Attach the hook to this db handle */ int(*xCallback)(void *, sqlite3*, const char*, int), void *pArg /* First argument passed to xCallback() */ ){ #ifndef SQLITE_OMIT_WAL void *pRet; sqlite3_mutex_enter(db->mutex); pRet = db->pWalArg; db->xWalCallback = xCallback; db->pWalArg = pArg; sqlite3_mutex_leave(db->mutex); return pRet; #else return 0; #endif } /* ** Checkpoint database zDb. If zDb is NULL, or if the buffer zDb points ** to contains a zero-length string, all attached databases are ** checkpointed. */ int sqlite3_wal_checkpoint(sqlite3 *db, const char *zDb){ #ifdef SQLITE_OMIT_WAL return SQLITE_OK; #else int rc; /* Return code */ int iDb = SQLITE_MAX_ATTACHED; /* sqlite3.aDb[] index of db to checkpoint */ sqlite3_mutex_enter(db->mutex); if( zDb && zDb[0] ){ iDb = sqlite3FindDbName(db, zDb); } if( iDb<0 ){ rc = SQLITE_ERROR; sqlite3Error(db, SQLITE_ERROR, "unknown database: %s", zDb); }else{ rc = sqlite3Checkpoint(db, iDb); sqlite3Error(db, rc, 0); } rc = sqlite3ApiExit(db, rc); sqlite3_mutex_leave(db->mutex); return rc; #endif } #ifndef SQLITE_OMIT_WAL /* ** Run a checkpoint on database iDb. This is a no-op if database iDb is ** not currently open in WAL mode. ** ** If a transaction is open on the database being checkpointed, this ** function returns SQLITE_LOCKED and a checkpoint is not attempted. If ** an error occurs while running the checkpoint, an SQLite error code is ** returned (i.e. SQLITE_IOERR). Otherwise, SQLITE_OK. ** ** The mutex on database handle db should be held by the caller. The mutex ** associated with the specific b-tree being checkpointed is taken by ** this function while the checkpoint is running. ** ** If iDb is passed SQLITE_MAX_ATTACHED, then all attached databases are ** checkpointed. If an error is encountered it is returned immediately - ** no attempt is made to checkpoint any remaining databases. */ int sqlite3Checkpoint(sqlite3 *db, int iDb){ int rc = SQLITE_OK; /* Return code */ int i; /* Used to iterate through attached dbs */ assert( sqlite3_mutex_held(db->mutex) ); for(i=0; i<db->nDb && rc==SQLITE_OK; i++){ if( i==iDb || iDb==SQLITE_MAX_ATTACHED ){ Btree *pBt = db->aDb[i].pBt; if( pBt ){ if( sqlite3BtreeIsInReadTrans(pBt) ){ rc = SQLITE_LOCKED; }else{ sqlite3BtreeEnter(pBt); rc = sqlite3PagerCheckpoint(sqlite3BtreePager(pBt)); sqlite3BtreeLeave(pBt); } } } } return rc; } #endif /* SQLITE_OMIT_WAL */ /* ** This function returns true if main-memory should be used instead of ** a temporary file for transient pager files and statement journals. ** The value returned depends on the value of db->temp_store (runtime ** parameter) and the compile time value of SQLITE_TEMP_STORE. The ** following table describes the relationship between these two values ** and this functions return value. |
︙ | ︙ | |||
1617 1618 1619 1620 1621 1622 1623 | db->aDb = db->aDbStatic; assert( sizeof(db->aLimit)==sizeof(aHardLimit) ); memcpy(db->aLimit, aHardLimit, sizeof(db->aLimit)); db->autoCommit = 1; db->nextAutovac = -1; db->nextPagesize = 0; | | | 1756 1757 1758 1759 1760 1761 1762 1763 1764 1765 1766 1767 1768 1769 1770 | db->aDb = db->aDbStatic; assert( sizeof(db->aLimit)==sizeof(aHardLimit) ); memcpy(db->aLimit, aHardLimit, sizeof(db->aLimit)); db->autoCommit = 1; db->nextAutovac = -1; db->nextPagesize = 0; db->flags |= SQLITE_ShortColNames | SQLITE_AutoIndex #if SQLITE_DEFAULT_FILE_FORMAT<4 | SQLITE_LegacyFileFmt #endif #ifdef SQLITE_ENABLE_LOAD_EXTENSION | SQLITE_LoadExtension #endif #if SQLITE_DEFAULT_RECURSIVE_TRIGGERS |
︙ | ︙ | |||
1754 1755 1756 1757 1758 1759 1760 1761 1762 1763 1764 1765 1766 1767 | sqlite3PagerLockingMode(sqlite3BtreePager(db->aDb[0].pBt), SQLITE_DEFAULT_LOCKING_MODE); #endif /* Enable the lookaside-malloc subsystem */ setupLookaside(db, 0, sqlite3GlobalConfig.szLookaside, sqlite3GlobalConfig.nLookaside); opendb_out: if( db ){ assert( db->mutex!=0 || isThreadsafe==0 || sqlite3GlobalConfig.bFullMutex==0 ); sqlite3_mutex_leave(db->mutex); } rc = sqlite3_errcode(db); | > > | 1893 1894 1895 1896 1897 1898 1899 1900 1901 1902 1903 1904 1905 1906 1907 1908 | sqlite3PagerLockingMode(sqlite3BtreePager(db->aDb[0].pBt), SQLITE_DEFAULT_LOCKING_MODE); #endif /* Enable the lookaside-malloc subsystem */ setupLookaside(db, 0, sqlite3GlobalConfig.szLookaside, sqlite3GlobalConfig.nLookaside); sqlite3_wal_autocheckpoint(db, SQLITE_DEFAULT_WAL_AUTOCHECKPOINT); opendb_out: if( db ){ assert( db->mutex!=0 || isThreadsafe==0 || sqlite3GlobalConfig.bFullMutex==0 ); sqlite3_mutex_leave(db->mutex); } rc = sqlite3_errcode(db); |
︙ | ︙ | |||
1941 1942 1943 1944 1945 1946 1947 | db->xCollNeeded16 = xCollNeeded16; db->pCollNeededArg = pCollNeededArg; sqlite3_mutex_leave(db->mutex); return SQLITE_OK; } #endif /* SQLITE_OMIT_UTF16 */ | < < | 2082 2083 2084 2085 2086 2087 2088 2089 2090 2091 2092 2093 2094 2095 2096 2097 2098 2099 2100 2101 2102 2103 | db->xCollNeeded16 = xCollNeeded16; db->pCollNeededArg = pCollNeededArg; sqlite3_mutex_leave(db->mutex); return SQLITE_OK; } #endif /* SQLITE_OMIT_UTF16 */ #ifndef SQLITE_OMIT_DEPRECATED /* ** This function is now an anachronism. It used to be used to recover from a ** malloc() failure, but SQLite now does this automatically. */ int sqlite3_global_recover(void){ return SQLITE_OK; } #endif /* ** Test to see whether or not the database connection is in autocommit ** mode. Return TRUE if it is and FALSE if not. Autocommit mode is on ** by default. Autocommit is disabled by a BEGIN statement and reenabled ** by the next COMMIT or ROLLBACK. |
︙ | ︙ | |||
1979 1980 1981 1982 1983 1984 1985 | ** ** 2. Invoke sqlite3_log() to provide the source code location where ** a low-level error is first detected. */ int sqlite3CorruptError(int lineno){ testcase( sqlite3GlobalConfig.xLog!=0 ); sqlite3_log(SQLITE_CORRUPT, | | > | > > | > > | 2118 2119 2120 2121 2122 2123 2124 2125 2126 2127 2128 2129 2130 2131 2132 2133 2134 2135 2136 2137 2138 2139 2140 2141 2142 2143 2144 2145 2146 2147 | ** ** 2. Invoke sqlite3_log() to provide the source code location where ** a low-level error is first detected. */ int sqlite3CorruptError(int lineno){ testcase( sqlite3GlobalConfig.xLog!=0 ); sqlite3_log(SQLITE_CORRUPT, "database corruption at line %d of [%.10s]", lineno, 20+sqlite3_sourceid()); return SQLITE_CORRUPT; } int sqlite3MisuseError(int lineno){ testcase( sqlite3GlobalConfig.xLog!=0 ); sqlite3_log(SQLITE_MISUSE, "misuse at line %d of [%.10s]", lineno, 20+sqlite3_sourceid()); return SQLITE_MISUSE; } int sqlite3CantopenError(int lineno){ testcase( sqlite3GlobalConfig.xLog!=0 ); sqlite3_log(SQLITE_CANTOPEN, "cannot open file at line %d of [%.10s]", lineno, 20+sqlite3_sourceid()); return SQLITE_CANTOPEN; } #ifndef SQLITE_OMIT_DEPRECATED /* ** This is a convenience routine that makes sure that all thread-specific |
︙ | ︙ | |||
2261 2262 2263 2264 2265 2266 2267 | ** ** IMPORTANT: Changing the PENDING byte from 0x40000000 results in ** an incompatible database file format. Changing the PENDING byte ** while any database connection is open results in undefined and ** dileterious behavior. */ case SQLITE_TESTCTRL_PENDING_BYTE: { | > > > | < | > > | 2405 2406 2407 2408 2409 2410 2411 2412 2413 2414 2415 2416 2417 2418 2419 2420 2421 2422 2423 2424 2425 | ** ** IMPORTANT: Changing the PENDING byte from 0x40000000 results in ** an incompatible database file format. Changing the PENDING byte ** while any database connection is open results in undefined and ** dileterious behavior. */ case SQLITE_TESTCTRL_PENDING_BYTE: { rc = PENDING_BYTE; #ifndef SQLITE_OMIT_WSD { unsigned int newVal = va_arg(ap, unsigned int); if( newVal ) sqlite3PendingByte = newVal; } #endif break; } /* ** sqlite3_test_control(SQLITE_TESTCTRL_ASSERT, int X) ** ** This action provides a run-time test to see whether or not |
︙ | ︙ | |||
2366 2367 2368 2369 2370 2371 2372 2373 2374 2375 2376 2377 2378 | case SQLITE_TESTCTRL_ISKEYWORD: { const char *zWord = va_arg(ap, const char*); int n = sqlite3Strlen30(zWord); rc = (sqlite3KeywordCode((u8*)zWord, n)!=TK_ID) ? SQLITE_N_KEYWORD : 0; break; } #endif } va_end(ap); #endif /* SQLITE_OMIT_BUILTIN_TEST */ return rc; } | > > > > > > > > > | 2514 2515 2516 2517 2518 2519 2520 2521 2522 2523 2524 2525 2526 2527 2528 2529 2530 2531 2532 2533 2534 2535 | case SQLITE_TESTCTRL_ISKEYWORD: { const char *zWord = va_arg(ap, const char*); int n = sqlite3Strlen30(zWord); rc = (sqlite3KeywordCode((u8*)zWord, n)!=TK_ID) ? SQLITE_N_KEYWORD : 0; break; } #endif /* sqlite3_test_control(SQLITE_TESTCTRL_PGHDRSZ) ** ** Return the size of a pcache header in bytes. */ case SQLITE_TESTCTRL_PGHDRSZ: { rc = sizeof(PgHdr); break; } } va_end(ap); #endif /* SQLITE_OMIT_BUILTIN_TEST */ return rc; } |
Changes to src/malloc.c.
︙ | ︙ | |||
311 312 313 314 315 316 317 | ** embedded processor. */ void *sqlite3ScratchMalloc(int n){ void *p; assert( n>0 ); #if SQLITE_THREADSAFE==0 && !defined(NDEBUG) | | | | 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 | ** embedded processor. */ void *sqlite3ScratchMalloc(int n){ void *p; assert( n>0 ); #if SQLITE_THREADSAFE==0 && !defined(NDEBUG) /* Verify that no more than two scratch allocation per thread ** is outstanding at one time. (This is only checked in the ** single-threaded case since checking in the multi-threaded case ** would be much more complicated.) */ assert( scratchAllocOut<=1 ); #endif if( sqlite3GlobalConfig.szScratch<n ){ goto scratch_overflow; }else{ sqlite3_mutex_enter(mem0.mutex); if( mem0.nScratchFree==0 ){ |
︙ | ︙ | |||
352 353 354 355 356 357 358 359 360 361 362 363 364 365 | sqlite3StatusSet(SQLITE_STATUS_SCRATCH_SIZE, n); n = mallocWithAlarm(n, &p); if( p ) sqlite3StatusAdd(SQLITE_STATUS_SCRATCH_OVERFLOW, n); sqlite3_mutex_leave(mem0.mutex); }else{ p = sqlite3GlobalConfig.m.xMalloc(n); } #if SQLITE_THREADSAFE==0 && !defined(NDEBUG) scratchAllocOut = p!=0; #endif return p; } void sqlite3ScratchFree(void *p){ if( p ){ | > < < < < < < < < < < > > > > > > > > > > > > > > > > | 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 | sqlite3StatusSet(SQLITE_STATUS_SCRATCH_SIZE, n); n = mallocWithAlarm(n, &p); if( p ) sqlite3StatusAdd(SQLITE_STATUS_SCRATCH_OVERFLOW, n); sqlite3_mutex_leave(mem0.mutex); }else{ p = sqlite3GlobalConfig.m.xMalloc(n); } sqlite3MemdebugSetType(p, MEMTYPE_SCRATCH); #if SQLITE_THREADSAFE==0 && !defined(NDEBUG) scratchAllocOut = p!=0; #endif return p; } void sqlite3ScratchFree(void *p){ if( p ){ if( sqlite3GlobalConfig.pScratch==0 || p<sqlite3GlobalConfig.pScratch || p>=(void*)mem0.aScratchFree ){ assert( sqlite3MemdebugHasType(p, MEMTYPE_SCRATCH) ); sqlite3MemdebugSetType(p, MEMTYPE_HEAP); if( sqlite3GlobalConfig.bMemstat ){ int iSize = sqlite3MallocSize(p); sqlite3_mutex_enter(mem0.mutex); sqlite3StatusAdd(SQLITE_STATUS_SCRATCH_OVERFLOW, -iSize); sqlite3StatusAdd(SQLITE_STATUS_MEMORY_USED, -iSize); sqlite3GlobalConfig.m.xFree(p); sqlite3_mutex_leave(mem0.mutex); }else{ sqlite3GlobalConfig.m.xFree(p); } }else{ int i; i = (int)((u8*)p - (u8*)sqlite3GlobalConfig.pScratch); i /= sqlite3GlobalConfig.szScratch; assert( i>=0 && i<sqlite3GlobalConfig.nScratch ); sqlite3_mutex_enter(mem0.mutex); assert( mem0.nScratchFree<(u32)sqlite3GlobalConfig.nScratch ); mem0.aScratchFree[mem0.nScratchFree++] = i; sqlite3StatusAdd(SQLITE_STATUS_SCRATCH_USED, -1); sqlite3_mutex_leave(mem0.mutex); #if SQLITE_THREADSAFE==0 && !defined(NDEBUG) /* Verify that no more than two scratch allocation per thread ** is outstanding at one time. (This is only checked in the ** single-threaded case since checking in the multi-threaded case ** would be much more complicated.) */ assert( scratchAllocOut>=1 && scratchAllocOut<=2 ); scratchAllocOut = 0; #endif } } } /* ** TRUE if p is a lookaside memory allocation from db */ #ifndef SQLITE_OMIT_LOOKASIDE static int isLookaside(sqlite3 *db, void *p){ return db && p && p>=db->lookaside.pStart && p<db->lookaside.pEnd; } #else #define isLookaside(A,B) 0 #endif /* ** Return the size of a memory allocation previously obtained from ** sqlite3Malloc() or sqlite3_malloc(). */ int sqlite3MallocSize(void *p){ assert( sqlite3MemdebugHasType(p, MEMTYPE_HEAP) ); return sqlite3GlobalConfig.m.xSize(p); } int sqlite3DbMallocSize(sqlite3 *db, void *p){ assert( db==0 || sqlite3_mutex_held(db->mutex) ); if( isLookaside(db, p) ){ return db->lookaside.sz; }else{ assert( sqlite3MemdebugHasType(p, db ? (MEMTYPE_DB|MEMTYPE_HEAP) : MEMTYPE_HEAP) ); return sqlite3GlobalConfig.m.xSize(p); } } /* ** Free memory previously obtained from sqlite3Malloc(). */ void sqlite3_free(void *p){ if( p==0 ) return; assert( sqlite3MemdebugHasType(p, MEMTYPE_HEAP) ); if( sqlite3GlobalConfig.bMemstat ){ sqlite3_mutex_enter(mem0.mutex); sqlite3StatusAdd(SQLITE_STATUS_MEMORY_USED, -sqlite3MallocSize(p)); sqlite3GlobalConfig.m.xFree(p); sqlite3_mutex_leave(mem0.mutex); }else{ sqlite3GlobalConfig.m.xFree(p); |
︙ | ︙ | |||
450 451 452 453 454 455 456 457 458 459 460 461 462 463 | assert( db==0 || sqlite3_mutex_held(db->mutex) ); if( isLookaside(db, p) ){ LookasideSlot *pBuf = (LookasideSlot*)p; pBuf->pNext = db->lookaside.pFree; db->lookaside.pFree = pBuf; db->lookaside.nOut--; }else{ sqlite3_free(p); } } /* ** Change the size of an existing memory allocation */ | > > | 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 | assert( db==0 || sqlite3_mutex_held(db->mutex) ); if( isLookaside(db, p) ){ LookasideSlot *pBuf = (LookasideSlot*)p; pBuf->pNext = db->lookaside.pFree; db->lookaside.pFree = pBuf; db->lookaside.nOut--; }else{ assert( sqlite3MemdebugHasType(p, MEMTYPE_DB|MEMTYPE_HEAP) ); sqlite3MemdebugSetType(p, MEMTYPE_HEAP); sqlite3_free(p); } } /* ** Change the size of an existing memory allocation */ |
︙ | ︙ | |||
482 483 484 485 486 487 488 489 490 491 492 493 494 495 | }else if( sqlite3GlobalConfig.bMemstat ){ sqlite3_mutex_enter(mem0.mutex); sqlite3StatusSet(SQLITE_STATUS_MALLOC_SIZE, nBytes); if( sqlite3StatusValue(SQLITE_STATUS_MEMORY_USED)+nNew-nOld >= mem0.alarmThreshold ){ sqlite3MallocAlarm(nNew-nOld); } pNew = sqlite3GlobalConfig.m.xRealloc(pOld, nNew); if( pNew==0 && mem0.alarmCallback ){ sqlite3MallocAlarm(nBytes); pNew = sqlite3GlobalConfig.m.xRealloc(pOld, nNew); } if( pNew ){ nNew = sqlite3MallocSize(pNew); | > | 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 | }else if( sqlite3GlobalConfig.bMemstat ){ sqlite3_mutex_enter(mem0.mutex); sqlite3StatusSet(SQLITE_STATUS_MALLOC_SIZE, nBytes); if( sqlite3StatusValue(SQLITE_STATUS_MEMORY_USED)+nNew-nOld >= mem0.alarmThreshold ){ sqlite3MallocAlarm(nNew-nOld); } assert( sqlite3MemdebugHasType(pOld, MEMTYPE_HEAP) ); pNew = sqlite3GlobalConfig.m.xRealloc(pOld, nNew); if( pNew==0 && mem0.alarmCallback ){ sqlite3MallocAlarm(nBytes); pNew = sqlite3GlobalConfig.m.xRealloc(pOld, nNew); } if( pNew ){ nNew = sqlite3MallocSize(pNew); |
︙ | ︙ | |||
579 580 581 582 583 584 585 586 587 588 589 590 591 592 | return 0; } #endif p = sqlite3Malloc(n); if( !p && db ){ db->mallocFailed = 1; } return p; } /* ** Resize the block of memory pointed to by p to n bytes. If the ** resize fails, set the mallocFailed flag in the connection object. */ | > > | 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 | return 0; } #endif p = sqlite3Malloc(n); if( !p && db ){ db->mallocFailed = 1; } sqlite3MemdebugSetType(p, (db && db->lookaside.bEnabled) ? MEMTYPE_DB : MEMTYPE_HEAP); return p; } /* ** Resize the block of memory pointed to by p to n bytes. If the ** resize fails, set the mallocFailed flag in the connection object. */ |
︙ | ︙ | |||
604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 | } pNew = sqlite3DbMallocRaw(db, n); if( pNew ){ memcpy(pNew, p, db->lookaside.sz); sqlite3DbFree(db, p); } }else{ pNew = sqlite3_realloc(p, n); if( !pNew ){ db->mallocFailed = 1; } } } return pNew; } /* ** Attempt to reallocate p. If the reallocation fails, then free p | > > > > | 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 | } pNew = sqlite3DbMallocRaw(db, n); if( pNew ){ memcpy(pNew, p, db->lookaside.sz); sqlite3DbFree(db, p); } }else{ assert( sqlite3MemdebugHasType(p, MEMTYPE_DB|MEMTYPE_HEAP) ); sqlite3MemdebugSetType(p, MEMTYPE_HEAP); pNew = sqlite3_realloc(p, n); if( !pNew ){ db->mallocFailed = 1; } sqlite3MemdebugSetType(pNew, db->lookaside.bEnabled ? MEMTYPE_DB : MEMTYPE_HEAP); } } return pNew; } /* ** Attempt to reallocate p. If the reallocation fails, then free p |
︙ | ︙ |
Changes to src/mem2.c.
︙ | ︙ | |||
53 54 55 56 57 58 59 | ** MemBlockHdr. */ struct MemBlockHdr { i64 iSize; /* Size of this allocation */ struct MemBlockHdr *pNext, *pPrev; /* Linked list of all unfreed memory */ char nBacktrace; /* Number of backtraces on this alloc */ char nBacktraceSlots; /* Available backtrace slots */ | | > | 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 | ** MemBlockHdr. */ struct MemBlockHdr { i64 iSize; /* Size of this allocation */ struct MemBlockHdr *pNext, *pPrev; /* Linked list of all unfreed memory */ char nBacktrace; /* Number of backtraces on this alloc */ char nBacktraceSlots; /* Available backtrace slots */ u8 nTitle; /* Bytes of title; includes '\0' */ u8 eType; /* Allocation type code */ int iForeGuard; /* Guard word for sanity */ }; /* ** Guard words */ #define FOREGUARD 0x80F5E153 |
︙ | ︙ | |||
261 262 263 264 265 266 267 268 269 270 271 272 273 274 | if( mem.pLast ){ mem.pLast->pNext = pHdr; }else{ mem.pFirst = pHdr; } mem.pLast = pHdr; pHdr->iForeGuard = FOREGUARD; pHdr->nBacktraceSlots = mem.nBacktrace; pHdr->nTitle = mem.nTitle; if( mem.nBacktrace ){ void *aAddr[40]; pHdr->nBacktrace = backtrace(aAddr, mem.nBacktrace+1)-1; memcpy(pBt, &aAddr[1], pHdr->nBacktrace*sizeof(void*)); assert(pBt[0]); | > | 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 | if( mem.pLast ){ mem.pLast->pNext = pHdr; }else{ mem.pFirst = pHdr; } mem.pLast = pHdr; pHdr->iForeGuard = FOREGUARD; pHdr->eType = MEMTYPE_HEAP; pHdr->nBacktraceSlots = mem.nBacktrace; pHdr->nTitle = mem.nTitle; if( mem.nBacktrace ){ void *aAddr[40]; pHdr->nBacktrace = backtrace(aAddr, mem.nBacktrace+1)-1; memcpy(pBt, &aAddr[1], pHdr->nBacktrace*sizeof(void*)); assert(pBt[0]); |
︙ | ︙ | |||
367 368 369 370 371 372 373 374 375 376 377 378 379 380 | sqlite3MemRoundup, sqlite3MemInit, sqlite3MemShutdown, 0 }; sqlite3_config(SQLITE_CONFIG_MALLOC, &defaultMethods); } /* ** Set the number of backtrace levels kept for each allocation. ** A value of zero turns off backtracing. The number is always rounded ** up to a multiple of 2. */ void sqlite3MemdebugBacktrace(int depth){ | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | sqlite3MemRoundup, sqlite3MemInit, sqlite3MemShutdown, 0 }; sqlite3_config(SQLITE_CONFIG_MALLOC, &defaultMethods); } /* ** Set the "type" of an allocation. */ void sqlite3MemdebugSetType(void *p, u8 eType){ if( p ){ struct MemBlockHdr *pHdr; pHdr = sqlite3MemsysGetHeader(p); assert( pHdr->iForeGuard==FOREGUARD ); pHdr->eType = eType; } } /* ** Return TRUE if the mask of type in eType matches the type of the ** allocation p. Also return true if p==NULL. ** ** This routine is designed for use within an assert() statement, to ** verify the type of an allocation. For example: ** ** assert( sqlite3MemdebugHasType(p, MEMTYPE_DB) ); */ int sqlite3MemdebugHasType(void *p, u8 eType){ int rc = 1; if( p ){ struct MemBlockHdr *pHdr; pHdr = sqlite3MemsysGetHeader(p); assert( pHdr->iForeGuard==FOREGUARD ); /* Allocation is valid */ assert( (pHdr->eType & (pHdr->eType-1))==0 ); /* Only one type bit set */ if( (pHdr->eType&eType)==0 ){ void **pBt; pBt = (void**)pHdr; pBt -= pHdr->nBacktraceSlots; backtrace_symbols_fd(pBt, pHdr->nBacktrace, fileno(stderr)); fprintf(stderr, "\n"); rc = 0; } } return rc; } /* ** Set the number of backtrace levels kept for each allocation. ** A value of zero turns off backtracing. The number is always rounded ** up to a multiple of 2. */ void sqlite3MemdebugBacktrace(int depth){ |
︙ | ︙ |
Changes to src/memjournal.c.
︙ | ︙ | |||
192 193 194 195 196 197 198 | ** Sync the file. ** ** Syncing an in-memory journal is a no-op. And, in fact, this routine ** is never called in a working implementation. This implementation ** exists purely as a contingency, in case some malfunction in some other ** part of SQLite causes Sync to be called by mistake. */ | | | < | < > | | > > > > | | 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 | ** Sync the file. ** ** Syncing an in-memory journal is a no-op. And, in fact, this routine ** is never called in a working implementation. This implementation ** exists purely as a contingency, in case some malfunction in some other ** part of SQLite causes Sync to be called by mistake. */ static int memjrnlSync(sqlite3_file *NotUsed, int NotUsed2){ UNUSED_PARAMETER2(NotUsed, NotUsed2); return SQLITE_OK; } /* ** Query the size of the file in bytes. */ static int memjrnlFileSize(sqlite3_file *pJfd, sqlite_int64 *pSize){ MemJournal *p = (MemJournal *)pJfd; *pSize = (sqlite_int64) p->endpoint.iOffset; return SQLITE_OK; } /* ** Table of methods for MemJournal sqlite3_file object. */ static const struct sqlite3_io_methods MemJournalMethods = { 1, /* iVersion */ memjrnlClose, /* xClose */ memjrnlRead, /* xRead */ memjrnlWrite, /* xWrite */ memjrnlTruncate, /* xTruncate */ memjrnlSync, /* xSync */ memjrnlFileSize, /* xFileSize */ 0, /* xLock */ 0, /* xUnlock */ 0, /* xCheckReservedLock */ 0, /* xFileControl */ 0, /* xSectorSize */ 0, /* xDeviceCharacteristics */ 0, /* xShmMap */ 0, /* xShmLock */ 0, /* xShmBarrier */ 0 /* xShmUnlock */ }; /* ** Open a journal file. */ void sqlite3MemJournalOpen(sqlite3_file *pJfd){ MemJournal *p = (MemJournal *)pJfd; assert( EIGHT_BYTE_ALIGNMENT(p) ); memset(p, 0, sqlite3MemJournalSize()); p->pMethod = (sqlite3_io_methods*)&MemJournalMethods; } /* ** Return true if the file-handle passed as an argument is ** an in-memory journal */ int sqlite3IsMemJournal(sqlite3_file *pJfd){ |
︙ | ︙ |
Changes to src/mutex.c.
︙ | ︙ | |||
27 28 29 30 31 32 33 | #ifndef SQLITE_MUTEX_OMIT /* ** Initialize the mutex system. */ int sqlite3MutexInit(void){ int rc = SQLITE_OK; | < | | | | | | | | > > > > > | | | | | | < | 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 | #ifndef SQLITE_MUTEX_OMIT /* ** Initialize the mutex system. */ int sqlite3MutexInit(void){ int rc = SQLITE_OK; if( !sqlite3GlobalConfig.mutex.xMutexAlloc ){ /* If the xMutexAlloc method has not been set, then the user did not ** install a mutex implementation via sqlite3_config() prior to ** sqlite3_initialize() being called. This block copies pointers to ** the default implementation into the sqlite3GlobalConfig structure. */ sqlite3_mutex_methods const *pFrom; sqlite3_mutex_methods *pTo = &sqlite3GlobalConfig.mutex; if( sqlite3GlobalConfig.bCoreMutex ){ pFrom = sqlite3DefaultMutex(); }else{ pFrom = sqlite3NoopMutex(); } memcpy(pTo, pFrom, offsetof(sqlite3_mutex_methods, xMutexAlloc)); memcpy(&pTo->xMutexFree, &pFrom->xMutexFree, sizeof(*pTo) - offsetof(sqlite3_mutex_methods, xMutexFree)); pTo->xMutexAlloc = pFrom->xMutexAlloc; } rc = sqlite3GlobalConfig.mutex.xMutexInit(); #ifdef SQLITE_DEBUG GLOBAL(int, mutexIsInit) = 1; #endif return rc; } |
︙ | ︙ |
Changes to src/mutex_noop.c.
︙ | ︙ | |||
23 24 25 26 27 28 29 30 | ** ** If compiled with SQLITE_DEBUG, then additional logic is inserted ** that does error checking on mutexes to make sure they are being ** called correctly. */ #include "sqliteInt.h" | > | < < | > > > | | | > > > | | | < < > > | | | | | > | > | | | | > | > | | > | | > | | | | > > > > > > | > > > > > | 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 | ** ** If compiled with SQLITE_DEBUG, then additional logic is inserted ** that does error checking on mutexes to make sure they are being ** called correctly. */ #include "sqliteInt.h" #ifndef SQLITE_MUTEX_OMIT #ifndef SQLITE_DEBUG /* ** Stub routines for all mutex methods. ** ** This routines provide no mutual exclusion or error checking. */ static int noopMutexInit(void){ return SQLITE_OK; } static int noopMutexEnd(void){ return SQLITE_OK; } static sqlite3_mutex *noopMutexAlloc(int id){ UNUSED_PARAMETER(id); return (sqlite3_mutex*)8; } static void noopMutexFree(sqlite3_mutex *p){ UNUSED_PARAMETER(p); return; } static void noopMutexEnter(sqlite3_mutex *p){ UNUSED_PARAMETER(p); return; } static int noopMutexTry(sqlite3_mutex *p){ UNUSED_PARAMETER(p); return SQLITE_OK; } static void noopMutexLeave(sqlite3_mutex *p){ UNUSED_PARAMETER(p); return; } sqlite3_mutex_methods const *sqlite3NoopMutex(void){ static const sqlite3_mutex_methods sMutex = { noopMutexInit, noopMutexEnd, noopMutexAlloc, noopMutexFree, noopMutexEnter, noopMutexTry, noopMutexLeave, 0, 0, }; return &sMutex; } #endif /* !SQLITE_DEBUG */ #ifdef SQLITE_DEBUG /* ** In this implementation, error checking is provided for testing ** and debugging purposes. The mutexes still do not provide any ** mutual exclusion. */ /* ** The mutex object */ typedef struct sqlite3_debug_mutex { int id; /* The mutex type */ int cnt; /* Number of entries without a matching leave */ } sqlite3_debug_mutex; /* ** The sqlite3_mutex_held() and sqlite3_mutex_notheld() routine are ** intended for use inside assert() statements. */ static int debugMutexHeld(sqlite3_mutex *pX){ sqlite3_debug_mutex *p = (sqlite3_debug_mutex*)pX; return p==0 || p->cnt>0; } static int debugMutexNotheld(sqlite3_mutex *pX){ sqlite3_debug_mutex *p = (sqlite3_debug_mutex*)pX; return p==0 || p->cnt==0; } /* ** Initialize and deinitialize the mutex subsystem. */ static int debugMutexInit(void){ return SQLITE_OK; } static int debugMutexEnd(void){ return SQLITE_OK; } /* ** The sqlite3_mutex_alloc() routine allocates a new ** mutex and returns a pointer to it. If it returns NULL ** that means that a mutex could not be allocated. */ static sqlite3_mutex *debugMutexAlloc(int id){ static sqlite3_debug_mutex aStatic[6]; sqlite3_debug_mutex *pNew = 0; switch( id ){ case SQLITE_MUTEX_FAST: case SQLITE_MUTEX_RECURSIVE: { pNew = sqlite3Malloc(sizeof(*pNew)); if( pNew ){ pNew->id = id; pNew->cnt = 0; } break; } default: { assert( id-2 >= 0 ); assert( id-2 < (int)(sizeof(aStatic)/sizeof(aStatic[0])) ); pNew = &aStatic[id-2]; pNew->id = id; break; } } return (sqlite3_mutex*)pNew; } /* ** This routine deallocates a previously allocated mutex. */ static void debugMutexFree(sqlite3_mutex *pX){ sqlite3_debug_mutex *p = (sqlite3_debug_mutex*)pX; assert( p->cnt==0 ); assert( p->id==SQLITE_MUTEX_FAST || p->id==SQLITE_MUTEX_RECURSIVE ); sqlite3_free(p); } /* ** The sqlite3_mutex_enter() and sqlite3_mutex_try() routines attempt ** to enter a mutex. If another thread is already within the mutex, ** sqlite3_mutex_enter() will block and sqlite3_mutex_try() will return ** SQLITE_BUSY. The sqlite3_mutex_try() interface returns SQLITE_OK ** upon successful entry. Mutexes created using SQLITE_MUTEX_RECURSIVE can ** be entered multiple times by the same thread. In such cases the, ** mutex must be exited an equal number of times before another thread ** can enter. If the same thread tries to enter any other kind of mutex ** more than once, the behavior is undefined. */ static void debugMutexEnter(sqlite3_mutex *pX){ sqlite3_debug_mutex *p = (sqlite3_debug_mutex*)pX; assert( p->id==SQLITE_MUTEX_RECURSIVE || debugMutexNotheld(pX) ); p->cnt++; } static int debugMutexTry(sqlite3_mutex *pX){ sqlite3_debug_mutex *p = (sqlite3_debug_mutex*)pX; assert( p->id==SQLITE_MUTEX_RECURSIVE || debugMutexNotheld(pX) ); p->cnt++; return SQLITE_OK; } /* ** The sqlite3_mutex_leave() routine exits a mutex that was ** previously entered by the same thread. The behavior ** is undefined if the mutex is not currently entered or ** is not currently allocated. SQLite will never do either. */ static void debugMutexLeave(sqlite3_mutex *pX){ sqlite3_debug_mutex *p = (sqlite3_debug_mutex*)pX; assert( debugMutexHeld(pX) ); p->cnt--; assert( p->id==SQLITE_MUTEX_RECURSIVE || debugMutexNotheld(pX) ); } sqlite3_mutex_methods const *sqlite3NoopMutex(void){ static const sqlite3_mutex_methods sMutex = { debugMutexInit, debugMutexEnd, debugMutexAlloc, debugMutexFree, debugMutexEnter, debugMutexTry, debugMutexLeave, debugMutexHeld, debugMutexNotheld }; return &sMutex; } #endif /* SQLITE_DEBUG */ /* ** If compiled with SQLITE_MUTEX_NOOP, then the no-op mutex implementation ** is used regardless of the run-time threadsafety setting. */ #ifdef SQLITE_MUTEX_NOOP sqlite3_mutex_methods const *sqlite3DefaultMutex(void){ return sqlite3NoopMutex(); } #endif /* SQLITE_MUTEX_NOOP */ #endif /* SQLITE_MUTEX_OMIT */ |
Changes to src/mutex_os2.c.
︙ | ︙ | |||
247 248 249 250 251 252 253 | DosQueryMutexSem(p->mutex, &holder1, &tid, &holder2); assert( p->owner==tid ); p->nRef--; assert( p->nRef==0 || p->id==SQLITE_MUTEX_RECURSIVE ); DosReleaseMutexSem(p->mutex); } | | | | 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 | DosQueryMutexSem(p->mutex, &holder1, &tid, &holder2); assert( p->owner==tid ); p->nRef--; assert( p->nRef==0 || p->id==SQLITE_MUTEX_RECURSIVE ); DosReleaseMutexSem(p->mutex); } sqlite3_mutex_methods const *sqlite3DefaultMutex(void){ static const sqlite3_mutex_methods sMutex = { os2MutexInit, os2MutexEnd, os2MutexAlloc, os2MutexFree, os2MutexEnter, os2MutexTry, os2MutexLeave, |
︙ | ︙ |
Changes to src/mutex_unix.c.
︙ | ︙ | |||
20 21 22 23 24 25 26 27 28 29 30 31 32 33 | ** Note that this implementation requires a version of pthreads that ** supports recursive mutexes. */ #ifdef SQLITE_MUTEX_PTHREADS #include <pthread.h> /* ** Each recursive mutex is an instance of the following structure. */ struct sqlite3_mutex { pthread_mutex_t mutex; /* Mutex controlling the lock */ int id; /* Mutex type */ | > > > > > > > > > > > | | < | | | 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 | ** Note that this implementation requires a version of pthreads that ** supports recursive mutexes. */ #ifdef SQLITE_MUTEX_PTHREADS #include <pthread.h> /* ** The sqlite3_mutex.id, sqlite3_mutex.nRef, and sqlite3_mutex.owner fields ** are necessary under two condidtions: (1) Debug builds and (2) using ** home-grown mutexes. Encapsulate these conditions into a single #define. */ #if defined(SQLITE_DEBUG) || defined(SQLITE_HOMEGROWN_RECURSIVE_MUTEX) # define SQLITE_MUTEX_NREF 1 #else # define SQLITE_MUTEX_NREF 0 #endif /* ** Each recursive mutex is an instance of the following structure. */ struct sqlite3_mutex { pthread_mutex_t mutex; /* Mutex controlling the lock */ #if SQLITE_MUTEX_NREF int id; /* Mutex type */ volatile int nRef; /* Number of entrances */ volatile pthread_t owner; /* Thread that is within this mutex */ int trace; /* True to trace changes */ #endif }; #if SQLITE_MUTEX_NREF #define SQLITE3_MUTEX_INITIALIZER { PTHREAD_MUTEX_INITIALIZER, 0, 0, (pthread_t)0, 0 } #else #define SQLITE3_MUTEX_INITIALIZER { PTHREAD_MUTEX_INITIALIZER } #endif /* ** The sqlite3_mutex_held() and sqlite3_mutex_notheld() routine are ** intended for use only inside assert() statements. On some platforms, ** there might be race conditions that can cause these routines to ** deliver incorrect results. In particular, if pthread_equal() is |
︙ | ︙ | |||
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 | /* Use a recursive mutex if it is available */ pthread_mutexattr_t recursiveAttr; pthread_mutexattr_init(&recursiveAttr); pthread_mutexattr_settype(&recursiveAttr, PTHREAD_MUTEX_RECURSIVE); pthread_mutex_init(&p->mutex, &recursiveAttr); pthread_mutexattr_destroy(&recursiveAttr); #endif p->id = iType; } break; } case SQLITE_MUTEX_FAST: { p = sqlite3MallocZero( sizeof(*p) ); if( p ){ p->id = iType; pthread_mutex_init(&p->mutex, 0); } break; } default: { assert( iType-2 >= 0 ); assert( iType-2 < ArraySize(staticMutexes) ); p = &staticMutexes[iType-2]; p->id = iType; break; } } return p; } | > > > > > > | 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 | /* Use a recursive mutex if it is available */ pthread_mutexattr_t recursiveAttr; pthread_mutexattr_init(&recursiveAttr); pthread_mutexattr_settype(&recursiveAttr, PTHREAD_MUTEX_RECURSIVE); pthread_mutex_init(&p->mutex, &recursiveAttr); pthread_mutexattr_destroy(&recursiveAttr); #endif #if SQLITE_MUTEX_NREF p->id = iType; #endif } break; } case SQLITE_MUTEX_FAST: { p = sqlite3MallocZero( sizeof(*p) ); if( p ){ #if SQLITE_MUTEX_NREF p->id = iType; #endif pthread_mutex_init(&p->mutex, 0); } break; } default: { assert( iType-2 >= 0 ); assert( iType-2 < ArraySize(staticMutexes) ); p = &staticMutexes[iType-2]; #if SQLITE_MUTEX_NREF p->id = iType; #endif break; } } return p; } |
︙ | ︙ | |||
214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 | p->nRef = 1; } } #else /* Use the built-in recursive mutexes if they are available. */ pthread_mutex_lock(&p->mutex); p->owner = pthread_self(); p->nRef++; #endif #ifdef SQLITE_DEBUG if( p->trace ){ printf("enter mutex %p (%d) with nRef=%d\n", p, p->trace, p->nRef); } #endif | > > | 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 | p->nRef = 1; } } #else /* Use the built-in recursive mutexes if they are available. */ pthread_mutex_lock(&p->mutex); #if SQLITE_MUTEX_NREF p->owner = pthread_self(); p->nRef++; #endif #endif #ifdef SQLITE_DEBUG if( p->trace ){ printf("enter mutex %p (%d) with nRef=%d\n", p, p->trace, p->nRef); } #endif |
︙ | ︙ | |||
257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 | rc = SQLITE_BUSY; } } #else /* Use the built-in recursive mutexes if they are available. */ if( pthread_mutex_trylock(&p->mutex)==0 ){ p->owner = pthread_self(); p->nRef++; rc = SQLITE_OK; }else{ rc = SQLITE_BUSY; } #endif #ifdef SQLITE_DEBUG | > > | 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 | rc = SQLITE_BUSY; } } #else /* Use the built-in recursive mutexes if they are available. */ if( pthread_mutex_trylock(&p->mutex)==0 ){ #if SQLITE_MUTEX_NREF p->owner = pthread_self(); p->nRef++; #endif rc = SQLITE_OK; }else{ rc = SQLITE_BUSY; } #endif #ifdef SQLITE_DEBUG |
︙ | ︙ | |||
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 | ** The sqlite3_mutex_leave() routine exits a mutex that was ** previously entered by the same thread. The behavior ** is undefined if the mutex is not currently entered or ** is not currently allocated. SQLite will never do either. */ static void pthreadMutexLeave(sqlite3_mutex *p){ assert( pthreadMutexHeld(p) ); p->nRef--; assert( p->nRef==0 || p->id==SQLITE_MUTEX_RECURSIVE ); #ifdef SQLITE_HOMEGROWN_RECURSIVE_MUTEX if( p->nRef==0 ){ pthread_mutex_unlock(&p->mutex); } #else pthread_mutex_unlock(&p->mutex); #endif #ifdef SQLITE_DEBUG if( p->trace ){ printf("leave mutex %p (%d) with nRef=%d\n", p, p->trace, p->nRef); } #endif } | > > | | | 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 | ** The sqlite3_mutex_leave() routine exits a mutex that was ** previously entered by the same thread. The behavior ** is undefined if the mutex is not currently entered or ** is not currently allocated. SQLite will never do either. */ static void pthreadMutexLeave(sqlite3_mutex *p){ assert( pthreadMutexHeld(p) ); #if SQLITE_MUTEX_NREF p->nRef--; #endif assert( p->nRef==0 || p->id==SQLITE_MUTEX_RECURSIVE ); #ifdef SQLITE_HOMEGROWN_RECURSIVE_MUTEX if( p->nRef==0 ){ pthread_mutex_unlock(&p->mutex); } #else pthread_mutex_unlock(&p->mutex); #endif #ifdef SQLITE_DEBUG if( p->trace ){ printf("leave mutex %p (%d) with nRef=%d\n", p, p->trace, p->nRef); } #endif } sqlite3_mutex_methods const *sqlite3DefaultMutex(void){ static const sqlite3_mutex_methods sMutex = { pthreadMutexInit, pthreadMutexEnd, pthreadMutexAlloc, pthreadMutexFree, pthreadMutexEnter, pthreadMutexTry, pthreadMutexLeave, |
︙ | ︙ |
Changes to src/mutex_w32.c.
︙ | ︙ | |||
21 22 23 24 25 26 27 | /* ** Each recursive mutex is an instance of the following structure. */ struct sqlite3_mutex { CRITICAL_SECTION mutex; /* Mutex controlling the lock */ int id; /* Mutex type */ | < < > > | | 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 | /* ** Each recursive mutex is an instance of the following structure. */ struct sqlite3_mutex { CRITICAL_SECTION mutex; /* Mutex controlling the lock */ int id; /* Mutex type */ #ifdef SQLITE_DEBUG volatile int nRef; /* Number of enterances */ volatile DWORD owner; /* Thread holding this mutex */ int trace; /* True to trace changes */ #endif }; #define SQLITE_W32_MUTEX_INITIALIZER { 0 } #ifdef SQLITE_DEBUG #define SQLITE3_MUTEX_INITIALIZER { SQLITE_W32_MUTEX_INITIALIZER, 0, 0L, (DWORD)0, 0 } #else #define SQLITE3_MUTEX_INITIALIZER { SQLITE_W32_MUTEX_INITIALIZER, 0 } #endif /* ** Return true (non-zero) if we are running under WinNT, Win2K, WinXP, ** or WinCE. Return false (zero) for Win95, Win98, or WinME. ** ** Here is an interesting observation: Win95, Win98, and WinME lack |
︙ | ︙ | |||
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 | sqlite3_mutex *p; switch( iType ){ case SQLITE_MUTEX_FAST: case SQLITE_MUTEX_RECURSIVE: { p = sqlite3MallocZero( sizeof(*p) ); if( p ){ p->id = iType; InitializeCriticalSection(&p->mutex); } break; } default: { assert( winMutex_isInit==1 ); assert( iType-2 >= 0 ); assert( iType-2 < ArraySize(winMutex_staticMutexes) ); p = &winMutex_staticMutexes[iType-2]; p->id = iType; break; } } return p; } | > > > > | 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 | sqlite3_mutex *p; switch( iType ){ case SQLITE_MUTEX_FAST: case SQLITE_MUTEX_RECURSIVE: { p = sqlite3MallocZero( sizeof(*p) ); if( p ){ #ifdef SQLITE_DEBUG p->id = iType; #endif InitializeCriticalSection(&p->mutex); } break; } default: { assert( winMutex_isInit==1 ); assert( iType-2 >= 0 ); assert( iType-2 < ArraySize(winMutex_staticMutexes) ); p = &winMutex_staticMutexes[iType-2]; #ifdef SQLITE_DEBUG p->id = iType; #endif break; } } return p; } |
︙ | ︙ | |||
230 231 232 233 234 235 236 237 238 239 240 241 | ** upon successful entry. Mutexes created using SQLITE_MUTEX_RECURSIVE can ** be entered multiple times by the same thread. In such cases the, ** mutex must be exited an equal number of times before another thread ** can enter. If the same thread tries to enter any other kind of mutex ** more than once, the behavior is undefined. */ static void winMutexEnter(sqlite3_mutex *p){ DWORD tid = GetCurrentThreadId(); assert( p->id==SQLITE_MUTEX_RECURSIVE || winMutexNotheld2(p, tid) ); EnterCriticalSection(&p->mutex); p->owner = tid; p->nRef++; | > > > < | 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 | ** upon successful entry. Mutexes created using SQLITE_MUTEX_RECURSIVE can ** be entered multiple times by the same thread. In such cases the, ** mutex must be exited an equal number of times before another thread ** can enter. If the same thread tries to enter any other kind of mutex ** more than once, the behavior is undefined. */ static void winMutexEnter(sqlite3_mutex *p){ #ifdef SQLITE_DEBUG DWORD tid = GetCurrentThreadId(); assert( p->id==SQLITE_MUTEX_RECURSIVE || winMutexNotheld2(p, tid) ); #endif EnterCriticalSection(&p->mutex); #ifdef SQLITE_DEBUG p->owner = tid; p->nRef++; if( p->trace ){ printf("enter mutex %p (%d) with nRef=%d\n", p, p->trace, p->nRef); } #endif } static int winMutexTry(sqlite3_mutex *p){ #ifndef NDEBUG |
︙ | ︙ | |||
284 285 286 287 288 289 290 | ** previously entered by the same thread. The behavior ** is undefined if the mutex is not currently entered or ** is not currently allocated. SQLite will never do either. */ static void winMutexLeave(sqlite3_mutex *p){ #ifndef NDEBUG DWORD tid = GetCurrentThreadId(); | < > | | | 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 | ** previously entered by the same thread. The behavior ** is undefined if the mutex is not currently entered or ** is not currently allocated. SQLite will never do either. */ static void winMutexLeave(sqlite3_mutex *p){ #ifndef NDEBUG DWORD tid = GetCurrentThreadId(); assert( p->nRef>0 ); assert( p->owner==tid ); p->nRef--; assert( p->nRef==0 || p->id==SQLITE_MUTEX_RECURSIVE ); #endif LeaveCriticalSection(&p->mutex); #ifdef SQLITE_DEBUG if( p->trace ){ printf("leave mutex %p (%d) with nRef=%d\n", p, p->trace, p->nRef); } #endif } sqlite3_mutex_methods const *sqlite3DefaultMutex(void){ static const sqlite3_mutex_methods sMutex = { winMutexInit, winMutexEnd, winMutexAlloc, winMutexFree, winMutexEnter, winMutexTry, winMutexLeave, |
︙ | ︙ |
Changes to src/notify.c.
︙ | ︙ | |||
153 154 155 156 157 158 159 160 161 162 163 164 165 166 | int rc = SQLITE_OK; sqlite3_mutex_enter(db->mutex); enterMutex(); if( xNotify==0 ){ removeFromBlockedList(db); db->pUnlockConnection = 0; db->xUnlockNotify = 0; db->pUnlockArg = 0; }else if( 0==db->pBlockingConnection ){ /* The blocking transaction has been concluded. Or there never was a ** blocking transaction. In either case, invoke the notify callback ** immediately. | > | 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 | int rc = SQLITE_OK; sqlite3_mutex_enter(db->mutex); enterMutex(); if( xNotify==0 ){ removeFromBlockedList(db); db->pBlockingConnection = 0; db->pUnlockConnection = 0; db->xUnlockNotify = 0; db->pUnlockArg = 0; }else if( 0==db->pBlockingConnection ){ /* The blocking transaction has been concluded. Or there never was a ** blocking transaction. In either case, invoke the notify callback ** immediately. |
︙ | ︙ |
Changes to src/os.c.
︙ | ︙ | |||
30 31 32 33 34 35 36 | ** sqlite3OsOpen() ** sqlite3OsRead() ** sqlite3OsWrite() ** sqlite3OsSync() ** sqlite3OsLock() ** */ | | > | > | 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 | ** sqlite3OsOpen() ** sqlite3OsRead() ** sqlite3OsWrite() ** sqlite3OsSync() ** sqlite3OsLock() ** */ #if defined(SQLITE_TEST) int sqlite3_memdebug_vfs_oom_test = 1; #define DO_OS_MALLOC_TEST(x) \ if (sqlite3_memdebug_vfs_oom_test && (!x || !sqlite3IsMemJournal(x))) { \ void *pTstAlloc = sqlite3Malloc(10); \ if (!pTstAlloc) return SQLITE_IOERR_NOMEM; \ sqlite3_free(pTstAlloc); \ } #else #define DO_OS_MALLOC_TEST(x) #endif |
︙ | ︙ | |||
94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 | int sqlite3OsSectorSize(sqlite3_file *id){ int (*xSectorSize)(sqlite3_file*) = id->pMethods->xSectorSize; return (xSectorSize ? xSectorSize(id) : SQLITE_DEFAULT_SECTOR_SIZE); } int sqlite3OsDeviceCharacteristics(sqlite3_file *id){ return id->pMethods->xDeviceCharacteristics(id); } /* ** The next group of routines are convenience wrappers around the ** VFS methods. */ int sqlite3OsOpen( sqlite3_vfs *pVfs, const char *zPath, sqlite3_file *pFile, int flags, int *pFlagsOut ){ int rc; DO_OS_MALLOC_TEST(0); | > > > > > > > > > > > > > > > > > > | | | 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 | int sqlite3OsSectorSize(sqlite3_file *id){ int (*xSectorSize)(sqlite3_file*) = id->pMethods->xSectorSize; return (xSectorSize ? xSectorSize(id) : SQLITE_DEFAULT_SECTOR_SIZE); } int sqlite3OsDeviceCharacteristics(sqlite3_file *id){ return id->pMethods->xDeviceCharacteristics(id); } int sqlite3OsShmLock(sqlite3_file *id, int offset, int n, int flags){ return id->pMethods->xShmLock(id, offset, n, flags); } void sqlite3OsShmBarrier(sqlite3_file *id){ id->pMethods->xShmBarrier(id); } int sqlite3OsShmUnmap(sqlite3_file *id, int deleteFlag){ return id->pMethods->xShmUnmap(id, deleteFlag); } int sqlite3OsShmMap( sqlite3_file *id, /* Database file handle */ int iPage, int pgsz, int bExtend, /* True to extend file if necessary */ void volatile **pp /* OUT: Pointer to mapping */ ){ return id->pMethods->xShmMap(id, iPage, pgsz, bExtend, pp); } /* ** The next group of routines are convenience wrappers around the ** VFS methods. */ int sqlite3OsOpen( sqlite3_vfs *pVfs, const char *zPath, sqlite3_file *pFile, int flags, int *pFlagsOut ){ int rc; DO_OS_MALLOC_TEST(0); /* 0x87f3f is a mask of SQLITE_OPEN_ flags that are valid to be passed ** down into the VFS layer. Some SQLITE_OPEN_ flags (for example, ** SQLITE_OPEN_FULLMUTEX or SQLITE_OPEN_SHAREDCACHE) are blocked before ** reaching the VFS. */ rc = pVfs->xOpen(pVfs, zPath, pFile, flags & 0x87f3f, pFlagsOut); assert( rc==SQLITE_OK || pFile->pMethods==0 ); return rc; } int sqlite3OsDelete(sqlite3_vfs *pVfs, const char *zPath, int dirSync){ return pVfs->xDelete(pVfs, zPath, dirSync); } int sqlite3OsAccess( |
︙ | ︙ | |||
157 158 159 160 161 162 163 | #endif /* SQLITE_OMIT_LOAD_EXTENSION */ int sqlite3OsRandomness(sqlite3_vfs *pVfs, int nByte, char *zBufOut){ return pVfs->xRandomness(pVfs, nByte, zBufOut); } int sqlite3OsSleep(sqlite3_vfs *pVfs, int nMicro){ return pVfs->xSleep(pVfs, nMicro); } | | > > > > > | > > > | 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 | #endif /* SQLITE_OMIT_LOAD_EXTENSION */ int sqlite3OsRandomness(sqlite3_vfs *pVfs, int nByte, char *zBufOut){ return pVfs->xRandomness(pVfs, nByte, zBufOut); } int sqlite3OsSleep(sqlite3_vfs *pVfs, int nMicro){ return pVfs->xSleep(pVfs, nMicro); } int sqlite3OsCurrentTimeInt64(sqlite3_vfs *pVfs, sqlite3_int64 *pTimeOut){ int rc; if( pVfs->iVersion>=2 && pVfs->xCurrentTimeInt64 ){ rc = pVfs->xCurrentTimeInt64(pVfs, pTimeOut); }else{ double r; rc = pVfs->xCurrentTime(pVfs, &r); *pTimeOut = (sqlite3_int64)(r*86400000.0); } return rc; } int sqlite3OsOpenMalloc( sqlite3_vfs *pVfs, const char *zFile, sqlite3_file **ppFile, int flags, |
︙ | ︙ |
Changes to src/os.h.
︙ | ︙ | |||
213 214 215 216 217 218 219 | ** Changing the value of PENDING_BYTE results in a subtly incompatible ** file format. Depending on how it is changed, you might not notice ** the incompatibility right away, even running a full regression test. ** The default location of PENDING_BYTE is the first byte past the ** 1GB boundary. ** */ | > > > | > | 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 | ** Changing the value of PENDING_BYTE results in a subtly incompatible ** file format. Depending on how it is changed, you might not notice ** the incompatibility right away, even running a full regression test. ** The default location of PENDING_BYTE is the first byte past the ** 1GB boundary. ** */ #ifdef SQLITE_OMIT_WSD # define PENDING_BYTE (0x40000000) #else # define PENDING_BYTE sqlite3PendingByte #endif #define RESERVED_BYTE (PENDING_BYTE+1) #define SHARED_FIRST (PENDING_BYTE+2) #define SHARED_SIZE 510 /* ** Wrapper around OS specific sqlite3_os_init() function. */ |
︙ | ︙ | |||
239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 | int sqlite3OsLock(sqlite3_file*, int); int sqlite3OsUnlock(sqlite3_file*, int); int sqlite3OsCheckReservedLock(sqlite3_file *id, int *pResOut); int sqlite3OsFileControl(sqlite3_file*,int,void*); #define SQLITE_FCNTL_DB_UNCHANGED 0xca093fa0 int sqlite3OsSectorSize(sqlite3_file *id); int sqlite3OsDeviceCharacteristics(sqlite3_file *id); /* ** Functions for accessing sqlite3_vfs methods */ int sqlite3OsOpen(sqlite3_vfs *, const char *, sqlite3_file*, int, int *); int sqlite3OsDelete(sqlite3_vfs *, const char *, int); int sqlite3OsAccess(sqlite3_vfs *, const char *, int, int *pResOut); int sqlite3OsFullPathname(sqlite3_vfs *, const char *, int, char *); #ifndef SQLITE_OMIT_LOAD_EXTENSION void *sqlite3OsDlOpen(sqlite3_vfs *, const char *); void sqlite3OsDlError(sqlite3_vfs *, int, char *); void (*sqlite3OsDlSym(sqlite3_vfs *, void *, const char *))(void); void sqlite3OsDlClose(sqlite3_vfs *, void *); #endif /* SQLITE_OMIT_LOAD_EXTENSION */ int sqlite3OsRandomness(sqlite3_vfs *, int, char *); int sqlite3OsSleep(sqlite3_vfs *, int); | > > > > | | 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 | int sqlite3OsLock(sqlite3_file*, int); int sqlite3OsUnlock(sqlite3_file*, int); int sqlite3OsCheckReservedLock(sqlite3_file *id, int *pResOut); int sqlite3OsFileControl(sqlite3_file*,int,void*); #define SQLITE_FCNTL_DB_UNCHANGED 0xca093fa0 int sqlite3OsSectorSize(sqlite3_file *id); int sqlite3OsDeviceCharacteristics(sqlite3_file *id); int sqlite3OsShmMap(sqlite3_file *,int,int,int,void volatile **); int sqlite3OsShmLock(sqlite3_file *id, int, int, int); void sqlite3OsShmBarrier(sqlite3_file *id); int sqlite3OsShmUnmap(sqlite3_file *id, int); /* ** Functions for accessing sqlite3_vfs methods */ int sqlite3OsOpen(sqlite3_vfs *, const char *, sqlite3_file*, int, int *); int sqlite3OsDelete(sqlite3_vfs *, const char *, int); int sqlite3OsAccess(sqlite3_vfs *, const char *, int, int *pResOut); int sqlite3OsFullPathname(sqlite3_vfs *, const char *, int, char *); #ifndef SQLITE_OMIT_LOAD_EXTENSION void *sqlite3OsDlOpen(sqlite3_vfs *, const char *); void sqlite3OsDlError(sqlite3_vfs *, int, char *); void (*sqlite3OsDlSym(sqlite3_vfs *, void *, const char *))(void); void sqlite3OsDlClose(sqlite3_vfs *, void *); #endif /* SQLITE_OMIT_LOAD_EXTENSION */ int sqlite3OsRandomness(sqlite3_vfs *, int, char *); int sqlite3OsSleep(sqlite3_vfs *, int); int sqlite3OsCurrentTimeInt64(sqlite3_vfs *, sqlite3_int64*); /* ** Convenience functions for opening and closing files using ** sqlite3_malloc() to obtain space for the file-handle structure. */ int sqlite3OsOpenMalloc(sqlite3_vfs *, const char *, sqlite3_file **, int,int*); int sqlite3OsCloseFree(sqlite3_file *); #endif /* _SQLITE_OS_H_ */ |
Changes to src/os_common.h.
︙ | ︙ | |||
27 28 29 30 31 32 33 | */ #ifdef MEMORY_DEBUG # error "The MEMORY_DEBUG macro is obsolete. Use SQLITE_DEBUG instead." #endif #ifdef SQLITE_DEBUG int sqlite3OSTrace = 0; | | < < < < < < < < < < < < < < | | 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 | */ #ifdef MEMORY_DEBUG # error "The MEMORY_DEBUG macro is obsolete. Use SQLITE_DEBUG instead." #endif #ifdef SQLITE_DEBUG int sqlite3OSTrace = 0; #define OSTRACE(X) if( sqlite3OSTrace ) sqlite3DebugPrintf X #else #define OSTRACE(X) #endif /* ** Macros for performance tracing. Normally turned off. Only works ** on i486 hardware. */ #ifdef SQLITE_PERFORMANCE_TRACE |
︙ | ︙ |
Changes to src/os_os2.c.
︙ | ︙ | |||
77 78 79 80 81 82 83 | /* ** Close a file. */ static int os2Close( sqlite3_file *id ){ APIRET rc = NO_ERROR; os2File *pFile; if( id && (pFile = (os2File*)id) != 0 ){ | | | 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 | /* ** Close a file. */ static int os2Close( sqlite3_file *id ){ APIRET rc = NO_ERROR; os2File *pFile; if( id && (pFile = (os2File*)id) != 0 ){ OSTRACE(( "CLOSE %d\n", pFile->h )); rc = DosClose( pFile->h ); pFile->locktype = NO_LOCK; if( pFile->pathToDel != NULL ){ rc = DosForceDelete( (PSZ)pFile->pathToDel ); free( pFile->pathToDel ); pFile->pathToDel = NULL; } |
︙ | ︙ | |||
108 109 110 111 112 113 114 | sqlite3_int64 offset /* Begin reading at this offset */ ){ ULONG fileLocation = 0L; ULONG got; os2File *pFile = (os2File*)id; assert( id!=0 ); SimulateIOError( return SQLITE_IOERR_READ ); | | | 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 | sqlite3_int64 offset /* Begin reading at this offset */ ){ ULONG fileLocation = 0L; ULONG got; os2File *pFile = (os2File*)id; assert( id!=0 ); SimulateIOError( return SQLITE_IOERR_READ ); OSTRACE(( "READ %d lock=%d\n", pFile->h, pFile->locktype )); if( DosSetFilePtr(pFile->h, offset, FILE_BEGIN, &fileLocation) != NO_ERROR ){ return SQLITE_IOERR; } if( DosRead( pFile->h, pBuf, amt, &got ) != NO_ERROR ){ return SQLITE_IOERR_READ; } if( got == (ULONG)amt ) |
︙ | ︙ | |||
141 142 143 144 145 146 147 | ULONG fileLocation = 0L; APIRET rc = NO_ERROR; ULONG wrote; os2File *pFile = (os2File*)id; assert( id!=0 ); SimulateIOError( return SQLITE_IOERR_WRITE ); SimulateDiskfullError( return SQLITE_FULL ); | | | 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 | ULONG fileLocation = 0L; APIRET rc = NO_ERROR; ULONG wrote; os2File *pFile = (os2File*)id; assert( id!=0 ); SimulateIOError( return SQLITE_IOERR_WRITE ); SimulateDiskfullError( return SQLITE_FULL ); OSTRACE(( "WRITE %d lock=%d\n", pFile->h, pFile->locktype )); if( DosSetFilePtr(pFile->h, offset, FILE_BEGIN, &fileLocation) != NO_ERROR ){ return SQLITE_IOERR; } assert( amt>0 ); while( amt > 0 && ( rc = DosWrite( pFile->h, (PVOID)pBuf, amt, &wrote ) ) == NO_ERROR && wrote > 0 |
︙ | ︙ | |||
163 164 165 166 167 168 169 | /* ** Truncate an open file to a specified size */ static int os2Truncate( sqlite3_file *id, i64 nByte ){ APIRET rc = NO_ERROR; os2File *pFile = (os2File*)id; | | | | 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 | /* ** Truncate an open file to a specified size */ static int os2Truncate( sqlite3_file *id, i64 nByte ){ APIRET rc = NO_ERROR; os2File *pFile = (os2File*)id; OSTRACE(( "TRUNCATE %d %lld\n", pFile->h, nByte )); SimulateIOError( return SQLITE_IOERR_TRUNCATE ); rc = DosSetFileSize( pFile->h, nByte ); return rc == NO_ERROR ? SQLITE_OK : SQLITE_IOERR_TRUNCATE; } #ifdef SQLITE_TEST /* ** Count the number of fullsyncs and normal syncs. This is used to test ** that syncs and fullsyncs are occuring at the right times. */ int sqlite3_sync_count = 0; int sqlite3_fullsync_count = 0; #endif /* ** Make sure all writes to a particular file are committed to disk. */ static int os2Sync( sqlite3_file *id, int flags ){ os2File *pFile = (os2File*)id; OSTRACE(( "SYNC %d lock=%d\n", pFile->h, pFile->locktype )); #ifdef SQLITE_TEST if( flags & SQLITE_SYNC_FULL){ sqlite3_fullsync_count++; } sqlite3_sync_count++; #endif /* If we compiled with the SQLITE_NO_SYNC flag, then syncing is a |
︙ | ︙ | |||
233 234 235 236 237 238 239 | memset(&LockArea, 0, sizeof(LockArea)); memset(&UnlockArea, 0, sizeof(UnlockArea)); LockArea.lOffset = SHARED_FIRST; LockArea.lRange = SHARED_SIZE; UnlockArea.lOffset = 0L; UnlockArea.lRange = 0L; res = DosSetFileLocks( pFile->h, &UnlockArea, &LockArea, LOCK_TIMEOUT, 1L ); | | | | 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 | memset(&LockArea, 0, sizeof(LockArea)); memset(&UnlockArea, 0, sizeof(UnlockArea)); LockArea.lOffset = SHARED_FIRST; LockArea.lRange = SHARED_SIZE; UnlockArea.lOffset = 0L; UnlockArea.lRange = 0L; res = DosSetFileLocks( pFile->h, &UnlockArea, &LockArea, LOCK_TIMEOUT, 1L ); OSTRACE(( "GETREADLOCK %d res=%d\n", pFile->h, res )); return res; } /* ** Undo a readlock */ static int unlockReadLock( os2File *id ){ FILELOCK LockArea, UnlockArea; APIRET res; memset(&LockArea, 0, sizeof(LockArea)); memset(&UnlockArea, 0, sizeof(UnlockArea)); LockArea.lOffset = 0L; LockArea.lRange = 0L; UnlockArea.lOffset = SHARED_FIRST; UnlockArea.lRange = SHARED_SIZE; res = DosSetFileLocks( id->h, &UnlockArea, &LockArea, LOCK_TIMEOUT, 1L ); OSTRACE(( "UNLOCK-READLOCK file handle=%d res=%d?\n", id->h, res )); return res; } /* ** Lock the file with the lock specified by parameter locktype - one ** of the following: ** |
︙ | ︙ | |||
292 293 294 295 296 297 298 | int gotPendingLock = 0;/* True if we acquired a PENDING lock this time */ FILELOCK LockArea, UnlockArea; os2File *pFile = (os2File*)id; memset(&LockArea, 0, sizeof(LockArea)); memset(&UnlockArea, 0, sizeof(UnlockArea)); assert( pFile!=0 ); | | | | 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 | int gotPendingLock = 0;/* True if we acquired a PENDING lock this time */ FILELOCK LockArea, UnlockArea; os2File *pFile = (os2File*)id; memset(&LockArea, 0, sizeof(LockArea)); memset(&UnlockArea, 0, sizeof(UnlockArea)); assert( pFile!=0 ); OSTRACE(( "LOCK %d %d was %d\n", pFile->h, locktype, pFile->locktype )); /* If there is already a lock of this type or more restrictive on the ** os2File, do nothing. Don't use the end_lock: exit path, as ** sqlite3_mutex_enter() hasn't been called yet. */ if( pFile->locktype>=locktype ){ OSTRACE(( "LOCK %d %d ok (already held)\n", pFile->h, locktype )); return SQLITE_OK; } /* Make sure the locking sequence is correct */ assert( pFile->locktype!=NO_LOCK || locktype==SHARED_LOCK ); assert( locktype!=PENDING_LOCK ); |
︙ | ︙ | |||
326 327 328 329 330 331 332 | UnlockArea.lOffset = 0L; UnlockArea.lRange = 0L; /* wait longer than LOCK_TIMEOUT here not to have to try multiple times */ res = DosSetFileLocks( pFile->h, &UnlockArea, &LockArea, 100L, 0L ); if( res == NO_ERROR ){ gotPendingLock = 1; | | | | | > | | | | | | | | | | | | 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 | UnlockArea.lOffset = 0L; UnlockArea.lRange = 0L; /* wait longer than LOCK_TIMEOUT here not to have to try multiple times */ res = DosSetFileLocks( pFile->h, &UnlockArea, &LockArea, 100L, 0L ); if( res == NO_ERROR ){ gotPendingLock = 1; OSTRACE(( "LOCK %d pending lock boolean set. res=%d\n", pFile->h, res )); } } /* Acquire a shared lock */ if( locktype==SHARED_LOCK && res == NO_ERROR ){ assert( pFile->locktype==NO_LOCK ); res = getReadLock(pFile); if( res == NO_ERROR ){ newLocktype = SHARED_LOCK; } OSTRACE(( "LOCK %d acquire shared lock. res=%d\n", pFile->h, res )); } /* Acquire a RESERVED lock */ if( locktype==RESERVED_LOCK && res == NO_ERROR ){ assert( pFile->locktype==SHARED_LOCK ); LockArea.lOffset = RESERVED_BYTE; LockArea.lRange = 1L; UnlockArea.lOffset = 0L; UnlockArea.lRange = 0L; res = DosSetFileLocks( pFile->h, &UnlockArea, &LockArea, LOCK_TIMEOUT, 0L ); if( res == NO_ERROR ){ newLocktype = RESERVED_LOCK; } OSTRACE(( "LOCK %d acquire reserved lock. res=%d\n", pFile->h, res )); } /* Acquire a PENDING lock */ if( locktype==EXCLUSIVE_LOCK && res == NO_ERROR ){ newLocktype = PENDING_LOCK; gotPendingLock = 0; OSTRACE(( "LOCK %d acquire pending lock. pending lock boolean unset.\n", pFile->h )); } /* Acquire an EXCLUSIVE lock */ if( locktype==EXCLUSIVE_LOCK && res == NO_ERROR ){ assert( pFile->locktype>=SHARED_LOCK ); res = unlockReadLock(pFile); OSTRACE(( "unreadlock = %d\n", res )); LockArea.lOffset = SHARED_FIRST; LockArea.lRange = SHARED_SIZE; UnlockArea.lOffset = 0L; UnlockArea.lRange = 0L; res = DosSetFileLocks( pFile->h, &UnlockArea, &LockArea, LOCK_TIMEOUT, 0L ); if( res == NO_ERROR ){ newLocktype = EXCLUSIVE_LOCK; }else{ OSTRACE(( "OS/2 error-code = %d\n", res )); getReadLock(pFile); } OSTRACE(( "LOCK %d acquire exclusive lock. res=%d\n", pFile->h, res )); } /* If we are holding a PENDING lock that ought to be released, then ** release it now. */ if( gotPendingLock && locktype==SHARED_LOCK ){ int r; LockArea.lOffset = 0L; LockArea.lRange = 0L; UnlockArea.lOffset = PENDING_BYTE; UnlockArea.lRange = 1L; r = DosSetFileLocks( pFile->h, &UnlockArea, &LockArea, LOCK_TIMEOUT, 0L ); OSTRACE(( "LOCK %d unlocking pending/is shared. r=%d\n", pFile->h, r )); } /* Update the state of the lock has held in the file descriptor then ** return the appropriate result code. */ if( res == NO_ERROR ){ rc = SQLITE_OK; }else{ OSTRACE(( "LOCK FAILED %d trying for %d but got %d\n", pFile->h, locktype, newLocktype )); rc = SQLITE_BUSY; } pFile->locktype = newLocktype; OSTRACE(( "LOCK %d now %d\n", pFile->h, pFile->locktype )); return rc; } /* ** This routine checks if there is a RESERVED lock held on the specified ** file by this or any other process. If such a lock is held, return ** non-zero, otherwise zero. */ static int os2CheckReservedLock( sqlite3_file *id, int *pOut ){ int r = 0; os2File *pFile = (os2File*)id; assert( pFile!=0 ); if( pFile->locktype>=RESERVED_LOCK ){ r = 1; OSTRACE(( "TEST WR-LOCK %d %d (local)\n", pFile->h, r )); }else{ FILELOCK LockArea, UnlockArea; APIRET rc = NO_ERROR; memset(&LockArea, 0, sizeof(LockArea)); memset(&UnlockArea, 0, sizeof(UnlockArea)); LockArea.lOffset = RESERVED_BYTE; LockArea.lRange = 1L; UnlockArea.lOffset = 0L; UnlockArea.lRange = 0L; rc = DosSetFileLocks( pFile->h, &UnlockArea, &LockArea, LOCK_TIMEOUT, 0L ); OSTRACE(( "TEST WR-LOCK %d lock reserved byte rc=%d\n", pFile->h, rc )); if( rc == NO_ERROR ){ APIRET rcu = NO_ERROR; /* return code for unlocking */ LockArea.lOffset = 0L; LockArea.lRange = 0L; UnlockArea.lOffset = RESERVED_BYTE; UnlockArea.lRange = 1L; rcu = DosSetFileLocks( pFile->h, &UnlockArea, &LockArea, LOCK_TIMEOUT, 0L ); OSTRACE(( "TEST WR-LOCK %d unlock reserved byte r=%d\n", pFile->h, rcu )); } r = !(rc == NO_ERROR); OSTRACE(( "TEST WR-LOCK %d %d (remote)\n", pFile->h, r )); } *pOut = r; return SQLITE_OK; } /* ** Lower the locking level on file descriptor id to locktype. locktype |
︙ | ︙ | |||
474 475 476 477 478 479 480 | APIRET res = NO_ERROR; FILELOCK LockArea, UnlockArea; memset(&LockArea, 0, sizeof(LockArea)); memset(&UnlockArea, 0, sizeof(UnlockArea)); assert( pFile!=0 ); assert( locktype<=SHARED_LOCK ); | | | | | | > | | > | | 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 | APIRET res = NO_ERROR; FILELOCK LockArea, UnlockArea; memset(&LockArea, 0, sizeof(LockArea)); memset(&UnlockArea, 0, sizeof(UnlockArea)); assert( pFile!=0 ); assert( locktype<=SHARED_LOCK ); OSTRACE(( "UNLOCK %d to %d was %d\n", pFile->h, locktype, pFile->locktype )); type = pFile->locktype; if( type>=EXCLUSIVE_LOCK ){ LockArea.lOffset = 0L; LockArea.lRange = 0L; UnlockArea.lOffset = SHARED_FIRST; UnlockArea.lRange = SHARED_SIZE; res = DosSetFileLocks( pFile->h, &UnlockArea, &LockArea, LOCK_TIMEOUT, 0L ); OSTRACE(( "UNLOCK %d exclusive lock res=%d\n", pFile->h, res )); if( locktype==SHARED_LOCK && getReadLock(pFile) != NO_ERROR ){ /* This should never happen. We should always be able to ** reacquire the read lock */ OSTRACE(( "UNLOCK %d to %d getReadLock() failed\n", pFile->h, locktype )); rc = SQLITE_IOERR_UNLOCK; } } if( type>=RESERVED_LOCK ){ LockArea.lOffset = 0L; LockArea.lRange = 0L; UnlockArea.lOffset = RESERVED_BYTE; UnlockArea.lRange = 1L; res = DosSetFileLocks( pFile->h, &UnlockArea, &LockArea, LOCK_TIMEOUT, 0L ); OSTRACE(( "UNLOCK %d reserved res=%d\n", pFile->h, res )); } if( locktype==NO_LOCK && type>=SHARED_LOCK ){ res = unlockReadLock(pFile); OSTRACE(( "UNLOCK %d is %d want %d res=%d\n", pFile->h, type, locktype, res )); } if( type>=PENDING_LOCK ){ LockArea.lOffset = 0L; LockArea.lRange = 0L; UnlockArea.lOffset = PENDING_BYTE; UnlockArea.lRange = 1L; res = DosSetFileLocks( pFile->h, &UnlockArea, &LockArea, LOCK_TIMEOUT, 0L ); OSTRACE(( "UNLOCK %d pending res=%d\n", pFile->h, res )); } pFile->locktype = locktype; OSTRACE(( "UNLOCK %d now %d\n", pFile->h, pFile->locktype )); return rc; } /* ** Control and query of the open file handle. */ static int os2FileControl(sqlite3_file *id, int op, void *pArg){ switch( op ){ case SQLITE_FCNTL_LOCKSTATE: { *(int*)pArg = ((os2File*)id)->locktype; OSTRACE(( "FCNTL_LOCKSTATE %d lock=%d\n", ((os2File*)id)->h, ((os2File*)id)->locktype )); return SQLITE_OK; } } return SQLITE_ERROR; } /* |
︙ | ︙ | |||
709 710 711 712 713 714 715 | } j = sqlite3Strlen30( zBuf ); sqlite3_randomness( 20, &zBuf[j] ); for( i = 0; i < 20; i++, j++ ){ zBuf[j] = (char)zChars[ ((unsigned char)zBuf[j])%(sizeof(zChars)-1) ]; } zBuf[j] = 0; | | | 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 | } j = sqlite3Strlen30( zBuf ); sqlite3_randomness( 20, &zBuf[j] ); for( i = 0; i < 20; i++, j++ ){ zBuf[j] = (char)zChars[ ((unsigned char)zBuf[j])%(sizeof(zChars)-1) ]; } zBuf[j] = 0; OSTRACE(( "TEMP FILENAME: %s\n", zBuf )); return SQLITE_OK; } /* ** Turn a relative pathname into a full pathname. Write the full ** pathname into zFull[]. zFull[] will be at least pVfs->mxPathname |
︙ | ︙ | |||
772 773 774 775 776 777 778 | } zName = zTmpname; } memset( pFile, 0, sizeof(*pFile) ); | | | | | | | | | | | | > | | | | | | | | 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 | } zName = zTmpname; } memset( pFile, 0, sizeof(*pFile) ); OSTRACE(( "OPEN want %d\n", flags )); if( flags & SQLITE_OPEN_READWRITE ){ ulOpenMode |= OPEN_ACCESS_READWRITE; OSTRACE(( "OPEN read/write\n" )); }else{ ulOpenMode |= OPEN_ACCESS_READONLY; OSTRACE(( "OPEN read only\n" )); } if( flags & SQLITE_OPEN_CREATE ){ ulOpenFlags |= OPEN_ACTION_OPEN_IF_EXISTS | OPEN_ACTION_CREATE_IF_NEW; OSTRACE(( "OPEN open new/create\n" )); }else{ ulOpenFlags |= OPEN_ACTION_OPEN_IF_EXISTS | OPEN_ACTION_FAIL_IF_NEW; OSTRACE(( "OPEN open existing\n" )); } if( flags & SQLITE_OPEN_MAIN_DB ){ ulOpenMode |= OPEN_SHARE_DENYNONE; OSTRACE(( "OPEN share read/write\n" )); }else{ ulOpenMode |= OPEN_SHARE_DENYWRITE; OSTRACE(( "OPEN share read only\n" )); } if( flags & SQLITE_OPEN_DELETEONCLOSE ){ char pathUtf8[CCHMAXPATH]; #ifdef NDEBUG /* when debugging we want to make sure it is deleted */ ulFileAttribute = FILE_HIDDEN; #endif os2FullPathname( pVfs, zName, CCHMAXPATH, pathUtf8 ); pFile->pathToDel = convertUtf8PathToCp( pathUtf8 ); OSTRACE(( "OPEN hidden/delete on close file attributes\n" )); }else{ pFile->pathToDel = NULL; OSTRACE(( "OPEN normal file attribute\n" )); } /* always open in random access mode for possibly better speed */ ulOpenMode |= OPEN_FLAGS_RANDOM; ulOpenMode |= OPEN_FLAGS_FAIL_ON_ERROR; ulOpenMode |= OPEN_FLAGS_NOINHERIT; zNameCp = convertUtf8PathToCp( zName ); rc = DosOpen( (PSZ)zNameCp, &h, &ulAction, 0L, ulFileAttribute, ulOpenFlags, ulOpenMode, (PEAOP2)NULL ); free( zNameCp ); if( rc != NO_ERROR ){ OSTRACE(( "OPEN Invalid handle rc=%d: zName=%s, ulAction=%#lx, ulAttr=%#lx, ulFlags=%#lx, ulMode=%#lx\n", rc, zName, ulAction, ulFileAttribute, ulOpenFlags, ulOpenMode )); if( pFile->pathToDel ) free( pFile->pathToDel ); pFile->pathToDel = NULL; if( flags & SQLITE_OPEN_READWRITE ){ OSTRACE(( "OPEN %d Invalid handle\n", ((flags | SQLITE_OPEN_READONLY) & ~SQLITE_OPEN_READWRITE) )); return os2Open( pVfs, zName, id, ((flags | SQLITE_OPEN_READONLY) & ~SQLITE_OPEN_READWRITE), pOutFlags ); }else{ return SQLITE_CANTOPEN; } } if( pOutFlags ){ *pOutFlags = flags & SQLITE_OPEN_READWRITE ? SQLITE_OPEN_READWRITE : SQLITE_OPEN_READONLY; } pFile->pMethod = &os2IoMethod; pFile->h = h; OpenCounter(+1); OSTRACE(( "OPEN %d pOutFlags=%d\n", pFile->h, pOutFlags )); return SQLITE_OK; } /* ** Delete the named file. */ static int os2Delete( sqlite3_vfs *pVfs, /* Not used on os2 */ const char *zFilename, /* Name of file to delete */ int syncDir /* Not used on os2 */ ){ APIRET rc = NO_ERROR; char *zFilenameCp = convertUtf8PathToCp( zFilename ); SimulateIOError( return SQLITE_IOERR_DELETE ); rc = DosDelete( (PSZ)zFilenameCp ); free( zFilenameCp ); OSTRACE(( "DELETE \"%s\"\n", zFilename )); return rc == NO_ERROR ? SQLITE_OK : SQLITE_IOERR_DELETE; } /* ** Check the existance and status of a file. */ static int os2Access( sqlite3_vfs *pVfs, /* Not used on os2 */ const char *zFilename, /* Name of file to check */ int flags, /* Type of test to make on this file */ int *pOut /* Write results here */ ){ FILESTATUS3 fsts3ConfigInfo; APIRET rc = NO_ERROR; char *zFilenameCp = convertUtf8PathToCp( zFilename ); memset( &fsts3ConfigInfo, 0, sizeof(fsts3ConfigInfo) ); rc = DosQueryPathInfo( (PSZ)zFilenameCp, FIL_STANDARD, &fsts3ConfigInfo, sizeof(FILESTATUS3) ); free( zFilenameCp ); OSTRACE(( "ACCESS fsts3ConfigInfo.attrFile=%d flags=%d rc=%d\n", fsts3ConfigInfo.attrFile, flags, rc )); switch( flags ){ case SQLITE_ACCESS_READ: case SQLITE_ACCESS_EXISTS: rc = (rc == NO_ERROR); OSTRACE(( "ACCESS %s access of read and exists rc=%d\n", zFilename, rc)); break; case SQLITE_ACCESS_READWRITE: rc = (rc == NO_ERROR) && ( (fsts3ConfigInfo.attrFile & FILE_READONLY) == 0 ); OSTRACE(( "ACCESS %s access of read/write rc=%d\n", zFilename, rc )); break; default: assert( !"Invalid flags argument" ); } *pOut = rc; return SQLITE_OK; } |
︙ | ︙ | |||
1107 1108 1109 1110 1111 1112 1113 | os2DlOpen, /* xDlOpen */ os2DlError, /* xDlError */ os2DlSym, /* xDlSym */ os2DlClose, /* xDlClose */ os2Randomness, /* xRandomness */ os2Sleep, /* xSleep */ os2CurrentTime, /* xCurrentTime */ | | | 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 | os2DlOpen, /* xDlOpen */ os2DlError, /* xDlError */ os2DlSym, /* xDlSym */ os2DlClose, /* xDlClose */ os2Randomness, /* xRandomness */ os2Sleep, /* xSleep */ os2CurrentTime, /* xCurrentTime */ os2GetLastError, /* xGetLastError */ }; sqlite3_vfs_register(&os2Vfs, 1); initUconvObjects(); return SQLITE_OK; } int sqlite3_os_end(void){ freeUconvObjects(); return SQLITE_OK; } #endif /* SQLITE_OS_OS2 */ |
Changes to src/os_unix.c.
︙ | ︙ | |||
115 116 117 118 119 120 121 122 123 124 125 126 127 128 | #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <unistd.h> #include <time.h> #include <sys/time.h> #include <errno.h> #if SQLITE_ENABLE_LOCKING_STYLE # include <sys/ioctl.h> # if OS_VXWORKS # include <semaphore.h> # include <limits.h> # else | > | 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 | #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <unistd.h> #include <time.h> #include <sys/time.h> #include <errno.h> #include <sys/mman.h> #if SQLITE_ENABLE_LOCKING_STYLE # include <sys/ioctl.h> # if OS_VXWORKS # include <semaphore.h> # include <limits.h> # else |
︙ | ︙ | |||
170 171 172 173 174 175 176 177 178 179 180 181 182 183 | /* ** Only set the lastErrno if the error code is a real error and not ** a normal expected return code of SQLITE_BUSY or SQLITE_OK */ #define IS_LOCK_ERROR(x) ((x != SQLITE_OK) && (x != SQLITE_BUSY)) /* ** Sometimes, after a file handle is closed by SQLite, the file descriptor ** cannot be closed immediately. In these cases, instances of the following ** structure are used to store the file descriptor while waiting for an ** opportunity to either close or reuse it. */ | > > > > > < < | | | | | | | | > > | | < < < | | | 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 | /* ** Only set the lastErrno if the error code is a real error and not ** a normal expected return code of SQLITE_BUSY or SQLITE_OK */ #define IS_LOCK_ERROR(x) ((x != SQLITE_OK) && (x != SQLITE_BUSY)) /* Forward references */ typedef struct unixShm unixShm; /* Connection shared memory */ typedef struct unixShmNode unixShmNode; /* Shared memory instance */ typedef struct unixInodeInfo unixInodeInfo; /* An i-node */ typedef struct UnixUnusedFd UnixUnusedFd; /* An unused file descriptor */ /* ** Sometimes, after a file handle is closed by SQLite, the file descriptor ** cannot be closed immediately. In these cases, instances of the following ** structure are used to store the file descriptor while waiting for an ** opportunity to either close or reuse it. */ struct UnixUnusedFd { int fd; /* File descriptor to close */ int flags; /* Flags this file descriptor was opened with */ UnixUnusedFd *pNext; /* Next unused file descriptor on same file */ }; /* ** The unixFile structure is subclass of sqlite3_file specific to the unix ** VFS implementations. */ typedef struct unixFile unixFile; struct unixFile { sqlite3_io_methods const *pMethod; /* Always the first entry */ unixInodeInfo *pInode; /* Info about locks on this inode */ int h; /* The file descriptor */ int dirfd; /* File descriptor for the directory */ unsigned char eFileLock; /* The type of lock held on this fd */ int lastErrno; /* The unix errno from last I/O error */ void *lockingContext; /* Locking style specific state */ UnixUnusedFd *pUnused; /* Pre-allocated UnixUnusedFd */ int fileFlags; /* Miscellanous flags */ const char *zPath; /* Name of the file */ unixShm *pShm; /* Shared memory segment information */ #if SQLITE_ENABLE_LOCKING_STYLE int openFlags; /* The flags specified at open() */ #endif #if SQLITE_ENABLE_LOCKING_STYLE || defined(__APPLE__) unsigned fsFlags; /* cached details from statfs() */ #endif #if OS_VXWORKS int isDelete; /* Delete on close if true */ struct vxworksFileId *pId; /* Unique file ID */ #endif #ifndef NDEBUG /* The next group of variables are used to track whether or not the ** transaction counter in bytes 24-27 of database files are updated ** whenever any part of the database changes. An assertion fault will ** occur if a file is updated without also updating the transaction ** counter. This test is made to avoid new problems similar to the |
︙ | ︙ | |||
283 284 285 286 287 288 289 | #else #define threadid 0 #endif /* ** Helper functions to obtain and relinquish the global mutex. The | | | 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 | #else #define threadid 0 #endif /* ** Helper functions to obtain and relinquish the global mutex. The ** global mutex is used to protect the unixInodeInfo and ** vxworksFileId objects used by this file, all of which may be ** shared by multiple threads. ** ** Function unixMutexHeld() is used to assert() that the global mutex ** is held when required. This function is only used as part of assert() ** statements. e.g. ** |
︙ | ︙ | |||
314 315 316 317 318 319 320 | #ifdef SQLITE_DEBUG /* ** Helper function for printing out trace information from debugging ** binaries. This returns the string represetation of the supplied ** integer lock-type. */ | | | | 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 | #ifdef SQLITE_DEBUG /* ** Helper function for printing out trace information from debugging ** binaries. This returns the string represetation of the supplied ** integer lock-type. */ static const char *azFileLock(int eFileLock){ switch( eFileLock ){ case NO_LOCK: return "NONE"; case SHARED_LOCK: return "SHARED"; case RESERVED_LOCK: return "RESERVED"; case PENDING_LOCK: return "PENDING"; case EXCLUSIVE_LOCK: return "EXCLUSIVE"; } return "ERROR"; |
︙ | ︙ | |||
652 653 654 655 656 657 658 | ** POSIX lock if the internal lock structure transitions between ** a locked and an unlocked state. ** ** But wait: there are yet more problems with POSIX advisory locks. ** ** If you close a file descriptor that points to a file that has locks, ** all locks on that file that are owned by the current process are | | | < | | | < < < < < < | < | < < < < | | < | < < < < < < < < < < < < < < | < < < < < < < < < < < < < < < < < < | | | | > > > > > < < < < < < < < < < < < < < < < < < < < < | | < < < < < < | > > | < < | | < < | < > | < < | < < < < | < < < | | < > > | < < < | < < < < < < > | | < | | < < < < < < < < < < < | | | < > | | < < < < < < < < > | < < < | < < < < < < < < < < < < < < < | < < < < < < | | > | | | > > | | | | | | | | | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | | | | | < < | < | | 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 | ** POSIX lock if the internal lock structure transitions between ** a locked and an unlocked state. ** ** But wait: there are yet more problems with POSIX advisory locks. ** ** If you close a file descriptor that points to a file that has locks, ** all locks on that file that are owned by the current process are ** released. To work around this problem, each unixInodeInfo object ** maintains a count of the number of pending locks on tha inode. ** When an attempt is made to close an unixFile, if there are ** other unixFile open on the same inode that are holding locks, the call ** to close() the file descriptor is deferred until all of the locks clear. ** The unixInodeInfo structure keeps a list of file descriptors that need to ** be closed and that list is walked (and cleared) when the last lock ** clears. ** ** Yet another problem: LinuxThreads do not play well with posix locks. ** ** Many older versions of linux use the LinuxThreads library which is ** not posix compliant. Under LinuxThreads, a lock created by thread ** A cannot be modified or overridden by a different thread B. ** Only thread A can modify the lock. Locking behavior is correct ** if the appliation uses the newer Native Posix Thread Library (NPTL) ** on linux - with NPTL a lock created by thread A can override locks ** in thread B. But there is no way to know at compile-time which ** threading library is being used. So there is no way to know at ** compile-time whether or not thread A can override locks on thread B. ** One has to do a run-time check to discover the behavior of the ** current process. ** ** SQLite used to support LinuxThreads. But support for LinuxThreads ** was dropped beginning with version 3.7.0. SQLite will still work with ** LinuxThreads provided that (1) there is no more than one connection ** per database file in the same process and (2) database connections ** do not move across threads. */ /* ** An instance of the following structure serves as the key used ** to locate a particular unixInodeInfo object. */ struct unixFileId { dev_t dev; /* Device number */ #if OS_VXWORKS struct vxworksFileId *pId; /* Unique file ID for vxworks. */ #else ino_t ino; /* Inode number */ #endif }; /* ** An instance of the following structure is allocated for each open ** inode. Or, on LinuxThreads, there is one of these structures for ** each inode opened by each thread. ** ** A single inode can have multiple file descriptors, so each unixFile ** structure contains a pointer to an instance of this object and this ** object keeps a count of the number of unixFile pointing to it. */ struct unixInodeInfo { struct unixFileId fileId; /* The lookup key */ int nShared; /* Number of SHARED locks held */ int eFileLock; /* One of SHARED_LOCK, RESERVED_LOCK etc. */ int nRef; /* Number of pointers to this structure */ unixShmNode *pShmNode; /* Shared memory associated with this inode */ int nLock; /* Number of outstanding file locks */ UnixUnusedFd *pUnused; /* Unused file descriptors to close */ unixInodeInfo *pNext; /* List of all unixInodeInfo objects */ unixInodeInfo *pPrev; /* .... doubly linked */ #if defined(SQLITE_ENABLE_LOCKING_STYLE) unsigned long long sharedByte; /* for AFP simulated shared lock */ #endif #if OS_VXWORKS sem_t *pSem; /* Named POSIX semaphore */ char aSemName[MAX_PATHNAME+2]; /* Name of that semaphore */ #endif }; /* ** A lists of all unixInodeInfo objects. */ static unixInodeInfo *inodeList = 0; /* ** Close all file descriptors accumuated in the unixInodeInfo->pUnused list. ** If all such file descriptors are closed without error, the list is ** cleared and SQLITE_OK returned. ** ** Otherwise, if an error occurs, then successfully closed file descriptor ** entries are removed from the list, and SQLITE_IOERR_CLOSE returned. ** not deleted and SQLITE_IOERR_CLOSE returned. */ static int closePendingFds(unixFile *pFile){ int rc = SQLITE_OK; unixInodeInfo *pInode = pFile->pInode; UnixUnusedFd *pError = 0; UnixUnusedFd *p; UnixUnusedFd *pNext; for(p=pInode->pUnused; p; p=pNext){ pNext = p->pNext; if( close(p->fd) ){ pFile->lastErrno = errno; rc = SQLITE_IOERR_CLOSE; p->pNext = pError; pError = p; }else{ sqlite3_free(p); } } pInode->pUnused = pError; return rc; } /* ** Release a unixInodeInfo structure previously allocated by findInodeInfo(). ** ** The mutex entered using the unixEnterMutex() function must be held ** when this function is called. */ static void releaseInodeInfo(unixFile *pFile){ unixInodeInfo *pInode = pFile->pInode; assert( unixMutexHeld() ); if( pInode ){ pInode->nRef--; if( pInode->nRef==0 ){ assert( pInode->pShmNode==0 ); closePendingFds(pFile); if( pInode->pPrev ){ assert( pInode->pPrev->pNext==pInode ); pInode->pPrev->pNext = pInode->pNext; }else{ assert( inodeList==pInode ); inodeList = pInode->pNext; } if( pInode->pNext ){ assert( pInode->pNext->pPrev==pInode ); pInode->pNext->pPrev = pInode->pPrev; } sqlite3_free(pInode); } } } /* ** Given a file descriptor, locate the unixInodeInfo object that ** describes that file descriptor. Create a new one if necessary. The ** return value might be uninitialized if an error occurs. ** ** The mutex entered using the unixEnterMutex() function must be held ** when this function is called. ** ** Return an appropriate error code. */ static int findInodeInfo( unixFile *pFile, /* Unix file with file desc used in the key */ unixInodeInfo **ppInode /* Return the unixInodeInfo object here */ ){ int rc; /* System call return code */ int fd; /* The file descriptor for pFile */ struct unixFileId fileId; /* Lookup key for the unixInodeInfo */ struct stat statbuf; /* Low-level file information */ unixInodeInfo *pInode = 0; /* Candidate unixInodeInfo object */ assert( unixMutexHeld() ); /* Get low-level information about the file that we can used to ** create a unique name for the file. */ fd = pFile->h; |
︙ | ︙ | |||
1014 1015 1016 1017 1018 1019 1020 | if( rc!=0 ){ pFile->lastErrno = errno; return SQLITE_IOERR; } } #endif | | | | | < < < < < < < < | < < < < < < < < < < < < < < < < < < < < < < < < < < < | | | | | | < | < | | | | | | > | | | | < < | < < < < < < < < < < < < < < < < < < < < < | | < < < < < < < < < < < < < < < < < < < < < < < < < < < < | | | 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 | if( rc!=0 ){ pFile->lastErrno = errno; return SQLITE_IOERR; } } #endif memset(&fileId, 0, sizeof(fileId)); fileId.dev = statbuf.st_dev; #if OS_VXWORKS fileId.pId = pFile->pId; #else fileId.ino = statbuf.st_ino; #endif pInode = inodeList; while( pInode && memcmp(&fileId, &pInode->fileId, sizeof(fileId)) ){ pInode = pInode->pNext; } if( pInode==0 ){ pInode = sqlite3_malloc( sizeof(*pInode) ); if( pInode==0 ){ return SQLITE_NOMEM; } memset(pInode, 0, sizeof(*pInode)); memcpy(&pInode->fileId, &fileId, sizeof(fileId)); pInode->nRef = 1; pInode->pNext = inodeList; pInode->pPrev = 0; if( inodeList ) inodeList->pPrev = pInode; inodeList = pInode; }else{ pInode->nRef++; } *ppInode = pInode; return SQLITE_OK; } /* ** This routine checks if there is a RESERVED lock held on the specified ** file by this or any other process. If such a lock is held, set *pResOut ** to a non-zero value otherwise *pResOut is set to zero. The return value ** is set to SQLITE_OK unless an I/O error occurs during lock checking. */ static int unixCheckReservedLock(sqlite3_file *id, int *pResOut){ int rc = SQLITE_OK; int reserved = 0; unixFile *pFile = (unixFile*)id; SimulateIOError( return SQLITE_IOERR_CHECKRESERVEDLOCK; ); assert( pFile ); unixEnterMutex(); /* Because pFile->pInode is shared across threads */ /* Check if a thread in this process holds such a lock */ if( pFile->pInode->eFileLock>SHARED_LOCK ){ reserved = 1; } /* Otherwise see if some other process holds it. */ #ifndef __DJGPP__ if( !reserved ){ |
︙ | ︙ | |||
1173 1174 1175 1176 1177 1178 1179 | } else if( lock.l_type!=F_UNLCK ){ reserved = 1; } } #endif unixLeaveMutex(); | | | | 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 | } else if( lock.l_type!=F_UNLCK ){ reserved = 1; } } #endif unixLeaveMutex(); OSTRACE(("TEST WR-LOCK %d %d %d (unix)\n", pFile->h, rc, reserved)); *pResOut = reserved; return rc; } /* ** Lock the file with the lock specified by parameter eFileLock - one ** of the following: ** ** (1) SHARED_LOCK ** (2) RESERVED_LOCK ** (3) PENDING_LOCK ** (4) EXCLUSIVE_LOCK ** |
︙ | ︙ | |||
1203 1204 1205 1206 1207 1208 1209 | ** SHARED -> (PENDING) -> EXCLUSIVE ** RESERVED -> (PENDING) -> EXCLUSIVE ** PENDING -> EXCLUSIVE ** ** This routine will only increase a lock. Use the sqlite3OsUnlock() ** routine to lower a locking level. */ | | | 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 | ** SHARED -> (PENDING) -> EXCLUSIVE ** RESERVED -> (PENDING) -> EXCLUSIVE ** PENDING -> EXCLUSIVE ** ** This routine will only increase a lock. Use the sqlite3OsUnlock() ** routine to lower a locking level. */ static int unixLock(sqlite3_file *id, int eFileLock){ /* The following describes the implementation of the various locks and ** lock transitions in terms of the POSIX advisory shared and exclusive ** lock primitives (called read-locks and write-locks below, to avoid ** confusion with SQLite lock names). The algorithms are complicated ** slightly in order to be compatible with windows systems simultaneously ** accessing the same database file, in case that is ever required. ** |
︙ | ︙ | |||
1244 1245 1246 1247 1248 1249 1250 | ** The reason a single byte cannot be used instead of the 'shared byte ** range' is that some versions of windows do not support read-locks. By ** locking a random byte from a range, concurrent SHARED locks may exist ** even if the locking primitive used is always a write-lock. */ int rc = SQLITE_OK; unixFile *pFile = (unixFile*)id; | | | | | | | | | | | | < < < < < < < < | | | | | | | | | | | | | | | | | | 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 | ** The reason a single byte cannot be used instead of the 'shared byte ** range' is that some versions of windows do not support read-locks. By ** locking a random byte from a range, concurrent SHARED locks may exist ** even if the locking primitive used is always a write-lock. */ int rc = SQLITE_OK; unixFile *pFile = (unixFile*)id; unixInodeInfo *pInode = pFile->pInode; struct flock lock; int s = 0; int tErrno = 0; assert( pFile ); OSTRACE(("LOCK %d %s was %s(%s,%d) pid=%d (unix)\n", pFile->h, azFileLock(eFileLock), azFileLock(pFile->eFileLock), azFileLock(pInode->eFileLock), pInode->nShared , getpid())); /* If there is already a lock of this type or more restrictive on the ** unixFile, do nothing. Don't use the end_lock: exit path, as ** unixEnterMutex() hasn't been called yet. */ if( pFile->eFileLock>=eFileLock ){ OSTRACE(("LOCK %d %s ok (already held) (unix)\n", pFile->h, azFileLock(eFileLock))); return SQLITE_OK; } /* Make sure the locking sequence is correct. ** (1) We never move from unlocked to anything higher than shared lock. ** (2) SQLite never explicitly requests a pendig lock. ** (3) A shared lock is always held when a reserve lock is requested. */ assert( pFile->eFileLock!=NO_LOCK || eFileLock==SHARED_LOCK ); assert( eFileLock!=PENDING_LOCK ); assert( eFileLock!=RESERVED_LOCK || pFile->eFileLock==SHARED_LOCK ); /* This mutex is needed because pFile->pInode is shared across threads */ unixEnterMutex(); pInode = pFile->pInode; /* If some thread using this PID has a lock via a different unixFile* ** handle that precludes the requested lock, return BUSY. */ if( (pFile->eFileLock!=pInode->eFileLock && (pInode->eFileLock>=PENDING_LOCK || eFileLock>SHARED_LOCK)) ){ rc = SQLITE_BUSY; goto end_lock; } /* If a SHARED lock is requested, and some thread using this PID already ** has a SHARED or RESERVED lock, then increment reference counts and ** return SQLITE_OK. */ if( eFileLock==SHARED_LOCK && (pInode->eFileLock==SHARED_LOCK || pInode->eFileLock==RESERVED_LOCK) ){ assert( eFileLock==SHARED_LOCK ); assert( pFile->eFileLock==0 ); assert( pInode->nShared>0 ); pFile->eFileLock = SHARED_LOCK; pInode->nShared++; pInode->nLock++; goto end_lock; } /* A PENDING lock is needed before acquiring a SHARED lock and before ** acquiring an EXCLUSIVE lock. For the SHARED lock, the PENDING will ** be released. */ lock.l_len = 1L; lock.l_whence = SEEK_SET; if( eFileLock==SHARED_LOCK || (eFileLock==EXCLUSIVE_LOCK && pFile->eFileLock<PENDING_LOCK) ){ lock.l_type = (eFileLock==SHARED_LOCK?F_RDLCK:F_WRLCK); lock.l_start = PENDING_BYTE; s = fcntl(pFile->h, F_SETLK, &lock); if( s==(-1) ){ tErrno = errno; rc = sqliteErrorFromPosixError(tErrno, SQLITE_IOERR_LOCK); if( IS_LOCK_ERROR(rc) ){ pFile->lastErrno = tErrno; } goto end_lock; } } /* If control gets to this point, then actually go ahead and make ** operating system calls for the specified lock. */ if( eFileLock==SHARED_LOCK ){ assert( pInode->nShared==0 ); assert( pInode->eFileLock==0 ); /* Now get the read-lock */ lock.l_start = SHARED_FIRST; lock.l_len = SHARED_SIZE; if( (s = fcntl(pFile->h, F_SETLK, &lock))==(-1) ){ tErrno = errno; } |
︙ | ︙ | |||
1369 1370 1371 1372 1373 1374 1375 | } if( s==(-1) ){ rc = sqliteErrorFromPosixError(tErrno, SQLITE_IOERR_LOCK); if( IS_LOCK_ERROR(rc) ){ pFile->lastErrno = tErrno; } }else{ | | | | | | | | 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 | } if( s==(-1) ){ rc = sqliteErrorFromPosixError(tErrno, SQLITE_IOERR_LOCK); if( IS_LOCK_ERROR(rc) ){ pFile->lastErrno = tErrno; } }else{ pFile->eFileLock = SHARED_LOCK; pInode->nLock++; pInode->nShared = 1; } }else if( eFileLock==EXCLUSIVE_LOCK && pInode->nShared>1 ){ /* We are trying for an exclusive lock but another thread in this ** same process is still holding a shared lock. */ rc = SQLITE_BUSY; }else{ /* The request was for a RESERVED or EXCLUSIVE lock. It is ** assumed that there is a SHARED or greater lock on the file ** already. */ assert( 0!=pFile->eFileLock ); lock.l_type = F_WRLCK; switch( eFileLock ){ case RESERVED_LOCK: lock.l_start = RESERVED_BYTE; break; case EXCLUSIVE_LOCK: lock.l_start = SHARED_FIRST; lock.l_len = SHARED_SIZE; break; |
︙ | ︙ | |||
1413 1414 1415 1416 1417 1418 1419 | #ifndef NDEBUG /* Set up the transaction-counter change checking flags when ** transitioning from a SHARED to a RESERVED lock. The change ** from SHARED to RESERVED marks the beginning of a normal ** write operation (not a hot journal rollback). */ if( rc==SQLITE_OK | | | | | | | | | | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | | | | | | | > | | | < < < | | | | > > | | 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 | #ifndef NDEBUG /* Set up the transaction-counter change checking flags when ** transitioning from a SHARED to a RESERVED lock. The change ** from SHARED to RESERVED marks the beginning of a normal ** write operation (not a hot journal rollback). */ if( rc==SQLITE_OK && pFile->eFileLock<=SHARED_LOCK && eFileLock==RESERVED_LOCK ){ pFile->transCntrChng = 0; pFile->dbUpdate = 0; pFile->inNormalWrite = 1; } #endif if( rc==SQLITE_OK ){ pFile->eFileLock = eFileLock; pInode->eFileLock = eFileLock; }else if( eFileLock==EXCLUSIVE_LOCK ){ pFile->eFileLock = PENDING_LOCK; pInode->eFileLock = PENDING_LOCK; } end_lock: unixLeaveMutex(); OSTRACE(("LOCK %d %s %s (unix)\n", pFile->h, azFileLock(eFileLock), rc==SQLITE_OK ? "ok" : "failed")); return rc; } /* ** Add the file descriptor used by file handle pFile to the corresponding ** pUnused list. */ static void setPendingFd(unixFile *pFile){ unixInodeInfo *pInode = pFile->pInode; UnixUnusedFd *p = pFile->pUnused; p->pNext = pInode->pUnused; pInode->pUnused = p; pFile->h = -1; pFile->pUnused = 0; } /* ** Lower the locking level on file descriptor pFile to eFileLock. eFileLock ** must be either NO_LOCK or SHARED_LOCK. ** ** If the locking level of the file descriptor is already at or below ** the requested locking level, this routine is a no-op. ** ** If handleNFSUnlock is true, then on downgrading an EXCLUSIVE_LOCK to SHARED ** the byte range is divided into 2 parts and the first part is unlocked then ** set to a read lock, then the other part is simply unlocked. This works ** around a bug in BSD NFS lockd (also seen on MacOSX 10.3+) that fails to ** remove the write lock on a region when a read lock is set. */ static int _posixUnlock(sqlite3_file *id, int eFileLock, int handleNFSUnlock){ unixFile *pFile = (unixFile*)id; unixInodeInfo *pInode; struct flock lock; int rc = SQLITE_OK; int h; int tErrno; /* Error code from system call errors */ assert( pFile ); OSTRACE(("UNLOCK %d %d was %d(%d,%d) pid=%d (unix)\n", pFile->h, eFileLock, pFile->eFileLock, pFile->pInode->eFileLock, pFile->pInode->nShared, getpid())); assert( eFileLock<=SHARED_LOCK ); if( pFile->eFileLock<=eFileLock ){ return SQLITE_OK; } unixEnterMutex(); h = pFile->h; pInode = pFile->pInode; assert( pInode->nShared!=0 ); if( pFile->eFileLock>SHARED_LOCK ){ assert( pInode->eFileLock==pFile->eFileLock ); SimulateIOErrorBenign(1); SimulateIOError( h=(-1) ) SimulateIOErrorBenign(0); #ifndef NDEBUG /* When reducing a lock such that other processes can start ** reading the database file again, make sure that the ** transaction counter was updated if any part of the database ** file changed. If the transaction counter is not updated, ** other connections to the same file might not realize that ** the file has changed and hence might not know to flush their ** cache. The use of a stale cache can lead to database corruption. */ #if 0 assert( pFile->inNormalWrite==0 || pFile->dbUpdate==0 || pFile->transCntrChng==1 ); #endif pFile->inNormalWrite = 0; #endif /* downgrading to a shared lock on NFS involves clearing the write lock ** before establishing the readlock - to avoid a race condition we downgrade ** the lock in 2 blocks, so that part of the range will be covered by a ** write lock until the rest is covered by a read lock: ** 1: [WWWWW] ** 2: [....W] ** 3: [RRRRW] ** 4: [RRRR.] */ if( eFileLock==SHARED_LOCK ){ if( handleNFSUnlock ){ off_t divSize = SHARED_SIZE - 1; lock.l_type = F_UNLCK; lock.l_whence = SEEK_SET; lock.l_start = SHARED_FIRST; lock.l_len = divSize; |
︙ | ︙ | |||
1607 1608 1609 1610 1611 1612 1613 | } } lock.l_type = F_UNLCK; lock.l_whence = SEEK_SET; lock.l_start = PENDING_BYTE; lock.l_len = 2L; assert( PENDING_BYTE+1==RESERVED_BYTE ); if( fcntl(h, F_SETLK, &lock)!=(-1) ){ | | | < < | | | | | < | | | | | | | | 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 | } } lock.l_type = F_UNLCK; lock.l_whence = SEEK_SET; lock.l_start = PENDING_BYTE; lock.l_len = 2L; assert( PENDING_BYTE+1==RESERVED_BYTE ); if( fcntl(h, F_SETLK, &lock)!=(-1) ){ pInode->eFileLock = SHARED_LOCK; }else{ tErrno = errno; rc = sqliteErrorFromPosixError(tErrno, SQLITE_IOERR_UNLOCK); if( IS_LOCK_ERROR(rc) ){ pFile->lastErrno = tErrno; } goto end_unlock; } } if( eFileLock==NO_LOCK ){ /* Decrement the shared lock counter. Release the lock using an ** OS call only when all threads in this same process have released ** the lock. */ pInode->nShared--; if( pInode->nShared==0 ){ lock.l_type = F_UNLCK; lock.l_whence = SEEK_SET; lock.l_start = lock.l_len = 0L; SimulateIOErrorBenign(1); SimulateIOError( h=(-1) ) SimulateIOErrorBenign(0); if( fcntl(h, F_SETLK, &lock)!=(-1) ){ pInode->eFileLock = NO_LOCK; }else{ tErrno = errno; rc = sqliteErrorFromPosixError(tErrno, SQLITE_IOERR_UNLOCK); if( IS_LOCK_ERROR(rc) ){ pFile->lastErrno = tErrno; } pInode->eFileLock = NO_LOCK; pFile->eFileLock = NO_LOCK; } } /* Decrement the count of locks against this same file. When the ** count reaches zero, close any other file descriptors whose close ** was deferred because of outstanding locks. */ pInode->nLock--; assert( pInode->nLock>=0 ); if( pInode->nLock==0 ){ int rc2 = closePendingFds(pFile); if( rc==SQLITE_OK ){ rc = rc2; } } } end_unlock: unixLeaveMutex(); if( rc==SQLITE_OK ) pFile->eFileLock = eFileLock; return rc; } /* ** Lower the locking level on file descriptor pFile to eFileLock. eFileLock ** must be either NO_LOCK or SHARED_LOCK. ** ** If the locking level of the file descriptor is already at or below ** the requested locking level, this routine is a no-op. */ static int unixUnlock(sqlite3_file *id, int eFileLock){ return _posixUnlock(id, eFileLock, 0); } /* ** This function performs the parts of the "close file" operation ** common to all locking schemes. It closes the directory and file ** handles, if they are valid, and sets all fields of the unixFile ** structure to 0. |
︙ | ︙ | |||
1715 1716 1717 1718 1719 1720 1721 | if( pFile->isDelete ){ unlink(pFile->pId->zCanonicalName); } vxworksReleaseFileId(pFile->pId); pFile->pId = 0; } #endif | | | | | < | 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 | if( pFile->isDelete ){ unlink(pFile->pId->zCanonicalName); } vxworksReleaseFileId(pFile->pId); pFile->pId = 0; } #endif OSTRACE(("CLOSE %-3d\n", pFile->h)); OpenCounter(-1); sqlite3_free(pFile->pUnused); memset(pFile, 0, sizeof(unixFile)); } return SQLITE_OK; } /* ** Close a file. */ static int unixClose(sqlite3_file *id){ int rc = SQLITE_OK; if( id ){ unixFile *pFile = (unixFile *)id; unixUnlock(id, NO_LOCK); unixEnterMutex(); if( pFile->pInode && pFile->pInode->nLock ){ /* If there are outstanding locks, do not actually close the file just ** yet because that would clear those locks. Instead, add the file ** descriptor to pInode->pUnused list. It will be automatically closed ** when the last lock is cleared. */ setPendingFd(pFile); } releaseInodeInfo(pFile); rc = closeUnixFile(id); unixLeaveMutex(); } return rc; } /************** End of the posix advisory lock implementation ***************** |
︙ | ︙ | |||
1840 1841 1842 1843 1844 1845 1846 | unixFile *pFile = (unixFile*)id; SimulateIOError( return SQLITE_IOERR_CHECKRESERVEDLOCK; ); assert( pFile ); /* Check if a thread in this process holds such a lock */ | | | | | 1548 1549 1550 1551 1552 1553 1554 1555 1556 1557 1558 1559 1560 1561 1562 1563 1564 1565 1566 1567 1568 1569 1570 1571 1572 1573 1574 1575 1576 1577 | unixFile *pFile = (unixFile*)id; SimulateIOError( return SQLITE_IOERR_CHECKRESERVEDLOCK; ); assert( pFile ); /* Check if a thread in this process holds such a lock */ if( pFile->eFileLock>SHARED_LOCK ){ /* Either this connection or some other connection in the same process ** holds a lock on the file. No need to check further. */ reserved = 1; }else{ /* The lock is held if and only if the lockfile exists */ const char *zLockFile = (const char*)pFile->lockingContext; reserved = access(zLockFile, 0)==0; } OSTRACE(("TEST WR-LOCK %d %d %d (dotlock)\n", pFile->h, rc, reserved)); *pResOut = reserved; return rc; } /* ** Lock the file with the lock specified by parameter eFileLock - one ** of the following: ** ** (1) SHARED_LOCK ** (2) RESERVED_LOCK ** (3) PENDING_LOCK ** (4) EXCLUSIVE_LOCK ** |
︙ | ︙ | |||
1881 1882 1883 1884 1885 1886 1887 | ** ** This routine will only increase a lock. Use the sqlite3OsUnlock() ** routine to lower a locking level. ** ** With dotfile locking, we really only support state (4): EXCLUSIVE. ** But we track the other locking levels internally. */ | | | | | 1589 1590 1591 1592 1593 1594 1595 1596 1597 1598 1599 1600 1601 1602 1603 1604 1605 1606 1607 1608 1609 1610 1611 1612 1613 1614 | ** ** This routine will only increase a lock. Use the sqlite3OsUnlock() ** routine to lower a locking level. ** ** With dotfile locking, we really only support state (4): EXCLUSIVE. ** But we track the other locking levels internally. */ static int dotlockLock(sqlite3_file *id, int eFileLock) { unixFile *pFile = (unixFile*)id; int fd; char *zLockFile = (char *)pFile->lockingContext; int rc = SQLITE_OK; /* If we have any lock, then the lock file already exists. All we have ** to do is adjust our internal record of the lock level. */ if( pFile->eFileLock > NO_LOCK ){ pFile->eFileLock = eFileLock; #if !OS_VXWORKS /* Always update the timestamp on the old file */ utimes(zLockFile, NULL); #endif return SQLITE_OK; } |
︙ | ︙ | |||
1921 1922 1923 1924 1925 1926 1927 | } if( close(fd) ){ pFile->lastErrno = errno; rc = SQLITE_IOERR_CLOSE; } /* got it, set the type and return ok */ | | | | | | | | | | | | | 1629 1630 1631 1632 1633 1634 1635 1636 1637 1638 1639 1640 1641 1642 1643 1644 1645 1646 1647 1648 1649 1650 1651 1652 1653 1654 1655 1656 1657 1658 1659 1660 1661 1662 1663 1664 1665 1666 1667 1668 1669 1670 1671 1672 1673 1674 1675 1676 1677 1678 1679 1680 1681 1682 1683 1684 1685 1686 1687 1688 1689 1690 1691 | } if( close(fd) ){ pFile->lastErrno = errno; rc = SQLITE_IOERR_CLOSE; } /* got it, set the type and return ok */ pFile->eFileLock = eFileLock; return rc; } /* ** Lower the locking level on file descriptor pFile to eFileLock. eFileLock ** must be either NO_LOCK or SHARED_LOCK. ** ** If the locking level of the file descriptor is already at or below ** the requested locking level, this routine is a no-op. ** ** When the locking level reaches NO_LOCK, delete the lock file. */ static int dotlockUnlock(sqlite3_file *id, int eFileLock) { unixFile *pFile = (unixFile*)id; char *zLockFile = (char *)pFile->lockingContext; assert( pFile ); OSTRACE(("UNLOCK %d %d was %d pid=%d (dotlock)\n", pFile->h, eFileLock, pFile->eFileLock, getpid())); assert( eFileLock<=SHARED_LOCK ); /* no-op if possible */ if( pFile->eFileLock==eFileLock ){ return SQLITE_OK; } /* To downgrade to shared, simply update our internal notion of the ** lock state. No need to mess with the file on disk. */ if( eFileLock==SHARED_LOCK ){ pFile->eFileLock = SHARED_LOCK; return SQLITE_OK; } /* To fully unlock the database, delete the lock file */ assert( eFileLock==NO_LOCK ); if( unlink(zLockFile) ){ int rc = 0; int tErrno = errno; if( ENOENT != tErrno ){ rc = sqliteErrorFromPosixError(tErrno, SQLITE_IOERR_UNLOCK); } if( IS_LOCK_ERROR(rc) ){ pFile->lastErrno = tErrno; } return rc; } pFile->eFileLock = NO_LOCK; return SQLITE_OK; } /* ** Close a file. Make sure the lock has been released before closing. */ static int dotlockClose(sqlite3_file *id) { |
︙ | ︙ | |||
2022 2023 2024 2025 2026 2027 2028 | unixFile *pFile = (unixFile*)id; SimulateIOError( return SQLITE_IOERR_CHECKRESERVEDLOCK; ); assert( pFile ); /* Check if a thread in this process holds such a lock */ | | | 1730 1731 1732 1733 1734 1735 1736 1737 1738 1739 1740 1741 1742 1743 1744 | unixFile *pFile = (unixFile*)id; SimulateIOError( return SQLITE_IOERR_CHECKRESERVEDLOCK; ); assert( pFile ); /* Check if a thread in this process holds such a lock */ if( pFile->eFileLock>SHARED_LOCK ){ reserved = 1; } /* Otherwise see if some other process holds it. */ if( !reserved ){ /* attempt to get the lock */ int lrc = flock(pFile->h, LOCK_EX | LOCK_NB); |
︙ | ︙ | |||
2053 2054 2055 2056 2057 2058 2059 | lrc = sqliteErrorFromPosixError(tErrno, SQLITE_IOERR_LOCK); if( IS_LOCK_ERROR(lrc) ){ pFile->lastErrno = tErrno; rc = lrc; } } } | | | | 1761 1762 1763 1764 1765 1766 1767 1768 1769 1770 1771 1772 1773 1774 1775 1776 1777 1778 1779 1780 1781 1782 1783 1784 1785 1786 1787 1788 | lrc = sqliteErrorFromPosixError(tErrno, SQLITE_IOERR_LOCK); if( IS_LOCK_ERROR(lrc) ){ pFile->lastErrno = tErrno; rc = lrc; } } } OSTRACE(("TEST WR-LOCK %d %d %d (flock)\n", pFile->h, rc, reserved)); #ifdef SQLITE_IGNORE_FLOCK_LOCK_ERRORS if( (rc & SQLITE_IOERR) == SQLITE_IOERR ){ rc = SQLITE_OK; reserved=1; } #endif /* SQLITE_IGNORE_FLOCK_LOCK_ERRORS */ *pResOut = reserved; return rc; } /* ** Lock the file with the lock specified by parameter eFileLock - one ** of the following: ** ** (1) SHARED_LOCK ** (2) RESERVED_LOCK ** (3) PENDING_LOCK ** (4) EXCLUSIVE_LOCK ** |
︙ | ︙ | |||
2094 2095 2096 2097 2098 2099 2100 | ** lock states in the sqlite3_file structure, but all locks SHARED or ** above are really EXCLUSIVE locks and exclude all other processes from ** access the file. ** ** This routine will only increase a lock. Use the sqlite3OsUnlock() ** routine to lower a locking level. */ | | | | | | | | | | | | | | | | | 1802 1803 1804 1805 1806 1807 1808 1809 1810 1811 1812 1813 1814 1815 1816 1817 1818 1819 1820 1821 1822 1823 1824 1825 1826 1827 1828 1829 1830 1831 1832 1833 1834 1835 1836 1837 1838 1839 1840 1841 1842 1843 1844 1845 1846 1847 1848 1849 1850 1851 1852 1853 1854 1855 1856 1857 1858 1859 1860 1861 1862 1863 1864 1865 1866 1867 1868 1869 1870 1871 1872 1873 1874 1875 1876 1877 1878 1879 1880 1881 1882 1883 1884 1885 1886 1887 1888 1889 1890 1891 1892 1893 1894 1895 | ** lock states in the sqlite3_file structure, but all locks SHARED or ** above are really EXCLUSIVE locks and exclude all other processes from ** access the file. ** ** This routine will only increase a lock. Use the sqlite3OsUnlock() ** routine to lower a locking level. */ static int flockLock(sqlite3_file *id, int eFileLock) { int rc = SQLITE_OK; unixFile *pFile = (unixFile*)id; assert( pFile ); /* if we already have a lock, it is exclusive. ** Just adjust level and punt on outta here. */ if (pFile->eFileLock > NO_LOCK) { pFile->eFileLock = eFileLock; return SQLITE_OK; } /* grab an exclusive lock */ if (flock(pFile->h, LOCK_EX | LOCK_NB)) { int tErrno = errno; /* didn't get, must be busy */ rc = sqliteErrorFromPosixError(tErrno, SQLITE_IOERR_LOCK); if( IS_LOCK_ERROR(rc) ){ pFile->lastErrno = tErrno; } } else { /* got it, set the type and return ok */ pFile->eFileLock = eFileLock; } OSTRACE(("LOCK %d %s %s (flock)\n", pFile->h, azFileLock(eFileLock), rc==SQLITE_OK ? "ok" : "failed")); #ifdef SQLITE_IGNORE_FLOCK_LOCK_ERRORS if( (rc & SQLITE_IOERR) == SQLITE_IOERR ){ rc = SQLITE_BUSY; } #endif /* SQLITE_IGNORE_FLOCK_LOCK_ERRORS */ return rc; } /* ** Lower the locking level on file descriptor pFile to eFileLock. eFileLock ** must be either NO_LOCK or SHARED_LOCK. ** ** If the locking level of the file descriptor is already at or below ** the requested locking level, this routine is a no-op. */ static int flockUnlock(sqlite3_file *id, int eFileLock) { unixFile *pFile = (unixFile*)id; assert( pFile ); OSTRACE(("UNLOCK %d %d was %d pid=%d (flock)\n", pFile->h, eFileLock, pFile->eFileLock, getpid())); assert( eFileLock<=SHARED_LOCK ); /* no-op if possible */ if( pFile->eFileLock==eFileLock ){ return SQLITE_OK; } /* shared can just be set because we always have an exclusive */ if (eFileLock==SHARED_LOCK) { pFile->eFileLock = eFileLock; return SQLITE_OK; } /* no, really, unlock. */ int rc = flock(pFile->h, LOCK_UN); if (rc) { int r, tErrno = errno; r = sqliteErrorFromPosixError(tErrno, SQLITE_IOERR_UNLOCK); if( IS_LOCK_ERROR(r) ){ pFile->lastErrno = tErrno; } #ifdef SQLITE_IGNORE_FLOCK_LOCK_ERRORS if( (r & SQLITE_IOERR) == SQLITE_IOERR ){ r = SQLITE_BUSY; } #endif /* SQLITE_IGNORE_FLOCK_LOCK_ERRORS */ return r; } else { pFile->eFileLock = NO_LOCK; return SQLITE_OK; } } /* ** Close a file. */ |
︙ | ︙ | |||
2221 2222 2223 2224 2225 2226 2227 | unixFile *pFile = (unixFile*)id; SimulateIOError( return SQLITE_IOERR_CHECKRESERVEDLOCK; ); assert( pFile ); /* Check if a thread in this process holds such a lock */ | | | | | | | 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 | unixFile *pFile = (unixFile*)id; SimulateIOError( return SQLITE_IOERR_CHECKRESERVEDLOCK; ); assert( pFile ); /* Check if a thread in this process holds such a lock */ if( pFile->eFileLock>SHARED_LOCK ){ reserved = 1; } /* Otherwise see if some other process holds it. */ if( !reserved ){ sem_t *pSem = pFile->pInode->pSem; struct stat statBuf; if( sem_trywait(pSem)==-1 ){ int tErrno = errno; if( EAGAIN != tErrno ){ rc = sqliteErrorFromPosixError(tErrno, SQLITE_IOERR_CHECKRESERVEDLOCK); pFile->lastErrno = tErrno; } else { /* someone else has the lock when we are in NO_LOCK */ reserved = (pFile->eFileLock < SHARED_LOCK); } }else{ /* we could have it if we want it */ sem_post(pSem); } } OSTRACE(("TEST WR-LOCK %d %d %d (sem)\n", pFile->h, rc, reserved)); *pResOut = reserved; return rc; } /* ** Lock the file with the lock specified by parameter eFileLock - one ** of the following: ** ** (1) SHARED_LOCK ** (2) RESERVED_LOCK ** (3) PENDING_LOCK ** (4) EXCLUSIVE_LOCK ** |
︙ | ︙ | |||
2279 2280 2281 2282 2283 2284 2285 | ** lock states in the sqlite3_file structure, but all locks SHARED or ** above are really EXCLUSIVE locks and exclude all other processes from ** access the file. ** ** This routine will only increase a lock. Use the sqlite3OsUnlock() ** routine to lower a locking level. */ | | | | | | | | | | | | | | | | | < | 1987 1988 1989 1990 1991 1992 1993 1994 1995 1996 1997 1998 1999 2000 2001 2002 2003 2004 2005 2006 2007 2008 2009 2010 2011 2012 2013 2014 2015 2016 2017 2018 2019 2020 2021 2022 2023 2024 2025 2026 2027 2028 2029 2030 2031 2032 2033 2034 2035 2036 2037 2038 2039 2040 2041 2042 2043 2044 2045 2046 2047 2048 2049 2050 2051 2052 2053 2054 2055 2056 2057 2058 2059 2060 2061 2062 2063 2064 2065 2066 2067 2068 2069 2070 2071 2072 2073 2074 2075 2076 2077 2078 | ** lock states in the sqlite3_file structure, but all locks SHARED or ** above are really EXCLUSIVE locks and exclude all other processes from ** access the file. ** ** This routine will only increase a lock. Use the sqlite3OsUnlock() ** routine to lower a locking level. */ static int semLock(sqlite3_file *id, int eFileLock) { unixFile *pFile = (unixFile*)id; int fd; sem_t *pSem = pFile->pInode->pSem; int rc = SQLITE_OK; /* if we already have a lock, it is exclusive. ** Just adjust level and punt on outta here. */ if (pFile->eFileLock > NO_LOCK) { pFile->eFileLock = eFileLock; rc = SQLITE_OK; goto sem_end_lock; } /* lock semaphore now but bail out when already locked. */ if( sem_trywait(pSem)==-1 ){ rc = SQLITE_BUSY; goto sem_end_lock; } /* got it, set the type and return ok */ pFile->eFileLock = eFileLock; sem_end_lock: return rc; } /* ** Lower the locking level on file descriptor pFile to eFileLock. eFileLock ** must be either NO_LOCK or SHARED_LOCK. ** ** If the locking level of the file descriptor is already at or below ** the requested locking level, this routine is a no-op. */ static int semUnlock(sqlite3_file *id, int eFileLock) { unixFile *pFile = (unixFile*)id; sem_t *pSem = pFile->pInode->pSem; assert( pFile ); assert( pSem ); OSTRACE(("UNLOCK %d %d was %d pid=%d (sem)\n", pFile->h, eFileLock, pFile->eFileLock, getpid())); assert( eFileLock<=SHARED_LOCK ); /* no-op if possible */ if( pFile->eFileLock==eFileLock ){ return SQLITE_OK; } /* shared can just be set because we always have an exclusive */ if (eFileLock==SHARED_LOCK) { pFile->eFileLock = eFileLock; return SQLITE_OK; } /* no, really unlock. */ if ( sem_post(pSem)==-1 ) { int rc, tErrno = errno; rc = sqliteErrorFromPosixError(tErrno, SQLITE_IOERR_UNLOCK); if( IS_LOCK_ERROR(rc) ){ pFile->lastErrno = tErrno; } return rc; } pFile->eFileLock = NO_LOCK; return SQLITE_OK; } /* ** Close a file. */ static int semClose(sqlite3_file *id) { if( id ){ unixFile *pFile = (unixFile*)id; semUnlock(id, NO_LOCK); assert( pFile ); unixEnterMutex(); releaseInodeInfo(pFile); unixLeaveMutex(); closeUnixFile(id); } return SQLITE_OK; } #endif /* OS_VXWORKS */ |
︙ | ︙ | |||
2426 2427 2428 2429 2430 2431 2432 | pb.unLockFlag = setLockFlag ? 0 : 1; pb.startEndFlag = 0; pb.offset = offset; pb.length = length; pb.fd = pFile->h; | | | | | | 2133 2134 2135 2136 2137 2138 2139 2140 2141 2142 2143 2144 2145 2146 2147 2148 2149 2150 2151 2152 2153 2154 2155 | pb.unLockFlag = setLockFlag ? 0 : 1; pb.startEndFlag = 0; pb.offset = offset; pb.length = length; pb.fd = pFile->h; OSTRACE(("AFPSETLOCK [%s] for %d%s in range %llx:%llx\n", (setLockFlag?"ON":"OFF"), pFile->h, (pb.fd==-1?"[testval-1]":""), offset, length)); err = fsctl(path, afpfsByteRangeLock2FSCTL, &pb, 0); if ( err==-1 ) { int rc; int tErrno = errno; OSTRACE(("AFPSETLOCK failed to fsctl() '%s' %d %s\n", path, tErrno, strerror(tErrno))); #ifdef SQLITE_IGNORE_AFP_LOCK_ERRORS rc = SQLITE_BUSY; #else rc = sqliteErrorFromPosixError(tErrno, setLockFlag ? SQLITE_IOERR_LOCK : SQLITE_IOERR_UNLOCK); #endif /* SQLITE_IGNORE_AFP_LOCK_ERRORS */ if( IS_LOCK_ERROR(rc) ){ |
︙ | ︙ | |||
2469 2470 2471 2472 2473 2474 2475 | assert( pFile ); afpLockingContext *context = (afpLockingContext *) pFile->lockingContext; if( context->reserved ){ *pResOut = 1; return SQLITE_OK; } | | | | 2176 2177 2178 2179 2180 2181 2182 2183 2184 2185 2186 2187 2188 2189 2190 2191 2192 2193 | assert( pFile ); afpLockingContext *context = (afpLockingContext *) pFile->lockingContext; if( context->reserved ){ *pResOut = 1; return SQLITE_OK; } unixEnterMutex(); /* Because pFile->pInode is shared across threads */ /* Check if a thread in this process holds such a lock */ if( pFile->pInode->eFileLock>SHARED_LOCK ){ reserved = 1; } /* Otherwise see if some other process holds it. */ if( !reserved ){ /* lock the RESERVED byte */ |
︙ | ︙ | |||
2495 2496 2497 2498 2499 2500 2501 | } if( IS_LOCK_ERROR(lrc) ){ rc=lrc; } } unixLeaveMutex(); | | | | 2202 2203 2204 2205 2206 2207 2208 2209 2210 2211 2212 2213 2214 2215 2216 2217 2218 2219 2220 2221 2222 2223 | } if( IS_LOCK_ERROR(lrc) ){ rc=lrc; } } unixLeaveMutex(); OSTRACE(("TEST WR-LOCK %d %d %d (afp)\n", pFile->h, rc, reserved)); *pResOut = reserved; return rc; } /* ** Lock the file with the lock specified by parameter eFileLock - one ** of the following: ** ** (1) SHARED_LOCK ** (2) RESERVED_LOCK ** (3) PENDING_LOCK ** (4) EXCLUSIVE_LOCK ** |
︙ | ︙ | |||
2525 2526 2527 2528 2529 2530 2531 | ** SHARED -> (PENDING) -> EXCLUSIVE ** RESERVED -> (PENDING) -> EXCLUSIVE ** PENDING -> EXCLUSIVE ** ** This routine will only increase a lock. Use the sqlite3OsUnlock() ** routine to lower a locking level. */ | | | | | | | | | | | | | < < < < < < < < | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | > | | | < < < | | | | | | | | | | | | | | | | | < < | | | | | | | < | 2232 2233 2234 2235 2236 2237 2238 2239 2240 2241 2242 2243 2244 2245 2246 2247 2248 2249 2250 2251 2252 2253 2254 2255 2256 2257 2258 2259 2260 2261 2262 2263 2264 2265 2266 2267 2268 2269 2270 2271 2272 2273 2274 2275 2276 2277 2278 2279 2280 2281 2282 2283 2284 2285 2286 2287 2288 2289 2290 2291 2292 2293 2294 2295 2296 2297 2298 2299 2300 2301 2302 2303 2304 2305 2306 2307 2308 2309 2310 2311 2312 2313 2314 2315 2316 2317 2318 2319 2320 2321 2322 2323 2324 2325 2326 2327 2328 2329 2330 2331 2332 2333 2334 2335 2336 2337 2338 2339 2340 2341 2342 2343 2344 2345 2346 2347 2348 2349 2350 2351 2352 2353 2354 2355 2356 2357 2358 2359 2360 2361 2362 2363 2364 2365 2366 2367 2368 2369 2370 2371 2372 2373 2374 2375 2376 2377 2378 2379 2380 2381 2382 2383 2384 2385 2386 2387 2388 2389 2390 2391 2392 2393 2394 2395 2396 2397 2398 2399 2400 2401 2402 2403 2404 2405 2406 2407 2408 2409 2410 2411 2412 2413 2414 2415 2416 2417 2418 2419 2420 2421 2422 2423 2424 2425 2426 2427 2428 2429 2430 2431 2432 2433 2434 2435 2436 2437 2438 2439 2440 2441 2442 2443 2444 2445 2446 2447 2448 2449 2450 2451 2452 2453 2454 2455 2456 2457 2458 2459 2460 2461 2462 2463 2464 2465 2466 2467 2468 2469 2470 2471 2472 2473 2474 2475 2476 2477 2478 2479 2480 2481 2482 2483 2484 2485 2486 2487 2488 2489 2490 2491 2492 2493 2494 2495 2496 2497 2498 2499 2500 2501 2502 2503 2504 2505 2506 2507 2508 2509 2510 2511 2512 2513 2514 2515 2516 2517 2518 2519 2520 2521 2522 2523 2524 2525 2526 2527 2528 2529 2530 2531 2532 2533 2534 2535 2536 2537 2538 2539 2540 2541 2542 2543 2544 2545 | ** SHARED -> (PENDING) -> EXCLUSIVE ** RESERVED -> (PENDING) -> EXCLUSIVE ** PENDING -> EXCLUSIVE ** ** This routine will only increase a lock. Use the sqlite3OsUnlock() ** routine to lower a locking level. */ static int afpLock(sqlite3_file *id, int eFileLock){ int rc = SQLITE_OK; unixFile *pFile = (unixFile*)id; unixInodeInfo *pInode = pFile->pInode; afpLockingContext *context = (afpLockingContext *) pFile->lockingContext; assert( pFile ); OSTRACE(("LOCK %d %s was %s(%s,%d) pid=%d (afp)\n", pFile->h, azFileLock(eFileLock), azFileLock(pFile->eFileLock), azFileLock(pInode->eFileLock), pInode->nShared , getpid())); /* If there is already a lock of this type or more restrictive on the ** unixFile, do nothing. Don't use the afp_end_lock: exit path, as ** unixEnterMutex() hasn't been called yet. */ if( pFile->eFileLock>=eFileLock ){ OSTRACE(("LOCK %d %s ok (already held) (afp)\n", pFile->h, azFileLock(eFileLock))); return SQLITE_OK; } /* Make sure the locking sequence is correct ** (1) We never move from unlocked to anything higher than shared lock. ** (2) SQLite never explicitly requests a pendig lock. ** (3) A shared lock is always held when a reserve lock is requested. */ assert( pFile->eFileLock!=NO_LOCK || eFileLock==SHARED_LOCK ); assert( eFileLock!=PENDING_LOCK ); assert( eFileLock!=RESERVED_LOCK || pFile->eFileLock==SHARED_LOCK ); /* This mutex is needed because pFile->pInode is shared across threads */ unixEnterMutex(); pInode = pFile->pInode; /* If some thread using this PID has a lock via a different unixFile* ** handle that precludes the requested lock, return BUSY. */ if( (pFile->eFileLock!=pInode->eFileLock && (pInode->eFileLock>=PENDING_LOCK || eFileLock>SHARED_LOCK)) ){ rc = SQLITE_BUSY; goto afp_end_lock; } /* If a SHARED lock is requested, and some thread using this PID already ** has a SHARED or RESERVED lock, then increment reference counts and ** return SQLITE_OK. */ if( eFileLock==SHARED_LOCK && (pInode->eFileLock==SHARED_LOCK || pInode->eFileLock==RESERVED_LOCK) ){ assert( eFileLock==SHARED_LOCK ); assert( pFile->eFileLock==0 ); assert( pInode->nShared>0 ); pFile->eFileLock = SHARED_LOCK; pInode->nShared++; pInode->nLock++; goto afp_end_lock; } /* A PENDING lock is needed before acquiring a SHARED lock and before ** acquiring an EXCLUSIVE lock. For the SHARED lock, the PENDING will ** be released. */ if( eFileLock==SHARED_LOCK || (eFileLock==EXCLUSIVE_LOCK && pFile->eFileLock<PENDING_LOCK) ){ int failed; failed = afpSetLock(context->dbPath, pFile, PENDING_BYTE, 1, 1); if (failed) { rc = failed; goto afp_end_lock; } } /* If control gets to this point, then actually go ahead and make ** operating system calls for the specified lock. */ if( eFileLock==SHARED_LOCK ){ int lrc1, lrc2, lrc1Errno; long lk, mask; assert( pInode->nShared==0 ); assert( pInode->eFileLock==0 ); mask = (sizeof(long)==8) ? LARGEST_INT64 : 0x7fffffff; /* Now get the read-lock SHARED_LOCK */ /* note that the quality of the randomness doesn't matter that much */ lk = random(); pInode->sharedByte = (lk & mask)%(SHARED_SIZE - 1); lrc1 = afpSetLock(context->dbPath, pFile, SHARED_FIRST+pInode->sharedByte, 1, 1); if( IS_LOCK_ERROR(lrc1) ){ lrc1Errno = pFile->lastErrno; } /* Drop the temporary PENDING lock */ lrc2 = afpSetLock(context->dbPath, pFile, PENDING_BYTE, 1, 0); if( IS_LOCK_ERROR(lrc1) ) { pFile->lastErrno = lrc1Errno; rc = lrc1; goto afp_end_lock; } else if( IS_LOCK_ERROR(lrc2) ){ rc = lrc2; goto afp_end_lock; } else if( lrc1 != SQLITE_OK ) { rc = lrc1; } else { pFile->eFileLock = SHARED_LOCK; pInode->nLock++; pInode->nShared = 1; } }else if( eFileLock==EXCLUSIVE_LOCK && pInode->nShared>1 ){ /* We are trying for an exclusive lock but another thread in this ** same process is still holding a shared lock. */ rc = SQLITE_BUSY; }else{ /* The request was for a RESERVED or EXCLUSIVE lock. It is ** assumed that there is a SHARED or greater lock on the file ** already. */ int failed = 0; assert( 0!=pFile->eFileLock ); if (eFileLock >= RESERVED_LOCK && pFile->eFileLock < RESERVED_LOCK) { /* Acquire a RESERVED lock */ failed = afpSetLock(context->dbPath, pFile, RESERVED_BYTE, 1,1); if( !failed ){ context->reserved = 1; } } if (!failed && eFileLock == EXCLUSIVE_LOCK) { /* Acquire an EXCLUSIVE lock */ /* Remove the shared lock before trying the range. we'll need to ** reestablish the shared lock if we can't get the afpUnlock */ if( !(failed = afpSetLock(context->dbPath, pFile, SHARED_FIRST + pInode->sharedByte, 1, 0)) ){ int failed2 = SQLITE_OK; /* now attemmpt to get the exclusive lock range */ failed = afpSetLock(context->dbPath, pFile, SHARED_FIRST, SHARED_SIZE, 1); if( failed && (failed2 = afpSetLock(context->dbPath, pFile, SHARED_FIRST + pInode->sharedByte, 1, 1)) ){ /* Can't reestablish the shared lock. Sqlite can't deal, this is ** a critical I/O error */ rc = ((failed & SQLITE_IOERR) == SQLITE_IOERR) ? failed2 : SQLITE_IOERR_LOCK; goto afp_end_lock; } }else{ rc = failed; } } if( failed ){ rc = failed; } } if( rc==SQLITE_OK ){ pFile->eFileLock = eFileLock; pInode->eFileLock = eFileLock; }else if( eFileLock==EXCLUSIVE_LOCK ){ pFile->eFileLock = PENDING_LOCK; pInode->eFileLock = PENDING_LOCK; } afp_end_lock: unixLeaveMutex(); OSTRACE(("LOCK %d %s %s (afp)\n", pFile->h, azFileLock(eFileLock), rc==SQLITE_OK ? "ok" : "failed")); return rc; } /* ** Lower the locking level on file descriptor pFile to eFileLock. eFileLock ** must be either NO_LOCK or SHARED_LOCK. ** ** If the locking level of the file descriptor is already at or below ** the requested locking level, this routine is a no-op. */ static int afpUnlock(sqlite3_file *id, int eFileLock) { int rc = SQLITE_OK; unixFile *pFile = (unixFile*)id; unixInodeInfo *pInode; afpLockingContext *context = (afpLockingContext *) pFile->lockingContext; int skipShared = 0; #ifdef SQLITE_TEST int h = pFile->h; #endif assert( pFile ); OSTRACE(("UNLOCK %d %d was %d(%d,%d) pid=%d (afp)\n", pFile->h, eFileLock, pFile->eFileLock, pFile->pInode->eFileLock, pFile->pInode->nShared, getpid())); assert( eFileLock<=SHARED_LOCK ); if( pFile->eFileLock<=eFileLock ){ return SQLITE_OK; } unixEnterMutex(); pInode = pFile->pInode; assert( pInode->nShared!=0 ); if( pFile->eFileLock>SHARED_LOCK ){ assert( pInode->eFileLock==pFile->eFileLock ); SimulateIOErrorBenign(1); SimulateIOError( h=(-1) ) SimulateIOErrorBenign(0); #ifndef NDEBUG /* When reducing a lock such that other processes can start ** reading the database file again, make sure that the ** transaction counter was updated if any part of the database ** file changed. If the transaction counter is not updated, ** other connections to the same file might not realize that ** the file has changed and hence might not know to flush their ** cache. The use of a stale cache can lead to database corruption. */ assert( pFile->inNormalWrite==0 || pFile->dbUpdate==0 || pFile->transCntrChng==1 ); pFile->inNormalWrite = 0; #endif if( pFile->eFileLock==EXCLUSIVE_LOCK ){ rc = afpSetLock(context->dbPath, pFile, SHARED_FIRST, SHARED_SIZE, 0); if( rc==SQLITE_OK && (eFileLock==SHARED_LOCK || pInode->nShared>1) ){ /* only re-establish the shared lock if necessary */ int sharedLockByte = SHARED_FIRST+pInode->sharedByte; rc = afpSetLock(context->dbPath, pFile, sharedLockByte, 1, 1); } else { skipShared = 1; } } if( rc==SQLITE_OK && pFile->eFileLock>=PENDING_LOCK ){ rc = afpSetLock(context->dbPath, pFile, PENDING_BYTE, 1, 0); } if( rc==SQLITE_OK && pFile->eFileLock>=RESERVED_LOCK && context->reserved ){ rc = afpSetLock(context->dbPath, pFile, RESERVED_BYTE, 1, 0); if( !rc ){ context->reserved = 0; } } if( rc==SQLITE_OK && (eFileLock==SHARED_LOCK || pInode->nShared>1)){ pInode->eFileLock = SHARED_LOCK; } } if( rc==SQLITE_OK && eFileLock==NO_LOCK ){ /* Decrement the shared lock counter. Release the lock using an ** OS call only when all threads in this same process have released ** the lock. */ unsigned long long sharedLockByte = SHARED_FIRST+pInode->sharedByte; pInode->nShared--; if( pInode->nShared==0 ){ SimulateIOErrorBenign(1); SimulateIOError( h=(-1) ) SimulateIOErrorBenign(0); if( !skipShared ){ rc = afpSetLock(context->dbPath, pFile, sharedLockByte, 1, 0); } if( !rc ){ pInode->eFileLock = NO_LOCK; pFile->eFileLock = NO_LOCK; } } if( rc==SQLITE_OK ){ pInode->nLock--; assert( pInode->nLock>=0 ); if( pInode->nLock==0 ){ rc = closePendingFds(pFile); } } } unixLeaveMutex(); if( rc==SQLITE_OK ) pFile->eFileLock = eFileLock; return rc; } /* ** Close a file & cleanup AFP specific locking context */ static int afpClose(sqlite3_file *id) { int rc = SQLITE_OK; if( id ){ unixFile *pFile = (unixFile*)id; afpUnlock(id, NO_LOCK); unixEnterMutex(); if( pFile->pInode && pFile->pInode->nLock ){ /* If there are outstanding locks, do not actually close the file just ** yet because that would clear those locks. Instead, add the file ** descriptor to pInode->aPending. It will be automatically closed when ** the last lock is cleared. */ setPendingFd(pFile); } releaseInodeInfo(pFile); sqlite3_free(pFile->lockingContext); rc = closeUnixFile(id); unixLeaveMutex(); } return rc; } |
︙ | ︙ | |||
2860 2861 2862 2863 2864 2865 2866 | ******************************************************************************/ /****************************************************************************** *************************** Begin NFS Locking ********************************/ #if defined(__APPLE__) && SQLITE_ENABLE_LOCKING_STYLE /* | | | | | 2554 2555 2556 2557 2558 2559 2560 2561 2562 2563 2564 2565 2566 2567 2568 2569 2570 2571 2572 2573 2574 2575 | ******************************************************************************/ /****************************************************************************** *************************** Begin NFS Locking ********************************/ #if defined(__APPLE__) && SQLITE_ENABLE_LOCKING_STYLE /* ** Lower the locking level on file descriptor pFile to eFileLock. eFileLock ** must be either NO_LOCK or SHARED_LOCK. ** ** If the locking level of the file descriptor is already at or below ** the requested locking level, this routine is a no-op. */ static int nfsUnlock(sqlite3_file *id, int eFileLock){ return _posixUnlock(id, eFileLock, 1); } #endif /* defined(__APPLE__) && SQLITE_ENABLE_LOCKING_STYLE */ /* ** The code above is the NFS lock implementation. The code is specific ** to MacOSX and does not work on other unix platforms. No alternative ** is available. |
︙ | ︙ | |||
2931 2932 2933 2934 2935 2936 2937 | } got = read(id->h, pBuf, cnt); #endif TIMER_END; if( got<0 ){ ((unixFile*)id)->lastErrno = errno; } | | > > | 2625 2626 2627 2628 2629 2630 2631 2632 2633 2634 2635 2636 2637 2638 2639 2640 2641 2642 2643 2644 2645 2646 2647 2648 2649 2650 2651 2652 2653 2654 2655 2656 2657 2658 2659 2660 2661 2662 2663 2664 2665 | } got = read(id->h, pBuf, cnt); #endif TIMER_END; if( got<0 ){ ((unixFile*)id)->lastErrno = errno; } OSTRACE(("READ %-3d %5d %7lld %llu\n", id->h, got, offset, TIMER_ELAPSED)); return got; } /* ** Read data from a file into a buffer. Return SQLITE_OK if all ** bytes were read successfully and SQLITE_IOERR if anything goes ** wrong. */ static int unixRead( sqlite3_file *id, void *pBuf, int amt, sqlite3_int64 offset ){ unixFile *pFile = (unixFile *)id; int got; assert( id ); /* If this is a database file (not a journal, master-journal or temp ** file), the bytes in the locking range should never be read or written. */ #if 0 assert( pFile->pUnused==0 || offset>=PENDING_BYTE+512 || offset+amt<=PENDING_BYTE ); #endif got = seekAndRead(pFile, offset, pBuf, amt); if( got==amt ){ return SQLITE_OK; }else if( got<0 ){ /* lastErrno set by seekAndRead */ return SQLITE_IOERR_READ; |
︙ | ︙ | |||
3005 3006 3007 3008 3009 3010 3011 | got = write(id->h, pBuf, cnt); #endif TIMER_END; if( got<0 ){ ((unixFile*)id)->lastErrno = errno; } | | | 2701 2702 2703 2704 2705 2706 2707 2708 2709 2710 2711 2712 2713 2714 2715 | got = write(id->h, pBuf, cnt); #endif TIMER_END; if( got<0 ){ ((unixFile*)id)->lastErrno = errno; } OSTRACE(("WRITE %-3d %5d %7lld %llu\n", id->h, got, offset, TIMER_ELAPSED)); return got; } /* ** Write data from a buffer into a file. Return SQLITE_OK on success ** or some other error code on failure. |
︙ | ︙ | |||
3027 3028 3029 3030 3031 3032 3033 3034 3035 3036 3037 3038 3039 3040 3041 3042 3043 3044 | unixFile *pFile = (unixFile*)id; int wrote = 0; assert( id ); assert( amt>0 ); /* If this is a database file (not a journal, master-journal or temp ** file), the bytes in the locking range should never be read or written. */ assert( pFile->pUnused==0 || offset>=PENDING_BYTE+512 || offset+amt<=PENDING_BYTE ); #ifndef NDEBUG /* If we are doing a normal write to a database file (as opposed to ** doing a hot-journal rollback or a write to some file other than a ** normal database file) then record the fact that the database ** has changed. If the transaction counter is modified, record that ** fact too. | > > | 2723 2724 2725 2726 2727 2728 2729 2730 2731 2732 2733 2734 2735 2736 2737 2738 2739 2740 2741 2742 | unixFile *pFile = (unixFile*)id; int wrote = 0; assert( id ); assert( amt>0 ); /* If this is a database file (not a journal, master-journal or temp ** file), the bytes in the locking range should never be read or written. */ #if 0 assert( pFile->pUnused==0 || offset>=PENDING_BYTE+512 || offset+amt<=PENDING_BYTE ); #endif #ifndef NDEBUG /* If we are doing a normal write to a database file (as opposed to ** doing a hot-journal rollback or a write to some file other than a ** normal database file) then record the fact that the database ** has changed. If the transaction counter is modified, record that ** fact too. |
︙ | ︙ | |||
3229 3230 3231 3232 3233 3234 3235 | /* Unix cannot, but some systems may return SQLITE_FULL from here. This ** line is to test that doing so does not cause any problems. */ SimulateDiskfullError( return SQLITE_FULL ); assert( pFile ); | | | | | 2927 2928 2929 2930 2931 2932 2933 2934 2935 2936 2937 2938 2939 2940 2941 2942 2943 2944 2945 2946 2947 2948 2949 2950 2951 | /* Unix cannot, but some systems may return SQLITE_FULL from here. This ** line is to test that doing so does not cause any problems. */ SimulateDiskfullError( return SQLITE_FULL ); assert( pFile ); OSTRACE(("SYNC %-3d\n", pFile->h)); rc = full_fsync(pFile->h, isFullsync, isDataOnly); SimulateIOError( rc=1 ); if( rc ){ pFile->lastErrno = errno; return SQLITE_IOERR_FSYNC; } if( pFile->dirfd>=0 ){ int err; OSTRACE(("DIRSYNC %-3d (have_fullfsync=%d fullsync=%d)\n", pFile->dirfd, HAVE_FULLFSYNC, isFullsync)); #ifndef SQLITE_DISABLE_DIRSYNC /* The directory sync is only attempted if full_fsync is ** turned off or unavailable. If a full_fsync occurred above, ** then the directory sync is superfluous. */ if( (!HAVE_FULLFSYNC || !isFullsync) && full_fsync(pFile->dirfd,0,0) ){ /* |
︙ | ︙ | |||
3311 3312 3313 3314 3315 3316 3317 | SimulateIOError( rc=1 ); if( rc!=0 ){ ((unixFile*)id)->lastErrno = errno; return SQLITE_IOERR_FSTAT; } *pSize = buf.st_size; | | | 3009 3010 3011 3012 3013 3014 3015 3016 3017 3018 3019 3020 3021 3022 3023 | SimulateIOError( rc=1 ); if( rc!=0 ){ ((unixFile*)id)->lastErrno = errno; return SQLITE_IOERR_FSTAT; } *pSize = buf.st_size; /* When opening a zero-size database, the findInodeInfo() procedure ** writes a single byte into that file in order to work around a bug ** in the OS-X msdos filesystem. In order to avoid problems with upper ** layers, we need to report this file size as zero even though it is ** really 1. Ticket #3260. */ if( *pSize==1 ) *pSize = 0; |
︙ | ︙ | |||
3338 3339 3340 3341 3342 3343 3344 | /* ** Information and control of an open file handle. */ static int unixFileControl(sqlite3_file *id, int op, void *pArg){ switch( op ){ case SQLITE_FCNTL_LOCKSTATE: { | | > > | 3036 3037 3038 3039 3040 3041 3042 3043 3044 3045 3046 3047 3048 3049 3050 3051 3052 3053 3054 3055 3056 3057 3058 3059 3060 3061 3062 | /* ** Information and control of an open file handle. */ static int unixFileControl(sqlite3_file *id, int op, void *pArg){ switch( op ){ case SQLITE_FCNTL_LOCKSTATE: { *(int*)pArg = ((unixFile*)id)->eFileLock; return SQLITE_OK; } case SQLITE_LAST_ERRNO: { *(int*)pArg = ((unixFile*)id)->lastErrno; return SQLITE_OK; } case SQLITE_FCNTL_SIZE_HINT: { #if 0 /* No performance advantage seen on Linux */ sqlite3_int64 szFile = *(sqlite3_int64*)pArg; unixFile *pFile = (unixFile*)id; ftruncate(pFile->h, szFile); #endif return SQLITE_OK; } #ifndef NDEBUG /* The pager calls this method to signal that it has done ** a rollback and that the database is therefore unchanged and ** it hence it is OK for the transaction change counter to be ** unchanged. |
︙ | ︙ | |||
3395 3396 3397 3398 3399 3400 3401 3402 3403 3404 3405 3406 3407 3408 | ** Return the device characteristics for the file. This is always 0 for unix. */ static int unixDeviceCharacteristics(sqlite3_file *NotUsed){ UNUSED_PARAMETER(NotUsed); return 0; } /* ** Here ends the implementation of all sqlite3_file methods. ** ********************** End sqlite3_file Methods ******************************* ******************************************************************************/ /* | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 3095 3096 3097 3098 3099 3100 3101 3102 3103 3104 3105 3106 3107 3108 3109 3110 3111 3112 3113 3114 3115 3116 3117 3118 3119 3120 3121 3122 3123 3124 3125 3126 3127 3128 3129 3130 3131 3132 3133 3134 3135 3136 3137 3138 3139 3140 3141 3142 3143 3144 3145 3146 3147 3148 3149 3150 3151 3152 3153 3154 3155 3156 3157 3158 3159 3160 3161 3162 3163 3164 3165 3166 3167 3168 3169 3170 3171 3172 3173 3174 3175 3176 3177 3178 3179 3180 3181 3182 3183 3184 3185 3186 3187 3188 3189 3190 3191 3192 3193 3194 3195 3196 3197 3198 3199 3200 3201 3202 3203 3204 3205 3206 3207 3208 3209 3210 3211 3212 3213 3214 3215 3216 3217 3218 3219 3220 3221 3222 3223 3224 3225 3226 3227 3228 3229 3230 3231 3232 3233 3234 3235 3236 3237 3238 3239 3240 3241 3242 3243 3244 3245 3246 3247 3248 3249 3250 3251 3252 3253 3254 3255 3256 3257 3258 3259 3260 3261 3262 3263 3264 3265 3266 3267 3268 3269 3270 3271 3272 3273 3274 3275 3276 3277 3278 3279 3280 3281 3282 3283 3284 3285 3286 3287 3288 3289 3290 3291 3292 3293 3294 3295 3296 3297 3298 3299 3300 3301 3302 3303 3304 3305 3306 3307 3308 3309 3310 3311 3312 3313 3314 3315 3316 3317 3318 3319 3320 3321 3322 3323 3324 3325 3326 3327 3328 3329 3330 3331 3332 3333 3334 3335 3336 3337 3338 3339 3340 3341 3342 3343 3344 3345 3346 3347 3348 3349 3350 3351 3352 3353 3354 3355 3356 3357 3358 3359 3360 3361 3362 3363 3364 3365 3366 3367 3368 3369 3370 3371 3372 3373 3374 3375 3376 3377 3378 3379 3380 3381 3382 3383 3384 3385 3386 3387 3388 3389 3390 3391 3392 3393 3394 3395 3396 3397 3398 3399 3400 3401 3402 3403 3404 3405 3406 3407 3408 3409 3410 3411 3412 3413 3414 3415 3416 3417 3418 3419 3420 3421 3422 3423 3424 3425 3426 3427 3428 3429 3430 3431 3432 3433 3434 3435 3436 3437 3438 3439 3440 3441 3442 3443 3444 3445 3446 3447 3448 3449 3450 3451 3452 3453 3454 3455 3456 3457 3458 3459 3460 3461 3462 3463 3464 3465 3466 3467 3468 3469 3470 3471 3472 3473 3474 3475 3476 3477 3478 3479 3480 3481 3482 3483 3484 3485 3486 3487 3488 3489 3490 3491 3492 3493 3494 3495 3496 3497 3498 3499 3500 3501 3502 3503 3504 3505 3506 3507 3508 3509 3510 3511 3512 3513 3514 3515 3516 3517 3518 3519 3520 3521 3522 3523 3524 3525 3526 3527 3528 3529 3530 3531 3532 3533 3534 3535 3536 3537 3538 3539 3540 3541 3542 3543 3544 3545 3546 3547 3548 3549 3550 3551 3552 3553 3554 3555 3556 3557 3558 3559 3560 3561 3562 3563 3564 3565 3566 3567 3568 3569 3570 3571 3572 3573 3574 3575 3576 3577 3578 3579 3580 3581 3582 3583 3584 3585 3586 3587 3588 3589 3590 3591 3592 3593 3594 3595 3596 3597 3598 3599 3600 3601 3602 3603 3604 3605 3606 3607 3608 3609 3610 3611 3612 3613 3614 3615 3616 3617 3618 3619 3620 3621 3622 3623 3624 3625 3626 3627 3628 3629 3630 3631 3632 3633 3634 3635 3636 3637 3638 3639 3640 3641 3642 3643 3644 3645 3646 3647 3648 3649 3650 3651 3652 3653 3654 3655 3656 3657 3658 3659 3660 3661 3662 3663 3664 3665 3666 3667 3668 3669 3670 3671 3672 3673 3674 3675 3676 3677 3678 3679 3680 3681 3682 3683 3684 3685 3686 3687 3688 3689 3690 3691 3692 3693 3694 3695 3696 3697 3698 3699 3700 3701 3702 3703 3704 3705 3706 3707 3708 3709 3710 3711 | ** Return the device characteristics for the file. This is always 0 for unix. */ static int unixDeviceCharacteristics(sqlite3_file *NotUsed){ UNUSED_PARAMETER(NotUsed); return 0; } #ifndef SQLITE_OMIT_WAL /* ** Object used to represent an shared memory buffer. ** ** When multiple threads all reference the same wal-index, each thread ** has its own unixShm object, but they all point to a single instance ** of this unixShmNode object. In other words, each wal-index is opened ** only once per process. ** ** Each unixShmNode object is connected to a single unixInodeInfo object. ** We could coalesce this object into unixInodeInfo, but that would mean ** every open file that does not use shared memory (in other words, most ** open files) would have to carry around this extra information. So ** the unixInodeInfo object contains a pointer to this unixShmNode object ** and the unixShmNode object is created only when needed. ** ** unixMutexHeld() must be true when creating or destroying ** this object or while reading or writing the following fields: ** ** nRef ** ** The following fields are read-only after the object is created: ** ** fid ** zFilename ** ** Either unixShmNode.mutex must be held or unixShmNode.nRef==0 and ** unixMutexHeld() is true when reading or writing any other field ** in this structure. */ struct unixShmNode { unixInodeInfo *pInode; /* unixInodeInfo that owns this SHM node */ sqlite3_mutex *mutex; /* Mutex to access this object */ char *zFilename; /* Name of the mmapped file */ int h; /* Open file descriptor */ int szRegion; /* Size of shared-memory regions */ int nRegion; /* Size of array apRegion */ char **apRegion; /* Array of mapped shared-memory regions */ int nRef; /* Number of unixShm objects pointing to this */ unixShm *pFirst; /* All unixShm objects pointing to this */ #ifdef SQLITE_DEBUG u8 exclMask; /* Mask of exclusive locks held */ u8 sharedMask; /* Mask of shared locks held */ u8 nextShmId; /* Next available unixShm.id value */ #endif }; /* ** Structure used internally by this VFS to record the state of an ** open shared memory connection. ** ** The following fields are initialized when this object is created and ** are read-only thereafter: ** ** unixShm.pFile ** unixShm.id ** ** All other fields are read/write. The unixShm.pFile->mutex must be held ** while accessing any read/write fields. */ struct unixShm { unixShmNode *pShmNode; /* The underlying unixShmNode object */ unixShm *pNext; /* Next unixShm with the same unixShmNode */ u8 hasMutex; /* True if holding the unixShmNode mutex */ u16 sharedMask; /* Mask of shared locks held */ u16 exclMask; /* Mask of exclusive locks held */ #ifdef SQLITE_DEBUG u8 id; /* Id of this connection within its unixShmNode */ #endif }; /* ** Constants used for locking */ #define UNIX_SHM_BASE ((22+SQLITE_SHM_NLOCK)*4) /* first lock byte */ #define UNIX_SHM_DMS (UNIX_SHM_BASE+SQLITE_SHM_NLOCK) /* deadman switch */ /* ** Apply posix advisory locks for all bytes from ofst through ofst+n-1. ** ** Locks block if the mask is exactly UNIX_SHM_C and are non-blocking ** otherwise. */ static int unixShmSystemLock( unixShmNode *pShmNode, /* Apply locks to this open shared-memory segment */ int lockType, /* F_UNLCK, F_RDLCK, or F_WRLCK */ int ofst, /* First byte of the locking range */ int n /* Number of bytes to lock */ ){ struct flock f; /* The posix advisory locking structure */ int rc = SQLITE_OK; /* Result code form fcntl() */ /* Access to the unixShmNode object is serialized by the caller */ assert( sqlite3_mutex_held(pShmNode->mutex) || pShmNode->nRef==0 ); /* Shared locks never span more than one byte */ assert( n==1 || lockType!=F_RDLCK ); /* Locks are within range */ assert( n>=1 && n<SQLITE_SHM_NLOCK ); /* Initialize the locking parameters */ memset(&f, 0, sizeof(f)); f.l_type = lockType; f.l_whence = SEEK_SET; f.l_start = ofst; f.l_len = n; rc = fcntl(pShmNode->h, F_SETLK, &f); rc = (rc!=(-1)) ? SQLITE_OK : SQLITE_BUSY; /* Update the global lock state and do debug tracing */ #ifdef SQLITE_DEBUG { u16 mask; OSTRACE(("SHM-LOCK ")); mask = (1<<(ofst+n)) - (1<<ofst); if( rc==SQLITE_OK ){ if( lockType==F_UNLCK ){ OSTRACE(("unlock %d ok", ofst)); pShmNode->exclMask &= ~mask; pShmNode->sharedMask &= ~mask; }else if( lockType==F_RDLCK ){ OSTRACE(("read-lock %d ok", ofst)); pShmNode->exclMask &= ~mask; pShmNode->sharedMask |= mask; }else{ assert( lockType==F_WRLCK ); OSTRACE(("write-lock %d ok", ofst)); pShmNode->exclMask |= mask; pShmNode->sharedMask &= ~mask; } }else{ if( lockType==F_UNLCK ){ OSTRACE(("unlock %d failed", ofst)); }else if( lockType==F_RDLCK ){ OSTRACE(("read-lock failed")); }else{ assert( lockType==F_WRLCK ); OSTRACE(("write-lock %d failed", ofst)); } } OSTRACE((" - afterwards %03x,%03x\n", pShmNode->sharedMask, pShmNode->exclMask)); } #endif return rc; } /* ** Purge the unixShmNodeList list of all entries with unixShmNode.nRef==0. ** ** This is not a VFS shared-memory method; it is a utility function called ** by VFS shared-memory methods. */ static void unixShmPurge(unixFile *pFd){ unixShmNode *p = pFd->pInode->pShmNode; assert( unixMutexHeld() ); if( p && p->nRef==0 ){ int i; assert( p->pInode==pFd->pInode ); if( p->mutex ) sqlite3_mutex_free(p->mutex); for(i=0; i<p->nRegion; i++){ munmap(p->apRegion[i], p->szRegion); } sqlite3_free(p->apRegion); if( p->h>=0 ) close(p->h); p->pInode->pShmNode = 0; sqlite3_free(p); } } /* ** Open a shared-memory area associated with open database file pDbFd. ** This particular implementation uses mmapped files. ** ** The file used to implement shared-memory is in the same directory ** as the open database file and has the same name as the open database ** file with the "-shm" suffix added. For example, if the database file ** is "/home/user1/config.db" then the file that is created and mmapped ** for shared memory will be called "/home/user1/config.db-shm". ** ** Another approach to is to use files in /dev/shm or /dev/tmp or an ** some other tmpfs mount. But if a file in a different directory ** from the database file is used, then differing access permissions ** or a chroot() might cause two different processes on the same ** database to end up using different files for shared memory - ** meaning that their memory would not really be shared - resulting ** in database corruption. Nevertheless, this tmpfs file usage ** can be enabled at compile-time using -DSQLITE_SHM_DIRECTORY="/dev/shm" ** or the equivalent. The use of the SQLITE_SHM_DIRECTORY compile-time ** option results in an incompatible build of SQLite; builds of SQLite ** that with differing SQLITE_SHM_DIRECTORY settings attempt to use the ** same database file at the same time, database corruption will likely ** result. The SQLITE_SHM_DIRECTORY compile-time option is considered ** "unsupported" and may go away in a future SQLite release. ** ** When opening a new shared-memory file, if no other instances of that ** file are currently open, in this process or in other processes, then ** the file must be truncated to zero length or have its header cleared. */ static int unixOpenSharedMemory(unixFile *pDbFd){ struct unixShm *p = 0; /* The connection to be opened */ struct unixShmNode *pShmNode; /* The underlying mmapped file */ int rc; /* Result code */ unixInodeInfo *pInode; /* The inode of fd */ char *zShmFilename; /* Name of the file used for SHM */ int nShmFilename; /* Size of the SHM filename in bytes */ /* Allocate space for the new unixShm object. */ p = sqlite3_malloc( sizeof(*p) ); if( p==0 ) return SQLITE_NOMEM; memset(p, 0, sizeof(*p)); assert( pDbFd->pShm==0 ); /* Check to see if a unixShmNode object already exists. Reuse an existing ** one if present. Create a new one if necessary. */ unixEnterMutex(); pInode = pDbFd->pInode; pShmNode = pInode->pShmNode; if( pShmNode==0 ){ struct stat sStat; /* fstat() info for database file */ /* Call fstat() to figure out the permissions on the database file. If ** a new *-shm file is created, an attempt will be made to create it ** with the same permissions. The actual permissions the file is created ** with are subject to the current umask setting. */ if( fstat(pDbFd->h, &sStat) ){ rc = SQLITE_IOERR_FSTAT; goto shm_open_err; } #ifdef SQLITE_SHM_DIRECTORY nShmFilename = sizeof(SQLITE_SHM_DIRECTORY) + 30; #else nShmFilename = 5 + (int)strlen(pDbFd->zPath); #endif pShmNode = sqlite3_malloc( sizeof(*pShmNode) + nShmFilename ); if( pShmNode==0 ){ rc = SQLITE_NOMEM; goto shm_open_err; } memset(pShmNode, 0, sizeof(*pShmNode)); zShmFilename = pShmNode->zFilename = (char*)&pShmNode[1]; #ifdef SQLITE_SHM_DIRECTORY sqlite3_snprintf(nShmFilename, zShmFilename, SQLITE_SHM_DIRECTORY "/sqlite-shm-%x-%x", (u32)sStat.st_ino, (u32)sStat.st_dev); #else sqlite3_snprintf(nShmFilename, zShmFilename, "%s-shm", pDbFd->zPath); #endif pShmNode->h = -1; pDbFd->pInode->pShmNode = pShmNode; pShmNode->pInode = pDbFd->pInode; pShmNode->mutex = sqlite3_mutex_alloc(SQLITE_MUTEX_FAST); if( pShmNode->mutex==0 ){ rc = SQLITE_NOMEM; goto shm_open_err; } pShmNode->h = open(zShmFilename, O_RDWR|O_CREAT, (sStat.st_mode & 0777)); if( pShmNode->h<0 ){ rc = SQLITE_CANTOPEN_BKPT; goto shm_open_err; } /* Check to see if another process is holding the dead-man switch. ** If not, truncate the file to zero length. */ rc = SQLITE_OK; if( unixShmSystemLock(pShmNode, F_WRLCK, UNIX_SHM_DMS, 1)==SQLITE_OK ){ if( ftruncate(pShmNode->h, 0) ){ rc = SQLITE_IOERR_SHMOPEN; } } if( rc==SQLITE_OK ){ rc = unixShmSystemLock(pShmNode, F_RDLCK, UNIX_SHM_DMS, 1); } if( rc ) goto shm_open_err; } /* Make the new connection a child of the unixShmNode */ p->pShmNode = pShmNode; #ifdef SQLITE_DEBUG p->id = pShmNode->nextShmId++; #endif pShmNode->nRef++; pDbFd->pShm = p; unixLeaveMutex(); /* The reference count on pShmNode has already been incremented under ** the cover of the unixEnterMutex() mutex and the pointer from the ** new (struct unixShm) object to the pShmNode has been set. All that is ** left to do is to link the new object into the linked list starting ** at pShmNode->pFirst. This must be done while holding the pShmNode->mutex ** mutex. */ sqlite3_mutex_enter(pShmNode->mutex); p->pNext = pShmNode->pFirst; pShmNode->pFirst = p; sqlite3_mutex_leave(pShmNode->mutex); return SQLITE_OK; /* Jump here on any error */ shm_open_err: unixShmPurge(pDbFd); /* This call frees pShmNode if required */ sqlite3_free(p); unixLeaveMutex(); return rc; } /* ** This function is called to obtain a pointer to region iRegion of the ** shared-memory associated with the database file fd. Shared-memory regions ** are numbered starting from zero. Each shared-memory region is szRegion ** bytes in size. ** ** If an error occurs, an error code is returned and *pp is set to NULL. ** ** Otherwise, if the bExtend parameter is 0 and the requested shared-memory ** region has not been allocated (by any client, including one running in a ** separate process), then *pp is set to NULL and SQLITE_OK returned. If ** bExtend is non-zero and the requested shared-memory region has not yet ** been allocated, it is allocated by this function. ** ** If the shared-memory region has already been allocated or is allocated by ** this call as described above, then it is mapped into this processes ** address space (if it is not already), *pp is set to point to the mapped ** memory and SQLITE_OK returned. */ static int unixShmMap( sqlite3_file *fd, /* Handle open on database file */ int iRegion, /* Region to retrieve */ int szRegion, /* Size of regions */ int bExtend, /* True to extend file if necessary */ void volatile **pp /* OUT: Mapped memory */ ){ unixFile *pDbFd = (unixFile*)fd; unixShm *p; unixShmNode *pShmNode; int rc = SQLITE_OK; /* If the shared-memory file has not yet been opened, open it now. */ if( pDbFd->pShm==0 ){ rc = unixOpenSharedMemory(pDbFd); if( rc!=SQLITE_OK ) return rc; } p = pDbFd->pShm; pShmNode = p->pShmNode; sqlite3_mutex_enter(pShmNode->mutex); assert( szRegion==pShmNode->szRegion || pShmNode->nRegion==0 ); if( pShmNode->nRegion<=iRegion ){ char **apNew; /* New apRegion[] array */ int nByte = (iRegion+1)*szRegion; /* Minimum required file size */ struct stat sStat; /* Used by fstat() */ pShmNode->szRegion = szRegion; /* The requested region is not mapped into this processes address space. ** Check to see if it has been allocated (i.e. if the wal-index file is ** large enough to contain the requested region). */ if( fstat(pShmNode->h, &sStat) ){ rc = SQLITE_IOERR_SHMSIZE; goto shmpage_out; } if( sStat.st_size<nByte ){ /* The requested memory region does not exist. If bExtend is set to ** false, exit early. *pp will be set to NULL and SQLITE_OK returned. ** ** Alternatively, if bExtend is true, use ftruncate() to allocate ** the requested memory region. */ if( !bExtend ) goto shmpage_out; if( ftruncate(pShmNode->h, nByte) ){ rc = SQLITE_IOERR_SHMSIZE; goto shmpage_out; } } /* Map the requested memory region into this processes address space. */ apNew = (char **)sqlite3_realloc( pShmNode->apRegion, (iRegion+1)*sizeof(char *) ); if( !apNew ){ rc = SQLITE_IOERR_NOMEM; goto shmpage_out; } pShmNode->apRegion = apNew; while(pShmNode->nRegion<=iRegion){ void *pMem = mmap(0, szRegion, PROT_READ|PROT_WRITE, MAP_SHARED, pShmNode->h, iRegion*szRegion ); if( pMem==MAP_FAILED ){ rc = SQLITE_IOERR; goto shmpage_out; } pShmNode->apRegion[pShmNode->nRegion] = pMem; pShmNode->nRegion++; } } shmpage_out: if( pShmNode->nRegion>iRegion ){ *pp = pShmNode->apRegion[iRegion]; }else{ *pp = 0; } sqlite3_mutex_leave(pShmNode->mutex); return rc; } /* ** Change the lock state for a shared-memory segment. ** ** Note that the relationship between SHAREd and EXCLUSIVE locks is a little ** different here than in posix. In xShmLock(), one can go from unlocked ** to shared and back or from unlocked to exclusive and back. But one may ** not go from shared to exclusive or from exclusive to shared. */ static int unixShmLock( sqlite3_file *fd, /* Database file holding the shared memory */ int ofst, /* First lock to acquire or release */ int n, /* Number of locks to acquire or release */ int flags /* What to do with the lock */ ){ unixFile *pDbFd = (unixFile*)fd; /* Connection holding shared memory */ unixShm *p = pDbFd->pShm; /* The shared memory being locked */ unixShm *pX; /* For looping over all siblings */ unixShmNode *pShmNode = p->pShmNode; /* The underlying file iNode */ int rc = SQLITE_OK; /* Result code */ u16 mask; /* Mask of locks to take or release */ assert( pShmNode==pDbFd->pInode->pShmNode ); assert( pShmNode->pInode==pDbFd->pInode ); assert( ofst>=0 && ofst+n<=SQLITE_SHM_NLOCK ); assert( n>=1 ); assert( flags==(SQLITE_SHM_LOCK | SQLITE_SHM_SHARED) || flags==(SQLITE_SHM_LOCK | SQLITE_SHM_EXCLUSIVE) || flags==(SQLITE_SHM_UNLOCK | SQLITE_SHM_SHARED) || flags==(SQLITE_SHM_UNLOCK | SQLITE_SHM_EXCLUSIVE) ); assert( n==1 || (flags & SQLITE_SHM_EXCLUSIVE)!=0 ); mask = (1<<(ofst+n)) - (1<<ofst); assert( n>1 || mask==(1<<ofst) ); sqlite3_mutex_enter(pShmNode->mutex); if( flags & SQLITE_SHM_UNLOCK ){ u16 allMask = 0; /* Mask of locks held by siblings */ /* See if any siblings hold this same lock */ for(pX=pShmNode->pFirst; pX; pX=pX->pNext){ if( pX==p ) continue; assert( (pX->exclMask & (p->exclMask|p->sharedMask))==0 ); allMask |= pX->sharedMask; } /* Unlock the system-level locks */ if( (mask & allMask)==0 ){ rc = unixShmSystemLock(pShmNode, F_UNLCK, ofst+UNIX_SHM_BASE, n); }else{ rc = SQLITE_OK; } /* Undo the local locks */ if( rc==SQLITE_OK ){ p->exclMask &= ~mask; p->sharedMask &= ~mask; } }else if( flags & SQLITE_SHM_SHARED ){ u16 allShared = 0; /* Union of locks held by connections other than "p" */ /* Find out which shared locks are already held by sibling connections. ** If any sibling already holds an exclusive lock, go ahead and return ** SQLITE_BUSY. */ for(pX=pShmNode->pFirst; pX; pX=pX->pNext){ if( (pX->exclMask & mask)!=0 ){ rc = SQLITE_BUSY; break; } allShared |= pX->sharedMask; } /* Get shared locks at the system level, if necessary */ if( rc==SQLITE_OK ){ if( (allShared & mask)==0 ){ rc = unixShmSystemLock(pShmNode, F_RDLCK, ofst+UNIX_SHM_BASE, n); }else{ rc = SQLITE_OK; } } /* Get the local shared locks */ if( rc==SQLITE_OK ){ p->sharedMask |= mask; } }else{ /* Make sure no sibling connections hold locks that will block this ** lock. If any do, return SQLITE_BUSY right away. */ for(pX=pShmNode->pFirst; pX; pX=pX->pNext){ if( (pX->exclMask & mask)!=0 || (pX->sharedMask & mask)!=0 ){ rc = SQLITE_BUSY; break; } } /* Get the exclusive locks at the system level. Then if successful ** also mark the local connection as being locked. */ if( rc==SQLITE_OK ){ rc = unixShmSystemLock(pShmNode, F_WRLCK, ofst+UNIX_SHM_BASE, n); if( rc==SQLITE_OK ){ assert( (p->sharedMask & mask)==0 ); p->exclMask |= mask; } } } sqlite3_mutex_leave(pShmNode->mutex); OSTRACE(("SHM-LOCK shmid-%d, pid-%d got %03x,%03x\n", p->id, getpid(), p->sharedMask, p->exclMask)); return rc; } /* ** Implement a memory barrier or memory fence on shared memory. ** ** All loads and stores begun before the barrier must complete before ** any load or store begun after the barrier. */ static void unixShmBarrier( sqlite3_file *fd /* Database file holding the shared memory */ ){ UNUSED_PARAMETER(fd); unixEnterMutex(); unixLeaveMutex(); } /* ** Close a connection to shared-memory. Delete the underlying ** storage if deleteFlag is true. ** ** If there is no shared memory associated with the connection then this ** routine is a harmless no-op. */ static int unixShmUnmap( sqlite3_file *fd, /* The underlying database file */ int deleteFlag /* Delete shared-memory if true */ ){ unixShm *p; /* The connection to be closed */ unixShmNode *pShmNode; /* The underlying shared-memory file */ unixShm **pp; /* For looping over sibling connections */ unixFile *pDbFd; /* The underlying database file */ pDbFd = (unixFile*)fd; p = pDbFd->pShm; if( p==0 ) return SQLITE_OK; pShmNode = p->pShmNode; assert( pShmNode==pDbFd->pInode->pShmNode ); assert( pShmNode->pInode==pDbFd->pInode ); /* Remove connection p from the set of connections associated ** with pShmNode */ sqlite3_mutex_enter(pShmNode->mutex); for(pp=&pShmNode->pFirst; (*pp)!=p; pp = &(*pp)->pNext){} *pp = p->pNext; /* Free the connection p */ sqlite3_free(p); pDbFd->pShm = 0; sqlite3_mutex_leave(pShmNode->mutex); /* If pShmNode->nRef has reached 0, then close the underlying ** shared-memory file, too */ unixEnterMutex(); assert( pShmNode->nRef>0 ); pShmNode->nRef--; if( pShmNode->nRef==0 ){ if( deleteFlag ) unlink(pShmNode->zFilename); unixShmPurge(pDbFd); } unixLeaveMutex(); return SQLITE_OK; } #else # define unixShmMap 0 # define unixShmLock 0 # define unixShmBarrier 0 # define unixShmUnmap 0 #endif /* #ifndef SQLITE_OMIT_WAL */ /* ** Here ends the implementation of all sqlite3_file methods. ** ********************** End sqlite3_file Methods ******************************* ******************************************************************************/ /* |
︙ | ︙ | |||
3435 3436 3437 3438 3439 3440 3441 | ** ** * A constant sqlite3_io_methods object call METHOD that has locking ** methods CLOSE, LOCK, UNLOCK, CKRESLOCK. ** ** * An I/O method finder function called FINDER that returns a pointer ** to the METHOD object in the previous bullet. */ | | | | > > > > > > > > > > | 3738 3739 3740 3741 3742 3743 3744 3745 3746 3747 3748 3749 3750 3751 3752 3753 3754 3755 3756 3757 3758 3759 3760 3761 3762 3763 3764 3765 3766 3767 3768 3769 3770 3771 3772 3773 3774 3775 3776 3777 3778 3779 3780 3781 3782 3783 3784 3785 3786 3787 3788 3789 3790 3791 3792 3793 3794 3795 3796 3797 3798 3799 3800 3801 3802 3803 3804 3805 3806 3807 3808 3809 3810 3811 3812 3813 3814 3815 3816 3817 3818 3819 3820 3821 3822 3823 3824 3825 3826 3827 3828 3829 3830 3831 3832 3833 3834 3835 3836 3837 3838 3839 3840 | ** ** * A constant sqlite3_io_methods object call METHOD that has locking ** methods CLOSE, LOCK, UNLOCK, CKRESLOCK. ** ** * An I/O method finder function called FINDER that returns a pointer ** to the METHOD object in the previous bullet. */ #define IOMETHODS(FINDER, METHOD, VERSION, CLOSE, LOCK, UNLOCK, CKLOCK) \ static const sqlite3_io_methods METHOD = { \ VERSION, /* iVersion */ \ CLOSE, /* xClose */ \ unixRead, /* xRead */ \ unixWrite, /* xWrite */ \ unixTruncate, /* xTruncate */ \ unixSync, /* xSync */ \ unixFileSize, /* xFileSize */ \ LOCK, /* xLock */ \ UNLOCK, /* xUnlock */ \ CKLOCK, /* xCheckReservedLock */ \ unixFileControl, /* xFileControl */ \ unixSectorSize, /* xSectorSize */ \ unixDeviceCharacteristics, /* xDeviceCapabilities */ \ unixShmMap, /* xShmMap */ \ unixShmLock, /* xShmLock */ \ unixShmBarrier, /* xShmBarrier */ \ unixShmUnmap /* xShmUnmap */ \ }; \ static const sqlite3_io_methods *FINDER##Impl(const char *z, unixFile *p){ \ UNUSED_PARAMETER(z); UNUSED_PARAMETER(p); \ return &METHOD; \ } \ static const sqlite3_io_methods *(*const FINDER)(const char*,unixFile *p) \ = FINDER##Impl; /* ** Here are all of the sqlite3_io_methods objects for each of the ** locking strategies. Functions that return pointers to these methods ** are also created. */ IOMETHODS( posixIoFinder, /* Finder function name */ posixIoMethods, /* sqlite3_io_methods object name */ 2, /* shared memory is enabled */ unixClose, /* xClose method */ unixLock, /* xLock method */ unixUnlock, /* xUnlock method */ unixCheckReservedLock /* xCheckReservedLock method */ ) IOMETHODS( nolockIoFinder, /* Finder function name */ nolockIoMethods, /* sqlite3_io_methods object name */ 1, /* shared memory is disabled */ nolockClose, /* xClose method */ nolockLock, /* xLock method */ nolockUnlock, /* xUnlock method */ nolockCheckReservedLock /* xCheckReservedLock method */ ) IOMETHODS( dotlockIoFinder, /* Finder function name */ dotlockIoMethods, /* sqlite3_io_methods object name */ 1, /* shared memory is disabled */ dotlockClose, /* xClose method */ dotlockLock, /* xLock method */ dotlockUnlock, /* xUnlock method */ dotlockCheckReservedLock /* xCheckReservedLock method */ ) #if SQLITE_ENABLE_LOCKING_STYLE && !OS_VXWORKS IOMETHODS( flockIoFinder, /* Finder function name */ flockIoMethods, /* sqlite3_io_methods object name */ 1, /* shared memory is disabled */ flockClose, /* xClose method */ flockLock, /* xLock method */ flockUnlock, /* xUnlock method */ flockCheckReservedLock /* xCheckReservedLock method */ ) #endif #if OS_VXWORKS IOMETHODS( semIoFinder, /* Finder function name */ semIoMethods, /* sqlite3_io_methods object name */ 1, /* shared memory is disabled */ semClose, /* xClose method */ semLock, /* xLock method */ semUnlock, /* xUnlock method */ semCheckReservedLock /* xCheckReservedLock method */ ) #endif #if defined(__APPLE__) && SQLITE_ENABLE_LOCKING_STYLE IOMETHODS( afpIoFinder, /* Finder function name */ afpIoMethods, /* sqlite3_io_methods object name */ 1, /* shared memory is disabled */ afpClose, /* xClose method */ afpLock, /* xLock method */ afpUnlock, /* xUnlock method */ afpCheckReservedLock /* xCheckReservedLock method */ ) #endif |
︙ | ︙ | |||
3538 3539 3540 3541 3542 3543 3544 3545 3546 3547 3548 3549 3550 3551 3552 3553 3554 3555 3556 3557 3558 3559 3560 3561 3562 3563 | static int proxyClose(sqlite3_file*); static int proxyLock(sqlite3_file*, int); static int proxyUnlock(sqlite3_file*, int); static int proxyCheckReservedLock(sqlite3_file*, int*); IOMETHODS( proxyIoFinder, /* Finder function name */ proxyIoMethods, /* sqlite3_io_methods object name */ proxyClose, /* xClose method */ proxyLock, /* xLock method */ proxyUnlock, /* xUnlock method */ proxyCheckReservedLock /* xCheckReservedLock method */ ) #endif /* nfs lockd on OSX 10.3+ doesn't clear write locks when a read lock is set */ #if defined(__APPLE__) && SQLITE_ENABLE_LOCKING_STYLE IOMETHODS( nfsIoFinder, /* Finder function name */ nfsIoMethods, /* sqlite3_io_methods object name */ unixClose, /* xClose method */ unixLock, /* xLock method */ nfsUnlock, /* xUnlock method */ unixCheckReservedLock /* xCheckReservedLock method */ ) #endif | > > | 3851 3852 3853 3854 3855 3856 3857 3858 3859 3860 3861 3862 3863 3864 3865 3866 3867 3868 3869 3870 3871 3872 3873 3874 3875 3876 3877 3878 | static int proxyClose(sqlite3_file*); static int proxyLock(sqlite3_file*, int); static int proxyUnlock(sqlite3_file*, int); static int proxyCheckReservedLock(sqlite3_file*, int*); IOMETHODS( proxyIoFinder, /* Finder function name */ proxyIoMethods, /* sqlite3_io_methods object name */ 1, /* shared memory is disabled */ proxyClose, /* xClose method */ proxyLock, /* xLock method */ proxyUnlock, /* xUnlock method */ proxyCheckReservedLock /* xCheckReservedLock method */ ) #endif /* nfs lockd on OSX 10.3+ doesn't clear write locks when a read lock is set */ #if defined(__APPLE__) && SQLITE_ENABLE_LOCKING_STYLE IOMETHODS( nfsIoFinder, /* Finder function name */ nfsIoMethods, /* sqlite3_io_methods object name */ 1, /* shared memory is disabled */ unixClose, /* xClose method */ unixLock, /* xLock method */ nfsUnlock, /* xUnlock method */ unixCheckReservedLock /* xCheckReservedLock method */ ) #endif |
︙ | ︙ | |||
3690 3691 3692 3693 3694 3695 3696 | int noLock, /* Omit locking if true */ int isDelete /* Delete on close if true */ ){ const sqlite3_io_methods *pLockingStyle; unixFile *pNew = (unixFile *)pId; int rc = SQLITE_OK; | | < | < > > | 4005 4006 4007 4008 4009 4010 4011 4012 4013 4014 4015 4016 4017 4018 4019 4020 4021 4022 4023 4024 4025 4026 4027 4028 4029 4030 4031 | int noLock, /* Omit locking if true */ int isDelete /* Delete on close if true */ ){ const sqlite3_io_methods *pLockingStyle; unixFile *pNew = (unixFile *)pId; int rc = SQLITE_OK; assert( pNew->pInode==NULL ); /* Parameter isDelete is only used on vxworks. Express this explicitly ** here to prevent compiler warnings about unused parameters. */ UNUSED_PARAMETER(isDelete); OSTRACE(("OPEN %-3d %s\n", h, zFilename)); pNew->h = h; pNew->dirfd = dirfd; pNew->fileFlags = 0; assert( zFilename==0 || zFilename[0]=='/' ); /* Never a relative pathname */ pNew->zPath = zFilename; #if OS_VXWORKS pNew->pId = vxworksFindFileId(zFilename); if( pNew->pId==0 ){ noLock = 1; rc = SQLITE_NOMEM; } |
︙ | ︙ | |||
3730 3731 3732 3733 3734 3735 3736 | if( pLockingStyle == &posixIoMethods #if defined(__APPLE__) && SQLITE_ENABLE_LOCKING_STYLE || pLockingStyle == &nfsIoMethods #endif ){ unixEnterMutex(); | | | | | | 4045 4046 4047 4048 4049 4050 4051 4052 4053 4054 4055 4056 4057 4058 4059 4060 4061 4062 4063 4064 4065 4066 4067 4068 4069 4070 4071 | if( pLockingStyle == &posixIoMethods #if defined(__APPLE__) && SQLITE_ENABLE_LOCKING_STYLE || pLockingStyle == &nfsIoMethods #endif ){ unixEnterMutex(); rc = findInodeInfo(pNew, &pNew->pInode); if( rc!=SQLITE_OK ){ /* If an error occured in findInodeInfo(), close the file descriptor ** immediately, before releasing the mutex. findInodeInfo() may fail ** in two scenarios: ** ** (a) A call to fstat() failed. ** (b) A malloc failed. ** ** Scenario (b) may only occur if the process is holding no other ** file descriptors open on the same file. If there were other file ** descriptors on this file, then no malloc would be required by ** findInodeInfo(). If this is the case, it is quite safe to close ** handle h - as it is guaranteed that no posix locks will be released ** by doing so. ** ** If scenario (a) caused the error then things are not so safe. The ** implicit assumption here is that if fstat() fails, things are in ** such bad shape that dropping a lock or two doesn't matter much. */ |
︙ | ︙ | |||
3773 3774 3775 3776 3777 3778 3779 | /* NB: zFilename exists and remains valid until the file is closed ** according to requirement F11141. So we do not need to make a ** copy of the filename. */ pCtx->dbPath = zFilename; pCtx->reserved = 0; srandomdev(); unixEnterMutex(); | | | 4088 4089 4090 4091 4092 4093 4094 4095 4096 4097 4098 4099 4100 4101 4102 | /* NB: zFilename exists and remains valid until the file is closed ** according to requirement F11141. So we do not need to make a ** copy of the filename. */ pCtx->dbPath = zFilename; pCtx->reserved = 0; srandomdev(); unixEnterMutex(); rc = findInodeInfo(pNew, &pNew->pInode); if( rc!=SQLITE_OK ){ sqlite3_free(pNew->lockingContext); close(h); h = -1; } unixLeaveMutex(); } |
︙ | ︙ | |||
3806 3807 3808 3809 3810 3811 3812 | #if OS_VXWORKS else if( pLockingStyle == &semIoMethods ){ /* Named semaphore locking uses the file path so it needs to be ** included in the semLockingContext */ unixEnterMutex(); | | | | | | | | 4121 4122 4123 4124 4125 4126 4127 4128 4129 4130 4131 4132 4133 4134 4135 4136 4137 4138 4139 4140 4141 4142 4143 4144 4145 4146 | #if OS_VXWORKS else if( pLockingStyle == &semIoMethods ){ /* Named semaphore locking uses the file path so it needs to be ** included in the semLockingContext */ unixEnterMutex(); rc = findInodeInfo(pNew, &pNew->pInode); if( (rc==SQLITE_OK) && (pNew->pInode->pSem==NULL) ){ char *zSemName = pNew->pInode->aSemName; int n; sqlite3_snprintf(MAX_PATHNAME, zSemName, "/%s.sem", pNew->pId->zCanonicalName); for( n=1; zSemName[n]; n++ ) if( zSemName[n]=='/' ) zSemName[n] = '_'; pNew->pInode->pSem = sem_open(zSemName, O_CREAT, 0666, 1); if( pNew->pInode->pSem == SEM_FAILED ){ rc = SQLITE_NOMEM; pNew->pInode->aSemName[0] = '\0'; } } unixLeaveMutex(); } #endif pNew->lastErrno = 0; |
︙ | ︙ | |||
3868 3869 3870 3871 3872 3873 3874 | if( ii>0 ){ zDirname[ii] = '\0'; fd = open(zDirname, O_RDONLY|O_BINARY, 0); if( fd>=0 ){ #ifdef FD_CLOEXEC fcntl(fd, F_SETFD, fcntl(fd, F_GETFD, 0) | FD_CLOEXEC); #endif | | > | < < | < > > > > > > > > > > > > > > > > > > > > > > > < | < < < < | < < < < < | < < | 4183 4184 4185 4186 4187 4188 4189 4190 4191 4192 4193 4194 4195 4196 4197 4198 4199 4200 4201 4202 4203 4204 4205 4206 4207 4208 4209 4210 4211 4212 4213 4214 4215 4216 4217 4218 4219 4220 4221 4222 4223 4224 4225 4226 4227 4228 4229 4230 4231 4232 4233 4234 4235 4236 4237 4238 4239 4240 4241 4242 4243 4244 4245 4246 4247 4248 4249 4250 4251 4252 4253 | if( ii>0 ){ zDirname[ii] = '\0'; fd = open(zDirname, O_RDONLY|O_BINARY, 0); if( fd>=0 ){ #ifdef FD_CLOEXEC fcntl(fd, F_SETFD, fcntl(fd, F_GETFD, 0) | FD_CLOEXEC); #endif OSTRACE(("OPENDIR %-3d %s\n", fd, zDirname)); } } *pFd = fd; return (fd>=0?SQLITE_OK:SQLITE_CANTOPEN_BKPT); } /* ** Return the name of a directory in which to put temporary files. ** If no suitable temporary file directory can be found, return NULL. */ static const char *unixTempFileDir(void){ static const char *azDirs[] = { 0, 0, "/var/tmp", "/usr/tmp", "/tmp", 0 /* List terminator */ }; unsigned int i; struct stat buf; const char *zDir = 0; azDirs[0] = sqlite3_temp_directory; if( !azDirs[1] ) azDirs[1] = getenv("TMPDIR"); for(i=0; i<sizeof(azDirs)/sizeof(azDirs[0]); zDir=azDirs[i++]){ if( zDir==0 ) continue; if( stat(zDir, &buf) ) continue; if( !S_ISDIR(buf.st_mode) ) continue; if( access(zDir, 07) ) continue; break; } return zDir; } /* ** Create a temporary file name in zBuf. zBuf must be allocated ** by the calling process and must be big enough to hold at least ** pVfs->mxPathname bytes. */ static int unixGetTempname(int nBuf, char *zBuf){ static const unsigned char zChars[] = "abcdefghijklmnopqrstuvwxyz" "ABCDEFGHIJKLMNOPQRSTUVWXYZ" "0123456789"; unsigned int i, j; const char *zDir; /* It's odd to simulate an io-error here, but really this is just ** using the io-error infrastructure to test that SQLite handles this ** function failing. */ SimulateIOError( return SQLITE_IOERR ); zDir = unixTempFileDir(); if( zDir==0 ) zDir = "."; /* Check that the output buffer is large enough for the temporary file ** name. If it is not, return SQLITE_ERROR. */ if( (strlen(zDir) + strlen(SQLITE_TEMP_FILE_PREFIX) + 17) >= (size_t)nBuf ){ return SQLITE_ERROR; } |
︙ | ︙ | |||
3981 3982 3983 3984 3985 3986 3987 | ** For this reason, if an error occurs in the stat() call here, it is ** ignored and -1 is returned. The caller will try to open a new file ** descriptor on the same path, fail, and return an error to SQLite. ** ** Even if a subsequent open() call does succeed, the consequences of ** not searching for a resusable file descriptor are not dire. */ if( 0==stat(zPath, &sStat) ){ | | | | | | | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 4305 4306 4307 4308 4309 4310 4311 4312 4313 4314 4315 4316 4317 4318 4319 4320 4321 4322 4323 4324 4325 4326 4327 4328 4329 4330 4331 4332 4333 4334 4335 4336 4337 4338 4339 4340 4341 4342 4343 4344 4345 4346 4347 4348 4349 4350 4351 4352 4353 4354 4355 4356 4357 4358 4359 4360 4361 4362 4363 4364 4365 4366 4367 4368 4369 4370 4371 4372 4373 4374 4375 4376 4377 4378 4379 4380 4381 4382 4383 4384 | ** For this reason, if an error occurs in the stat() call here, it is ** ignored and -1 is returned. The caller will try to open a new file ** descriptor on the same path, fail, and return an error to SQLite. ** ** Even if a subsequent open() call does succeed, the consequences of ** not searching for a resusable file descriptor are not dire. */ if( 0==stat(zPath, &sStat) ){ unixInodeInfo *pInode; unixEnterMutex(); pInode = inodeList; while( pInode && (pInode->fileId.dev!=sStat.st_dev || pInode->fileId.ino!=sStat.st_ino) ){ pInode = pInode->pNext; } if( pInode ){ UnixUnusedFd **pp; for(pp=&pInode->pUnused; *pp && (*pp)->flags!=flags; pp=&((*pp)->pNext)); pUnused = *pp; if( pUnused ){ *pp = pUnused->pNext; } } unixLeaveMutex(); } #endif /* if !OS_VXWORKS */ return pUnused; } /* ** This function is called by unixOpen() to determine the unix permissions ** to create new files with. If no error occurs, then SQLITE_OK is returned ** and a value suitable for passing as the third argument to open(2) is ** written to *pMode. If an IO error occurs, an SQLite error code is ** returned and the value of *pMode is not modified. ** ** If the file being opened is a temporary file, it is always created with ** the octal permissions 0600 (read/writable by owner only). If the file ** is a database or master journal file, it is created with the permissions ** mask SQLITE_DEFAULT_FILE_PERMISSIONS. ** ** Finally, if the file being opened is a WAL or regular journal file, then ** this function queries the file-system for the permissions on the ** corresponding database file and sets *pMode to this value. Whenever ** possible, WAL and journal files are created using the same permissions ** as the associated database file. */ static int findCreateFileMode( const char *zPath, /* Path of file (possibly) being created */ int flags, /* Flags passed as 4th argument to xOpen() */ mode_t *pMode /* OUT: Permissions to open file with */ ){ int rc = SQLITE_OK; /* Return Code */ if( flags & (SQLITE_OPEN_WAL|SQLITE_OPEN_MAIN_JOURNAL) ){ char zDb[MAX_PATHNAME+1]; /* Database file path */ int nDb; /* Number of valid bytes in zDb */ struct stat sStat; /* Output of stat() on database file */ nDb = sqlite3Strlen30(zPath) - ((flags & SQLITE_OPEN_WAL) ? 4 : 8); memcpy(zDb, zPath, nDb); zDb[nDb] = '\0'; if( 0==stat(zDb, &sStat) ){ *pMode = sStat.st_mode & 0777; }else{ rc = SQLITE_IOERR_FSTAT; } }else if( flags & SQLITE_OPEN_DELETEONCLOSE ){ *pMode = 0600; }else{ *pMode = SQLITE_DEFAULT_FILE_PERMISSIONS; } return rc; } /* ** Open the file zPath. ** ** Previously, the SQLite OS layer used three functions in place of this ** one: ** |
︙ | ︙ | |||
4053 4054 4055 4056 4057 4058 4059 | int isAutoProxy = (flags & SQLITE_OPEN_AUTOPROXY); #endif /* If creating a master or main-file journal, this function will open ** a file-descriptor on the directory too. The first time unixSync() ** is called the directory file descriptor will be fsync()ed and close()d. */ | | > | > | | | > | | | > > > > > > | | 4422 4423 4424 4425 4426 4427 4428 4429 4430 4431 4432 4433 4434 4435 4436 4437 4438 4439 4440 4441 4442 4443 4444 4445 4446 4447 4448 4449 4450 4451 4452 4453 4454 4455 4456 4457 4458 4459 4460 4461 4462 4463 4464 4465 4466 4467 4468 4469 4470 4471 4472 4473 4474 4475 4476 4477 4478 4479 4480 4481 4482 4483 4484 4485 4486 4487 4488 4489 4490 4491 4492 4493 4494 4495 4496 4497 4498 4499 4500 4501 4502 4503 4504 4505 4506 4507 4508 4509 4510 4511 4512 4513 4514 4515 4516 4517 | int isAutoProxy = (flags & SQLITE_OPEN_AUTOPROXY); #endif /* If creating a master or main-file journal, this function will open ** a file-descriptor on the directory too. The first time unixSync() ** is called the directory file descriptor will be fsync()ed and close()d. */ int isOpenDirectory = (isCreate && ( eType==SQLITE_OPEN_MASTER_JOURNAL || eType==SQLITE_OPEN_MAIN_JOURNAL || eType==SQLITE_OPEN_WAL )); /* If argument zPath is a NULL pointer, this function is required to open ** a temporary file. Use this buffer to store the file name in. */ char zTmpname[MAX_PATHNAME+1]; const char *zName = zPath; /* Check the following statements are true: ** ** (a) Exactly one of the READWRITE and READONLY flags must be set, and ** (b) if CREATE is set, then READWRITE must also be set, and ** (c) if EXCLUSIVE is set, then CREATE must also be set. ** (d) if DELETEONCLOSE is set, then CREATE must also be set. */ assert((isReadonly==0 || isReadWrite==0) && (isReadWrite || isReadonly)); assert(isCreate==0 || isReadWrite); assert(isExclusive==0 || isCreate); assert(isDelete==0 || isCreate); /* The main DB, main journal, WAL file and master journal are never ** automatically deleted. Nor are they ever temporary files. */ assert( (!isDelete && zName) || eType!=SQLITE_OPEN_MAIN_DB ); assert( (!isDelete && zName) || eType!=SQLITE_OPEN_MAIN_JOURNAL ); assert( (!isDelete && zName) || eType!=SQLITE_OPEN_MASTER_JOURNAL ); assert( (!isDelete && zName) || eType!=SQLITE_OPEN_WAL ); /* Assert that the upper layer has set one of the "file-type" flags. */ assert( eType==SQLITE_OPEN_MAIN_DB || eType==SQLITE_OPEN_TEMP_DB || eType==SQLITE_OPEN_MAIN_JOURNAL || eType==SQLITE_OPEN_TEMP_JOURNAL || eType==SQLITE_OPEN_SUBJOURNAL || eType==SQLITE_OPEN_MASTER_JOURNAL || eType==SQLITE_OPEN_TRANSIENT_DB || eType==SQLITE_OPEN_WAL ); memset(p, 0, sizeof(unixFile)); if( eType==SQLITE_OPEN_MAIN_DB ){ UnixUnusedFd *pUnused; pUnused = findReusableFd(zName, flags); if( pUnused ){ fd = pUnused->fd; }else{ pUnused = sqlite3_malloc(sizeof(*pUnused)); if( !pUnused ){ return SQLITE_NOMEM; } } p->pUnused = pUnused; }else if( !zName ){ /* If zName is NULL, the upper layer is requesting a temp file. */ assert(isDelete && !isOpenDirectory); rc = unixGetTempname(MAX_PATHNAME+1, zTmpname); if( rc!=SQLITE_OK ){ return rc; } zName = zTmpname; } /* Determine the value of the flags parameter passed to POSIX function ** open(). These must be calculated even if open() is not called, as ** they may be stored as part of the file handle and used by the ** 'conch file' locking functions later on. */ if( isReadonly ) openFlags |= O_RDONLY; if( isReadWrite ) openFlags |= O_RDWR; if( isCreate ) openFlags |= O_CREAT; if( isExclusive ) openFlags |= (O_EXCL|O_NOFOLLOW); openFlags |= (O_LARGEFILE|O_BINARY); if( fd<0 ){ mode_t openMode; /* Permissions to create file with */ rc = findCreateFileMode(zName, flags, &openMode); if( rc!=SQLITE_OK ){ assert( !p->pUnused ); assert( eType==SQLITE_OPEN_WAL || eType==SQLITE_OPEN_MAIN_JOURNAL ); return rc; } fd = open(zName, openFlags, openMode); OSTRACE(("OPENX %-3d %s 0%o\n", fd, zName, openFlags)); if( fd<0 && errno!=EISDIR && isReadWrite && !isExclusive ){ /* Failed to open the file for read/write access. Try read-only. */ flags &= ~(SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE); openFlags &= ~(O_RDWR|O_CREAT); flags |= SQLITE_OPEN_READONLY; openFlags |= O_RDONLY; fd = open(zName, openFlags, openMode); |
︙ | ︙ | |||
4266 4267 4268 4269 4270 4271 4272 | sqlite3_vfs *NotUsed, /* VFS containing this as the xDelete method */ const char *zPath, /* Name of file to be deleted */ int dirSync /* If true, fsync() directory after deleting file */ ){ int rc = SQLITE_OK; UNUSED_PARAMETER(NotUsed); SimulateIOError(return SQLITE_IOERR_DELETE); | | > > | 4644 4645 4646 4647 4648 4649 4650 4651 4652 4653 4654 4655 4656 4657 4658 4659 4660 | sqlite3_vfs *NotUsed, /* VFS containing this as the xDelete method */ const char *zPath, /* Name of file to be deleted */ int dirSync /* If true, fsync() directory after deleting file */ ){ int rc = SQLITE_OK; UNUSED_PARAMETER(NotUsed); SimulateIOError(return SQLITE_IOERR_DELETE); if( unlink(zPath)==(-1) && errno!=ENOENT ){ return SQLITE_IOERR_DELETE; } #ifndef SQLITE_DISABLE_DIRSYNC if( dirSync ){ int fd; rc = openDirectory(zPath, &fd); if( rc==SQLITE_OK ){ #if OS_VXWORKS if( fsync(fd)==-1 ) |
︙ | ︙ | |||
4323 4324 4325 4326 4327 4328 4329 4330 4331 4332 4333 4334 4335 4336 | amode = R_OK; break; default: assert(!"Invalid flags argument"); } *pResOut = (access(zPath, amode)==0); return SQLITE_OK; } /* ** Turn a relative pathname into a full pathname. The relative path ** is stored as a nul-terminated string in the buffer pointed to by | > > > > > > | 4703 4704 4705 4706 4707 4708 4709 4710 4711 4712 4713 4714 4715 4716 4717 4718 4719 4720 4721 4722 | amode = R_OK; break; default: assert(!"Invalid flags argument"); } *pResOut = (access(zPath, amode)==0); if( flags==SQLITE_ACCESS_EXISTS && *pResOut ){ struct stat buf; if( 0==stat(zPath, &buf) && buf.st_size==0 ){ *pResOut = 0; } } return SQLITE_OK; } /* ** Turn a relative pathname into a full pathname. The relative path ** is stored as a nul-terminated string in the buffer pointed to by |
︙ | ︙ | |||
4509 4510 4511 4512 4513 4514 4515 4516 4517 4518 4519 4520 4521 4522 | ** The following variable, if set to a non-zero value, is interpreted as ** the number of seconds since 1970 and is used to set the result of ** sqlite3OsCurrentTime() during testing. */ #ifdef SQLITE_TEST int sqlite3_current_time = 0; /* Fake system time in seconds since 1970. */ #endif /* ** Find the current time (in Universal Coordinated Time). Write the ** current time and date as a Julian Day number into *prNow and ** return 0. Return 1 if the time and date cannot be found. */ static int unixCurrentTime(sqlite3_vfs *NotUsed, double *prNow){ | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > < < < | < < < < < < < < < < < < < < < < < < < > > > | 4895 4896 4897 4898 4899 4900 4901 4902 4903 4904 4905 4906 4907 4908 4909 4910 4911 4912 4913 4914 4915 4916 4917 4918 4919 4920 4921 4922 4923 4924 4925 4926 4927 4928 4929 4930 4931 4932 4933 4934 4935 4936 4937 4938 4939 4940 4941 4942 4943 4944 4945 4946 4947 4948 4949 4950 4951 4952 4953 4954 4955 4956 4957 4958 4959 4960 4961 4962 4963 4964 4965 4966 4967 4968 4969 4970 | ** The following variable, if set to a non-zero value, is interpreted as ** the number of seconds since 1970 and is used to set the result of ** sqlite3OsCurrentTime() during testing. */ #ifdef SQLITE_TEST int sqlite3_current_time = 0; /* Fake system time in seconds since 1970. */ #endif /* ** Find the current time (in Universal Coordinated Time). Write into *piNow ** the current time and date as a Julian Day number times 86_400_000. In ** other words, write into *piNow the number of milliseconds since the Julian ** epoch of noon in Greenwich on November 24, 4714 B.C according to the ** proleptic Gregorian calendar. ** ** On success, return 0. Return 1 if the time and date cannot be found. */ static int unixCurrentTimeInt64(sqlite3_vfs *NotUsed, sqlite3_int64 *piNow){ static const sqlite3_int64 unixEpoch = 24405875*(sqlite3_int64)8640000; #if defined(NO_GETTOD) time_t t; time(&t); *piNow = ((sqlite3_int64)i)*1000 + unixEpoch; #elif OS_VXWORKS struct timespec sNow; clock_gettime(CLOCK_REALTIME, &sNow); *piNow = unixEpoch + 1000*(sqlite3_int64)sNow.tv_sec + sNow.tv_nsec/1000000; #else struct timeval sNow; gettimeofday(&sNow, 0); *piNow = unixEpoch + 1000*(sqlite3_int64)sNow.tv_sec + sNow.tv_usec/1000; #endif #ifdef SQLITE_TEST if( sqlite3_current_time ){ *piNow = 1000*(sqlite3_int64)sqlite3_current_time + unixEpoch; } #endif UNUSED_PARAMETER(NotUsed); return 0; } /* ** Find the current time (in Universal Coordinated Time). Write the ** current time and date as a Julian Day number into *prNow and ** return 0. Return 1 if the time and date cannot be found. */ static int unixCurrentTime(sqlite3_vfs *NotUsed, double *prNow){ sqlite3_int64 i; UNUSED_PARAMETER(NotUsed); unixCurrentTimeInt64(0, &i); *prNow = i/86400000.0; return 0; } /* ** We added the xGetLastError() method with the intention of providing ** better low-level error messages when operating-system problems come up ** during SQLite operation. But so far, none of that has been implemented ** in the core. So this routine is never called. For now, it is merely ** a place-holder. */ static int unixGetLastError(sqlite3_vfs *NotUsed, int NotUsed2, char *NotUsed3){ UNUSED_PARAMETER(NotUsed); UNUSED_PARAMETER(NotUsed2); UNUSED_PARAMETER(NotUsed3); return 0; } /* ************************ End of sqlite3_vfs methods *************************** ******************************************************************************/ /****************************************************************************** ************************** Begin Proxy Locking ******************************** |
︙ | ︙ | |||
4747 4748 4749 4750 4751 4752 4753 | #ifdef LOCKPROXYDIR len = strlcpy(lPath, LOCKPROXYDIR, maxLen); #else # ifdef _CS_DARWIN_USER_TEMP_DIR { if( !confstr(_CS_DARWIN_USER_TEMP_DIR, lPath, maxLen) ){ | | | | | | 5148 5149 5150 5151 5152 5153 5154 5155 5156 5157 5158 5159 5160 5161 5162 5163 5164 5165 5166 5167 5168 5169 5170 5171 5172 5173 5174 5175 5176 5177 5178 5179 5180 5181 5182 5183 5184 5185 | #ifdef LOCKPROXYDIR len = strlcpy(lPath, LOCKPROXYDIR, maxLen); #else # ifdef _CS_DARWIN_USER_TEMP_DIR { if( !confstr(_CS_DARWIN_USER_TEMP_DIR, lPath, maxLen) ){ OSTRACE(("GETLOCKPATH failed %s errno=%d pid=%d\n", lPath, errno, getpid())); return SQLITE_IOERR_LOCK; } len = strlcat(lPath, "sqliteplocks", maxLen); } # else len = strlcpy(lPath, "/tmp/", maxLen); # endif #endif if( lPath[len-1]!='/' ){ len = strlcat(lPath, "/", maxLen); } /* transform the db path to a unique cache name */ dbLen = (int)strlen(dbPath); for( i=0; i<dbLen && (i+len+7)<(int)maxLen; i++){ char c = dbPath[i]; lPath[i+len] = (c=='/')?'_':c; } lPath[i+len]='\0'; strlcat(lPath, ":auto:", maxLen); OSTRACE(("GETLOCKPATH proxy lock path=%s pid=%d\n", lPath, getpid())); return SQLITE_OK; } /* ** Creates the lock file and any missing directories in lockPath */ static int proxyCreateLockPath(const char *lockPath){ |
︙ | ︙ | |||
4795 4796 4797 4798 4799 4800 4801 | /* only mkdir if leaf dir != "." or "/" or ".." */ if( i-start>2 || (i-start==1 && buf[start] != '.' && buf[start] != '/') || (i-start==2 && buf[start] != '.' && buf[start+1] != '.') ){ buf[i]='\0'; if( mkdir(buf, SQLITE_DEFAULT_PROXYDIR_PERMISSIONS) ){ int err=errno; if( err!=EEXIST ) { | | | | | 5196 5197 5198 5199 5200 5201 5202 5203 5204 5205 5206 5207 5208 5209 5210 5211 5212 5213 5214 5215 5216 5217 5218 5219 5220 5221 | /* only mkdir if leaf dir != "." or "/" or ".." */ if( i-start>2 || (i-start==1 && buf[start] != '.' && buf[start] != '/') || (i-start==2 && buf[start] != '.' && buf[start+1] != '.') ){ buf[i]='\0'; if( mkdir(buf, SQLITE_DEFAULT_PROXYDIR_PERMISSIONS) ){ int err=errno; if( err!=EEXIST ) { OSTRACE(("CREATELOCKPATH FAILED creating %s, " "'%s' proxy lock path=%s pid=%d\n", buf, strerror(err), lockPath, getpid())); return err; } } } start=i+1; } buf[i] = lockPath[i]; } OSTRACE(("CREATELOCKPATH proxy lock path=%s pid=%d\n", lockPath, getpid())); return 0; } /* ** Create a new VFS file descriptor (stored in memory obtained from ** sqlite3_malloc) and open the file named "path" in the file descriptor. ** |
︙ | ︙ | |||
4905 4906 4907 4908 4909 4910 4911 4912 4913 4914 4915 4916 4917 4918 | #ifdef SQLITE_TEST /* simulate multiple hosts by creating unique hostid file paths */ int sqlite3_hostid_num = 0; #endif #define PROXY_HOSTIDLEN 16 /* conch file host id length */ /* get the host ID via gethostuuid(), pHostID must point to PROXY_HOSTIDLEN ** bytes of writable memory. */ static int proxyGetHostID(unsigned char *pHostID, int *pError){ struct timespec timeout = {1, 0}; /* 1 sec timeout */ assert(PROXY_HOSTIDLEN == sizeof(uuid_t)); | > > > | 5306 5307 5308 5309 5310 5311 5312 5313 5314 5315 5316 5317 5318 5319 5320 5321 5322 | #ifdef SQLITE_TEST /* simulate multiple hosts by creating unique hostid file paths */ int sqlite3_hostid_num = 0; #endif #define PROXY_HOSTIDLEN 16 /* conch file host id length */ /* Not always defined in the headers as it ought to be */ extern int gethostuuid(uuid_t id, const struct timespec *wait); /* get the host ID via gethostuuid(), pHostID must point to PROXY_HOSTIDLEN ** bytes of writable memory. */ static int proxyGetHostID(unsigned char *pHostID, int *pError){ struct timespec timeout = {1, 0}; /* 1 sec timeout */ assert(PROXY_HOSTIDLEN == sizeof(uuid_t)); |
︙ | ︙ | |||
4954 4955 4956 4957 4958 4959 4960 4961 4962 4963 4964 4965 4966 4967 4968 4969 4970 4971 4972 4973 4974 4975 4976 4977 4978 4979 4980 | char buf[PROXY_MAXCONCHLEN]; char *cPath = pCtx->conchFilePath; size_t readLen = 0; size_t pathLen = 0; char errmsg[64] = ""; int fd = -1; int rc = -1; /* create a new path by replace the trailing '-conch' with '-break' */ pathLen = strlcpy(tPath, cPath, MAXPATHLEN); if( pathLen>MAXPATHLEN || pathLen<6 || (strlcpy(&tPath[pathLen-5], "break", 6) != 5) ){ sprintf(errmsg, "path error (len %d)", (int)pathLen); goto end_breaklock; } /* read the conch content */ readLen = pread(conchFile->h, buf, PROXY_MAXCONCHLEN, 0); if( readLen<PROXY_PATHINDEX ){ sprintf(errmsg, "read error (len %d)", (int)readLen); goto end_breaklock; } /* write it out to the temporary break file */ fd = open(tPath, (O_RDWR|O_CREAT|O_EXCL), SQLITE_DEFAULT_FILE_PERMISSIONS); if( fd<0 ){ sprintf(errmsg, "create failed (%d)", errno); goto end_breaklock; } | > | | 5358 5359 5360 5361 5362 5363 5364 5365 5366 5367 5368 5369 5370 5371 5372 5373 5374 5375 5376 5377 5378 5379 5380 5381 5382 5383 5384 5385 5386 5387 5388 5389 5390 5391 5392 5393 | char buf[PROXY_MAXCONCHLEN]; char *cPath = pCtx->conchFilePath; size_t readLen = 0; size_t pathLen = 0; char errmsg[64] = ""; int fd = -1; int rc = -1; UNUSED_PARAMETER(myHostID); /* create a new path by replace the trailing '-conch' with '-break' */ pathLen = strlcpy(tPath, cPath, MAXPATHLEN); if( pathLen>MAXPATHLEN || pathLen<6 || (strlcpy(&tPath[pathLen-5], "break", 6) != 5) ){ sprintf(errmsg, "path error (len %d)", (int)pathLen); goto end_breaklock; } /* read the conch content */ readLen = pread(conchFile->h, buf, PROXY_MAXCONCHLEN, 0); if( readLen<PROXY_PATHINDEX ){ sprintf(errmsg, "read error (len %d)", (int)readLen); goto end_breaklock; } /* write it out to the temporary break file */ fd = open(tPath, (O_RDWR|O_CREAT|O_EXCL), SQLITE_DEFAULT_FILE_PERMISSIONS); if( fd<0 ){ sprintf(errmsg, "create failed (%d)", errno); goto end_breaklock; } if( pwrite(fd, buf, readLen, 0) != (ssize_t)readLen ){ sprintf(errmsg, "write failed (%d)", errno); goto end_breaklock; } if( rename(tPath, cPath) ){ sprintf(errmsg, "rename failed (%d)", errno); goto end_breaklock; } |
︙ | ︙ | |||
5098 5099 5100 5101 5102 5103 5104 | int rc = SQLITE_OK; int createConch = 0; int hostIdMatch = 0; int readLen = 0; int tryOldLockPath = 0; int forceNewLockPath = 0; | | | | 5503 5504 5505 5506 5507 5508 5509 5510 5511 5512 5513 5514 5515 5516 5517 5518 | int rc = SQLITE_OK; int createConch = 0; int hostIdMatch = 0; int readLen = 0; int tryOldLockPath = 0; int forceNewLockPath = 0; OSTRACE(("TAKECONCH %d for %s pid=%d\n", conchFile->h, (pCtx->lockProxyPath ? pCtx->lockProxyPath : ":auto:"), getpid())); rc = proxyGetHostID(myHostID, &pError); if( (rc&0xff)==SQLITE_IOERR ){ pFile->lastErrno = pError; goto end_takeconch; } rc = proxyConchLock(pFile, myHostID, SHARED_LOCK); |
︙ | ︙ | |||
5179 5180 5181 5182 5183 5184 5185 | /* update conch with host and path (this will fail if other process ** has a shared lock already), if the host id matches, use the big ** stick. */ futimes(conchFile->h, NULL); if( hostIdMatch && !createConch ){ | | | 5584 5585 5586 5587 5588 5589 5590 5591 5592 5593 5594 5595 5596 5597 5598 | /* update conch with host and path (this will fail if other process ** has a shared lock already), if the host id matches, use the big ** stick. */ futimes(conchFile->h, NULL); if( hostIdMatch && !createConch ){ if( conchFile->pInode && conchFile->pInode->nShared>1 ){ /* We are trying for an exclusive lock but another thread in this ** same process is still holding a shared lock. */ rc = SQLITE_BUSY; } else { rc = proxyConchLock(pFile, myHostID, EXCLUSIVE_LOCK); } }else{ |
︙ | ︙ | |||
5235 5236 5237 5238 5239 5240 5241 | #endif } } } conchFile->pMethod->xUnlock((sqlite3_file*)conchFile, SHARED_LOCK); end_takeconch: | | | | 5640 5641 5642 5643 5644 5645 5646 5647 5648 5649 5650 5651 5652 5653 5654 5655 5656 5657 5658 5659 5660 5661 5662 5663 5664 5665 5666 5667 5668 5669 | #endif } } } conchFile->pMethod->xUnlock((sqlite3_file*)conchFile, SHARED_LOCK); end_takeconch: OSTRACE(("TRANSPROXY: CLOSE %d\n", pFile->h)); if( rc==SQLITE_OK && pFile->openFlags ){ if( pFile->h>=0 ){ #ifdef STRICT_CLOSE_ERROR if( close(pFile->h) ){ pFile->lastErrno = errno; return SQLITE_IOERR_CLOSE; } #else close(pFile->h); /* silently leak fd if fail */ #endif } pFile->h = -1; int fd = open(pCtx->dbPath, pFile->openFlags, SQLITE_DEFAULT_FILE_PERMISSIONS); OSTRACE(("TRANSPROXY: OPEN %d\n", fd)); if( fd>=0 ){ pFile->h = fd; }else{ rc=SQLITE_CANTOPEN_BKPT; /* SQLITE_BUSY? proxyTakeConch called during locking */ } } |
︙ | ︙ | |||
5292 5293 5294 5295 5296 5297 5298 | afpLockingContext *afpCtx; afpCtx = (afpLockingContext *)pCtx->lockProxy->lockingContext; afpCtx->dbPath = pCtx->lockProxyPath; } } else { conchFile->pMethod->xUnlock((sqlite3_file*)conchFile, NO_LOCK); } | | > | > | | | | | | 5697 5698 5699 5700 5701 5702 5703 5704 5705 5706 5707 5708 5709 5710 5711 5712 5713 5714 5715 5716 5717 5718 5719 5720 5721 5722 5723 5724 5725 5726 5727 5728 5729 5730 5731 5732 5733 5734 5735 5736 5737 | afpLockingContext *afpCtx; afpCtx = (afpLockingContext *)pCtx->lockProxy->lockingContext; afpCtx->dbPath = pCtx->lockProxyPath; } } else { conchFile->pMethod->xUnlock((sqlite3_file*)conchFile, NO_LOCK); } OSTRACE(("TAKECONCH %d %s\n", conchFile->h, rc==SQLITE_OK?"ok":"failed")); return rc; } while (1); /* in case we need to retry the :auto: lock file - ** we should never get here except via the 'continue' call. */ } } /* ** If pFile holds a lock on a conch file, then release that lock. */ static int proxyReleaseConch(unixFile *pFile){ int rc = SQLITE_OK; /* Subroutine return code */ proxyLockingContext *pCtx; /* The locking context for the proxy lock */ unixFile *conchFile; /* Name of the conch file */ pCtx = (proxyLockingContext *)pFile->lockingContext; conchFile = pCtx->conchFile; OSTRACE(("RELEASECONCH %d for %s pid=%d\n", conchFile->h, (pCtx->lockProxyPath ? pCtx->lockProxyPath : ":auto:"), getpid())); if( pCtx->conchHeld>0 ){ rc = conchFile->pMethod->xUnlock((sqlite3_file*)conchFile, NO_LOCK); } pCtx->conchHeld = 0; OSTRACE(("RELEASECONCH %d %s\n", conchFile->h, (rc==SQLITE_OK ? "ok" : "failed"))); return rc; } /* ** Given the name of a database file, compute the name of its conch file. ** Store the conch filename in memory obtained from sqlite3_malloc(). ** Make *pConchPath point to the new name. Return SQLITE_OK on success |
︙ | ︙ | |||
5373 5374 5375 5376 5377 5378 5379 | ** the local lock file path */ static int switchLockProxyPath(unixFile *pFile, const char *path) { proxyLockingContext *pCtx = (proxyLockingContext*)pFile->lockingContext; char *oldPath = pCtx->lockProxyPath; int rc = SQLITE_OK; | | | 5780 5781 5782 5783 5784 5785 5786 5787 5788 5789 5790 5791 5792 5793 5794 | ** the local lock file path */ static int switchLockProxyPath(unixFile *pFile, const char *path) { proxyLockingContext *pCtx = (proxyLockingContext*)pFile->lockingContext; char *oldPath = pCtx->lockProxyPath; int rc = SQLITE_OK; if( pFile->eFileLock!=NO_LOCK ){ return SQLITE_BUSY; } /* nothing to do if the path is NULL, :auto: or matches the existing path */ if( !path || path[0]=='\0' || !strcmp(path, ":auto:") || (oldPath && !strncmp(oldPath, path, MAXPATHLEN)) ){ return SQLITE_OK; |
︙ | ︙ | |||
5440 5441 5442 5443 5444 5445 5446 | */ static int proxyTransformUnixFile(unixFile *pFile, const char *path) { proxyLockingContext *pCtx; char dbPath[MAXPATHLEN+1]; /* Name of the database file */ char *lockPath=NULL; int rc = SQLITE_OK; | | | | | 5847 5848 5849 5850 5851 5852 5853 5854 5855 5856 5857 5858 5859 5860 5861 5862 5863 5864 5865 5866 5867 5868 5869 5870 5871 5872 | */ static int proxyTransformUnixFile(unixFile *pFile, const char *path) { proxyLockingContext *pCtx; char dbPath[MAXPATHLEN+1]; /* Name of the database file */ char *lockPath=NULL; int rc = SQLITE_OK; if( pFile->eFileLock!=NO_LOCK ){ return SQLITE_BUSY; } proxyGetDbPathForUnixFile(pFile, dbPath); if( !path || path[0]=='\0' || !strcmp(path, ":auto:") ){ lockPath=NULL; }else{ lockPath=(char *)path; } OSTRACE(("TRANSPROXY %d for %s pid=%d\n", pFile->h, (lockPath ? lockPath : ":auto:"), getpid())); pCtx = sqlite3_malloc( sizeof(*pCtx) ); if( pCtx==0 ){ return SQLITE_NOMEM; } memset(pCtx, 0, sizeof(*pCtx)); |
︙ | ︙ | |||
5511 5512 5513 5514 5515 5516 5517 | pCtx->conchFile->pMethod->xClose((sqlite3_file *)pCtx->conchFile); sqlite3_free(pCtx->conchFile); } sqlite3_free(pCtx->lockProxyPath); sqlite3_free(pCtx->conchFilePath); sqlite3_free(pCtx); } | | | | 5918 5919 5920 5921 5922 5923 5924 5925 5926 5927 5928 5929 5930 5931 5932 5933 | pCtx->conchFile->pMethod->xClose((sqlite3_file *)pCtx->conchFile); sqlite3_free(pCtx->conchFile); } sqlite3_free(pCtx->lockProxyPath); sqlite3_free(pCtx->conchFilePath); sqlite3_free(pCtx); } OSTRACE(("TRANSPROXY %d %s\n", pFile->h, (rc==SQLITE_OK ? "ok" : "failed"))); return rc; } /* ** This routine handles sqlite3_file_control() calls that are specific ** to proxy locking. |
︙ | ︙ | |||
5607 5608 5609 5610 5611 5612 5613 | pResOut=0; } } return rc; } /* | | | 6014 6015 6016 6017 6018 6019 6020 6021 6022 6023 6024 6025 6026 6027 6028 | pResOut=0; } } return rc; } /* ** Lock the file with the lock specified by parameter eFileLock - one ** of the following: ** ** (1) SHARED_LOCK ** (2) RESERVED_LOCK ** (3) PENDING_LOCK ** (4) EXCLUSIVE_LOCK ** |
︙ | ︙ | |||
5630 5631 5632 5633 5634 5635 5636 | ** SHARED -> (PENDING) -> EXCLUSIVE ** RESERVED -> (PENDING) -> EXCLUSIVE ** PENDING -> EXCLUSIVE ** ** This routine will only increase a lock. Use the sqlite3OsUnlock() ** routine to lower a locking level. */ | | | | | | | | | 6037 6038 6039 6040 6041 6042 6043 6044 6045 6046 6047 6048 6049 6050 6051 6052 6053 6054 6055 6056 6057 6058 6059 6060 6061 6062 6063 6064 6065 6066 6067 6068 6069 6070 6071 6072 6073 6074 6075 6076 6077 6078 6079 6080 6081 6082 6083 | ** SHARED -> (PENDING) -> EXCLUSIVE ** RESERVED -> (PENDING) -> EXCLUSIVE ** PENDING -> EXCLUSIVE ** ** This routine will only increase a lock. Use the sqlite3OsUnlock() ** routine to lower a locking level. */ static int proxyLock(sqlite3_file *id, int eFileLock) { unixFile *pFile = (unixFile*)id; int rc = proxyTakeConch(pFile); if( rc==SQLITE_OK ){ proxyLockingContext *pCtx = (proxyLockingContext *)pFile->lockingContext; if( pCtx->conchHeld>0 ){ unixFile *proxy = pCtx->lockProxy; rc = proxy->pMethod->xLock((sqlite3_file*)proxy, eFileLock); pFile->eFileLock = proxy->eFileLock; }else{ /* conchHeld < 0 is lockless */ } } return rc; } /* ** Lower the locking level on file descriptor pFile to eFileLock. eFileLock ** must be either NO_LOCK or SHARED_LOCK. ** ** If the locking level of the file descriptor is already at or below ** the requested locking level, this routine is a no-op. */ static int proxyUnlock(sqlite3_file *id, int eFileLock) { unixFile *pFile = (unixFile*)id; int rc = proxyTakeConch(pFile); if( rc==SQLITE_OK ){ proxyLockingContext *pCtx = (proxyLockingContext *)pFile->lockingContext; if( pCtx->conchHeld>0 ){ unixFile *proxy = pCtx->lockProxy; rc = proxy->pMethod->xUnlock((sqlite3_file*)proxy, eFileLock); pFile->eFileLock = proxy->eFileLock; }else{ /* conchHeld < 0 is lockless */ } } return rc; } |
︙ | ︙ | |||
5757 5758 5759 5760 5761 5762 5763 | ** Most finders simply return a pointer to a fixed sqlite3_io_methods ** object. But the "autolockIoFinder" available on MacOSX does a little ** more than that; it looks at the filesystem type that hosts the ** database file and tries to choose an locking method appropriate for ** that filesystem time. */ #define UNIXVFS(VFSNAME, FINDER) { \ | | | > | 6164 6165 6166 6167 6168 6169 6170 6171 6172 6173 6174 6175 6176 6177 6178 6179 6180 6181 6182 6183 6184 6185 6186 6187 6188 6189 6190 6191 6192 6193 6194 6195 6196 | ** Most finders simply return a pointer to a fixed sqlite3_io_methods ** object. But the "autolockIoFinder" available on MacOSX does a little ** more than that; it looks at the filesystem type that hosts the ** database file and tries to choose an locking method appropriate for ** that filesystem time. */ #define UNIXVFS(VFSNAME, FINDER) { \ 2, /* iVersion */ \ sizeof(unixFile), /* szOsFile */ \ MAX_PATHNAME, /* mxPathname */ \ 0, /* pNext */ \ VFSNAME, /* zName */ \ (void*)&FINDER, /* pAppData */ \ unixOpen, /* xOpen */ \ unixDelete, /* xDelete */ \ unixAccess, /* xAccess */ \ unixFullPathname, /* xFullPathname */ \ unixDlOpen, /* xDlOpen */ \ unixDlError, /* xDlError */ \ unixDlSym, /* xDlSym */ \ unixDlClose, /* xDlClose */ \ unixRandomness, /* xRandomness */ \ unixSleep, /* xSleep */ \ unixCurrentTime, /* xCurrentTime */ \ unixGetLastError, /* xGetLastError */ \ unixCurrentTimeInt64, /* xCurrentTimeInt64 */ \ } /* ** All default VFSes for unix are contained in the following array. ** ** Note that the sqlite3_vfs.pNext field of the VFS object is modified ** by the SQLite core when the VFS is registered. So the following |
︙ | ︙ |
Changes to src/os_win.c.
︙ | ︙ | |||
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 | ** reduced API. */ #if SQLITE_OS_WINCE # define AreFileApisANSI() 1 # define FormatMessageW(a,b,c,d,e,f,g) 0 #endif /* ** WinCE lacks native support for file locking so we have to fake it ** with some code of our own. */ #if SQLITE_OS_WINCE typedef struct winceLock { int nReaders; /* Number of reader locks obtained */ BOOL bPending; /* Indicates a pending lock has been obtained */ BOOL bReserved; /* Indicates a reserved lock has been obtained */ BOOL bExclusive; /* Indicates an exclusive lock has been obtained */ } winceLock; #endif /* ** The winFile structure is a subclass of sqlite3_file* specific to the win32 ** portability layer. */ typedef struct winFile winFile; struct winFile { | > > > > | > > > | 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 | ** reduced API. */ #if SQLITE_OS_WINCE # define AreFileApisANSI() 1 # define FormatMessageW(a,b,c,d,e,f,g) 0 #endif /* Forward references */ typedef struct winShm winShm; /* A connection to shared-memory */ typedef struct winShmNode winShmNode; /* A region of shared-memory */ /* ** WinCE lacks native support for file locking so we have to fake it ** with some code of our own. */ #if SQLITE_OS_WINCE typedef struct winceLock { int nReaders; /* Number of reader locks obtained */ BOOL bPending; /* Indicates a pending lock has been obtained */ BOOL bReserved; /* Indicates a reserved lock has been obtained */ BOOL bExclusive; /* Indicates an exclusive lock has been obtained */ } winceLock; #endif /* ** The winFile structure is a subclass of sqlite3_file* specific to the win32 ** portability layer. */ typedef struct winFile winFile; struct winFile { const sqlite3_io_methods *pMethod; /*** Must be first ***/ sqlite3_vfs *pVfs; /* The VFS used to open this file */ HANDLE h; /* Handle for accessing the file */ unsigned char locktype; /* Type of lock currently held on this file */ short sharedLockByte; /* Randomly chosen byte used as a shared lock */ DWORD lastErrno; /* The Windows errno from the last I/O error */ DWORD sectorSize; /* Sector size of the device file is on */ winShm *pShm; /* Instance of shared memory on this file */ const char *zPath; /* Full pathname of this file */ #if SQLITE_OS_WINCE WCHAR *zDeleteOnClose; /* Name of file to delete when closing */ HANDLE hMutex; /* Mutex used to control access to shared lock */ HANDLE hShared; /* Shared memory segment used for locking */ winceLock local; /* Locks obtained by this instance of winFile */ winceLock *shared; /* Global shared lock memory for the file */ #endif |
︙ | ︙ | |||
625 626 627 628 629 630 631 | */ #define MX_CLOSE_ATTEMPT 3 static int winClose(sqlite3_file *id){ int rc, cnt = 0; winFile *pFile = (winFile*)id; assert( id!=0 ); | > | > > | 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 | */ #define MX_CLOSE_ATTEMPT 3 static int winClose(sqlite3_file *id){ int rc, cnt = 0; winFile *pFile = (winFile*)id; assert( id!=0 ); assert( pFile->pShm==0 ); OSTRACE(("CLOSE %d\n", pFile->h)); do{ rc = CloseHandle(pFile->h); /* SimulateIOError( rc=0; cnt=MX_CLOSE_ATTEMPT; ); */ }while( rc==0 && ++cnt < MX_CLOSE_ATTEMPT && (Sleep(100), 1) ); #if SQLITE_OS_WINCE #define WINCE_DELETION_ATTEMPTS 3 winceDestroyLock(pFile); if( pFile->zDeleteOnClose ){ int cnt = 0; while( DeleteFileW(pFile->zDeleteOnClose)==0 && GetFileAttributesW(pFile->zDeleteOnClose)!=0xffffffff && cnt++ < WINCE_DELETION_ATTEMPTS ){ Sleep(100); /* Wait a little before trying again */ } free(pFile->zDeleteOnClose); } #endif OSTRACE(("CLOSE %d %s\n", pFile->h, rc ? "ok" : "failed")); OpenCounter(-1); return rc ? SQLITE_OK : SQLITE_IOERR; } /* ** Some microsoft compilers lack this definition. */ |
︙ | ︙ | |||
675 676 677 678 679 680 681 | DWORD rc; winFile *pFile = (winFile*)id; DWORD error; DWORD got; assert( id!=0 ); SimulateIOError(return SQLITE_IOERR_READ); | | | 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 | DWORD rc; winFile *pFile = (winFile*)id; DWORD error; DWORD got; assert( id!=0 ); SimulateIOError(return SQLITE_IOERR_READ); OSTRACE(("READ %d lock=%d\n", pFile->h, pFile->locktype)); rc = SetFilePointer(pFile->h, lowerBits, &upperBits, FILE_BEGIN); if( rc==INVALID_SET_FILE_POINTER && (error=GetLastError())!=NO_ERROR ){ pFile->lastErrno = error; return SQLITE_FULL; } if( !ReadFile(pFile->h, pBuf, amt, &got, 0) ){ pFile->lastErrno = GetLastError(); |
︙ | ︙ | |||
714 715 716 717 718 719 720 | winFile *pFile = (winFile*)id; DWORD error; DWORD wrote = 0; assert( id!=0 ); SimulateIOError(return SQLITE_IOERR_WRITE); SimulateDiskfullError(return SQLITE_FULL); | | > | > > > > | > > > | > | | | | < | | > | | < < < > > > > > > > > > > > > > > > > | | 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 | winFile *pFile = (winFile*)id; DWORD error; DWORD wrote = 0; assert( id!=0 ); SimulateIOError(return SQLITE_IOERR_WRITE); SimulateDiskfullError(return SQLITE_FULL); OSTRACE(("WRITE %d lock=%d\n", pFile->h, pFile->locktype)); rc = SetFilePointer(pFile->h, lowerBits, &upperBits, FILE_BEGIN); if( rc==INVALID_SET_FILE_POINTER && (error=GetLastError())!=NO_ERROR ){ pFile->lastErrno = error; if( pFile->lastErrno==ERROR_HANDLE_DISK_FULL ){ return SQLITE_FULL; }else{ return SQLITE_IOERR_WRITE; } } assert( amt>0 ); while( amt>0 && (rc = WriteFile(pFile->h, pBuf, amt, &wrote, 0))!=0 && wrote>0 ){ amt -= wrote; pBuf = &((char*)pBuf)[wrote]; } if( !rc || amt>(int)wrote ){ pFile->lastErrno = GetLastError(); if( pFile->lastErrno==ERROR_HANDLE_DISK_FULL ){ return SQLITE_FULL; }else{ return SQLITE_IOERR_WRITE; } } return SQLITE_OK; } /* ** Truncate an open file to a specified size */ static int winTruncate(sqlite3_file *id, sqlite3_int64 nByte){ LONG upperBits = (LONG)((nByte>>32) & 0x7fffffff); LONG lowerBits = (LONG)(nByte & 0xffffffff); DWORD dwRet; winFile *pFile = (winFile*)id; DWORD error; int rc = SQLITE_OK; assert( id!=0 ); OSTRACE(("TRUNCATE %d %lld\n", pFile->h, nByte)); SimulateIOError(return SQLITE_IOERR_TRUNCATE); dwRet = SetFilePointer(pFile->h, lowerBits, &upperBits, FILE_BEGIN); if( dwRet==INVALID_SET_FILE_POINTER && (error=GetLastError())!=NO_ERROR ){ pFile->lastErrno = error; rc = SQLITE_IOERR_TRUNCATE; /* SetEndOfFile will fail if nByte is negative */ }else if( !SetEndOfFile(pFile->h) ){ pFile->lastErrno = GetLastError(); rc = SQLITE_IOERR_TRUNCATE; } OSTRACE(("TRUNCATE %d %lld %s\n", pFile->h, nByte, rc==SQLITE_OK ? "ok" : "failed")); return rc; } #ifdef SQLITE_TEST /* ** Count the number of fullsyncs and normal syncs. This is used to test ** that syncs and fullsyncs are occuring at the right times. */ int sqlite3_sync_count = 0; int sqlite3_fullsync_count = 0; #endif /* ** Make sure all writes to a particular file are committed to disk. */ static int winSync(sqlite3_file *id, int flags){ #if !defined(NDEBUG) || !defined(SQLITE_NO_SYNC) || defined(SQLITE_DEBUG) winFile *pFile = (winFile*)id; #else UNUSED_PARAMETER(id); #endif assert( pFile ); /* Check that one of SQLITE_SYNC_NORMAL or FULL was passed */ assert((flags&0x0F)==SQLITE_SYNC_NORMAL || (flags&0x0F)==SQLITE_SYNC_FULL ); OSTRACE(("SYNC %d lock=%d\n", pFile->h, pFile->locktype)); #ifndef SQLITE_TEST UNUSED_PARAMETER(flags); #else if( flags & SQLITE_SYNC_FULL ){ sqlite3_fullsync_count++; } sqlite3_sync_count++; #endif /* Unix cannot, but some systems may return SQLITE_FULL from here. This ** line is to test that doing so does not cause any problems. */ SimulateDiskfullError( return SQLITE_FULL ); SimulateIOError( return SQLITE_IOERR; ); /* If we compiled with the SQLITE_NO_SYNC flag, then syncing is a ** no-op */ #ifdef SQLITE_NO_SYNC return SQLITE_OK; #else if( FlushFileBuffers(pFile->h) ){ return SQLITE_OK; }else{ pFile->lastErrno = GetLastError(); return SQLITE_IOERR; } |
︙ | ︙ | |||
920 921 922 923 924 925 926 | int res = 1; /* Result of a windows lock call */ int newLocktype; /* Set pFile->locktype to this value before exiting */ int gotPendingLock = 0;/* True if we acquired a PENDING lock this time */ winFile *pFile = (winFile*)id; DWORD error = NO_ERROR; assert( id!=0 ); | | | | 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 | int res = 1; /* Result of a windows lock call */ int newLocktype; /* Set pFile->locktype to this value before exiting */ int gotPendingLock = 0;/* True if we acquired a PENDING lock this time */ winFile *pFile = (winFile*)id; DWORD error = NO_ERROR; assert( id!=0 ); OSTRACE(("LOCK %d %d was %d(%d)\n", pFile->h, locktype, pFile->locktype, pFile->sharedLockByte)); /* If there is already a lock of this type or more restrictive on the ** OsFile, do nothing. Don't use the end_lock: exit path, as ** sqlite3OsEnterMutex() hasn't been called yet. */ if( pFile->locktype>=locktype ){ return SQLITE_OK; |
︙ | ︙ | |||
951 952 953 954 955 956 957 | && (pFile->locktype==RESERVED_LOCK)) ){ int cnt = 3; while( cnt-->0 && (res = LockFile(pFile->h, PENDING_BYTE, 0, 1, 0))==0 ){ /* Try 3 times to get the pending lock. The pending lock might be ** held by another reader process who will release it momentarily. */ | | | 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 | && (pFile->locktype==RESERVED_LOCK)) ){ int cnt = 3; while( cnt-->0 && (res = LockFile(pFile->h, PENDING_BYTE, 0, 1, 0))==0 ){ /* Try 3 times to get the pending lock. The pending lock might be ** held by another reader process who will release it momentarily. */ OSTRACE(("could not get a PENDING lock. cnt=%d\n", cnt)); Sleep(1); } gotPendingLock = res; if( !res ){ error = GetLastError(); } } |
︙ | ︙ | |||
996 997 998 999 1000 1001 1002 | } /* Acquire an EXCLUSIVE lock */ if( locktype==EXCLUSIVE_LOCK && res ){ assert( pFile->locktype>=SHARED_LOCK ); res = unlockReadLock(pFile); | | | | | > > | | | 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 | } /* Acquire an EXCLUSIVE lock */ if( locktype==EXCLUSIVE_LOCK && res ){ assert( pFile->locktype>=SHARED_LOCK ); res = unlockReadLock(pFile); OSTRACE(("unreadlock = %d\n", res)); res = LockFile(pFile->h, SHARED_FIRST, 0, SHARED_SIZE, 0); if( res ){ newLocktype = EXCLUSIVE_LOCK; }else{ error = GetLastError(); OSTRACE(("error-code = %d\n", error)); getReadLock(pFile); } } /* If we are holding a PENDING lock that ought to be released, then ** release it now. */ if( gotPendingLock && locktype==SHARED_LOCK ){ UnlockFile(pFile->h, PENDING_BYTE, 0, 1, 0); } /* Update the state of the lock has held in the file descriptor then ** return the appropriate result code. */ if( res ){ rc = SQLITE_OK; }else{ OSTRACE(("LOCK FAILED %d trying for %d but got %d\n", pFile->h, locktype, newLocktype)); pFile->lastErrno = error; rc = SQLITE_BUSY; } pFile->locktype = (u8)newLocktype; return rc; } /* ** This routine checks if there is a RESERVED lock held on the specified ** file by this or any other process. If such a lock is held, return ** non-zero, otherwise zero. */ static int winCheckReservedLock(sqlite3_file *id, int *pResOut){ int rc; winFile *pFile = (winFile*)id; SimulateIOError( return SQLITE_IOERR_CHECKRESERVEDLOCK; ); assert( id!=0 ); if( pFile->locktype>=RESERVED_LOCK ){ rc = 1; OSTRACE(("TEST WR-LOCK %d %d (local)\n", pFile->h, rc)); }else{ rc = LockFile(pFile->h, RESERVED_BYTE, 0, 1, 0); if( rc ){ UnlockFile(pFile->h, RESERVED_BYTE, 0, 1, 0); } rc = !rc; OSTRACE(("TEST WR-LOCK %d %d (remote)\n", pFile->h, rc)); } *pResOut = rc; return SQLITE_OK; } /* ** Lower the locking level on file descriptor id to locktype. locktype |
︙ | ︙ | |||
1071 1072 1073 1074 1075 1076 1077 | */ static int winUnlock(sqlite3_file *id, int locktype){ int type; winFile *pFile = (winFile*)id; int rc = SQLITE_OK; assert( pFile!=0 ); assert( locktype<=SHARED_LOCK ); | | | | 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 | */ static int winUnlock(sqlite3_file *id, int locktype){ int type; winFile *pFile = (winFile*)id; int rc = SQLITE_OK; assert( pFile!=0 ); assert( locktype<=SHARED_LOCK ); OSTRACE(("UNLOCK %d to %d was %d(%d)\n", pFile->h, locktype, pFile->locktype, pFile->sharedLockByte)); type = pFile->locktype; if( type>=EXCLUSIVE_LOCK ){ UnlockFile(pFile->h, SHARED_FIRST, 0, SHARED_SIZE, 0); if( locktype==SHARED_LOCK && !getReadLock(pFile) ){ /* This should never happen. We should always be able to ** reacquire the read lock */ rc = SQLITE_IOERR_UNLOCK; |
︙ | ︙ | |||
1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 | return SQLITE_OK; } case SQLITE_FCNTL_SIZE_HINT: { sqlite3_int64 sz = *(sqlite3_int64*)pArg; winTruncate(id, sz); return SQLITE_OK; } } return SQLITE_ERROR; } /* ** Return the sector size in bytes of the underlying block device for ** the specified file. This is almost always 512 bytes, but may be | > > > > > > > | 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 | return SQLITE_OK; } case SQLITE_FCNTL_SIZE_HINT: { sqlite3_int64 sz = *(sqlite3_int64*)pArg; winTruncate(id, sz); return SQLITE_OK; } case SQLITE_FCNTL_SIZE_HINT: { sqlite3_int64 sz = *(sqlite3_int64*)pArg; SimulateIOErrorBenign(1); winTruncate(id, sz); SimulateIOErrorBenign(0); return SQLITE_OK; } } return SQLITE_ERROR; } /* ** Return the sector size in bytes of the underlying block device for ** the specified file. This is almost always 512 bytes, but may be |
︙ | ︙ | |||
1137 1138 1139 1140 1141 1142 1143 | } /* ** Return a vector of device characteristics. */ static int winDeviceCharacteristics(sqlite3_file *id){ UNUSED_PARAMETER(id); | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | | | | | | | | | | | | > > > > | | | < > > | 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334 1335 1336 1337 1338 1339 1340 1341 1342 1343 1344 1345 1346 1347 1348 1349 1350 1351 1352 1353 1354 1355 1356 1357 1358 1359 1360 1361 1362 1363 1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 1375 1376 1377 1378 1379 1380 1381 1382 1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398 1399 1400 1401 1402 1403 1404 1405 1406 1407 1408 1409 1410 1411 1412 1413 1414 1415 1416 1417 1418 1419 1420 1421 1422 1423 1424 1425 1426 1427 1428 1429 1430 1431 1432 1433 1434 1435 1436 1437 1438 1439 1440 1441 1442 1443 1444 1445 1446 1447 1448 1449 1450 1451 1452 1453 1454 1455 1456 1457 1458 1459 1460 1461 1462 1463 1464 1465 1466 1467 1468 1469 1470 1471 1472 1473 1474 1475 1476 1477 1478 1479 1480 1481 1482 1483 1484 1485 1486 1487 1488 1489 1490 1491 1492 1493 1494 1495 1496 1497 1498 1499 1500 1501 1502 1503 1504 1505 1506 1507 1508 1509 1510 1511 1512 1513 1514 1515 1516 1517 1518 1519 1520 1521 1522 1523 1524 1525 1526 1527 1528 1529 1530 1531 1532 1533 1534 1535 1536 1537 1538 1539 1540 1541 1542 1543 1544 1545 1546 1547 1548 1549 1550 1551 1552 1553 1554 1555 1556 1557 1558 1559 1560 1561 1562 1563 1564 1565 1566 1567 1568 1569 1570 1571 1572 1573 1574 1575 1576 1577 1578 1579 1580 1581 1582 1583 1584 1585 1586 1587 1588 1589 1590 1591 1592 1593 1594 1595 1596 1597 1598 1599 1600 1601 1602 1603 1604 1605 1606 1607 1608 1609 1610 1611 1612 1613 1614 1615 1616 1617 1618 1619 1620 1621 1622 1623 1624 1625 1626 1627 1628 1629 1630 1631 1632 1633 1634 1635 1636 1637 1638 1639 1640 1641 1642 1643 1644 1645 1646 1647 1648 1649 1650 1651 1652 1653 1654 1655 1656 1657 1658 1659 1660 1661 1662 1663 1664 1665 1666 1667 1668 1669 1670 1671 1672 1673 1674 1675 1676 1677 1678 1679 1680 1681 1682 1683 1684 1685 1686 1687 1688 1689 1690 1691 1692 1693 1694 1695 1696 1697 1698 1699 1700 1701 1702 1703 1704 1705 1706 1707 1708 1709 1710 1711 1712 1713 1714 1715 1716 1717 1718 1719 1720 1721 1722 1723 1724 1725 1726 1727 1728 1729 1730 1731 1732 1733 1734 1735 1736 1737 1738 1739 1740 1741 1742 1743 1744 1745 1746 1747 1748 1749 1750 1751 1752 1753 1754 1755 1756 1757 1758 1759 1760 1761 1762 1763 1764 1765 1766 1767 1768 1769 1770 1771 1772 1773 1774 1775 1776 1777 1778 1779 1780 1781 1782 1783 1784 1785 1786 1787 1788 1789 1790 1791 1792 1793 1794 1795 1796 1797 1798 1799 1800 1801 1802 1803 1804 1805 1806 1807 1808 1809 1810 1811 1812 1813 1814 1815 1816 1817 1818 1819 1820 1821 1822 1823 1824 1825 1826 1827 1828 1829 1830 1831 1832 | } /* ** Return a vector of device characteristics. */ static int winDeviceCharacteristics(sqlite3_file *id){ UNUSED_PARAMETER(id); return SQLITE_IOCAP_UNDELETABLE_WHEN_OPEN; } #ifndef SQLITE_OMIT_WAL /* ** Helper functions to obtain and relinquish the global mutex. The ** global mutex is used to protect the winLockInfo objects used by ** this file, all of which may be shared by multiple threads. ** ** Function winShmMutexHeld() is used to assert() that the global mutex ** is held when required. This function is only used as part of assert() ** statements. e.g. ** ** winShmEnterMutex() ** assert( winShmMutexHeld() ); ** winShmLeaveMutex() */ static void winShmEnterMutex(void){ sqlite3_mutex_enter(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER)); } static void winShmLeaveMutex(void){ sqlite3_mutex_leave(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER)); } #ifdef SQLITE_DEBUG static int winShmMutexHeld(void) { return sqlite3_mutex_held(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER)); } #endif /* ** Object used to represent a single file opened and mmapped to provide ** shared memory. When multiple threads all reference the same ** log-summary, each thread has its own winFile object, but they all ** point to a single instance of this object. In other words, each ** log-summary is opened only once per process. ** ** winShmMutexHeld() must be true when creating or destroying ** this object or while reading or writing the following fields: ** ** nRef ** pNext ** ** The following fields are read-only after the object is created: ** ** fid ** zFilename ** ** Either winShmNode.mutex must be held or winShmNode.nRef==0 and ** winShmMutexHeld() is true when reading or writing any other field ** in this structure. ** */ struct winShmNode { sqlite3_mutex *mutex; /* Mutex to access this object */ char *zFilename; /* Name of the file */ winFile hFile; /* File handle from winOpen */ int szRegion; /* Size of shared-memory regions */ int nRegion; /* Size of array apRegion */ struct ShmRegion { HANDLE hMap; /* File handle from CreateFileMapping */ void *pMap; } *aRegion; DWORD lastErrno; /* The Windows errno from the last I/O error */ int nRef; /* Number of winShm objects pointing to this */ winShm *pFirst; /* All winShm objects pointing to this */ winShmNode *pNext; /* Next in list of all winShmNode objects */ #ifdef SQLITE_DEBUG u8 nextShmId; /* Next available winShm.id value */ #endif }; /* ** A global array of all winShmNode objects. ** ** The winShmMutexHeld() must be true while reading or writing this list. */ static winShmNode *winShmNodeList = 0; /* ** Structure used internally by this VFS to record the state of an ** open shared memory connection. ** ** The following fields are initialized when this object is created and ** are read-only thereafter: ** ** winShm.pShmNode ** winShm.id ** ** All other fields are read/write. The winShm.pShmNode->mutex must be held ** while accessing any read/write fields. */ struct winShm { winShmNode *pShmNode; /* The underlying winShmNode object */ winShm *pNext; /* Next winShm with the same winShmNode */ u8 hasMutex; /* True if holding the winShmNode mutex */ u16 sharedMask; /* Mask of shared locks held */ u16 exclMask; /* Mask of exclusive locks held */ #ifdef SQLITE_DEBUG u8 id; /* Id of this connection with its winShmNode */ #endif }; /* ** Constants used for locking */ #define WIN_SHM_BASE ((22+SQLITE_SHM_NLOCK)*4) /* first lock byte */ #define WIN_SHM_DMS (WIN_SHM_BASE+SQLITE_SHM_NLOCK) /* deadman switch */ /* ** Apply advisory locks for all n bytes beginning at ofst. */ #define _SHM_UNLCK 1 #define _SHM_RDLCK 2 #define _SHM_WRLCK 3 static int winShmSystemLock( winShmNode *pFile, /* Apply locks to this open shared-memory segment */ int lockType, /* _SHM_UNLCK, _SHM_RDLCK, or _SHM_WRLCK */ int ofst, /* Offset to first byte to be locked/unlocked */ int nByte /* Number of bytes to lock or unlock */ ){ OVERLAPPED ovlp; DWORD dwFlags; int rc = 0; /* Result code form Lock/UnlockFileEx() */ /* Access to the winShmNode object is serialized by the caller */ assert( sqlite3_mutex_held(pFile->mutex) || pFile->nRef==0 ); /* Initialize the locking parameters */ dwFlags = LOCKFILE_FAIL_IMMEDIATELY; if( lockType == _SHM_WRLCK ) dwFlags |= LOCKFILE_EXCLUSIVE_LOCK; memset(&ovlp, 0, sizeof(OVERLAPPED)); ovlp.Offset = ofst; /* Release/Acquire the system-level lock */ if( lockType==_SHM_UNLCK ){ rc = UnlockFileEx(pFile->hFile.h, 0, nByte, 0, &ovlp); }else{ rc = LockFileEx(pFile->hFile.h, dwFlags, 0, nByte, 0, &ovlp); } if( rc!= 0 ){ rc = SQLITE_OK; }else{ pFile->lastErrno = GetLastError(); rc = SQLITE_BUSY; } OSTRACE(("SHM-LOCK %d %s %s 0x%08lx\n", pFile->hFile.h, rc==SQLITE_OK ? "ok" : "failed", lockType==_SHM_UNLCK ? "UnlockFileEx" : "LockFileEx", pFile->lastErrno)); return rc; } /* Forward references to VFS methods */ static int winOpen(sqlite3_vfs*,const char*,sqlite3_file*,int,int*); static int winDelete(sqlite3_vfs *,const char*,int); /* ** Purge the winShmNodeList list of all entries with winShmNode.nRef==0. ** ** This is not a VFS shared-memory method; it is a utility function called ** by VFS shared-memory methods. */ static void winShmPurge(sqlite3_vfs *pVfs, int deleteFlag){ winShmNode **pp; winShmNode *p; assert( winShmMutexHeld() ); pp = &winShmNodeList; while( (p = *pp)!=0 ){ if( p->nRef==0 ){ int i; if( p->mutex ) sqlite3_mutex_free(p->mutex); for(i=0; i<p->nRegion; i++){ UnmapViewOfFile(p->aRegion[i].pMap); CloseHandle(p->aRegion[i].hMap); } if( p->hFile.h != INVALID_HANDLE_VALUE ){ SimulateIOErrorBenign(1); winClose((sqlite3_file *)&p->hFile); SimulateIOErrorBenign(0); } if( deleteFlag ){ SimulateIOErrorBenign(1); winDelete(pVfs, p->zFilename, 0); SimulateIOErrorBenign(0); } *pp = p->pNext; sqlite3_free(p->aRegion); sqlite3_free(p); }else{ pp = &p->pNext; } } } /* ** Open the shared-memory area associated with database file pDbFd. ** ** When opening a new shared-memory file, if no other instances of that ** file are currently open, in this process or in other processes, then ** the file must be truncated to zero length or have its header cleared. */ static int winOpenSharedMemory(winFile *pDbFd){ struct winShm *p; /* The connection to be opened */ struct winShmNode *pShmNode = 0; /* The underlying mmapped file */ int rc; /* Result code */ struct winShmNode *pNew; /* Newly allocated winShmNode */ int nName; /* Size of zName in bytes */ assert( pDbFd->pShm==0 ); /* Not previously opened */ /* Allocate space for the new sqlite3_shm object. Also speculatively ** allocate space for a new winShmNode and filename. */ p = sqlite3_malloc( sizeof(*p) ); if( p==0 ) return SQLITE_NOMEM; memset(p, 0, sizeof(*p)); nName = sqlite3Strlen30(pDbFd->zPath); pNew = sqlite3_malloc( sizeof(*pShmNode) + nName + 15 ); if( pNew==0 ){ sqlite3_free(p); return SQLITE_NOMEM; } memset(pNew, 0, sizeof(*pNew)); pNew->zFilename = (char*)&pNew[1]; sqlite3_snprintf(nName+15, pNew->zFilename, "%s-shm", pDbFd->zPath); /* Look to see if there is an existing winShmNode that can be used. ** If no matching winShmNode currently exists, create a new one. */ winShmEnterMutex(); for(pShmNode = winShmNodeList; pShmNode; pShmNode=pShmNode->pNext){ /* TBD need to come up with better match here. Perhaps ** use FILE_ID_BOTH_DIR_INFO Structure. */ if( sqlite3StrICmp(pShmNode->zFilename, pNew->zFilename)==0 ) break; } if( pShmNode ){ sqlite3_free(pNew); }else{ pShmNode = pNew; pNew = 0; ((winFile*)(&pShmNode->hFile))->h = INVALID_HANDLE_VALUE; pShmNode->pNext = winShmNodeList; winShmNodeList = pShmNode; pShmNode->mutex = sqlite3_mutex_alloc(SQLITE_MUTEX_FAST); if( pShmNode->mutex==0 ){ rc = SQLITE_NOMEM; goto shm_open_err; } rc = winOpen(pDbFd->pVfs, pShmNode->zFilename, /* Name of the file (UTF-8) */ (sqlite3_file*)&pShmNode->hFile, /* File handle here */ SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, /* Mode flags */ 0); if( SQLITE_OK!=rc ){ rc = SQLITE_CANTOPEN_BKPT; goto shm_open_err; } /* Check to see if another process is holding the dead-man switch. ** If not, truncate the file to zero length. */ if( winShmSystemLock(pShmNode, _SHM_WRLCK, WIN_SHM_DMS, 1)==SQLITE_OK ){ rc = winTruncate((sqlite3_file *)&pShmNode->hFile, 0); if( rc!=SQLITE_OK ){ rc = SQLITE_IOERR_SHMOPEN; } } if( rc==SQLITE_OK ){ winShmSystemLock(pShmNode, _SHM_UNLCK, WIN_SHM_DMS, 1); rc = winShmSystemLock(pShmNode, _SHM_RDLCK, WIN_SHM_DMS, 1); } if( rc ) goto shm_open_err; } /* Make the new connection a child of the winShmNode */ p->pShmNode = pShmNode; #ifdef SQLITE_DEBUG p->id = pShmNode->nextShmId++; #endif pShmNode->nRef++; pDbFd->pShm = p; winShmLeaveMutex(); /* The reference count on pShmNode has already been incremented under ** the cover of the winShmEnterMutex() mutex and the pointer from the ** new (struct winShm) object to the pShmNode has been set. All that is ** left to do is to link the new object into the linked list starting ** at pShmNode->pFirst. This must be done while holding the pShmNode->mutex ** mutex. */ sqlite3_mutex_enter(pShmNode->mutex); p->pNext = pShmNode->pFirst; pShmNode->pFirst = p; sqlite3_mutex_leave(pShmNode->mutex); return SQLITE_OK; /* Jump here on any error */ shm_open_err: winShmSystemLock(pShmNode, _SHM_UNLCK, WIN_SHM_DMS, 1); winShmPurge(pDbFd->pVfs, 0); /* This call frees pShmNode if required */ sqlite3_free(p); sqlite3_free(pNew); winShmLeaveMutex(); return rc; } /* ** Close a connection to shared-memory. Delete the underlying ** storage if deleteFlag is true. */ static int winShmUnmap( sqlite3_file *fd, /* Database holding shared memory */ int deleteFlag /* Delete after closing if true */ ){ winFile *pDbFd; /* Database holding shared-memory */ winShm *p; /* The connection to be closed */ winShmNode *pShmNode; /* The underlying shared-memory file */ winShm **pp; /* For looping over sibling connections */ pDbFd = (winFile*)fd; p = pDbFd->pShm; if( p==0 ) return SQLITE_OK; pShmNode = p->pShmNode; /* Remove connection p from the set of connections associated ** with pShmNode */ sqlite3_mutex_enter(pShmNode->mutex); for(pp=&pShmNode->pFirst; (*pp)!=p; pp = &(*pp)->pNext){} *pp = p->pNext; /* Free the connection p */ sqlite3_free(p); pDbFd->pShm = 0; sqlite3_mutex_leave(pShmNode->mutex); /* If pShmNode->nRef has reached 0, then close the underlying ** shared-memory file, too */ winShmEnterMutex(); assert( pShmNode->nRef>0 ); pShmNode->nRef--; if( pShmNode->nRef==0 ){ winShmPurge(pDbFd->pVfs, deleteFlag); } winShmLeaveMutex(); return SQLITE_OK; } /* ** Change the lock state for a shared-memory segment. */ static int winShmLock( sqlite3_file *fd, /* Database file holding the shared memory */ int ofst, /* First lock to acquire or release */ int n, /* Number of locks to acquire or release */ int flags /* What to do with the lock */ ){ winFile *pDbFd = (winFile*)fd; /* Connection holding shared memory */ winShm *p = pDbFd->pShm; /* The shared memory being locked */ winShm *pX; /* For looping over all siblings */ winShmNode *pShmNode = p->pShmNode; int rc = SQLITE_OK; /* Result code */ u16 mask; /* Mask of locks to take or release */ assert( ofst>=0 && ofst+n<=SQLITE_SHM_NLOCK ); assert( n>=1 ); assert( flags==(SQLITE_SHM_LOCK | SQLITE_SHM_SHARED) || flags==(SQLITE_SHM_LOCK | SQLITE_SHM_EXCLUSIVE) || flags==(SQLITE_SHM_UNLOCK | SQLITE_SHM_SHARED) || flags==(SQLITE_SHM_UNLOCK | SQLITE_SHM_EXCLUSIVE) ); assert( n==1 || (flags & SQLITE_SHM_EXCLUSIVE)!=0 ); mask = (u16)((1U<<(ofst+n)) - (1U<<ofst)); assert( n>1 || mask==(1<<ofst) ); sqlite3_mutex_enter(pShmNode->mutex); if( flags & SQLITE_SHM_UNLOCK ){ u16 allMask = 0; /* Mask of locks held by siblings */ /* See if any siblings hold this same lock */ for(pX=pShmNode->pFirst; pX; pX=pX->pNext){ if( pX==p ) continue; assert( (pX->exclMask & (p->exclMask|p->sharedMask))==0 ); allMask |= pX->sharedMask; } /* Unlock the system-level locks */ if( (mask & allMask)==0 ){ rc = winShmSystemLock(pShmNode, _SHM_UNLCK, ofst+WIN_SHM_BASE, n); }else{ rc = SQLITE_OK; } /* Undo the local locks */ if( rc==SQLITE_OK ){ p->exclMask &= ~mask; p->sharedMask &= ~mask; } }else if( flags & SQLITE_SHM_SHARED ){ u16 allShared = 0; /* Union of locks held by connections other than "p" */ /* Find out which shared locks are already held by sibling connections. ** If any sibling already holds an exclusive lock, go ahead and return ** SQLITE_BUSY. */ for(pX=pShmNode->pFirst; pX; pX=pX->pNext){ if( (pX->exclMask & mask)!=0 ){ rc = SQLITE_BUSY; break; } allShared |= pX->sharedMask; } /* Get shared locks at the system level, if necessary */ if( rc==SQLITE_OK ){ if( (allShared & mask)==0 ){ rc = winShmSystemLock(pShmNode, _SHM_RDLCK, ofst+WIN_SHM_BASE, n); }else{ rc = SQLITE_OK; } } /* Get the local shared locks */ if( rc==SQLITE_OK ){ p->sharedMask |= mask; } }else{ /* Make sure no sibling connections hold locks that will block this ** lock. If any do, return SQLITE_BUSY right away. */ for(pX=pShmNode->pFirst; pX; pX=pX->pNext){ if( (pX->exclMask & mask)!=0 || (pX->sharedMask & mask)!=0 ){ rc = SQLITE_BUSY; break; } } /* Get the exclusive locks at the system level. Then if successful ** also mark the local connection as being locked. */ if( rc==SQLITE_OK ){ rc = winShmSystemLock(pShmNode, _SHM_WRLCK, ofst+WIN_SHM_BASE, n); if( rc==SQLITE_OK ){ assert( (p->sharedMask & mask)==0 ); p->exclMask |= mask; } } } sqlite3_mutex_leave(pShmNode->mutex); OSTRACE(("SHM-LOCK shmid-%d, pid-%d got %03x,%03x %s\n", p->id, (int)GetCurrentProcessId(), p->sharedMask, p->exclMask, rc ? "failed" : "ok")); return rc; } /* ** Implement a memory barrier or memory fence on shared memory. ** ** All loads and stores begun before the barrier must complete before ** any load or store begun after the barrier. */ static void winShmBarrier( sqlite3_file *fd /* Database holding the shared memory */ ){ UNUSED_PARAMETER(fd); /* MemoryBarrier(); // does not work -- do not know why not */ winShmEnterMutex(); winShmLeaveMutex(); } /* ** This function is called to obtain a pointer to region iRegion of the ** shared-memory associated with the database file fd. Shared-memory regions ** are numbered starting from zero. Each shared-memory region is szRegion ** bytes in size. ** ** If an error occurs, an error code is returned and *pp is set to NULL. ** ** Otherwise, if the isWrite parameter is 0 and the requested shared-memory ** region has not been allocated (by any client, including one running in a ** separate process), then *pp is set to NULL and SQLITE_OK returned. If ** isWrite is non-zero and the requested shared-memory region has not yet ** been allocated, it is allocated by this function. ** ** If the shared-memory region has already been allocated or is allocated by ** this call as described above, then it is mapped into this processes ** address space (if it is not already), *pp is set to point to the mapped ** memory and SQLITE_OK returned. */ static int winShmMap( sqlite3_file *fd, /* Handle open on database file */ int iRegion, /* Region to retrieve */ int szRegion, /* Size of regions */ int isWrite, /* True to extend file if necessary */ void volatile **pp /* OUT: Mapped memory */ ){ winFile *pDbFd = (winFile*)fd; winShm *p = pDbFd->pShm; winShmNode *pShmNode; int rc = SQLITE_OK; if( !p ){ rc = winOpenSharedMemory(pDbFd); if( rc!=SQLITE_OK ) return rc; p = pDbFd->pShm; } pShmNode = p->pShmNode; sqlite3_mutex_enter(pShmNode->mutex); assert( szRegion==pShmNode->szRegion || pShmNode->nRegion==0 ); if( pShmNode->nRegion<=iRegion ){ struct ShmRegion *apNew; /* New aRegion[] array */ int nByte = (iRegion+1)*szRegion; /* Minimum required file size */ sqlite3_int64 sz; /* Current size of wal-index file */ pShmNode->szRegion = szRegion; /* The requested region is not mapped into this processes address space. ** Check to see if it has been allocated (i.e. if the wal-index file is ** large enough to contain the requested region). */ rc = winFileSize((sqlite3_file *)&pShmNode->hFile, &sz); if( rc!=SQLITE_OK ){ rc = SQLITE_IOERR_SHMSIZE; goto shmpage_out; } if( sz<nByte ){ /* The requested memory region does not exist. If isWrite is set to ** zero, exit early. *pp will be set to NULL and SQLITE_OK returned. ** ** Alternatively, if isWrite is non-zero, use ftruncate() to allocate ** the requested memory region. */ if( !isWrite ) goto shmpage_out; rc = winTruncate((sqlite3_file *)&pShmNode->hFile, nByte); if( rc!=SQLITE_OK ){ rc = SQLITE_IOERR_SHMSIZE; goto shmpage_out; } } /* Map the requested memory region into this processes address space. */ apNew = (struct ShmRegion *)sqlite3_realloc( pShmNode->aRegion, (iRegion+1)*sizeof(apNew[0]) ); if( !apNew ){ rc = SQLITE_IOERR_NOMEM; goto shmpage_out; } pShmNode->aRegion = apNew; while( pShmNode->nRegion<=iRegion ){ HANDLE hMap; /* file-mapping handle */ void *pMap = 0; /* Mapped memory region */ hMap = CreateFileMapping(pShmNode->hFile.h, NULL, PAGE_READWRITE, 0, nByte, NULL ); if( hMap ){ pMap = MapViewOfFile(hMap, FILE_MAP_WRITE | FILE_MAP_READ, 0, 0, nByte ); } if( !pMap ){ pShmNode->lastErrno = GetLastError(); rc = SQLITE_IOERR; if( hMap ) CloseHandle(hMap); goto shmpage_out; } pShmNode->aRegion[pShmNode->nRegion].pMap = pMap; pShmNode->aRegion[pShmNode->nRegion].hMap = hMap; pShmNode->nRegion++; } } shmpage_out: if( pShmNode->nRegion>iRegion ){ char *p = (char *)pShmNode->aRegion[iRegion].pMap; *pp = (void *)&p[iRegion*szRegion]; }else{ *pp = 0; } sqlite3_mutex_leave(pShmNode->mutex); return rc; } #else # define winShmMap 0 # define winShmLock 0 # define winShmBarrier 0 # define winShmUnmap 0 #endif /* #ifndef SQLITE_OMIT_WAL */ /* ** Here ends the implementation of all sqlite3_file methods. ** ********************** End sqlite3_file Methods ******************************* ******************************************************************************/ /* ** This vector defines all the methods that can operate on an ** sqlite3_file for win32. */ static const sqlite3_io_methods winIoMethod = { 2, /* iVersion */ winClose, /* xClose */ winRead, /* xRead */ winWrite, /* xWrite */ winTruncate, /* xTruncate */ winSync, /* xSync */ winFileSize, /* xFileSize */ winLock, /* xLock */ winUnlock, /* xUnlock */ winCheckReservedLock, /* xCheckReservedLock */ winFileControl, /* xFileControl */ winSectorSize, /* xSectorSize */ winDeviceCharacteristics, /* xDeviceCharacteristics */ winShmMap, /* xShmMap */ winShmLock, /* xShmLock */ winShmBarrier, /* xShmBarrier */ winShmUnmap /* xShmUnmap */ }; /**************************************************************************** **************************** sqlite3_vfs methods **************************** ** ** This division contains the implementation of methods on the ** sqlite3_vfs object. */ /* ** Convert a UTF-8 filename into whatever form the underlying ** operating system wants filenames in. Space to hold the result ** is obtained from malloc and must be freed by the calling ** function. */ |
︙ | ︙ | |||
1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 | static int getTempname(int nBuf, char *zBuf){ static char zChars[] = "abcdefghijklmnopqrstuvwxyz" "ABCDEFGHIJKLMNOPQRSTUVWXYZ" "0123456789"; size_t i, j; char zTempPath[MAX_PATH+1]; if( sqlite3_temp_directory ){ sqlite3_snprintf(MAX_PATH-30, zTempPath, "%s", sqlite3_temp_directory); }else if( isNT() ){ char *zMulti; WCHAR zWidePath[MAX_PATH]; GetTempPathW(MAX_PATH-30, zWidePath); zMulti = unicodeToUtf8(zWidePath); | > > > > > > > | 1852 1853 1854 1855 1856 1857 1858 1859 1860 1861 1862 1863 1864 1865 1866 1867 1868 1869 1870 1871 1872 | static int getTempname(int nBuf, char *zBuf){ static char zChars[] = "abcdefghijklmnopqrstuvwxyz" "ABCDEFGHIJKLMNOPQRSTUVWXYZ" "0123456789"; size_t i, j; char zTempPath[MAX_PATH+1]; /* It's odd to simulate an io-error here, but really this is just ** using the io-error infrastructure to test that SQLite handles this ** function failing. */ SimulateIOError( return SQLITE_IOERR ); if( sqlite3_temp_directory ){ sqlite3_snprintf(MAX_PATH-30, zTempPath, "%s", sqlite3_temp_directory); }else if( isNT() ){ char *zMulti; WCHAR zWidePath[MAX_PATH]; GetTempPathW(MAX_PATH-30, zWidePath); zMulti = unicodeToUtf8(zWidePath); |
︙ | ︙ | |||
1229 1230 1231 1232 1233 1234 1235 1236 1237 | sqlite3_snprintf(MAX_PATH-30, zTempPath, "%s", zUtf8); free(zUtf8); }else{ return SQLITE_NOMEM; } #endif } for(i=sqlite3Strlen30(zTempPath); i>0 && zTempPath[i-1]=='\\'; i--){} zTempPath[i] = 0; | > > > > > > > > > | | | > | | 1890 1891 1892 1893 1894 1895 1896 1897 1898 1899 1900 1901 1902 1903 1904 1905 1906 1907 1908 1909 1910 1911 1912 1913 1914 1915 1916 1917 1918 1919 1920 1921 1922 1923 1924 | sqlite3_snprintf(MAX_PATH-30, zTempPath, "%s", zUtf8); free(zUtf8); }else{ return SQLITE_NOMEM; } #endif } /* Check that the output buffer is large enough for the temporary file ** name. If it is not, return SQLITE_ERROR. */ if( (sqlite3Strlen30(zTempPath) + sqlite3Strlen30(SQLITE_TEMP_FILE_PREFIX) + 17) >= nBuf ){ return SQLITE_ERROR; } for(i=sqlite3Strlen30(zTempPath); i>0 && zTempPath[i-1]=='\\'; i--){} zTempPath[i] = 0; sqlite3_snprintf(nBuf-17, zBuf, "%s\\"SQLITE_TEMP_FILE_PREFIX, zTempPath); j = sqlite3Strlen30(zBuf); sqlite3_randomness(15, &zBuf[j]); for(i=0; i<15; i++, j++){ zBuf[j] = (char)zChars[ ((unsigned char)zBuf[j])%(sizeof(zChars)-1) ]; } zBuf[j] = 0; OSTRACE(("TEMP FILENAME: %s\n", zBuf)); return SQLITE_OK; } /* ** The return value of getLastErrorMsg ** is zero if the error message fits in the buffer, or non-zero ** otherwise (if the message was truncated). |
︙ | ︙ | |||
1330 1331 1332 1333 1334 1335 1336 1337 1338 1339 1340 1341 1342 1343 | winFile *pFile = (winFile*)id; void *zConverted; /* Filename in OS encoding */ const char *zUtf8Name = zName; /* Filename in UTF-8 encoding */ char zTmpname[MAX_PATH+1]; /* Buffer used to create temp filename */ assert( id!=0 ); UNUSED_PARAMETER(pVfs); /* If the second argument to this function is NULL, generate a ** temporary file name to use */ if( !zUtf8Name ){ int rc = getTempname(MAX_PATH+1, zTmpname); if( rc!=SQLITE_OK ){ | > > | 2001 2002 2003 2004 2005 2006 2007 2008 2009 2010 2011 2012 2013 2014 2015 2016 | winFile *pFile = (winFile*)id; void *zConverted; /* Filename in OS encoding */ const char *zUtf8Name = zName; /* Filename in UTF-8 encoding */ char zTmpname[MAX_PATH+1]; /* Buffer used to create temp filename */ assert( id!=0 ); UNUSED_PARAMETER(pVfs); pFile->h = INVALID_HANDLE_VALUE; /* If the second argument to this function is NULL, generate a ** temporary file name to use */ if( !zUtf8Name ){ int rc = getTempname(MAX_PATH+1, zTmpname); if( rc!=SQLITE_OK ){ |
︙ | ︙ | |||
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 | NULL, dwCreationDisposition, dwFlagsAndAttributes, NULL ); #endif } if( h==INVALID_HANDLE_VALUE ){ free(zConverted); if( flags & SQLITE_OPEN_READWRITE ){ return winOpen(pVfs, zName, id, ((flags|SQLITE_OPEN_READONLY)&~SQLITE_OPEN_READWRITE), pOutFlags); }else{ return SQLITE_CANTOPEN_BKPT; } } if( pOutFlags ){ if( flags & SQLITE_OPEN_READWRITE ){ *pOutFlags = SQLITE_OPEN_READWRITE; }else{ *pOutFlags = SQLITE_OPEN_READONLY; } } memset(pFile, 0, sizeof(*pFile)); pFile->pMethod = &winIoMethod; pFile->h = h; pFile->lastErrno = NO_ERROR; pFile->sectorSize = getSectorSize(pVfs, zUtf8Name); #if SQLITE_OS_WINCE if( (flags & (SQLITE_OPEN_READWRITE|SQLITE_OPEN_MAIN_DB)) == (SQLITE_OPEN_READWRITE|SQLITE_OPEN_MAIN_DB) && !winceCreateLock(zName, pFile) ){ CloseHandle(h); | > > > > > > > | 2085 2086 2087 2088 2089 2090 2091 2092 2093 2094 2095 2096 2097 2098 2099 2100 2101 2102 2103 2104 2105 2106 2107 2108 2109 2110 2111 2112 2113 2114 2115 2116 2117 2118 2119 2120 2121 2122 2123 2124 2125 | NULL, dwCreationDisposition, dwFlagsAndAttributes, NULL ); #endif } OSTRACE(("OPEN %d %s 0x%lx %s\n", h, zName, dwDesiredAccess, h==INVALID_HANDLE_VALUE ? "failed" : "ok")); if( h==INVALID_HANDLE_VALUE ){ pFile->lastErrno = GetLastError(); free(zConverted); if( flags & SQLITE_OPEN_READWRITE ){ return winOpen(pVfs, zName, id, ((flags|SQLITE_OPEN_READONLY)&~SQLITE_OPEN_READWRITE), pOutFlags); }else{ return SQLITE_CANTOPEN_BKPT; } } if( pOutFlags ){ if( flags & SQLITE_OPEN_READWRITE ){ *pOutFlags = SQLITE_OPEN_READWRITE; }else{ *pOutFlags = SQLITE_OPEN_READONLY; } } memset(pFile, 0, sizeof(*pFile)); pFile->pMethod = &winIoMethod; pFile->h = h; pFile->lastErrno = NO_ERROR; pFile->pVfs = pVfs; pFile->pShm = 0; pFile->zPath = zName; pFile->sectorSize = getSectorSize(pVfs, zUtf8Name); #if SQLITE_OS_WINCE if( (flags & (SQLITE_OPEN_READWRITE|SQLITE_OPEN_MAIN_DB)) == (SQLITE_OPEN_READWRITE|SQLITE_OPEN_MAIN_DB) && !winceCreateLock(zName, pFile) ){ CloseHandle(h); |
︙ | ︙ | |||
1474 1475 1476 1477 1478 1479 1480 | sqlite3_vfs *pVfs, /* Not used on win32 */ const char *zFilename, /* Name of file to delete */ int syncDir /* Not used on win32 */ ){ int cnt = 0; DWORD rc; DWORD error = 0; | | > > > < | 2154 2155 2156 2157 2158 2159 2160 2161 2162 2163 2164 2165 2166 2167 2168 2169 2170 2171 2172 2173 2174 2175 2176 | sqlite3_vfs *pVfs, /* Not used on win32 */ const char *zFilename, /* Name of file to delete */ int syncDir /* Not used on win32 */ ){ int cnt = 0; DWORD rc; DWORD error = 0; void *zConverted; UNUSED_PARAMETER(pVfs); UNUSED_PARAMETER(syncDir); SimulateIOError(return SQLITE_IOERR_DELETE); zConverted = convertUtf8Filename(zFilename); if( zConverted==0 ){ return SQLITE_NOMEM; } if( isNT() ){ do{ DeleteFileW(zConverted); }while( ( ((rc = GetFileAttributesW(zConverted)) != INVALID_FILE_ATTRIBUTES) || ((error = GetLastError()) == ERROR_ACCESS_DENIED)) && (++cnt < MX_DELETION_ATTEMPTS) && (Sleep(100), 1) ); |
︙ | ︙ | |||
1503 1504 1505 1506 1507 1508 1509 | }while( ( ((rc = GetFileAttributesA(zConverted)) != INVALID_FILE_ATTRIBUTES) || ((error = GetLastError()) == ERROR_ACCESS_DENIED)) && (++cnt < MX_DELETION_ATTEMPTS) && (Sleep(100), 1) ); #endif } free(zConverted); | | > > > | > > > > > | > > > > > > > > > > > > > > > > > > > > | 2185 2186 2187 2188 2189 2190 2191 2192 2193 2194 2195 2196 2197 2198 2199 2200 2201 2202 2203 2204 2205 2206 2207 2208 2209 2210 2211 2212 2213 2214 2215 2216 2217 2218 2219 2220 2221 2222 2223 2224 2225 2226 2227 2228 2229 2230 2231 2232 2233 2234 2235 2236 2237 2238 2239 2240 2241 2242 2243 2244 2245 2246 2247 2248 2249 | }while( ( ((rc = GetFileAttributesA(zConverted)) != INVALID_FILE_ATTRIBUTES) || ((error = GetLastError()) == ERROR_ACCESS_DENIED)) && (++cnt < MX_DELETION_ATTEMPTS) && (Sleep(100), 1) ); #endif } free(zConverted); OSTRACE(("DELETE \"%s\" %s\n", zFilename, ( (rc==INVALID_FILE_ATTRIBUTES) && (error==ERROR_FILE_NOT_FOUND)) ? "ok" : "failed" )); return ( (rc == INVALID_FILE_ATTRIBUTES) && (error == ERROR_FILE_NOT_FOUND)) ? SQLITE_OK : SQLITE_IOERR_DELETE; } /* ** Check the existance and status of a file. */ static int winAccess( sqlite3_vfs *pVfs, /* Not used on win32 */ const char *zFilename, /* Name of file to check */ int flags, /* Type of test to make on this file */ int *pResOut /* OUT: Result */ ){ DWORD attr; int rc = 0; void *zConverted; UNUSED_PARAMETER(pVfs); SimulateIOError( return SQLITE_IOERR_ACCESS; ); zConverted = convertUtf8Filename(zFilename); if( zConverted==0 ){ return SQLITE_NOMEM; } if( isNT() ){ WIN32_FILE_ATTRIBUTE_DATA sAttrData; memset(&sAttrData, 0, sizeof(sAttrData)); if( GetFileAttributesExW((WCHAR*)zConverted, GetFileExInfoStandard, &sAttrData) ){ /* For an SQLITE_ACCESS_EXISTS query, treat a zero-length file ** as if it does not exist. */ if( flags==SQLITE_ACCESS_EXISTS && sAttrData.nFileSizeHigh==0 && sAttrData.nFileSizeLow==0 ){ attr = INVALID_FILE_ATTRIBUTES; }else{ attr = sAttrData.dwFileAttributes; } }else{ if( GetLastError()!=ERROR_FILE_NOT_FOUND ){ free(zConverted); return SQLITE_IOERR_ACCESS; }else{ attr = INVALID_FILE_ATTRIBUTES; } } /* isNT() is 1 if SQLITE_OS_WINCE==1, so this else is never executed. ** Since the ASCII version of these Windows API do not exist for WINCE, ** it's important to not reference them for WINCE builds. */ #if SQLITE_OS_WINCE==0 }else{ attr = GetFileAttributesA((char*)zConverted); |
︙ | ︙ | |||
1565 1566 1567 1568 1569 1570 1571 1572 1573 1574 1575 1576 1577 1578 1579 1580 1581 1582 1583 1584 1585 1586 1587 1588 1589 1590 1591 1592 1593 1594 | sqlite3_vfs *pVfs, /* Pointer to vfs object */ const char *zRelative, /* Possibly relative input path */ int nFull, /* Size of output buffer in bytes */ char *zFull /* Output buffer */ ){ #if defined(__CYGWIN__) UNUSED_PARAMETER(nFull); cygwin_conv_to_full_win32_path(zRelative, zFull); return SQLITE_OK; #endif #if SQLITE_OS_WINCE UNUSED_PARAMETER(nFull); /* WinCE has no concept of a relative pathname, or so I am told. */ sqlite3_snprintf(pVfs->mxPathname, zFull, "%s", zRelative); return SQLITE_OK; #endif #if !SQLITE_OS_WINCE && !defined(__CYGWIN__) int nByte; void *zConverted; char *zOut; UNUSED_PARAMETER(nFull); zConverted = convertUtf8Filename(zRelative); if( isNT() ){ WCHAR *zTemp; nByte = GetFullPathNameW((WCHAR*)zConverted, 0, 0, 0) + 3; zTemp = malloc( nByte*sizeof(zTemp[0]) ); if( zTemp==0 ){ | > > > > > > > > > | 2275 2276 2277 2278 2279 2280 2281 2282 2283 2284 2285 2286 2287 2288 2289 2290 2291 2292 2293 2294 2295 2296 2297 2298 2299 2300 2301 2302 2303 2304 2305 2306 2307 2308 2309 2310 2311 2312 2313 | sqlite3_vfs *pVfs, /* Pointer to vfs object */ const char *zRelative, /* Possibly relative input path */ int nFull, /* Size of output buffer in bytes */ char *zFull /* Output buffer */ ){ #if defined(__CYGWIN__) SimulateIOError( return SQLITE_ERROR ); UNUSED_PARAMETER(nFull); cygwin_conv_to_full_win32_path(zRelative, zFull); return SQLITE_OK; #endif #if SQLITE_OS_WINCE SimulateIOError( return SQLITE_ERROR ); UNUSED_PARAMETER(nFull); /* WinCE has no concept of a relative pathname, or so I am told. */ sqlite3_snprintf(pVfs->mxPathname, zFull, "%s", zRelative); return SQLITE_OK; #endif #if !SQLITE_OS_WINCE && !defined(__CYGWIN__) int nByte; void *zConverted; char *zOut; /* It's odd to simulate an io-error here, but really this is just ** using the io-error infrastructure to test that SQLite handles this ** function failing. This function could fail if, for example, the ** current working directory has been unlinked. */ SimulateIOError( return SQLITE_ERROR ); UNUSED_PARAMETER(nFull); zConverted = convertUtf8Filename(zRelative); if( isNT() ){ WCHAR *zTemp; nByte = GetFullPathNameW((WCHAR*)zConverted, 0, 0, 0) + 3; zTemp = malloc( nByte*sizeof(zTemp[0]) ); if( zTemp==0 ){ |
︙ | ︙ | |||
1648 1649 1650 1651 1652 1653 1654 1655 1656 1657 1658 1659 1660 1661 1662 | DWORD dwDummy; /* ** We need to get the full path name of the file ** to get the drive letter to look up the sector ** size. */ rc = winFullPathname(pVfs, zRelative, MAX_PATH, zFullpath); if( rc == SQLITE_OK ) { void *zConverted = convertUtf8Filename(zFullpath); if( zConverted ){ if( isNT() ){ /* trim path to just drive reference */ WCHAR *p = zConverted; | > > | 2367 2368 2369 2370 2371 2372 2373 2374 2375 2376 2377 2378 2379 2380 2381 2382 2383 | DWORD dwDummy; /* ** We need to get the full path name of the file ** to get the drive letter to look up the sector ** size. */ SimulateIOErrorBenign(1); rc = winFullPathname(pVfs, zRelative, MAX_PATH, zFullpath); SimulateIOErrorBenign(0); if( rc == SQLITE_OK ) { void *zConverted = convertUtf8Filename(zFullpath); if( zConverted ){ if( isNT() ){ /* trim path to just drive reference */ WCHAR *p = zConverted; |
︙ | ︙ | |||
1796 1797 1798 1799 1800 1801 1802 | static int winSleep(sqlite3_vfs *pVfs, int microsec){ Sleep((microsec+999)/1000); UNUSED_PARAMETER(pVfs); return ((microsec+999)/1000)*1000; } /* | | > | | | | > > > > | | < < < | < | < | < | < | | > | < | | < < < < | > > > > > > > > > > > > > > > > | 2517 2518 2519 2520 2521 2522 2523 2524 2525 2526 2527 2528 2529 2530 2531 2532 2533 2534 2535 2536 2537 2538 2539 2540 2541 2542 2543 2544 2545 2546 2547 2548 2549 2550 2551 2552 2553 2554 2555 2556 2557 2558 2559 2560 2561 2562 2563 2564 2565 2566 2567 2568 2569 2570 2571 2572 2573 2574 2575 2576 2577 2578 2579 2580 2581 2582 2583 2584 2585 2586 2587 2588 2589 2590 2591 2592 2593 2594 2595 2596 2597 2598 | static int winSleep(sqlite3_vfs *pVfs, int microsec){ Sleep((microsec+999)/1000); UNUSED_PARAMETER(pVfs); return ((microsec+999)/1000)*1000; } /* ** The following variable, if set to a non-zero value, is interpreted as ** the number of seconds since 1970 and is used to set the result of ** sqlite3OsCurrentTime() during testing. */ #ifdef SQLITE_TEST int sqlite3_current_time = 0; /* Fake system time in seconds since 1970. */ #endif /* ** Find the current time (in Universal Coordinated Time). Write into *piNow ** the current time and date as a Julian Day number times 86_400_000. In ** other words, write into *piNow the number of milliseconds since the Julian ** epoch of noon in Greenwich on November 24, 4714 B.C according to the ** proleptic Gregorian calendar. ** ** On success, return 0. Return 1 if the time and date cannot be found. */ static int winCurrentTimeInt64(sqlite3_vfs *pVfs, sqlite3_int64 *piNow){ /* FILETIME structure is a 64-bit value representing the number of 100-nanosecond intervals since January 1, 1601 (= JD 2305813.5). */ FILETIME ft; static const sqlite3_int64 winFiletimeEpoch = 23058135*(sqlite3_int64)8640000; #ifdef SQLITE_TEST static const sqlite3_int64 unixEpoch = 24405875*(sqlite3_int64)8640000; #endif /* 2^32 - to avoid use of LL and warnings in gcc */ static const sqlite3_int64 max32BitValue = (sqlite3_int64)2000000000 + (sqlite3_int64)2000000000 + (sqlite3_int64)294967296; #if SQLITE_OS_WINCE SYSTEMTIME time; GetSystemTime(&time); /* if SystemTimeToFileTime() fails, it returns zero. */ if (!SystemTimeToFileTime(&time,&ft)){ return 1; } #else GetSystemTimeAsFileTime( &ft ); #endif *piNow = winFiletimeEpoch + ((((sqlite3_int64)ft.dwHighDateTime)*max32BitValue) + (sqlite3_int64)ft.dwLowDateTime)/(sqlite3_int64)10000; #ifdef SQLITE_TEST if( sqlite3_current_time ){ *piNow = 1000*(sqlite3_int64)sqlite3_current_time + unixEpoch; } #endif UNUSED_PARAMETER(pVfs); return 0; } /* ** Find the current time (in Universal Coordinated Time). Write the ** current time and date as a Julian Day number into *prNow and ** return 0. Return 1 if the time and date cannot be found. */ int winCurrentTime(sqlite3_vfs *pVfs, double *prNow){ int rc; sqlite3_int64 i; rc = winCurrentTimeInt64(pVfs, &i); if( !rc ){ *prNow = i/86400000.0; } return rc; } /* ** The idea is that this function works like a combination of ** GetLastError() and FormatMessage() on windows (or errno and ** strerror_r() on unix). After an error is returned by an OS ** function, SQLite calls this function with zBuf pointing to ** a buffer of nBuf bytes. The OS layer should populate the |
︙ | ︙ | |||
1889 1890 1891 1892 1893 1894 1895 1896 1897 1898 1899 1900 1901 | ** by sqlite into the error message available to the user using ** sqlite3_errmsg(), possibly making IO errors easier to debug. */ static int winGetLastError(sqlite3_vfs *pVfs, int nBuf, char *zBuf){ UNUSED_PARAMETER(pVfs); return getLastErrorMsg(nBuf, zBuf); } /* ** Initialize and deinitialize the operating system interface. */ int sqlite3_os_init(void){ static sqlite3_vfs winVfs = { | > > | | | | | | < | | | | | | | | | | | | > | 2620 2621 2622 2623 2624 2625 2626 2627 2628 2629 2630 2631 2632 2633 2634 2635 2636 2637 2638 2639 2640 2641 2642 2643 2644 2645 2646 2647 2648 2649 2650 2651 2652 2653 2654 2655 2656 2657 2658 2659 2660 2661 2662 2663 | ** by sqlite into the error message available to the user using ** sqlite3_errmsg(), possibly making IO errors easier to debug. */ static int winGetLastError(sqlite3_vfs *pVfs, int nBuf, char *zBuf){ UNUSED_PARAMETER(pVfs); return getLastErrorMsg(nBuf, zBuf); } /* ** Initialize and deinitialize the operating system interface. */ int sqlite3_os_init(void){ static sqlite3_vfs winVfs = { 2, /* iVersion */ sizeof(winFile), /* szOsFile */ MAX_PATH, /* mxPathname */ 0, /* pNext */ "win32", /* zName */ 0, /* pAppData */ winOpen, /* xOpen */ winDelete, /* xDelete */ winAccess, /* xAccess */ winFullPathname, /* xFullPathname */ winDlOpen, /* xDlOpen */ winDlError, /* xDlError */ winDlSym, /* xDlSym */ winDlClose, /* xDlClose */ winRandomness, /* xRandomness */ winSleep, /* xSleep */ winCurrentTime, /* xCurrentTime */ winGetLastError, /* xGetLastError */ winCurrentTimeInt64, /* xCurrentTimeInt64 */ }; sqlite3_vfs_register(&winVfs, 1); return SQLITE_OK; } int sqlite3_os_end(void){ return SQLITE_OK; } #endif /* SQLITE_OS_WIN */ |
Changes to src/pager.c.
︙ | ︙ | |||
16 17 18 19 20 21 22 23 24 25 26 27 28 29 | ** is separate from the database file. The pager also implements file ** locking to prevent two processes from writing the same database ** file simultaneously, or one process from reading the database while ** another is writing. */ #ifndef SQLITE_OMIT_DISKIO #include "sqliteInt.h" /* ** Macros for troubleshooting. Normally turned off */ #if 0 int sqlite3PagerTrace=1; /* True to enable tracing */ #define sqlite3DebugPrintf printf | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | ** is separate from the database file. The pager also implements file ** locking to prevent two processes from writing the same database ** file simultaneously, or one process from reading the database while ** another is writing. */ #ifndef SQLITE_OMIT_DISKIO #include "sqliteInt.h" #include "wal.h" /* ******************** NOTES ON THE DESIGN OF THE PAGER ************************ ** ** Within this comment block, a page is deemed to have been synced ** automatically as soon as it is written when PRAGMA synchronous=OFF. ** Otherwise, the page is not synced until the xSync method of the VFS ** is called successfully on the file containing the page. ** ** Definition: A page of the database file is said to be "overwriteable" if ** one or more of the following are true about the page: ** ** (a) The original content of the page as it was at the beginning of ** the transaction has been written into the rollback journal and ** synced. ** ** (b) The page was a freelist leaf page at the start of the transaction. ** ** (c) The page number is greater than the largest page that existed in ** the database file at the start of the transaction. ** ** (1) A page of the database file is never overwritten unless one of the ** following are true: ** ** (a) The page and all other pages on the same sector are overwriteable. ** ** (b) The atomic page write optimization is enabled, and the entire ** transaction other than the update of the transaction sequence ** number consists of a single page change. ** ** (2) The content of a page written into the rollback journal exactly matches ** both the content in the database when the rollback journal was written ** and the content in the database at the beginning of the current ** transaction. ** ** (3) Writes to the database file are an integer multiple of the page size ** in length and are aligned to a page boundary. ** ** (4) Reads from the database file are either aligned on a page boundary and ** an integer multiple of the page size in length or are taken from the ** first 100 bytes of the database file. ** ** (5) All writes to the database file are synced prior to the rollback journal ** being deleted, truncated, or zeroed. ** ** (6) If a master journal file is used, then all writes to the database file ** are synced prior to the master journal being deleted. ** ** Definition: Two databases (or the same database at two points it time) ** are said to be "logically equivalent" if they give the same answer to ** all queries. Note in particular the the content of freelist leaf ** pages can be changed arbitarily without effecting the logical equivalence ** of the database. ** ** (7) At any time, if any subset, including the empty set and the total set, ** of the unsynced changes to a rollback journal are removed and the ** journal is rolled back, the resulting database file will be logical ** equivalent to the database file at the beginning of the transaction. ** ** (8) When a transaction is rolled back, the xTruncate method of the VFS ** is called to restore the database file to the same size it was at ** the beginning of the transaction. (In some VFSes, the xTruncate ** method is a no-op, but that does not change the fact the SQLite will ** invoke it.) ** ** (9) Whenever the database file is modified, at least one bit in the range ** of bytes from 24 through 39 inclusive will be changed prior to releasing ** the EXCLUSIVE lock. ** ** (10) The pattern of bits in bytes 24 through 39 shall not repeat in less ** than one billion transactions. ** ** (11) A database file is well-formed at the beginning and at the conclusion ** of every transaction. ** ** (12) An EXCLUSIVE lock is held on the database file when writing to ** the database file. ** ** (13) A SHARED lock is held on the database file while reading any ** content out of the database file. */ /* ** Macros for troubleshooting. Normally turned off */ #if 0 int sqlite3PagerTrace=1; /* True to enable tracing */ #define sqlite3DebugPrintf printf |
︙ | ︙ | |||
135 136 137 138 139 140 141 142 143 144 145 146 147 148 | typedef struct PagerSavepoint PagerSavepoint; struct PagerSavepoint { i64 iOffset; /* Starting offset in main journal */ i64 iHdrOffset; /* See above */ Bitvec *pInSavepoint; /* Set of pages in this savepoint */ Pgno nOrig; /* Original number of pages in file */ Pgno iSubRec; /* Index of first record in sub-journal */ }; /* ** A open page cache is an instance of the following structure. ** ** errCode ** | > > > | 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 | typedef struct PagerSavepoint PagerSavepoint; struct PagerSavepoint { i64 iOffset; /* Starting offset in main journal */ i64 iHdrOffset; /* See above */ Bitvec *pInSavepoint; /* Set of pages in this savepoint */ Pgno nOrig; /* Original number of pages in file */ Pgno iSubRec; /* Index of first record in sub-journal */ #ifndef SQLITE_OMIT_WAL u32 aWalData[WAL_SAVEPOINT_NDATA]; /* WAL savepoint context */ #endif }; /* ** A open page cache is an instance of the following structure. ** ** errCode ** |
︙ | ︙ | |||
193 194 195 196 197 198 199 | ** It is cleared at the end of each transaction. ** ** It is used when committing or otherwise ending a transaction. If ** the dbModified flag is clear then less work has to be done. ** ** journalStarted ** | | > | 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 | ** It is cleared at the end of each transaction. ** ** It is used when committing or otherwise ending a transaction. If ** the dbModified flag is clear then less work has to be done. ** ** journalStarted ** ** This flag is set whenever the the main journal is opened and ** initialized ** ** The point of this flag is that it must be set after the ** first journal header in a journal file has been synced to disk. ** After this has happened, new pages appended to the database ** do not need the PGHDR_NEED_SYNC flag set, as they do not need ** to wait for a journal sync before they can be written out to ** the database file (see function pager_write()). |
︙ | ︙ | |||
217 218 219 220 221 222 223 | ** then attempts to upgrade to an exclusive lock. If this attempt ** fails, then SQLITE_BUSY may be returned to the user and the user ** may attempt to commit the transaction again later (calling ** CommitPhaseOne() again). This flag is used to ensure that the ** master journal name is only written to the journal file the first ** time CommitPhaseOne() is called. ** | | > > > | > > > | 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 | ** then attempts to upgrade to an exclusive lock. If this attempt ** fails, then SQLITE_BUSY may be returned to the user and the user ** may attempt to commit the transaction again later (calling ** CommitPhaseOne() again). This flag is used to ensure that the ** master journal name is only written to the journal file the first ** time CommitPhaseOne() is called. ** ** doNotSpill, doNotSyncSpill ** ** When enabled, cache spills are prohibited. The doNotSpill variable ** inhibits all cache spill and doNotSyncSpill inhibits those spills that ** would require a journal sync. The doNotSyncSpill is set and cleared ** by sqlite3PagerWrite() in order to prevent a journal sync from happening ** in between the journalling of two pages on the same sector. The ** doNotSpill value set to prevent pagerStress() from trying to use ** the journal during a rollback. ** ** needSync ** ** TODO: It might be easier to set this variable in writeJournalHdr() ** and writeMasterJournal() only. Change its meaning to "unsynced data ** has been written to the journal". ** |
︙ | ︙ | |||
263 264 265 266 267 268 269 | */ u8 state; /* PAGER_UNLOCK, _SHARED, _RESERVED, etc. */ u8 dbModified; /* True if there are any changes to the Db */ u8 needSync; /* True if an fsync() is needed on the journal */ u8 journalStarted; /* True if header of journal is synced */ u8 changeCountDone; /* Set after incrementing the change-counter */ u8 setMaster; /* True if a m-j name has been written to jrnl */ | | > > | 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 | */ u8 state; /* PAGER_UNLOCK, _SHARED, _RESERVED, etc. */ u8 dbModified; /* True if there are any changes to the Db */ u8 needSync; /* True if an fsync() is needed on the journal */ u8 journalStarted; /* True if header of journal is synced */ u8 changeCountDone; /* Set after incrementing the change-counter */ u8 setMaster; /* True if a m-j name has been written to jrnl */ u8 doNotSpill; /* Do not spill the cache when non-zero */ u8 doNotSyncSpill; /* Do not do a spill that requires jrnl sync */ u8 dbSizeValid; /* Set when dbSize is correct */ u8 subjInMemory; /* True to use in-memory sub-journals */ Pgno dbSize; /* Number of pages in the database */ Pgno dbOrigSize; /* dbSize before the current transaction */ Pgno dbFileSize; /* Number of pages in the database file */ int errCode; /* One of several kinds of errors */ int nRec; /* Pages journalled since last j-header written */ u32 cksumInit; /* Quasi-random value added to every checksum */ u32 nSubRec; /* Number of records written to sub-journal */ Bitvec *pInJournal; /* One bit for each page in the database file */ sqlite3_file *fd; /* File descriptor for database */ sqlite3_file *jfd; /* File descriptor for main journal */ sqlite3_file *sjfd; /* File descriptor for sub-journal */ i64 journalOff; /* Current write offset in the journal file */ i64 journalHdr; /* Byte offset to previous journal header */ i64 journalSizeLimit; /* Size limit for persistent journal files */ PagerSavepoint *aSavepoint; /* Array of active savepoints */ int nSavepoint; /* Number of elements in aSavepoint[] */ char dbFileVers[16]; /* Changes whenever database file changes */ u32 sectorSize; /* Assumed sector size during rollback */ u16 nExtra; /* Add this many bytes to each in-memory page */ i16 nReserve; /* Number of unused bytes at end of each page */ |
︙ | ︙ | |||
305 306 307 308 309 310 311 | #ifdef SQLITE_HAS_CODEC void *(*xCodec)(void*,void*,Pgno,int); /* Routine for en/decoding data */ void (*xCodecSizeChng)(void*,int,int); /* Notify of page size changes */ void (*xCodecFree)(void*); /* Destructor for the codec */ void *pCodec; /* First argument to xCodec... methods */ #endif char *pTmpSpace; /* Pager.pageSize bytes of space for tmp use */ | < > > > > | 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 | #ifdef SQLITE_HAS_CODEC void *(*xCodec)(void*,void*,Pgno,int); /* Routine for en/decoding data */ void (*xCodecSizeChng)(void*,int,int); /* Notify of page size changes */ void (*xCodecFree)(void*); /* Destructor for the codec */ void *pCodec; /* First argument to xCodec... methods */ #endif char *pTmpSpace; /* Pager.pageSize bytes of space for tmp use */ PCache *pPCache; /* Pointer to page cache object */ sqlite3_backup *pBackup; /* Pointer to list of ongoing backup processes */ #ifndef SQLITE_OMIT_WAL Wal *pWal; /* Write-ahead log used by "journal_mode=wal" */ char *zWal; /* File name for write-ahead log */ #endif }; /* ** The following global variables hold counters used for ** testing purposes only. These variables do not exist in ** a non-testing build. These variables are not thread-safe. */ |
︙ | ︙ | |||
821 822 823 824 825 826 827 828 829 830 831 832 833 834 | ** database page size. Since the zHeader buffer is only Pager.pageSize ** bytes in size, more than one call to sqlite3OsWrite() may be required ** to populate the entire journal header sector. */ for(nWrite=0; rc==SQLITE_OK&&nWrite<JOURNAL_HDR_SZ(pPager); nWrite+=nHeader){ IOTRACE(("JHDR %p %lld %d\n", pPager, pPager->journalHdr, nHeader)) rc = sqlite3OsWrite(pPager->jfd, zHeader, nHeader, pPager->journalOff); pPager->journalOff += nHeader; } return rc; } /* | > | 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 | ** database page size. Since the zHeader buffer is only Pager.pageSize ** bytes in size, more than one call to sqlite3OsWrite() may be required ** to populate the entire journal header sector. */ for(nWrite=0; rc==SQLITE_OK&&nWrite<JOURNAL_HDR_SZ(pPager); nWrite+=nHeader){ IOTRACE(("JHDR %p %lld %d\n", pPager, pPager->journalHdr, nHeader)) rc = sqlite3OsWrite(pPager->jfd, zHeader, nHeader, pPager->journalOff); assert( pPager->journalHdr <= pPager->journalOff ); pPager->journalOff += nHeader; } return rc; } /* |
︙ | ︙ | |||
979 980 981 982 983 984 985 986 987 988 989 990 991 992 | || pPager->journalMode==PAGER_JOURNALMODE_MEMORY || pPager->journalMode==PAGER_JOURNALMODE_OFF ){ return SQLITE_OK; } pPager->setMaster = 1; assert( isOpen(pPager->jfd) ); /* Calculate the length in bytes and the checksum of zMaster */ for(nMaster=0; zMaster[nMaster]; nMaster++){ cksum += zMaster[nMaster]; } /* If in full-sync mode, advance to the next disk sector before writing | > | 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 | || pPager->journalMode==PAGER_JOURNALMODE_MEMORY || pPager->journalMode==PAGER_JOURNALMODE_OFF ){ return SQLITE_OK; } pPager->setMaster = 1; assert( isOpen(pPager->jfd) ); assert( pPager->journalHdr <= pPager->journalOff ); /* Calculate the length in bytes and the checksum of zMaster */ for(nMaster=0; zMaster[nMaster]; nMaster++){ cksum += zMaster[nMaster]; } /* If in full-sync mode, advance to the next disk sector before writing |
︙ | ︙ | |||
1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 | testcase( rc==SQLITE_NOMEM ); assert( rc==SQLITE_OK || rc==SQLITE_NOMEM ); } } return rc; } /* ** Unlock the database file. This function is a no-op if the pager ** is in exclusive mode. ** ** If the pager is currently in error state, discard the contents of ** the cache and reset the Pager structure internal state. If there is ** an open journal-file, then the next time a shared-lock is obtained ** on the pager file (by this or any other process), it will be ** treated as a hot-journal and rolled back. */ static void pager_unlock(Pager *pPager){ if( !pPager->exclusiveMode ){ | > > > > > > > > > > > > > > > > | > > | | | > > > > > > > > > | > > | > > > > | > | 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 | testcase( rc==SQLITE_NOMEM ); assert( rc==SQLITE_OK || rc==SQLITE_NOMEM ); } } return rc; } /* ** Return true if this pager uses a write-ahead log instead of the usual ** rollback journal. Otherwise false. */ #ifndef SQLITE_OMIT_WAL static int pagerUseWal(Pager *pPager){ return (pPager->pWal!=0); } #else # define pagerUseWal(x) 0 # define pagerRollbackWal(x) 0 # define pagerWalFrames(v,w,x,y,z) 0 # define pagerOpenWalIfPresent(z) SQLITE_OK # define pagerBeginReadTransaction(z) SQLITE_OK #endif /* ** Unlock the database file. This function is a no-op if the pager ** is in exclusive mode. ** ** If the pager is currently in error state, discard the contents of ** the cache and reset the Pager structure internal state. If there is ** an open journal-file, then the next time a shared-lock is obtained ** on the pager file (by this or any other process), it will be ** treated as a hot-journal and rolled back. */ static void pager_unlock(Pager *pPager){ if( !pPager->exclusiveMode ){ int rc = SQLITE_OK; /* Return code */ int iDc = isOpen(pPager->fd)?sqlite3OsDeviceCharacteristics(pPager->fd):0; /* If the operating system support deletion of open files, then ** close the journal file when dropping the database lock. Otherwise ** another connection with journal_mode=delete might delete the file ** out from under us. */ assert( (PAGER_JOURNALMODE_MEMORY & 5)!=1 ); assert( (PAGER_JOURNALMODE_OFF & 5)!=1 ); assert( (PAGER_JOURNALMODE_WAL & 5)!=1 ); assert( (PAGER_JOURNALMODE_DELETE & 5)!=1 ); assert( (PAGER_JOURNALMODE_TRUNCATE & 5)==1 ); assert( (PAGER_JOURNALMODE_PERSIST & 5)==1 ); if( 0==(iDc & SQLITE_IOCAP_UNDELETABLE_WHEN_OPEN) || 1!=(pPager->journalMode & 5) ){ sqlite3OsClose(pPager->jfd); } sqlite3BitvecDestroy(pPager->pInJournal); pPager->pInJournal = 0; releaseAllSavepoints(pPager); /* If the file is unlocked, somebody else might change it. The ** values stored in Pager.dbSize etc. might become invalid if ** this happens. One can argue that this doesn't need to be cleared ** until the change-counter check fails in PagerSharedLock(). ** Clearing the page size cache here is being conservative. */ pPager->dbSizeValid = 0; if( pagerUseWal(pPager) ){ sqlite3WalEndReadTransaction(pPager->pWal); }else{ rc = osUnlock(pPager->fd, NO_LOCK); } if( rc ){ pPager->errCode = rc; } IOTRACE(("UNLOCK %p\n", pPager)) /* If Pager.errCode is set, the contents of the pager cache cannot be ** trusted. Now that the pager file is unlocked, the contents of the |
︙ | ︙ | |||
1155 1156 1157 1158 1159 1160 1161 | ** This function should be called when an IOERR, CORRUPT or FULL error ** may have occurred. The first argument is a pointer to the pager ** structure, the second the error-code about to be returned by a pager ** API function. The value returned is a copy of the second argument ** to this function. ** ** If the second argument is SQLITE_IOERR, SQLITE_CORRUPT, or SQLITE_FULL | | | 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 | ** This function should be called when an IOERR, CORRUPT or FULL error ** may have occurred. The first argument is a pointer to the pager ** structure, the second the error-code about to be returned by a pager ** API function. The value returned is a copy of the second argument ** to this function. ** ** If the second argument is SQLITE_IOERR, SQLITE_CORRUPT, or SQLITE_FULL ** the error becomes persistent. Until the persistent error is cleared, ** subsequent API calls on this Pager will immediately return the same ** error code. ** ** A persistent error indicates that the contents of the pager-cache ** cannot be trusted. This state can be cleared by completely discarding ** the contents of the pager-cache. If a transaction was active when ** the persistent error occurred, then the rollback journal may need |
︙ | ︙ | |||
1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 | if( pPager->state<PAGER_RESERVED ){ return SQLITE_OK; } releaseAllSavepoints(pPager); assert( isOpen(pPager->jfd) || pPager->pInJournal==0 ); if( isOpen(pPager->jfd) ){ /* Finalize the journal file. */ if( sqlite3IsMemJournal(pPager->jfd) ){ assert( pPager->journalMode==PAGER_JOURNALMODE_MEMORY ); sqlite3OsClose(pPager->jfd); }else if( pPager->journalMode==PAGER_JOURNALMODE_TRUNCATE ){ if( pPager->journalOff==0 ){ rc = SQLITE_OK; }else{ rc = sqlite3OsTruncate(pPager->jfd, 0); } pPager->journalOff = 0; pPager->journalStarted = 0; | > | | | > > | < | | | > | > > > > > > > > > > | | 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 | if( pPager->state<PAGER_RESERVED ){ return SQLITE_OK; } releaseAllSavepoints(pPager); assert( isOpen(pPager->jfd) || pPager->pInJournal==0 ); if( isOpen(pPager->jfd) ){ assert( !pagerUseWal(pPager) ); /* Finalize the journal file. */ if( sqlite3IsMemJournal(pPager->jfd) ){ assert( pPager->journalMode==PAGER_JOURNALMODE_MEMORY ); sqlite3OsClose(pPager->jfd); }else if( pPager->journalMode==PAGER_JOURNALMODE_TRUNCATE ){ if( pPager->journalOff==0 ){ rc = SQLITE_OK; }else{ rc = sqlite3OsTruncate(pPager->jfd, 0); } pPager->journalOff = 0; pPager->journalStarted = 0; }else if( pPager->journalMode==PAGER_JOURNALMODE_PERSIST || (pPager->exclusiveMode && pPager->journalMode!=PAGER_JOURNALMODE_WAL) ){ rc = zeroJournalHdr(pPager, hasMaster); pager_error(pPager, rc); pPager->journalOff = 0; pPager->journalStarted = 0; }else{ /* This branch may be executed with Pager.journalMode==MEMORY if ** a hot-journal was just rolled back. In this case the journal ** file should be closed and deleted. If this connection writes to ** the database file, it will do so using an in-memory journal. */ assert( pPager->journalMode==PAGER_JOURNALMODE_DELETE || pPager->journalMode==PAGER_JOURNALMODE_MEMORY || pPager->journalMode==PAGER_JOURNALMODE_WAL ); sqlite3OsClose(pPager->jfd); if( !pPager->tempFile ){ rc = sqlite3OsDelete(pPager->pVfs, pPager->zJournal, 0); } } #ifdef SQLITE_CHECK_PAGES sqlite3PcacheIterateDirty(pPager->pPCache, pager_set_pagehash); #endif } sqlite3BitvecDestroy(pPager->pInJournal); pPager->pInJournal = 0; pPager->nRec = 0; sqlite3PcacheCleanAll(pPager->pPCache); if( pagerUseWal(pPager) ){ rc2 = sqlite3WalEndWriteTransaction(pPager->pWal); assert( rc2==SQLITE_OK ); pPager->state = PAGER_SHARED; /* If the connection was in locking_mode=exclusive mode but is no longer, ** drop the EXCLUSIVE lock held on the database file. */ if( !pPager->exclusiveMode && sqlite3WalExclusiveMode(pPager->pWal, 0) ){ rc2 = osUnlock(pPager->fd, SHARED_LOCK); } }else if( !pPager->exclusiveMode ){ rc2 = osUnlock(pPager->fd, SHARED_LOCK); pPager->state = PAGER_SHARED; pPager->changeCountDone = 0; }else if( pPager->state==PAGER_SYNCED ){ pPager->state = PAGER_EXCLUSIVE; } pPager->setMaster = 0; |
︙ | ︙ | |||
1368 1369 1370 1371 1372 1373 1374 1375 1376 1377 1378 1379 1380 1381 | while( i>0 ){ cksum += aData[i]; i -= 200; } return cksum; } /* ** Read a single page from either the journal file (if isMainJrnl==1) or ** from the sub-journal (if isMainJrnl==0) and playback that page. ** The page begins at offset *pOffset into the file. The *pOffset ** value is increased to the start of the next page in the journal. ** ** The isMainJrnl flag is true if this is the main rollback journal and | > > > > > > > > > > > > > > > | 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 | while( i>0 ){ cksum += aData[i]; i -= 200; } return cksum; } /* ** Report the current page size and number of reserved bytes back ** to the codec. */ #ifdef SQLITE_HAS_CODEC static void pagerReportSize(Pager *pPager){ if( pPager->xCodecSizeChng ){ pPager->xCodecSizeChng(pPager->pCodec, pPager->pageSize, (int)pPager->nReserve); } } #else # define pagerReportSize(X) /* No-op if we do not support a codec */ #endif /* ** Read a single page from either the journal file (if isMainJrnl==1) or ** from the sub-journal (if isMainJrnl==0) and playback that page. ** The page begins at offset *pOffset into the file. The *pOffset ** value is increased to the start of the next page in the journal. ** ** The isMainJrnl flag is true if this is the main rollback journal and |
︙ | ︙ | |||
1408 1409 1410 1411 1412 1413 1414 | ** ** If this is a savepoint rollback, then memory may have to be dynamically ** allocated by this function. If this is the case and an allocation fails, ** SQLITE_NOMEM is returned. */ static int pager_playback_one_page( Pager *pPager, /* The pager being played back */ | < < < | > > > > | 1569 1570 1571 1572 1573 1574 1575 1576 1577 1578 1579 1580 1581 1582 1583 1584 1585 1586 1587 1588 1589 1590 1591 1592 1593 1594 1595 1596 1597 1598 1599 1600 1601 1602 1603 | ** ** If this is a savepoint rollback, then memory may have to be dynamically ** allocated by this function. If this is the case and an allocation fails, ** SQLITE_NOMEM is returned. */ static int pager_playback_one_page( Pager *pPager, /* The pager being played back */ i64 *pOffset, /* Offset of record to playback */ Bitvec *pDone, /* Bitvec of pages already played back */ int isMainJrnl, /* 1 -> main journal. 0 -> sub-journal. */ int isSavepnt /* True for a savepoint rollback */ ){ int rc; PgHdr *pPg; /* An existing page in the cache */ Pgno pgno; /* The page number of a page in journal */ u32 cksum; /* Checksum used for sanity checking */ char *aData; /* Temporary storage for the page */ sqlite3_file *jfd; /* The file descriptor for the journal file */ int isSynced; /* True if journal page is synced */ assert( (isMainJrnl&~1)==0 ); /* isMainJrnl is 0 or 1 */ assert( (isSavepnt&~1)==0 ); /* isSavepnt is 0 or 1 */ assert( isMainJrnl || pDone ); /* pDone always used on sub-journals */ assert( isSavepnt || pDone==0 ); /* pDone never used on non-savepoint */ aData = pPager->pTmpSpace; assert( aData ); /* Temp storage must have already been allocated */ assert( pagerUseWal(pPager)==0 || (!isMainJrnl && isSavepnt) ); /* Read the page number and page data from the journal or sub-journal ** file. Return an error code to the caller if an IO error occurs. */ jfd = isMainJrnl ? pPager->jfd : pPager->sjfd; rc = read32bits(jfd, *pOffset, &pgno); if( rc!=SQLITE_OK ) return rc; |
︙ | ︙ | |||
1459 1460 1461 1462 1463 1464 1465 1466 1467 1468 1469 | rc = read32bits(jfd, (*pOffset)-4, &cksum); if( rc ) return rc; if( !isSavepnt && pager_cksum(pPager, (u8*)aData)!=cksum ){ return SQLITE_DONE; } } if( pDone && (rc = sqlite3BitvecSet(pDone, pgno))!=SQLITE_OK ){ return rc; } | > > > > > > > | > > | 1621 1622 1623 1624 1625 1626 1627 1628 1629 1630 1631 1632 1633 1634 1635 1636 1637 1638 1639 1640 1641 1642 1643 1644 1645 1646 1647 1648 | rc = read32bits(jfd, (*pOffset)-4, &cksum); if( rc ) return rc; if( !isSavepnt && pager_cksum(pPager, (u8*)aData)!=cksum ){ return SQLITE_DONE; } } /* If this page has already been played by before during the current ** rollback, then don't bother to play it back again. */ if( pDone && (rc = sqlite3BitvecSet(pDone, pgno))!=SQLITE_OK ){ return rc; } assert( pPager->state==PAGER_RESERVED || pPager->state>=PAGER_EXCLUSIVE ); /* When playing back page 1, restore the nReserve setting */ if( pgno==1 && pPager->nReserve!=((u8*)aData)[20] ){ pPager->nReserve = ((u8*)aData)[20]; pagerReportSize(pPager); } /* If the pager is in RESERVED state, then there must be a copy of this ** page in the pager cache. In this case just update the pager cache, ** not the database file. The page is left marked dirty in this case. ** ** An exception to the above rule: If the database is in no-sync mode ** and a page is moved during an incremental vacuum then the page may |
︙ | ︙ | |||
1497 1498 1499 1500 1501 1502 1503 | ** in the main journal either because the page is not in cache or else ** the page is marked as needSync==0. ** ** 2008-04-14: When attempting to vacuum a corrupt database file, it ** is possible to fail a statement on a database that does not yet exist. ** Do not attempt to write if database file has never been opened. */ | > > > | > > > > > > < | > > | 1668 1669 1670 1671 1672 1673 1674 1675 1676 1677 1678 1679 1680 1681 1682 1683 1684 1685 1686 1687 1688 1689 1690 1691 1692 1693 1694 1695 1696 1697 1698 1699 1700 1701 1702 1703 | ** in the main journal either because the page is not in cache or else ** the page is marked as needSync==0. ** ** 2008-04-14: When attempting to vacuum a corrupt database file, it ** is possible to fail a statement on a database that does not yet exist. ** Do not attempt to write if database file has never been opened. */ if( pagerUseWal(pPager) ){ pPg = 0; }else{ pPg = pager_lookup(pPager, pgno); } assert( pPg || !MEMDB ); PAGERTRACE(("PLAYBACK %d page %d hash(%08x) %s\n", PAGERID(pPager), pgno, pager_datahash(pPager->pageSize, (u8*)aData), (isMainJrnl?"main-journal":"sub-journal") )); if( isMainJrnl ){ isSynced = pPager->noSync || (*pOffset <= pPager->journalHdr); }else{ isSynced = (pPg==0 || 0==(pPg->flags & PGHDR_NEED_SYNC)); } if( (pPager->state>=PAGER_EXCLUSIVE) && isOpen(pPager->fd) && isSynced ){ i64 ofst = (pgno-1)*(i64)pPager->pageSize; testcase( !isSavepnt && pPg!=0 && (pPg->flags&PGHDR_NEED_SYNC)!=0 ); assert( !pagerUseWal(pPager) ); rc = sqlite3OsWrite(pPager->fd, (u8*)aData, pPager->pageSize, ofst); if( pgno>pPager->dbFileSize ){ pPager->dbFileSize = pgno; } if( pPager->pBackup ){ CODEC1(pPager, aData, pgno, 3, rc=SQLITE_NOMEM); sqlite3BackupUpdate(pPager->pBackup, pgno, (u8*)aData); |
︙ | ︙ | |||
1536 1537 1538 1539 1540 1541 1542 | ** ** The solution is to add an in-memory page to the cache containing ** the data just read from the sub-journal. Mark the page as dirty ** and if the pager requires a journal-sync, then mark the page as ** requiring a journal-sync before it is written. */ assert( isSavepnt ); | > > | > > | < | > > | 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 | ** ** The solution is to add an in-memory page to the cache containing ** the data just read from the sub-journal. Mark the page as dirty ** and if the pager requires a journal-sync, then mark the page as ** requiring a journal-sync before it is written. */ assert( isSavepnt ); assert( pPager->doNotSpill==0 ); pPager->doNotSpill++; rc = sqlite3PagerAcquire(pPager, pgno, &pPg, 1); assert( pPager->doNotSpill==1 ); pPager->doNotSpill--; if( rc!=SQLITE_OK ) return rc; pPg->flags &= ~PGHDR_NEED_READ; sqlite3PcacheMakeDirty(pPg); } if( pPg ){ /* No page should ever be explicitly rolled back that is in use, except ** for page 1 which is held in use in order to keep the lock on the ** database active. However such a page may be rolled back as a result ** of an internal error resulting in an automatic call to ** sqlite3PagerRollback(). */ void *pData; pData = pPg->pData; memcpy(pData, (u8*)aData, pPager->pageSize); pPager->xReiniter(pPg); if( isMainJrnl && (!isSavepnt || *pOffset<=pPager->journalHdr) ){ /* If the contents of this page were just restored from the main ** journal file, then its content must be as they were when the ** transaction was first opened. In this case we can mark the page ** as clean, since there will be no need to write it out to the ** database. ** ** There is one exception to this rule. If the page is being rolled ** back as part of a savepoint (or statement) rollback from an ** unsynced portion of the main journal file, then it is not safe ** to mark the page as clean. This is because marking the page as ** clean will clear the PGHDR_NEED_SYNC flag. Since the page is ** already in the journal file (recorded in Pager.pInJournal) and ** the PGHDR_NEED_SYNC flag is cleared, if the page is written to ** again within this transaction, it will be marked as dirty but ** the PGHDR_NEED_SYNC flag will not be set. It could then potentially ** be written out into the database file before its journal file ** segment is synced. If a crash occurs during or following this, ** database corruption may ensue. */ assert( !pagerUseWal(pPager) ); sqlite3PcacheMakeClean(pPg); } #ifdef SQLITE_CHECK_PAGES pPg->pageHash = pager_pagehash(pPg); #endif /* If this was page 1, then restore the value of Pager.dbFileVers. ** Do this before any decoding. */ |
︙ | ︙ | |||
1640 1641 1642 1643 1644 1645 1646 1647 1648 1649 1650 1651 1652 1653 1654 1655 1656 1657 1658 1659 1660 | static int pager_delmaster(Pager *pPager, const char *zMaster){ sqlite3_vfs *pVfs = pPager->pVfs; int rc; /* Return code */ sqlite3_file *pMaster; /* Malloc'd master-journal file descriptor */ sqlite3_file *pJournal; /* Malloc'd child-journal file descriptor */ char *zMasterJournal = 0; /* Contents of master journal file */ i64 nMasterJournal; /* Size of master journal file */ /* Allocate space for both the pJournal and pMaster file descriptors. ** If successful, open the master journal file for reading. */ pMaster = (sqlite3_file *)sqlite3MallocZero(pVfs->szOsFile * 2); pJournal = (sqlite3_file *)(((u8 *)pMaster) + pVfs->szOsFile); if( !pMaster ){ rc = SQLITE_NOMEM; }else{ const int flags = (SQLITE_OPEN_READONLY|SQLITE_OPEN_MASTER_JOURNAL); rc = sqlite3OsOpen(pVfs, zMaster, pMaster, flags, 0); } if( rc!=SQLITE_OK ) goto delmaster_out; | > > > < < < < < < < < | | > > | > > > | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | < | < < | > | 1826 1827 1828 1829 1830 1831 1832 1833 1834 1835 1836 1837 1838 1839 1840 1841 1842 1843 1844 1845 1846 1847 1848 1849 1850 1851 1852 1853 1854 1855 1856 1857 1858 1859 1860 1861 1862 1863 1864 1865 1866 1867 1868 1869 1870 1871 1872 1873 1874 1875 1876 1877 1878 1879 1880 1881 1882 1883 1884 1885 1886 1887 1888 1889 1890 1891 1892 1893 1894 1895 1896 1897 1898 1899 1900 1901 1902 1903 1904 1905 1906 1907 1908 1909 1910 1911 1912 1913 1914 1915 1916 1917 1918 | static int pager_delmaster(Pager *pPager, const char *zMaster){ sqlite3_vfs *pVfs = pPager->pVfs; int rc; /* Return code */ sqlite3_file *pMaster; /* Malloc'd master-journal file descriptor */ sqlite3_file *pJournal; /* Malloc'd child-journal file descriptor */ char *zMasterJournal = 0; /* Contents of master journal file */ i64 nMasterJournal; /* Size of master journal file */ char *zJournal; /* Pointer to one journal within MJ file */ char *zMasterPtr; /* Space to hold MJ filename from a journal file */ int nMasterPtr; /* Amount of space allocated to zMasterPtr[] */ /* Allocate space for both the pJournal and pMaster file descriptors. ** If successful, open the master journal file for reading. */ pMaster = (sqlite3_file *)sqlite3MallocZero(pVfs->szOsFile * 2); pJournal = (sqlite3_file *)(((u8 *)pMaster) + pVfs->szOsFile); if( !pMaster ){ rc = SQLITE_NOMEM; }else{ const int flags = (SQLITE_OPEN_READONLY|SQLITE_OPEN_MASTER_JOURNAL); rc = sqlite3OsOpen(pVfs, zMaster, pMaster, flags, 0); } if( rc!=SQLITE_OK ) goto delmaster_out; /* Load the entire master journal file into space obtained from ** sqlite3_malloc() and pointed to by zMasterJournal. Also obtain ** sufficient space (in zMasterPtr) to hold the names of master ** journal files extracted from regular rollback-journals. */ rc = sqlite3OsFileSize(pMaster, &nMasterJournal); if( rc!=SQLITE_OK ) goto delmaster_out; nMasterPtr = pVfs->mxPathname+1; zMasterJournal = sqlite3Malloc((int)nMasterJournal + nMasterPtr + 1); if( !zMasterJournal ){ rc = SQLITE_NOMEM; goto delmaster_out; } zMasterPtr = &zMasterJournal[nMasterJournal+1]; rc = sqlite3OsRead(pMaster, zMasterJournal, (int)nMasterJournal, 0); if( rc!=SQLITE_OK ) goto delmaster_out; zMasterJournal[nMasterJournal] = 0; zJournal = zMasterJournal; while( (zJournal-zMasterJournal)<nMasterJournal ){ int exists; rc = sqlite3OsAccess(pVfs, zJournal, SQLITE_ACCESS_EXISTS, &exists); if( rc!=SQLITE_OK ){ goto delmaster_out; } if( exists ){ /* One of the journals pointed to by the master journal exists. ** Open it and check if it points at the master journal. If ** so, return without deleting the master journal file. */ int c; int flags = (SQLITE_OPEN_READONLY|SQLITE_OPEN_MAIN_JOURNAL); rc = sqlite3OsOpen(pVfs, zJournal, pJournal, flags, 0); if( rc!=SQLITE_OK ){ goto delmaster_out; } rc = readMasterJournal(pJournal, zMasterPtr, nMasterPtr); sqlite3OsClose(pJournal); if( rc!=SQLITE_OK ){ goto delmaster_out; } c = zMasterPtr[0]!=0 && strcmp(zMasterPtr, zMaster)==0; if( c ){ /* We have a match. Do not delete the master journal file. */ goto delmaster_out; } } zJournal += (sqlite3Strlen30(zJournal)+1); } sqlite3OsClose(pMaster); rc = sqlite3OsDelete(pVfs, zMaster, 0); delmaster_out: sqlite3_free(zMasterJournal); if( pMaster ){ sqlite3OsClose(pMaster); assert( !isOpen(pJournal) ); sqlite3_free(pMaster); } return rc; } /* ** This function is used to change the actual size of the database ** file in the file-system. This only happens when committing a transaction, |
︙ | ︙ | |||
1904 1905 1906 1907 1908 1909 1910 | needPagerReset = isHot; /* This loop terminates either when a readJournalHdr() or ** pager_playback_one_page() call returns SQLITE_DONE or an IO error ** occurs. */ while( 1 ){ | < < | 2088 2089 2090 2091 2092 2093 2094 2095 2096 2097 2098 2099 2100 2101 | needPagerReset = isHot; /* This loop terminates either when a readJournalHdr() or ** pager_playback_one_page() call returns SQLITE_DONE or an IO error ** occurs. */ while( 1 ){ /* Read the next journal header from the journal file. If there are ** not enough bytes left in the journal file for a complete header, or ** it is corrupted, then a process must of failed while writing it. ** This indicates nothing more needs to be rolled back. */ rc = readJournalHdr(pPager, isHot, szJ, &nRec, &mxPg); if( rc!=SQLITE_OK ){ |
︙ | ︙ | |||
1946 1947 1948 1949 1950 1951 1952 | ** the journal, it means that the journal might contain additional ** pages that need to be rolled back and that the number of pages ** should be computed based on the journal file size. */ if( nRec==0 && !isHot && pPager->journalHdr+JOURNAL_HDR_SZ(pPager)==pPager->journalOff ){ nRec = (int)((szJ - pPager->journalOff) / JOURNAL_PG_SZ(pPager)); | < | 2128 2129 2130 2131 2132 2133 2134 2135 2136 2137 2138 2139 2140 2141 | ** the journal, it means that the journal might contain additional ** pages that need to be rolled back and that the number of pages ** should be computed based on the journal file size. */ if( nRec==0 && !isHot && pPager->journalHdr+JOURNAL_HDR_SZ(pPager)==pPager->journalOff ){ nRec = (int)((szJ - pPager->journalOff) / JOURNAL_PG_SZ(pPager)); } /* If this is the first header read from the journal, truncate the ** database file back to its original size. */ if( pPager->journalOff==JOURNAL_HDR_SZ(pPager) ){ rc = pager_truncate(pPager, mxPg); |
︙ | ︙ | |||
1968 1969 1970 1971 1972 1973 1974 | ** database file and/or page cache. */ for(u=0; u<nRec; u++){ if( needPagerReset ){ pager_reset(pPager); needPagerReset = 0; } | | > > > > > > > > | 2149 2150 2151 2152 2153 2154 2155 2156 2157 2158 2159 2160 2161 2162 2163 2164 2165 2166 2167 2168 2169 2170 2171 2172 2173 2174 2175 2176 | ** database file and/or page cache. */ for(u=0; u<nRec; u++){ if( needPagerReset ){ pager_reset(pPager); needPagerReset = 0; } rc = pager_playback_one_page(pPager,&pPager->journalOff,0,1,0); if( rc!=SQLITE_OK ){ if( rc==SQLITE_DONE ){ rc = SQLITE_OK; pPager->journalOff = szJ; break; }else if( rc==SQLITE_IOERR_SHORT_READ ){ /* If the journal has been truncated, simply stop reading and ** processing the journal. This might happen if the journal was ** not completely written and synced prior to a crash. In that ** case, the database should have never been written in the ** first place so it is OK to simply abandon the rollback. */ rc = SQLITE_OK; goto end_playback; }else{ /* If we are unable to rollback, quit and return the error ** code. This will cause the pager to enter the error state ** so that no further harm will be done. Perhaps the next ** process to come along will be able to rollback the database. */ goto end_playback; |
︙ | ︙ | |||
2038 2039 2040 2041 2042 2043 2044 2045 2046 2047 2048 2049 2050 2051 | ** back a journal created by a process with a different sector size ** value. Reset it to the correct value for this process. */ setSectorSize(pPager); return rc; } /* ** Playback savepoint pSavepoint. Or, if pSavepoint==NULL, then playback ** the entire master journal file. The case pSavepoint==NULL occurs when ** a ROLLBACK TO command is invoked on a SAVEPOINT that is a transaction ** savepoint. ** ** When pSavepoint is not NULL (meaning a non-transaction savepoint is | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 2227 2228 2229 2230 2231 2232 2233 2234 2235 2236 2237 2238 2239 2240 2241 2242 2243 2244 2245 2246 2247 2248 2249 2250 2251 2252 2253 2254 2255 2256 2257 2258 2259 2260 2261 2262 2263 2264 2265 2266 2267 2268 2269 2270 2271 2272 2273 2274 2275 2276 2277 2278 2279 2280 2281 2282 2283 2284 2285 2286 2287 2288 2289 2290 2291 2292 2293 2294 2295 2296 2297 2298 2299 2300 2301 2302 2303 2304 2305 2306 2307 2308 2309 2310 2311 2312 2313 2314 2315 2316 2317 2318 2319 2320 2321 2322 2323 2324 2325 2326 2327 2328 2329 2330 2331 2332 2333 2334 2335 2336 2337 2338 2339 2340 2341 2342 2343 2344 2345 2346 2347 2348 2349 2350 2351 2352 2353 2354 2355 2356 2357 2358 2359 2360 2361 2362 2363 2364 2365 2366 2367 2368 2369 2370 2371 2372 2373 2374 2375 2376 2377 2378 2379 2380 2381 2382 2383 2384 2385 2386 2387 2388 2389 2390 2391 2392 2393 2394 2395 2396 2397 2398 2399 2400 2401 2402 2403 2404 2405 2406 2407 2408 2409 2410 2411 2412 2413 2414 2415 2416 2417 2418 2419 2420 2421 2422 2423 2424 2425 2426 2427 2428 2429 2430 2431 2432 2433 2434 2435 2436 2437 2438 2439 2440 2441 2442 2443 2444 2445 2446 2447 2448 2449 2450 2451 2452 2453 2454 2455 2456 2457 2458 2459 2460 2461 2462 2463 2464 2465 2466 2467 2468 2469 2470 2471 2472 2473 2474 2475 2476 2477 2478 2479 2480 2481 2482 2483 2484 2485 2486 2487 2488 2489 2490 2491 2492 2493 2494 2495 2496 | ** back a journal created by a process with a different sector size ** value. Reset it to the correct value for this process. */ setSectorSize(pPager); return rc; } /* ** Read the content for page pPg out of the database file and into ** pPg->pData. A shared lock or greater must be held on the database ** file before this function is called. ** ** If page 1 is read, then the value of Pager.dbFileVers[] is set to ** the value read from the database file. ** ** If an IO error occurs, then the IO error is returned to the caller. ** Otherwise, SQLITE_OK is returned. */ static int readDbPage(PgHdr *pPg){ Pager *pPager = pPg->pPager; /* Pager object associated with page pPg */ Pgno pgno = pPg->pgno; /* Page number to read */ int rc = SQLITE_OK; /* Return code */ int isInWal = 0; /* True if page is in log file */ int pgsz = pPager->pageSize; /* Number of bytes to read */ assert( pPager->state>=PAGER_SHARED && !MEMDB ); assert( isOpen(pPager->fd) ); if( NEVER(!isOpen(pPager->fd)) ){ assert( pPager->tempFile ); memset(pPg->pData, 0, pPager->pageSize); return SQLITE_OK; } if( pagerUseWal(pPager) ){ /* Try to pull the page from the write-ahead log. */ rc = sqlite3WalRead(pPager->pWal, pgno, &isInWal, pgsz, pPg->pData); } if( rc==SQLITE_OK && !isInWal ){ i64 iOffset = (pgno-1)*(i64)pPager->pageSize; rc = sqlite3OsRead(pPager->fd, pPg->pData, pgsz, iOffset); if( rc==SQLITE_IOERR_SHORT_READ ){ rc = SQLITE_OK; } } if( pgno==1 ){ if( rc ){ /* If the read is unsuccessful, set the dbFileVers[] to something ** that will never be a valid file version. dbFileVers[] is a copy ** of bytes 24..39 of the database. Bytes 28..31 should always be ** zero or the size of the database in page. Bytes 32..35 and 35..39 ** should be page numbers which are never 0xffffffff. So filling ** pPager->dbFileVers[] with all 0xff bytes should suffice. ** ** For an encrypted database, the situation is more complex: bytes ** 24..39 of the database are white noise. But the probability of ** white noising equaling 16 bytes of 0xff is vanishingly small so ** we should still be ok. */ memset(pPager->dbFileVers, 0xff, sizeof(pPager->dbFileVers)); }else{ u8 *dbFileVers = &((u8*)pPg->pData)[24]; memcpy(&pPager->dbFileVers, dbFileVers, sizeof(pPager->dbFileVers)); } } CODEC1(pPager, pPg->pData, pgno, 3, rc = SQLITE_NOMEM); PAGER_INCR(sqlite3_pager_readdb_count); PAGER_INCR(pPager->nRead); IOTRACE(("PGIN %p %d\n", pPager, pgno)); PAGERTRACE(("FETCH %d page %d hash(%08x)\n", PAGERID(pPager), pgno, pager_pagehash(pPg))); return rc; } #ifndef SQLITE_OMIT_WAL /* ** This function is invoked once for each page that has already been ** written into the log file when a WAL transaction is rolled back. ** Parameter iPg is the page number of said page. The pCtx argument ** is actually a pointer to the Pager structure. ** ** If page iPg is present in the cache, and has no outstanding references, ** it is discarded. Otherwise, if there are one or more outstanding ** references, the page content is reloaded from the database. If the ** attempt to reload content from the database is required and fails, ** return an SQLite error code. Otherwise, SQLITE_OK. */ static int pagerUndoCallback(void *pCtx, Pgno iPg){ int rc = SQLITE_OK; Pager *pPager = (Pager *)pCtx; PgHdr *pPg; pPg = sqlite3PagerLookup(pPager, iPg); if( pPg ){ if( sqlite3PcachePageRefcount(pPg)==1 ){ sqlite3PcacheDrop(pPg); }else{ rc = readDbPage(pPg); if( rc==SQLITE_OK ){ pPager->xReiniter(pPg); } sqlite3PagerUnref(pPg); } } /* Normally, if a transaction is rolled back, any backup processes are ** updated as data is copied out of the rollback journal and into the ** database. This is not generally possible with a WAL database, as ** rollback involves simply truncating the log file. Therefore, if one ** or more frames have already been written to the log (and therefore ** also copied into the backup databases) as part of this transaction, ** the backups must be restarted. */ sqlite3BackupRestart(pPager->pBackup); return rc; } /* ** This function is called to rollback a transaction on a WAL database. */ static int pagerRollbackWal(Pager *pPager){ int rc; /* Return Code */ PgHdr *pList; /* List of dirty pages to revert */ /* For all pages in the cache that are currently dirty or have already ** been written (but not committed) to the log file, do one of the ** following: ** ** + Discard the cached page (if refcount==0), or ** + Reload page content from the database (if refcount>0). */ pPager->dbSize = pPager->dbOrigSize; rc = sqlite3WalUndo(pPager->pWal, pagerUndoCallback, (void *)pPager); pList = sqlite3PcacheDirtyList(pPager->pPCache); while( pList && rc==SQLITE_OK ){ PgHdr *pNext = pList->pDirty; rc = pagerUndoCallback((void *)pPager, pList->pgno); pList = pNext; } return rc; } /* ** This function is a wrapper around sqlite3WalFrames(). As well as logging ** the contents of the list of pages headed by pList (connected by pDirty), ** this function notifies any active backup processes that the pages have ** changed. */ static int pagerWalFrames( Pager *pPager, /* Pager object */ PgHdr *pList, /* List of frames to log */ Pgno nTruncate, /* Database size after this commit */ int isCommit, /* True if this is a commit */ int sync_flags /* Flags to pass to OsSync() (or 0) */ ){ int rc; /* Return code */ assert( pPager->pWal ); rc = sqlite3WalFrames(pPager->pWal, pPager->pageSize, pList, nTruncate, isCommit, sync_flags ); if( rc==SQLITE_OK && pPager->pBackup ){ PgHdr *p; for(p=pList; p; p=p->pDirty){ sqlite3BackupUpdate(pPager->pBackup, p->pgno, (u8 *)p->pData); } } return rc; } /* ** Begin a read transaction on the WAL. ** ** This routine used to be called "pagerOpenSnapshot()" because it essentially ** makes a snapshot of the database at the current point in time and preserves ** that snapshot for use by the reader in spite of concurrently changes by ** other writers or checkpointers. */ static int pagerBeginReadTransaction(Pager *pPager){ int rc; /* Return code */ int changed = 0; /* True if cache must be reset */ assert( pagerUseWal(pPager) ); /* sqlite3WalEndReadTransaction() was not called for the previous ** transaction in locking_mode=EXCLUSIVE. So call it now. If we ** are in locking_mode=NORMAL and EndRead() was previously called, ** the duplicate call is harmless. */ sqlite3WalEndReadTransaction(pPager->pWal); rc = sqlite3WalBeginReadTransaction(pPager->pWal, &changed); if( rc==SQLITE_OK ){ int dummy; if( changed ){ pager_reset(pPager); assert( pPager->errCode || pPager->dbSizeValid==0 ); } rc = sqlite3PagerPagecount(pPager, &dummy); } pPager->state = PAGER_SHARED; return rc; } /* ** Check if the *-wal file that corresponds to the database opened by pPager ** exists if the database is not empy, or verify that the *-wal file does ** not exist (by deleting it) if the database file is empty. ** ** If the database is not empty and the *-wal file exists, open the pager ** in WAL mode. If the database is empty or if no *-wal file exists and ** if no error occurs, make sure Pager.journalMode is not set to ** PAGER_JOURNALMODE_WAL. ** ** Return SQLITE_OK or an error code. ** ** If the WAL file is opened, also open a snapshot (read transaction). ** ** The caller must hold a SHARED lock on the database file to call this ** function. Because an EXCLUSIVE lock on the db file is required to delete ** a WAL on a none-empty database, this ensures there is no race condition ** between the xAccess() below and an xDelete() being executed by some ** other connection. */ static int pagerOpenWalIfPresent(Pager *pPager){ int rc = SQLITE_OK; if( !pPager->tempFile ){ int isWal; /* True if WAL file exists */ int nPage; /* Size of the database file */ assert( pPager->state>=SHARED_LOCK ); rc = sqlite3PagerPagecount(pPager, &nPage); if( rc ) return rc; if( nPage==0 ){ rc = sqlite3OsDelete(pPager->pVfs, pPager->zWal, 0); isWal = 0; }else{ rc = sqlite3OsAccess( pPager->pVfs, pPager->zWal, SQLITE_ACCESS_EXISTS, &isWal ); } if( rc==SQLITE_OK ){ if( isWal ){ pager_reset(pPager); rc = sqlite3PagerOpenWal(pPager, 0); if( rc==SQLITE_OK ){ rc = pagerBeginReadTransaction(pPager); } }else if( pPager->journalMode==PAGER_JOURNALMODE_WAL ){ pPager->journalMode = PAGER_JOURNALMODE_DELETE; } } } return rc; } #endif /* ** Playback savepoint pSavepoint. Or, if pSavepoint==NULL, then playback ** the entire master journal file. The case pSavepoint==NULL occurs when ** a ROLLBACK TO command is invoked on a SAVEPOINT that is a transaction ** savepoint. ** ** When pSavepoint is not NULL (meaning a non-transaction savepoint is |
︙ | ︙ | |||
2094 2095 2096 2097 2098 2099 2100 2101 2102 2103 2104 2105 2106 2107 2108 2109 2110 2111 2112 2113 2114 2115 | } } /* Set the database size back to the value it was before the savepoint ** being reverted was opened. */ pPager->dbSize = pSavepoint ? pSavepoint->nOrig : pPager->dbOrigSize; /* Use pPager->journalOff as the effective size of the main rollback ** journal. The actual file might be larger than this in ** PAGER_JOURNALMODE_TRUNCATE or PAGER_JOURNALMODE_PERSIST. But anything ** past pPager->journalOff is off-limits to us. */ szJ = pPager->journalOff; /* Begin by rolling back records from the main journal starting at ** PagerSavepoint.iOffset and continuing to the next journal header. ** There might be records in the main journal that have a page number ** greater than the current database size (pPager->dbSize) but those ** will be skipped automatically. Pages are added to pDone as they ** are played back. */ | > > > > > > | | | 2539 2540 2541 2542 2543 2544 2545 2546 2547 2548 2549 2550 2551 2552 2553 2554 2555 2556 2557 2558 2559 2560 2561 2562 2563 2564 2565 2566 2567 2568 2569 2570 2571 2572 2573 2574 2575 2576 2577 2578 | } } /* Set the database size back to the value it was before the savepoint ** being reverted was opened. */ pPager->dbSize = pSavepoint ? pSavepoint->nOrig : pPager->dbOrigSize; pPager->changeCountDone = pPager->tempFile; if( !pSavepoint && pagerUseWal(pPager) ){ return pagerRollbackWal(pPager); } /* Use pPager->journalOff as the effective size of the main rollback ** journal. The actual file might be larger than this in ** PAGER_JOURNALMODE_TRUNCATE or PAGER_JOURNALMODE_PERSIST. But anything ** past pPager->journalOff is off-limits to us. */ szJ = pPager->journalOff; assert( pagerUseWal(pPager)==0 || szJ==0 ); /* Begin by rolling back records from the main journal starting at ** PagerSavepoint.iOffset and continuing to the next journal header. ** There might be records in the main journal that have a page number ** greater than the current database size (pPager->dbSize) but those ** will be skipped automatically. Pages are added to pDone as they ** are played back. */ if( pSavepoint && !pagerUseWal(pPager) ){ iHdrOff = pSavepoint->iHdrOffset ? pSavepoint->iHdrOffset : szJ; pPager->journalOff = pSavepoint->iOffset; while( rc==SQLITE_OK && pPager->journalOff<iHdrOff ){ rc = pager_playback_one_page(pPager, &pPager->journalOff, pDone, 1, 1); } assert( rc!=SQLITE_DONE ); }else{ pPager->journalOff = 0; } /* Continue rolling back records out of the main journal starting at |
︙ | ︙ | |||
2143 2144 2145 2146 2147 2148 2149 | */ if( nJRec==0 && pPager->journalHdr+JOURNAL_HDR_SZ(pPager)==pPager->journalOff ){ nJRec = (u32)((szJ - pPager->journalOff)/JOURNAL_PG_SZ(pPager)); } for(ii=0; rc==SQLITE_OK && ii<nJRec && pPager->journalOff<szJ; ii++){ | | | > > > > | > | 2594 2595 2596 2597 2598 2599 2600 2601 2602 2603 2604 2605 2606 2607 2608 2609 2610 2611 2612 2613 2614 2615 2616 2617 2618 2619 2620 2621 2622 2623 2624 2625 2626 2627 2628 2629 2630 2631 2632 2633 2634 2635 2636 | */ if( nJRec==0 && pPager->journalHdr+JOURNAL_HDR_SZ(pPager)==pPager->journalOff ){ nJRec = (u32)((szJ - pPager->journalOff)/JOURNAL_PG_SZ(pPager)); } for(ii=0; rc==SQLITE_OK && ii<nJRec && pPager->journalOff<szJ; ii++){ rc = pager_playback_one_page(pPager, &pPager->journalOff, pDone, 1, 1); } assert( rc!=SQLITE_DONE ); } assert( rc!=SQLITE_OK || pPager->journalOff>=szJ ); /* Finally, rollback pages from the sub-journal. Page that were ** previously rolled back out of the main journal (and are hence in pDone) ** will be skipped. Out-of-range pages are also skipped. */ if( pSavepoint ){ u32 ii; /* Loop counter */ i64 offset = pSavepoint->iSubRec*(4+pPager->pageSize); if( pagerUseWal(pPager) ){ rc = sqlite3WalSavepointUndo(pPager->pWal, pSavepoint->aWalData); } for(ii=pSavepoint->iSubRec; rc==SQLITE_OK && ii<pPager->nSubRec; ii++){ assert( offset==ii*(4+pPager->pageSize) ); rc = pager_playback_one_page(pPager, &offset, pDone, 0, 1); } assert( rc!=SQLITE_DONE ); } sqlite3BitvecDestroy(pDone); if( rc==SQLITE_OK ){ pPager->journalOff = szJ; } return rc; } /* ** Change the maximum number of in-memory pages that are allowed. */ void sqlite3PagerSetCachesize(Pager *pPager, int mxPage){ |
︙ | ︙ | |||
2284 2285 2286 2287 2288 2289 2290 | int (*xBusyHandler)(void *), /* Pointer to busy-handler function */ void *pBusyHandlerArg /* Argument to pass to xBusyHandler */ ){ pPager->xBusyHandler = xBusyHandler; pPager->pBusyHandlerArg = pBusyHandlerArg; } | < < < < < < < < < < < < < < < | 2740 2741 2742 2743 2744 2745 2746 2747 2748 2749 2750 2751 2752 2753 | int (*xBusyHandler)(void *), /* Pointer to busy-handler function */ void *pBusyHandlerArg /* Argument to pass to xBusyHandler */ ){ pPager->xBusyHandler = xBusyHandler; pPager->pBusyHandlerArg = pBusyHandlerArg; } /* ** Change the page size used by the Pager object. The new page size ** is passed in *pPageSize. ** ** If the pager is in the error state when this function is called, it ** is a no-op. The value returned is the error state error code (i.e. ** one of SQLITE_IOERR, SQLITE_CORRUPT or SQLITE_FULL). |
︙ | ︙ | |||
2379 2380 2381 2382 2383 2384 2385 2386 2387 2388 | ** Attempt to set the maximum database page count if mxPage is positive. ** Make no changes if mxPage is zero or negative. And never reduce the ** maximum page count below the current size of the database. ** ** Regardless of mxPage, return the current maximum page count. */ int sqlite3PagerMaxPageCount(Pager *pPager, int mxPage){ if( mxPage>0 ){ pPager->mxPgno = mxPage; } | > > | > > | 2820 2821 2822 2823 2824 2825 2826 2827 2828 2829 2830 2831 2832 2833 2834 2835 2836 2837 2838 2839 2840 2841 | ** Attempt to set the maximum database page count if mxPage is positive. ** Make no changes if mxPage is zero or negative. And never reduce the ** maximum page count below the current size of the database. ** ** Regardless of mxPage, return the current maximum page count. */ int sqlite3PagerMaxPageCount(Pager *pPager, int mxPage){ int nPage; if( mxPage>0 ){ pPager->mxPgno = mxPage; } if( pPager->state!=PAGER_UNLOCK ){ sqlite3PagerPagecount(pPager, &nPage); assert( (int)pPager->mxPgno>=nPage ); } return pPager->mxPgno; } /* ** The following set of routines are used to disable the simulated ** I/O error mechanism. These routines are used to avoid simulated ** errors in places where we do not care about errors. |
︙ | ︙ | |||
2428 2429 2430 2431 2432 2433 2434 2435 2436 2437 2438 2439 2440 2441 | ** the error code is returned to the caller and the contents of the ** output buffer undefined. */ int sqlite3PagerReadFileheader(Pager *pPager, int N, unsigned char *pDest){ int rc = SQLITE_OK; memset(pDest, 0, N); assert( isOpen(pPager->fd) || pPager->tempFile ); if( isOpen(pPager->fd) ){ IOTRACE(("DBHDR %p 0 %d\n", pPager, N)) rc = sqlite3OsRead(pPager->fd, pDest, N, 0); if( rc==SQLITE_IOERR_SHORT_READ ){ rc = SQLITE_OK; } } | > > > > > > > | 2873 2874 2875 2876 2877 2878 2879 2880 2881 2882 2883 2884 2885 2886 2887 2888 2889 2890 2891 2892 2893 | ** the error code is returned to the caller and the contents of the ** output buffer undefined. */ int sqlite3PagerReadFileheader(Pager *pPager, int N, unsigned char *pDest){ int rc = SQLITE_OK; memset(pDest, 0, N); assert( isOpen(pPager->fd) || pPager->tempFile ); /* This routine is only called by btree immediately after creating ** the Pager object. There has not been an opportunity to transition ** to WAL mode yet. */ assert( !pagerUseWal(pPager) ); if( isOpen(pPager->fd) ){ IOTRACE(("DBHDR %p 0 %d\n", pPager, N)) rc = sqlite3OsRead(pPager->fd, pDest, N, 0); if( rc==SQLITE_IOERR_SHORT_READ ){ rc = SQLITE_OK; } } |
︙ | ︙ | |||
2454 2455 2456 2457 2458 2459 2460 | ** the query attempt returns an IO error, the IO error code is returned ** and *pnPage is left unchanged. ** ** Otherwise, if everything is successful, then SQLITE_OK is returned ** and *pnPage is set to the number of pages in the database. */ int sqlite3PagerPagecount(Pager *pPager, int *pnPage){ | | < < < < < > > > > > | | > | | | > | | | | > < | < | 2906 2907 2908 2909 2910 2911 2912 2913 2914 2915 2916 2917 2918 2919 2920 2921 2922 2923 2924 2925 2926 2927 2928 2929 2930 2931 2932 2933 2934 2935 2936 2937 2938 2939 2940 2941 2942 2943 2944 2945 2946 2947 2948 2949 2950 2951 2952 2953 2954 2955 2956 2957 2958 2959 2960 2961 2962 2963 | ** the query attempt returns an IO error, the IO error code is returned ** and *pnPage is left unchanged. ** ** Otherwise, if everything is successful, then SQLITE_OK is returned ** and *pnPage is set to the number of pages in the database. */ int sqlite3PagerPagecount(Pager *pPager, int *pnPage){ Pgno nPage = 0; /* Value to return via *pnPage */ /* Determine the number of pages in the file. Store this in nPage. */ if( pPager->dbSizeValid ){ nPage = pPager->dbSize; }else{ int rc; /* Error returned by OsFileSize() */ i64 n = 0; /* File size in bytes returned by OsFileSize() */ if( pagerUseWal(pPager) && pPager->state!=PAGER_UNLOCK ){ sqlite3WalDbsize(pPager->pWal, &nPage); } if( nPage==0 ){ assert( isOpen(pPager->fd) || pPager->tempFile ); if( isOpen(pPager->fd) ){ if( SQLITE_OK!=(rc = sqlite3OsFileSize(pPager->fd, &n)) ){ pager_error(pPager, rc); return rc; } } if( n>0 && n<pPager->pageSize ){ nPage = 1; }else{ nPage = (Pgno)(n / pPager->pageSize); } } if( pPager->state!=PAGER_UNLOCK ){ pPager->dbSize = nPage; pPager->dbFileSize = nPage; pPager->dbSizeValid = 1; } } /* If the current number of pages in the file is greater than the ** configured maximum pager number, increase the allowed limit so ** that the file can be read. */ if( nPage>pPager->mxPgno ){ pPager->mxPgno = (Pgno)nPage; } /* Set the output variable and return SQLITE_OK */ *pnPage = nPage; return SQLITE_OK; } /* ** Try to obtain a lock of type locktype on the database file. If ** a similar or greater lock is already held, this function is a no-op |
︙ | ︙ | |||
2601 2602 2603 2604 2605 2606 2607 2608 2609 2610 2611 2612 2613 2614 2615 2616 2617 2618 2619 2620 2621 2622 2623 2624 2625 2626 2627 2628 2629 2630 2631 2632 2633 2634 2635 2636 | assert( pPager->dbSizeValid ); assert( pPager->dbSize>=nPage ); assert( pPager->state>=PAGER_RESERVED ); pPager->dbSize = nPage; assertTruncateConstraint(pPager); } /* ** Shutdown the page cache. Free all memory and close all files. ** ** If a transaction was in progress when this routine is called, that ** transaction is rolled back. All outstanding pages are invalidated ** and their memory is freed. Any attempt to use a page associated ** with this page cache after this function returns will likely ** result in a coredump. ** ** This function always succeeds. If a transaction is active an attempt ** is made to roll it back. If an error occurs during the rollback ** a hot journal may be left in the filesystem but no error is returned ** to the caller. */ int sqlite3PagerClose(Pager *pPager){ disable_simulated_io_errors(); sqlite3BeginBenignMalloc(); pPager->errCode = 0; pPager->exclusiveMode = 0; pager_reset(pPager); if( MEMDB ){ pager_unlock(pPager); }else{ /* Set Pager.journalHdr to -1 for the benefit of the pager_playback() ** call which may be made from within pagerUnlockAndRollback(). If it ** is not -1, then the unsynced portion of an open journal file may ** be played back into the database. If a power failure occurs while ** this is happening, the database may become corrupt. */ | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > | | 3054 3055 3056 3057 3058 3059 3060 3061 3062 3063 3064 3065 3066 3067 3068 3069 3070 3071 3072 3073 3074 3075 3076 3077 3078 3079 3080 3081 3082 3083 3084 3085 3086 3087 3088 3089 3090 3091 3092 3093 3094 3095 3096 3097 3098 3099 3100 3101 3102 3103 3104 3105 3106 3107 3108 3109 3110 3111 3112 3113 3114 3115 3116 3117 3118 3119 3120 3121 3122 3123 3124 3125 3126 3127 3128 3129 3130 3131 3132 3133 3134 3135 3136 3137 3138 3139 3140 3141 3142 3143 | assert( pPager->dbSizeValid ); assert( pPager->dbSize>=nPage ); assert( pPager->state>=PAGER_RESERVED ); pPager->dbSize = nPage; assertTruncateConstraint(pPager); } /* ** This function is called before attempting a hot-journal rollback. It ** syncs the journal file to disk, then sets pPager->journalHdr to the ** size of the journal file so that the pager_playback() routine knows ** that the entire journal file has been synced. ** ** Syncing a hot-journal to disk before attempting to roll it back ensures ** that if a power-failure occurs during the rollback, the process that ** attempts rollback following system recovery sees the same journal ** content as this process. ** ** If everything goes as planned, SQLITE_OK is returned. Otherwise, ** an SQLite error code. */ static int pagerSyncHotJournal(Pager *pPager){ int rc = SQLITE_OK; if( !pPager->noSync ){ rc = sqlite3OsSync(pPager->jfd, SQLITE_SYNC_NORMAL); } if( rc==SQLITE_OK ){ rc = sqlite3OsFileSize(pPager->jfd, &pPager->journalHdr); } return rc; } /* ** Shutdown the page cache. Free all memory and close all files. ** ** If a transaction was in progress when this routine is called, that ** transaction is rolled back. All outstanding pages are invalidated ** and their memory is freed. Any attempt to use a page associated ** with this page cache after this function returns will likely ** result in a coredump. ** ** This function always succeeds. If a transaction is active an attempt ** is made to roll it back. If an error occurs during the rollback ** a hot journal may be left in the filesystem but no error is returned ** to the caller. */ int sqlite3PagerClose(Pager *pPager){ u8 *pTmp = (u8 *)pPager->pTmpSpace; disable_simulated_io_errors(); sqlite3BeginBenignMalloc(); pPager->errCode = 0; pPager->exclusiveMode = 0; #ifndef SQLITE_OMIT_WAL sqlite3WalClose(pPager->pWal, (pPager->noSync ? 0 : pPager->sync_flags), pPager->pageSize, pTmp ); pPager->pWal = 0; #endif pager_reset(pPager); if( MEMDB ){ pager_unlock(pPager); }else{ /* Set Pager.journalHdr to -1 for the benefit of the pager_playback() ** call which may be made from within pagerUnlockAndRollback(). If it ** is not -1, then the unsynced portion of an open journal file may ** be played back into the database. If a power failure occurs while ** this is happening, the database may become corrupt. */ if( isOpen(pPager->jfd) ){ pPager->errCode = pagerSyncHotJournal(pPager); } pagerUnlockAndRollback(pPager); } sqlite3EndBenignMalloc(); enable_simulated_io_errors(); PAGERTRACE(("CLOSE %d\n", PAGERID(pPager))); IOTRACE(("CLOSE %p\n", pPager)) sqlite3OsClose(pPager->jfd); sqlite3OsClose(pPager->fd); sqlite3PageFree(pTmp); sqlite3PcacheClose(pPager->pPCache); #ifdef SQLITE_HAS_CODEC if( pPager->xCodecFree ) pPager->xCodecFree(pPager->pCodec); #endif assert( !pPager->aSavepoint && !pPager->pInJournal ); |
︙ | ︙ | |||
2720 2721 2722 2723 2724 2725 2726 | if( 0==(iDc&SQLITE_IOCAP_SAFE_APPEND) ){ /* This block deals with an obscure problem. If the last connection ** that wrote to this database was operating in persistent-journal ** mode, then the journal file may at this point actually be larger ** than Pager.journalOff bytes. If the next thing in the journal ** file happens to be a journal-header (written as part of the | | | | | | 3211 3212 3213 3214 3215 3216 3217 3218 3219 3220 3221 3222 3223 3224 3225 3226 3227 3228 3229 3230 3231 3232 3233 3234 3235 3236 3237 3238 3239 3240 3241 3242 3243 3244 3245 3246 3247 | if( 0==(iDc&SQLITE_IOCAP_SAFE_APPEND) ){ /* This block deals with an obscure problem. If the last connection ** that wrote to this database was operating in persistent-journal ** mode, then the journal file may at this point actually be larger ** than Pager.journalOff bytes. If the next thing in the journal ** file happens to be a journal-header (written as part of the ** previous connection's transaction), and a crash or power-failure ** occurs after nRec is updated but before this connection writes ** anything else to the journal file (or commits/rolls back its ** transaction), then SQLite may become confused when doing the ** hot-journal rollback following recovery. It may roll back all ** of this connections data, then proceed to rolling back the old, ** out-of-date data that follows it. Database corruption. ** ** To work around this, if the journal file does appear to contain ** a valid header following Pager.journalOff, then write a 0x00 ** byte to the start of it to prevent it from being recognized. ** ** Variable iNextHdrOffset is set to the offset at which this ** problematic header will occur, if it exists. aMagic is used ** as a temporary buffer to inspect the first couple of bytes of ** the potential journal header. */ i64 iNextHdrOffset; u8 aMagic[8]; u8 zHeader[sizeof(aJournalMagic)+4]; memcpy(zHeader, aJournalMagic, sizeof(aJournalMagic)); put32bits(&zHeader[sizeof(aJournalMagic)], pPager->nRec); iNextHdrOffset = journalHdrOffset(pPager); rc = sqlite3OsRead(pPager->jfd, aMagic, 8, iNextHdrOffset); if( rc==SQLITE_OK && 0==memcmp(aMagic, aJournalMagic, 8) ){ static const u8 zerobyte = 0; rc = sqlite3OsWrite(pPager->jfd, &zerobyte, 1, iNextHdrOffset); } |
︙ | ︙ | |||
2774 2775 2776 2777 2778 2779 2780 | IOTRACE(("JSYNC %p\n", pPager)) rc = sqlite3OsSync(pPager->jfd, pPager->sync_flags); if( rc!=SQLITE_OK ) return rc; } IOTRACE(("JHDR %p %lld\n", pPager, pPager->journalHdr)); rc = sqlite3OsWrite( pPager->jfd, zHeader, sizeof(zHeader), pPager->journalHdr | | > | 3265 3266 3267 3268 3269 3270 3271 3272 3273 3274 3275 3276 3277 3278 3279 3280 3281 3282 3283 3284 3285 3286 3287 3288 3289 3290 3291 3292 3293 3294 3295 3296 3297 | IOTRACE(("JSYNC %p\n", pPager)) rc = sqlite3OsSync(pPager->jfd, pPager->sync_flags); if( rc!=SQLITE_OK ) return rc; } IOTRACE(("JHDR %p %lld\n", pPager, pPager->journalHdr)); rc = sqlite3OsWrite( pPager->jfd, zHeader, sizeof(zHeader), pPager->journalHdr ); if( rc!=SQLITE_OK ) return rc; } if( 0==(iDc&SQLITE_IOCAP_SEQUENTIAL) ){ PAGERTRACE(("SYNC journal of %d\n", PAGERID(pPager))); IOTRACE(("JSYNC %p\n", pPager)) rc = sqlite3OsSync(pPager->jfd, pPager->sync_flags| (pPager->sync_flags==SQLITE_SYNC_FULL?SQLITE_SYNC_DATAONLY:0) ); if( rc!=SQLITE_OK ) return rc; } } /* The journal file was just successfully synced. Set Pager.needSync ** to zero and clear the PGHDR_NEED_SYNC flag on all pagess. */ pPager->needSync = 0; pPager->journalStarted = 1; pPager->journalHdr = pPager->journalOff; sqlite3PcacheClearSyncFlags(pPager->pPCache); } return SQLITE_OK; } /* |
︙ | ︙ | |||
2830 2831 2832 2833 2834 2835 2836 | ** in Pager.dbFileVers[] is updated to match the new value stored in ** the database file. ** ** If everything is successful, SQLITE_OK is returned. If an IO error ** occurs, an IO error code is returned. Or, if the EXCLUSIVE lock cannot ** be obtained, SQLITE_BUSY is returned. */ | | < < < < > > > > > > > > > > > > | 3322 3323 3324 3325 3326 3327 3328 3329 3330 3331 3332 3333 3334 3335 3336 3337 3338 3339 3340 3341 3342 3343 3344 3345 3346 3347 3348 3349 3350 3351 3352 3353 3354 3355 3356 3357 3358 3359 3360 3361 3362 3363 3364 3365 3366 3367 3368 3369 3370 3371 3372 3373 3374 3375 3376 3377 3378 | ** in Pager.dbFileVers[] is updated to match the new value stored in ** the database file. ** ** If everything is successful, SQLITE_OK is returned. If an IO error ** occurs, an IO error code is returned. Or, if the EXCLUSIVE lock cannot ** be obtained, SQLITE_BUSY is returned. */ static int pager_write_pagelist(Pager *pPager, PgHdr *pList){ int rc; /* Return code */ /* At this point there may be either a RESERVED or EXCLUSIVE lock on the ** database file. If there is already an EXCLUSIVE lock, the following ** call is a no-op. ** ** Moving the lock from RESERVED to EXCLUSIVE actually involves going ** through an intermediate state PENDING. A PENDING lock prevents new ** readers from attaching to the database but is unsufficient for us to ** write. The idea of a PENDING lock is to prevent new readers from ** coming in while we wait for existing readers to clear. ** ** While the pager is in the RESERVED state, the original database file ** is unchanged and we can rollback without having to playback the ** journal into the original database file. Once we transition to ** EXCLUSIVE, it means the database file has been changed and any rollback ** will require a journal playback. */ assert( !pagerUseWal(pPager) ); assert( pPager->state>=PAGER_RESERVED ); rc = pager_wait_on_lock(pPager, EXCLUSIVE_LOCK); /* If the file is a temp-file has not yet been opened, open it now. It ** is not possible for rc to be other than SQLITE_OK if this branch ** is taken, as pager_wait_on_lock() is a no-op for temp-files. */ if( !isOpen(pPager->fd) ){ assert( pPager->tempFile && rc==SQLITE_OK ); rc = pagerOpentemp(pPager, pPager->fd, pPager->vfsFlags); } #if 0 /* Before the first write, give the VFS a hint of what the final ** file size will be. */ assert( rc!=SQLITE_OK || isOpen(pPager->fd) ); if( rc==SQLITE_OK && pPager->dbSize>(pPager->dbOrigSize+1) ){ sqlite3_int64 szFile = pPager->pageSize * (sqlite3_int64)pPager->dbSize; sqlite3OsFileControl(pPager->fd, SQLITE_FCNTL_SIZE_HINT, &szFile); } #endif /* Before the first write, give the VFS a hint of what the final ** file size will be. */ if( rc==SQLITE_OK && pPager->dbSize>(pPager->dbFileSize+1) && isOpen(pPager->fd) ){ |
︙ | ︙ | |||
2927 2928 2929 2930 2931 2932 2933 2934 2935 2936 2937 2938 2939 2940 2941 2942 2943 2944 2945 2946 2947 2948 2949 2950 | pList->pageHash = pager_pagehash(pList); #endif pList = pList->pDirty; } return rc; } /* ** Append a record of the current state of page pPg to the sub-journal. ** It is the callers responsibility to use subjRequiresPage() to check ** that it is really required before calling this function. ** ** If successful, set the bit corresponding to pPg->pgno in the bitvecs ** for all open savepoints before returning. ** ** This function returns SQLITE_OK if everything is successful, an IO ** error code if the attempt to write to the sub-journal fails, or ** SQLITE_NOMEM if a malloc fails while setting a bit in a savepoint ** bitvec. */ static int subjournalPage(PgHdr *pPg){ int rc = SQLITE_OK; Pager *pPager = pPg->pPager; | > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > > > > > > | | | | | | < < | | | > < | 3427 3428 3429 3430 3431 3432 3433 3434 3435 3436 3437 3438 3439 3440 3441 3442 3443 3444 3445 3446 3447 3448 3449 3450 3451 3452 3453 3454 3455 3456 3457 3458 3459 3460 3461 3462 3463 3464 3465 3466 3467 3468 3469 3470 3471 3472 3473 3474 3475 3476 3477 3478 3479 3480 3481 3482 3483 3484 3485 3486 3487 3488 3489 3490 3491 3492 3493 3494 3495 3496 3497 3498 3499 3500 3501 3502 3503 3504 3505 3506 3507 3508 3509 3510 3511 | pList->pageHash = pager_pagehash(pList); #endif pList = pList->pDirty; } return rc; } /* ** Ensure that the sub-journal file is open. If it is already open, this ** function is a no-op. ** ** SQLITE_OK is returned if everything goes according to plan. An ** SQLITE_IOERR_XXX error code is returned if a call to sqlite3OsOpen() ** fails. */ static int openSubJournal(Pager *pPager){ int rc = SQLITE_OK; if( !isOpen(pPager->sjfd) ){ if( pPager->journalMode==PAGER_JOURNALMODE_MEMORY || pPager->subjInMemory ){ sqlite3MemJournalOpen(pPager->sjfd); }else{ rc = pagerOpentemp(pPager, pPager->sjfd, SQLITE_OPEN_SUBJOURNAL); } } return rc; } /* ** Append a record of the current state of page pPg to the sub-journal. ** It is the callers responsibility to use subjRequiresPage() to check ** that it is really required before calling this function. ** ** If successful, set the bit corresponding to pPg->pgno in the bitvecs ** for all open savepoints before returning. ** ** This function returns SQLITE_OK if everything is successful, an IO ** error code if the attempt to write to the sub-journal fails, or ** SQLITE_NOMEM if a malloc fails while setting a bit in a savepoint ** bitvec. */ static int subjournalPage(PgHdr *pPg){ int rc = SQLITE_OK; Pager *pPager = pPg->pPager; if( pPager->journalMode!=PAGER_JOURNALMODE_OFF ){ /* Open the sub-journal, if it has not already been opened */ assert( pPager->useJournal ); assert( isOpen(pPager->jfd) || pagerUseWal(pPager) ); assert( isOpen(pPager->sjfd) || pPager->nSubRec==0 ); assert( pagerUseWal(pPager) || pageInJournal(pPg) || pPg->pgno>pPager->dbOrigSize ); rc = openSubJournal(pPager); /* If the sub-journal was opened successfully (or was already open), ** write the journal record into the file. */ if( rc==SQLITE_OK ){ void *pData = pPg->pData; i64 offset = pPager->nSubRec*(4+pPager->pageSize); char *pData2; CODEC2(pPager, pData, pPg->pgno, 7, return SQLITE_NOMEM, pData2); PAGERTRACE(("STMT-JOURNAL %d page %d\n", PAGERID(pPager), pPg->pgno)); rc = write32bits(pPager->sjfd, offset, pPg->pgno); if( rc==SQLITE_OK ){ rc = sqlite3OsWrite(pPager->sjfd, pData2, pPager->pageSize, offset+4); } } } if( rc==SQLITE_OK ){ pPager->nSubRec++; assert( pPager->nSavepoint>0 ); rc = addToSavepointBitvecs(pPager, pPg->pgno); } return rc; } /* ** This function is called by the pcache layer when it has reached some ** soft memory limit. The first argument is a pointer to a Pager object ** (cast as a void*). The pager is always 'purgeable' (not an in-memory ** database). The second argument is a reference to a page that is ** currently dirty but has no outstanding references. The page |
︙ | ︙ | |||
2993 2994 2995 2996 2997 2998 2999 | static int pagerStress(void *p, PgHdr *pPg){ Pager *pPager = (Pager *)p; int rc = SQLITE_OK; assert( pPg->pPager==pPager ); assert( pPg->flags&PGHDR_DIRTY ); | | < | < < | < | | > > | < | | < > > > > > > > > > > > > | | > | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | < | > | 3525 3526 3527 3528 3529 3530 3531 3532 3533 3534 3535 3536 3537 3538 3539 3540 3541 3542 3543 3544 3545 3546 3547 3548 3549 3550 3551 3552 3553 3554 3555 3556 3557 3558 3559 3560 3561 3562 3563 3564 3565 3566 3567 3568 3569 3570 3571 3572 3573 3574 3575 3576 3577 3578 3579 3580 3581 3582 3583 3584 3585 3586 3587 3588 3589 3590 3591 3592 3593 3594 3595 3596 3597 3598 3599 3600 3601 3602 3603 3604 3605 3606 3607 3608 3609 3610 3611 3612 3613 3614 3615 | static int pagerStress(void *p, PgHdr *pPg){ Pager *pPager = (Pager *)p; int rc = SQLITE_OK; assert( pPg->pPager==pPager ); assert( pPg->flags&PGHDR_DIRTY ); /* The doNotSyncSpill flag is set during times when doing a sync of ** journal (and adding a new header) is not allowed. This occurs ** during calls to sqlite3PagerWrite() while trying to journal multiple ** pages belonging to the same sector. ** ** The doNotSpill flag inhibits all cache spilling regardless of whether ** or not a sync is required. This is set during a rollback. ** ** Spilling is also inhibited when in an error state. */ if( pPager->errCode ) return SQLITE_OK; if( pPager->doNotSpill ) return SQLITE_OK; if( pPager->doNotSyncSpill && (pPg->flags & PGHDR_NEED_SYNC)!=0 ){ return SQLITE_OK; } pPg->pDirty = 0; if( pagerUseWal(pPager) ){ /* Write a single frame for this page to the log. */ if( subjRequiresPage(pPg) ){ rc = subjournalPage(pPg); } if( rc==SQLITE_OK ){ rc = pagerWalFrames(pPager, pPg, 0, 0, 0); } }else{ /* Sync the journal file if required. */ if( pPg->flags&PGHDR_NEED_SYNC ){ assert( !pPager->noSync ); rc = syncJournal(pPager); if( rc==SQLITE_OK && !(pPager->journalMode==PAGER_JOURNALMODE_MEMORY) && !(sqlite3OsDeviceCharacteristics(pPager->fd)&SQLITE_IOCAP_SAFE_APPEND) ){ pPager->nRec = 0; rc = writeJournalHdr(pPager); } } /* If the page number of this page is larger than the current size of ** the database image, it may need to be written to the sub-journal. ** This is because the call to pager_write_pagelist() below will not ** actually write data to the file in this case. ** ** Consider the following sequence of events: ** ** BEGIN; ** <journal page X> ** <modify page X> ** SAVEPOINT sp; ** <shrink database file to Y pages> ** pagerStress(page X) ** ROLLBACK TO sp; ** ** If (X>Y), then when pagerStress is called page X will not be written ** out to the database file, but will be dropped from the cache. Then, ** following the "ROLLBACK TO sp" statement, reading page X will read ** data from the database file. This will be the copy of page X as it ** was when the transaction started, not as it was when "SAVEPOINT sp" ** was executed. ** ** The solution is to write the current data for page X into the ** sub-journal file now (if it is not already there), so that it will ** be restored to its current value when the "ROLLBACK TO sp" is ** executed. */ if( NEVER( rc==SQLITE_OK && pPg->pgno>pPager->dbSize && subjRequiresPage(pPg) ) ){ rc = subjournalPage(pPg); } /* Write the contents of the page out to the database file. */ if( rc==SQLITE_OK ){ rc = pager_write_pagelist(pPager, pPg); } } /* Mark the page as clean. */ if( rc==SQLITE_OK ){ PAGERTRACE(("STRESS %d page %d\n", PAGERID(pPager), pPg->pgno)); sqlite3PcacheMakeClean(pPg); } |
︙ | ︙ | |||
3199 3200 3201 3202 3203 3204 3205 3206 3207 3208 3209 3210 3211 3212 3213 3214 3215 3216 3217 3218 3219 3220 3221 3222 3223 3224 3225 | pPtr = (u8 *)sqlite3MallocZero( ROUND8(sizeof(*pPager)) + /* Pager structure */ ROUND8(pcacheSize) + /* PCache object */ ROUND8(pVfs->szOsFile) + /* The main db file */ journalFileSize * 2 + /* The two journal files */ nPathname + 1 + /* zFilename */ nPathname + 8 + 1 /* zJournal */ ); assert( EIGHT_BYTE_ALIGNMENT(SQLITE_INT_TO_PTR(journalFileSize)) ); if( !pPtr ){ sqlite3_free(zPathname); return SQLITE_NOMEM; } pPager = (Pager*)(pPtr); pPager->pPCache = (PCache*)(pPtr += ROUND8(sizeof(*pPager))); pPager->fd = (sqlite3_file*)(pPtr += ROUND8(pcacheSize)); pPager->sjfd = (sqlite3_file*)(pPtr += ROUND8(pVfs->szOsFile)); pPager->jfd = (sqlite3_file*)(pPtr += journalFileSize); pPager->zFilename = (char*)(pPtr += journalFileSize); assert( EIGHT_BYTE_ALIGNMENT(pPager->jfd) ); /* Fill in the Pager.zFilename and Pager.zJournal buffers, if required. */ if( zPathname ){ pPager->zJournal = (char*)(pPtr += nPathname + 1); memcpy(pPager->zFilename, zPathname, nPathname); memcpy(pPager->zJournal, zPathname, nPathname); memcpy(&pPager->zJournal[nPathname], "-journal", 8); | > > > | > > > > > > > > > | 3740 3741 3742 3743 3744 3745 3746 3747 3748 3749 3750 3751 3752 3753 3754 3755 3756 3757 3758 3759 3760 3761 3762 3763 3764 3765 3766 3767 3768 3769 3770 3771 3772 3773 3774 3775 3776 3777 3778 3779 3780 3781 3782 3783 3784 3785 3786 | pPtr = (u8 *)sqlite3MallocZero( ROUND8(sizeof(*pPager)) + /* Pager structure */ ROUND8(pcacheSize) + /* PCache object */ ROUND8(pVfs->szOsFile) + /* The main db file */ journalFileSize * 2 + /* The two journal files */ nPathname + 1 + /* zFilename */ nPathname + 8 + 1 /* zJournal */ #ifndef SQLITE_OMIT_WAL + nPathname + 4 + 1 /* zWal */ #endif ); assert( EIGHT_BYTE_ALIGNMENT(SQLITE_INT_TO_PTR(journalFileSize)) ); if( !pPtr ){ sqlite3_free(zPathname); return SQLITE_NOMEM; } pPager = (Pager*)(pPtr); pPager->pPCache = (PCache*)(pPtr += ROUND8(sizeof(*pPager))); pPager->fd = (sqlite3_file*)(pPtr += ROUND8(pcacheSize)); pPager->sjfd = (sqlite3_file*)(pPtr += ROUND8(pVfs->szOsFile)); pPager->jfd = (sqlite3_file*)(pPtr += journalFileSize); pPager->zFilename = (char*)(pPtr += journalFileSize); assert( EIGHT_BYTE_ALIGNMENT(pPager->jfd) ); /* Fill in the Pager.zFilename and Pager.zJournal buffers, if required. */ if( zPathname ){ pPager->zJournal = (char*)(pPtr += nPathname + 1); memcpy(pPager->zFilename, zPathname, nPathname); memcpy(pPager->zJournal, zPathname, nPathname); memcpy(&pPager->zJournal[nPathname], "-journal", 8); if( pPager->zFilename[0]==0 ){ pPager->zJournal[0] = 0; } #ifndef SQLITE_OMIT_WAL else{ pPager->zWal = &pPager->zJournal[nPathname+8+1]; memcpy(pPager->zWal, zPathname, nPathname); memcpy(&pPager->zWal[nPathname], "-wal", 4); } #endif sqlite3_free(zPathname); } pPager->pVfs = pVfs; pPager->vfsFlags = vfsFlags; /* Open the pager file. */ |
︙ | ︙ | |||
3389 3390 3391 3392 3393 3394 3395 | ** SQLITE_OK returned. If no hot-journal file is present, *pExists is ** set to 0 and SQLITE_OK returned. If an IO error occurs while trying ** to determine whether or not a hot-journal file exists, the IO error ** code is returned and the value of *pExists is undefined. */ static int hasHotJournal(Pager *pPager, int *pExists){ sqlite3_vfs * const pVfs = pPager->pVfs; | | | > < > > > > | > | 3942 3943 3944 3945 3946 3947 3948 3949 3950 3951 3952 3953 3954 3955 3956 3957 3958 3959 3960 3961 3962 3963 3964 3965 3966 3967 3968 3969 3970 3971 | ** SQLITE_OK returned. If no hot-journal file is present, *pExists is ** set to 0 and SQLITE_OK returned. If an IO error occurs while trying ** to determine whether or not a hot-journal file exists, the IO error ** code is returned and the value of *pExists is undefined. */ static int hasHotJournal(Pager *pPager, int *pExists){ sqlite3_vfs * const pVfs = pPager->pVfs; int rc = SQLITE_OK; /* Return code */ int exists = 1; /* True if a journal file is present */ int jrnlOpen = !!isOpen(pPager->jfd); assert( pPager!=0 ); assert( pPager->useJournal ); assert( isOpen(pPager->fd) ); assert( pPager->state <= PAGER_SHARED ); assert( jrnlOpen==0 || ( sqlite3OsDeviceCharacteristics(pPager->jfd) & SQLITE_IOCAP_UNDELETABLE_WHEN_OPEN )); *pExists = 0; if( !jrnlOpen ){ rc = sqlite3OsAccess(pVfs, pPager->zJournal, SQLITE_ACCESS_EXISTS, &exists); } if( rc==SQLITE_OK && exists ){ int locked; /* True if some process holds a RESERVED lock */ /* Race condition here: Another process might have been holding the ** the RESERVED lock and have a journal open at the sqlite3OsAccess() ** call above, but then delete the journal and drop the lock before ** we get to the following sqlite3OsCheckReservedLock() call. If that |
︙ | ︙ | |||
3437 3438 3439 3440 3441 3442 3443 | }else{ /* The journal file exists and no other connection has a reserved ** or greater lock on the database file. Now check that there is ** at least one non-zero bytes at the start of the journal file. ** If there is, then we consider this journal to be hot. If not, ** it can be ignored. */ | > | | > > | > < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | 3995 3996 3997 3998 3999 4000 4001 4002 4003 4004 4005 4006 4007 4008 4009 4010 4011 4012 4013 4014 4015 4016 4017 4018 4019 4020 4021 4022 4023 4024 4025 4026 4027 4028 4029 4030 4031 4032 4033 4034 4035 4036 4037 4038 4039 4040 | }else{ /* The journal file exists and no other connection has a reserved ** or greater lock on the database file. Now check that there is ** at least one non-zero bytes at the start of the journal file. ** If there is, then we consider this journal to be hot. If not, ** it can be ignored. */ if( !jrnlOpen ){ int f = SQLITE_OPEN_READONLY|SQLITE_OPEN_MAIN_JOURNAL; rc = sqlite3OsOpen(pVfs, pPager->zJournal, pPager->jfd, f, &f); } if( rc==SQLITE_OK ){ u8 first = 0; rc = sqlite3OsRead(pPager->jfd, (void *)&first, 1, 0); if( rc==SQLITE_IOERR_SHORT_READ ){ rc = SQLITE_OK; } if( !jrnlOpen ){ sqlite3OsClose(pPager->jfd); } *pExists = (first!=0); }else if( rc==SQLITE_CANTOPEN ){ /* If we cannot open the rollback journal file in order to see if ** its has a zero header, that might be due to an I/O error, or ** it might be due to the race condition described above and in ** ticket #3883. Either way, assume that the journal is hot. ** This might be a false positive. But if it is, then the ** automatic journal playback and recovery mechanism will deal ** with it under an EXCLUSIVE lock where we do not need to ** worry so much with race conditions. */ *pExists = 1; rc = SQLITE_OK; } } } } } return rc; } /* ** This function is called to obtain a shared lock on the database file. ** It is illegal to call sqlite3PagerAcquire() until after this function ** has been successfully called. If a shared-lock is already held when |
︙ | ︙ | |||
3582 3583 3584 3585 3586 3587 3588 | if( isOpen(pPager->jfd) || pPager->zJournal ){ isErrorReset = 1; } pPager->errCode = SQLITE_OK; pager_reset(pPager); } | > > | | 4083 4084 4085 4086 4087 4088 4089 4090 4091 4092 4093 4094 4095 4096 4097 4098 4099 | if( isOpen(pPager->jfd) || pPager->zJournal ){ isErrorReset = 1; } pPager->errCode = SQLITE_OK; pager_reset(pPager); } if( pagerUseWal(pPager) ){ rc = pagerBeginReadTransaction(pPager); }else if( pPager->state==PAGER_UNLOCK || isErrorReset ){ sqlite3_vfs * const pVfs = pPager->pVfs; int isHotJournal = 0; assert( !MEMDB ); assert( sqlite3PcacheRefCount(pPager->pPCache)==0 ); if( pPager->noReadlock ){ assert( pPager->readOnly ); pPager->state = PAGER_SHARED; |
︙ | ︙ | |||
3665 3666 3667 3668 3669 3670 3671 | } } } if( rc!=SQLITE_OK ){ goto failed; } | | > > > | > > > > > | > | 4168 4169 4170 4171 4172 4173 4174 4175 4176 4177 4178 4179 4180 4181 4182 4183 4184 4185 4186 4187 4188 4189 4190 4191 4192 4193 4194 4195 4196 4197 4198 4199 4200 4201 4202 4203 | } } } if( rc!=SQLITE_OK ){ goto failed; } /* Reset the journal status fields to indicates that we have no ** rollback journal at this time. */ pPager->journalStarted = 0; pPager->journalOff = 0; pPager->setMaster = 0; pPager->journalHdr = 0; /* Make sure the journal file has been synced to disk. */ /* Playback and delete the journal. Drop the database write ** lock and reacquire the read lock. Purge the cache before ** playing back the hot-journal so that we don't end up with ** an inconsistent cache. Sync the hot journal before playing ** it back since the process that crashed and left the hot journal ** probably did not sync it and we are required to always sync ** the journal before playing it back. */ if( isOpen(pPager->jfd) ){ rc = pagerSyncHotJournal(pPager); if( rc==SQLITE_OK ){ rc = pager_playback(pPager, 1); } if( rc!=SQLITE_OK ){ rc = pager_error(pPager, rc); goto failed; } } assert( (pPager->state==PAGER_SHARED) || (pPager->exclusiveMode && pPager->state>PAGER_SHARED) |
︙ | ︙ | |||
3705 3706 3707 3708 3709 3710 3711 3712 | ** other bytes change randomly with each file change when ** a codec is in use. ** ** There is a vanishingly small chance that a change will not be ** detected. The chance of an undetected change is so small that ** it can be neglected. */ char dbFileVers[sizeof(pPager->dbFileVers)]; | > | < | > > > > > | 4217 4218 4219 4220 4221 4222 4223 4224 4225 4226 4227 4228 4229 4230 4231 4232 4233 4234 4235 4236 4237 4238 4239 4240 4241 4242 4243 4244 4245 4246 4247 4248 4249 4250 4251 4252 4253 4254 4255 4256 4257 4258 4259 | ** other bytes change randomly with each file change when ** a codec is in use. ** ** There is a vanishingly small chance that a change will not be ** detected. The chance of an undetected change is so small that ** it can be neglected. */ int nPage = 0; char dbFileVers[sizeof(pPager->dbFileVers)]; sqlite3PagerPagecount(pPager, &nPage); if( pPager->errCode ){ rc = pPager->errCode; goto failed; } if( nPage>0 ){ IOTRACE(("CKVERS %p %d\n", pPager, sizeof(dbFileVers))); rc = sqlite3OsRead(pPager->fd, &dbFileVers, sizeof(dbFileVers), 24); if( rc!=SQLITE_OK ){ goto failed; } }else{ memset(dbFileVers, 0, sizeof(dbFileVers)); } if( memcmp(pPager->dbFileVers, dbFileVers, sizeof(dbFileVers))!=0 ){ pager_reset(pPager); } } assert( pPager->exclusiveMode || pPager->state==PAGER_SHARED ); /* If there is a WAL file in the file-system, open this database in WAL ** mode. Otherwise, the following function call is a no-op. */ rc = pagerOpenWalIfPresent(pPager); } failed: if( rc!=SQLITE_OK ){ /* pager_unlock() is a no-op for exclusive mode and in-memory databases. */ pager_unlock(pPager); } |
︙ | ︙ | |||
3783 3784 3785 3786 3787 3788 3789 | ** ** If noContent is true, it means that we do not care about the contents ** of the page. This occurs in two seperate scenarios: ** ** a) When reading a free-list leaf page from the database, and ** ** b) When a savepoint is being rolled back and we need to load | | | 4300 4301 4302 4303 4304 4305 4306 4307 4308 4309 4310 4311 4312 4313 4314 | ** ** If noContent is true, it means that we do not care about the contents ** of the page. This occurs in two seperate scenarios: ** ** a) When reading a free-list leaf page from the database, and ** ** b) When a savepoint is being rolled back and we need to load ** a new page into the cache to be filled with the data read ** from the savepoint journal. ** ** If noContent is true, then the data returned is zeroed instead of ** being read from the database. Additionally, the bits corresponding ** to pgno in Pager.pInJournal (bitvec of pages already written to the ** journal file) and the PagerSavepoint.pInSavepoint bitvecs of any open ** savepoints are set. This means if the page is made writable at any |
︙ | ︙ | |||
3839 3840 3841 3842 3843 3844 3845 | ** Set pPg to 0 and jump to the exception handler. */ pPg = 0; goto pager_acquire_err; } assert( (*ppPage)->pgno==pgno ); assert( (*ppPage)->pPager==pPager || (*ppPage)->pPager==0 ); | | | 4356 4357 4358 4359 4360 4361 4362 4363 4364 4365 4366 4367 4368 4369 4370 | ** Set pPg to 0 and jump to the exception handler. */ pPg = 0; goto pager_acquire_err; } assert( (*ppPage)->pgno==pgno ); assert( (*ppPage)->pPager==pPager || (*ppPage)->pPager==0 ); if( (*ppPage)->pPager && !noContent ){ /* In this case the pcache already contains an initialized copy of ** the page. Return without further ado. */ assert( pgno<=PAGER_MAX_PGNO && pgno!=PAGER_MJ_PGNO(pPager) ); PAGER_INCR(pPager->nHit); return SQLITE_OK; }else{ |
︙ | ︙ | |||
3869 3870 3871 3872 3873 3874 3875 | rc = sqlite3PagerPagecount(pPager, &nMax); if( rc!=SQLITE_OK ){ goto pager_acquire_err; } if( MEMDB || nMax<(int)pgno || noContent || !isOpen(pPager->fd) ){ if( pgno>pPager->mxPgno ){ | | | | 4386 4387 4388 4389 4390 4391 4392 4393 4394 4395 4396 4397 4398 4399 4400 4401 | rc = sqlite3PagerPagecount(pPager, &nMax); if( rc!=SQLITE_OK ){ goto pager_acquire_err; } if( MEMDB || nMax<(int)pgno || noContent || !isOpen(pPager->fd) ){ if( pgno>pPager->mxPgno ){ rc = SQLITE_FULL; goto pager_acquire_err; } if( noContent ){ /* Failure to set the bits in the InJournal bit-vectors is benign. ** It merely means that we might do some extra work to journal a ** page that does not need to be journaled. Nevertheless, be sure ** to test the case where a malloc error occurs while trying to set ** a bit in a bit vector. |
︙ | ︙ | |||
3954 3955 3956 3957 3958 3959 3960 | if( pPg ){ Pager *pPager = pPg->pPager; sqlite3PcacheRelease(pPg); pagerUnlockIfUnused(pPager); } } | < < < < < < < < < < < < < < < < < < < < < | 4471 4472 4473 4474 4475 4476 4477 4478 4479 4480 4481 4482 4483 4484 | if( pPg ){ Pager *pPager = pPg->pPager; sqlite3PcacheRelease(pPg); pagerUnlockIfUnused(pPager); } } /* ** This function is called at the start of every write transaction. ** There must already be a RESERVED or EXCLUSIVE lock on the database ** file when this routine is called. ** ** Open the journal file for pager pPager and write a journal header ** to the start of it. If there are active savepoints, open the sub-journal |
︙ | ︙ | |||
3999 4000 4001 4002 4003 4004 4005 4006 4007 4008 4009 4010 4011 4012 4013 4014 4015 4016 4017 | ** ** Return SQLITE_OK if everything is successful. Otherwise, return ** SQLITE_NOMEM if the attempt to allocate Pager.pInJournal fails, or ** an IO error code if opening or writing the journal file fails. */ static int pager_open_journal(Pager *pPager){ int rc = SQLITE_OK; /* Return code */ sqlite3_vfs * const pVfs = pPager->pVfs; /* Local cache of vfs pointer */ assert( pPager->state>=PAGER_RESERVED ); assert( pPager->useJournal ); assert( pPager->journalMode!=PAGER_JOURNALMODE_OFF ); assert( pPager->pInJournal==0 ); /* If already in the error state, this function is a no-op. But on ** the other hand, this routine is never called if we are already in ** an error state. */ if( NEVER(pPager->errCode) ) return pPager->errCode; | > < < < | | | | 4495 4496 4497 4498 4499 4500 4501 4502 4503 4504 4505 4506 4507 4508 4509 4510 4511 4512 4513 4514 4515 4516 4517 4518 4519 4520 4521 4522 4523 4524 4525 | ** ** Return SQLITE_OK if everything is successful. Otherwise, return ** SQLITE_NOMEM if the attempt to allocate Pager.pInJournal fails, or ** an IO error code if opening or writing the journal file fails. */ static int pager_open_journal(Pager *pPager){ int rc = SQLITE_OK; /* Return code */ int nPage; /* Size of database file */ sqlite3_vfs * const pVfs = pPager->pVfs; /* Local cache of vfs pointer */ assert( pPager->state>=PAGER_RESERVED ); assert( pPager->useJournal ); assert( pPager->journalMode!=PAGER_JOURNALMODE_OFF ); assert( pPager->pInJournal==0 ); /* If already in the error state, this function is a no-op. But on ** the other hand, this routine is never called if we are already in ** an error state. */ if( NEVER(pPager->errCode) ) return pPager->errCode; testcase( pPager->dbSizeValid==0 ); rc = sqlite3PagerPagecount(pPager, &nPage); if( rc ) return rc; pPager->pInJournal = sqlite3BitvecCreate(nPage); if( pPager->pInJournal==0 ){ return SQLITE_NOMEM; } /* Open the journal file if it is not already open. */ if( !isOpen(pPager->jfd) ){ if( pPager->journalMode==PAGER_JOURNALMODE_MEMORY ){ |
︙ | ︙ | |||
4059 4060 4061 4062 4063 4064 4065 | pPager->needSync = 0; pPager->nRec = 0; pPager->journalOff = 0; pPager->setMaster = 0; pPager->journalHdr = 0; rc = writeJournalHdr(pPager); } | < < < | 4553 4554 4555 4556 4557 4558 4559 4560 4561 4562 4563 4564 4565 4566 | pPager->needSync = 0; pPager->nRec = 0; pPager->journalOff = 0; pPager->setMaster = 0; pPager->journalHdr = 0; rc = writeJournalHdr(pPager); } if( rc!=SQLITE_OK ){ sqlite3BitvecDestroy(pPager->pInJournal); pPager->pInJournal = 0; } return rc; } |
︙ | ︙ | |||
4099 4100 4101 4102 4103 4104 4105 4106 4107 4108 4109 | ** sub-journal is implemented in-memory if pPager is an in-memory database, ** or using a temporary file otherwise. */ int sqlite3PagerBegin(Pager *pPager, int exFlag, int subjInMemory){ int rc = SQLITE_OK; assert( pPager->state!=PAGER_UNLOCK ); pPager->subjInMemory = (u8)subjInMemory; if( pPager->state==PAGER_SHARED ){ assert( pPager->pInJournal==0 ); assert( !MEMDB && !pPager->tempFile ); | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | | | | | | | | | | | | < < < < < | | < < | < > | < < < < | < < < | | | | | | | | | > > > | | > | | | < | < | > > > > > | 4590 4591 4592 4593 4594 4595 4596 4597 4598 4599 4600 4601 4602 4603 4604 4605 4606 4607 4608 4609 4610 4611 4612 4613 4614 4615 4616 4617 4618 4619 4620 4621 4622 4623 4624 4625 4626 4627 4628 4629 4630 4631 4632 4633 4634 4635 4636 4637 4638 4639 4640 4641 4642 4643 4644 4645 4646 4647 4648 4649 4650 4651 4652 4653 4654 4655 4656 4657 4658 4659 4660 4661 4662 4663 4664 4665 4666 4667 4668 4669 4670 4671 4672 4673 4674 4675 4676 4677 4678 4679 4680 4681 4682 4683 4684 4685 4686 4687 4688 4689 4690 4691 4692 4693 4694 4695 4696 4697 4698 4699 4700 4701 4702 4703 4704 4705 4706 4707 4708 4709 4710 4711 4712 4713 4714 4715 4716 4717 4718 4719 4720 4721 4722 4723 4724 4725 4726 4727 4728 4729 4730 4731 4732 4733 4734 4735 4736 4737 4738 4739 4740 4741 4742 4743 4744 4745 4746 4747 4748 4749 4750 4751 | ** sub-journal is implemented in-memory if pPager is an in-memory database, ** or using a temporary file otherwise. */ int sqlite3PagerBegin(Pager *pPager, int exFlag, int subjInMemory){ int rc = SQLITE_OK; assert( pPager->state!=PAGER_UNLOCK ); pPager->subjInMemory = (u8)subjInMemory; if( pPager->state==PAGER_SHARED ){ assert( pPager->pInJournal==0 ); assert( !MEMDB && !pPager->tempFile ); if( pagerUseWal(pPager) ){ /* If the pager is configured to use locking_mode=exclusive, and an ** exclusive lock on the database is not already held, obtain it now. */ if( pPager->exclusiveMode && sqlite3WalExclusiveMode(pPager->pWal, -1) ){ rc = sqlite3OsLock(pPager->fd, EXCLUSIVE_LOCK); pPager->state = PAGER_SHARED; if( rc!=SQLITE_OK ){ return rc; } sqlite3WalExclusiveMode(pPager->pWal, 1); } /* Grab the write lock on the log file. If successful, upgrade to ** PAGER_RESERVED state. Otherwise, return an error code to the caller. ** The busy-handler is not invoked if another connection already ** holds the write-lock. If possible, the upper layer will call it. ** ** WAL mode sets Pager.state to PAGER_RESERVED when it has an open ** transaction, but never to PAGER_EXCLUSIVE. This is because in ** PAGER_EXCLUSIVE state the code to roll back savepoint transactions ** may copy data from the sub-journal into the database file as well ** as into the page cache. Which would be incorrect in WAL mode. */ rc = sqlite3WalBeginWriteTransaction(pPager->pWal); if( rc==SQLITE_OK ){ pPager->dbOrigSize = pPager->dbSize; pPager->state = PAGER_RESERVED; pPager->journalOff = 0; } assert( rc!=SQLITE_OK || pPager->state==PAGER_RESERVED ); assert( rc==SQLITE_OK || pPager->state==PAGER_SHARED ); }else{ /* Obtain a RESERVED lock on the database file. If the exFlag parameter ** is true, then immediately upgrade this to an EXCLUSIVE lock. The ** busy-handler callback can be used when upgrading to the EXCLUSIVE ** lock, but not when obtaining the RESERVED lock. */ rc = sqlite3OsLock(pPager->fd, RESERVED_LOCK); if( rc==SQLITE_OK ){ pPager->state = PAGER_RESERVED; if( exFlag ){ rc = pager_wait_on_lock(pPager, EXCLUSIVE_LOCK); } } } /* No need to open the journal file at this time. It will be ** opened before it is written to. If we defer opening the journal, ** we might save the work of creating a file if the transaction ** ends up being a no-op. */ if( rc!=SQLITE_OK ){ assert( !pPager->dbModified ); /* Ignore any IO error that occurs within pager_end_transaction(). The ** purpose of this call is to reset the internal state of the pager ** sub-system. It doesn't matter if the journal-file is not properly ** finalized at this point (since it is not a valid journal file anyway). */ pager_end_transaction(pPager, 0); } } PAGERTRACE(("TRANSACTION %d\n", PAGERID(pPager))); return rc; } /* ** Mark a single data page as writeable. The page is written into the ** main journal or sub-journal as required. If the page is written into ** one of the journals, the corresponding bit is set in the ** Pager.pInJournal bitvec and the PagerSavepoint.pInSavepoint bitvecs ** of any open savepoints as appropriate. */ static int pager_write(PgHdr *pPg){ void *pData = pPg->pData; Pager *pPager = pPg->pPager; int rc = SQLITE_OK; /* This routine is not called unless a transaction has already been ** started. */ assert( pPager->state>=PAGER_RESERVED ); /* If an error has been previously detected, report the same error ** again. */ if( NEVER(pPager->errCode) ) return pPager->errCode; /* Higher-level routines never call this function if database is not ** writable. But check anyway, just for robustness. */ if( NEVER(pPager->readOnly) ) return SQLITE_PERM; assert( !pPager->setMaster ); CHECK_PAGE(pPg); /* Mark the page as dirty. If the page has already been written ** to the journal then we can return right away. */ sqlite3PcacheMakeDirty(pPg); if( pageInJournal(pPg) && !subjRequiresPage(pPg) ){ assert( !pagerUseWal(pPager) ); pPager->dbModified = 1; }else{ /* If we get this far, it means that the page needs to be ** written to the transaction journal or the ckeckpoint journal ** or both. ** ** Higher level routines should have already started a transaction, ** which means they have acquired the necessary locks but the rollback ** journal might not yet be open. */ assert( pPager->state>=RESERVED_LOCK ); if( pPager->pInJournal==0 && pPager->journalMode!=PAGER_JOURNALMODE_OFF && !pagerUseWal(pPager) ){ assert( pPager->useJournal ); rc = pager_open_journal(pPager); if( rc!=SQLITE_OK ) return rc; } pPager->dbModified = 1; /* The transaction journal now exists and we have a RESERVED or an ** EXCLUSIVE lock on the main database file. Write the current page to ** the transaction journal if it is not there already. */ if( !pageInJournal(pPg) && isOpen(pPager->jfd) ){ assert( !pagerUseWal(pPager) ); if( pPg->pgno<=pPager->dbOrigSize ){ u32 cksum; char *pData2; /* We should never write to the journal file the page that ** contains the database locks. The following assert verifies ** that we do not. */ assert( pPg->pgno!=PAGER_MJ_PGNO(pPager) ); assert( pPager->journalHdr <= pPager->journalOff ); CODEC2(pPager, pData, pPg->pgno, 7, return SQLITE_NOMEM, pData2); cksum = pager_cksum(pPager, (u8*)pData2); rc = write32bits(pPager->jfd, pPager->journalOff, pPg->pgno); if( rc==SQLITE_OK ){ rc = sqlite3OsWrite(pPager->jfd, pData2, pPager->pageSize, pPager->journalOff + 4); pPager->journalOff += pPager->pageSize+4; |
︙ | ︙ | |||
4321 4322 4323 4324 4325 4326 4327 | PgHdr *pPg = pDbPage; Pager *pPager = pPg->pPager; Pgno nPagePerSector = (pPager->sectorSize/pPager->pageSize); if( nPagePerSector>1 ){ Pgno nPageCount; /* Total number of pages in database file */ Pgno pg1; /* First page of the sector pPg is located on. */ | | | | > | | | > | | | | | | | | | | > | 4840 4841 4842 4843 4844 4845 4846 4847 4848 4849 4850 4851 4852 4853 4854 4855 4856 4857 4858 4859 4860 4861 4862 4863 4864 4865 4866 4867 4868 4869 4870 4871 4872 4873 4874 4875 4876 4877 4878 4879 4880 4881 4882 4883 4884 | PgHdr *pPg = pDbPage; Pager *pPager = pPg->pPager; Pgno nPagePerSector = (pPager->sectorSize/pPager->pageSize); if( nPagePerSector>1 ){ Pgno nPageCount; /* Total number of pages in database file */ Pgno pg1; /* First page of the sector pPg is located on. */ int nPage = 0; /* Number of pages starting at pg1 to journal */ int ii; /* Loop counter */ int needSync = 0; /* True if any page has PGHDR_NEED_SYNC */ /* Set the doNotSyncSpill flag to 1. This is because we cannot allow ** a journal header to be written between the pages journaled by ** this function. */ assert( !MEMDB ); assert( pPager->doNotSyncSpill==0 ); pPager->doNotSyncSpill++; /* This trick assumes that both the page-size and sector-size are ** an integer power of 2. It sets variable pg1 to the identifier ** of the first page of the sector pPg is located on. */ pg1 = ((pPg->pgno-1) & ~(nPagePerSector-1)) + 1; rc = sqlite3PagerPagecount(pPager, (int *)&nPageCount); if( rc==SQLITE_OK ){ if( pPg->pgno>nPageCount ){ nPage = (pPg->pgno - pg1)+1; }else if( (pg1+nPagePerSector-1)>nPageCount ){ nPage = nPageCount+1-pg1; }else{ nPage = nPagePerSector; } assert(nPage>0); assert(pg1<=pPg->pgno); assert((pg1+nPage)>pPg->pgno); } for(ii=0; ii<nPage && rc==SQLITE_OK; ii++){ Pgno pg = pg1+ii; PgHdr *pPage; if( pg==pPg->pgno || !sqlite3BitvecTest(pPager->pInJournal, pg) ){ if( pg!=PAGER_MJ_PGNO(pPager) ){ rc = sqlite3PagerGet(pPager, pg, &pPage); |
︙ | ︙ | |||
4391 4392 4393 4394 4395 4396 4397 | pPage->flags |= PGHDR_NEED_SYNC; sqlite3PagerUnref(pPage); } } assert(pPager->needSync); } | | | | 4913 4914 4915 4916 4917 4918 4919 4920 4921 4922 4923 4924 4925 4926 4927 4928 | pPage->flags |= PGHDR_NEED_SYNC; sqlite3PagerUnref(pPage); } } assert(pPager->needSync); } assert( pPager->doNotSyncSpill==1 ); pPager->doNotSyncSpill--; }else{ rc = pager_write(pDbPage); } return rc; } /* |
︙ | ︙ | |||
4498 4499 4500 4501 4502 4503 4504 4505 4506 4507 | } if( rc==SQLITE_OK ){ /* Increment the value just read and write it back to byte 24. */ change_counter = sqlite3Get4byte((u8*)pPager->dbFileVers); change_counter++; put32bits(((char*)pPgHdr->pData)+24, change_counter); /* If running in direct mode, write the contents of page 1 to the file. */ if( DIRECT_MODE ){ | > > > > > > | > > | > | 5020 5021 5022 5023 5024 5025 5026 5027 5028 5029 5030 5031 5032 5033 5034 5035 5036 5037 5038 5039 5040 5041 5042 5043 5044 5045 5046 5047 5048 | } if( rc==SQLITE_OK ){ /* Increment the value just read and write it back to byte 24. */ change_counter = sqlite3Get4byte((u8*)pPager->dbFileVers); change_counter++; put32bits(((char*)pPgHdr->pData)+24, change_counter); /* Also store the SQLite version number in bytes 96..99 and in ** bytes 92..95 store the change counter for which the version number ** is valid. */ put32bits(((char*)pPgHdr->pData)+92, change_counter); put32bits(((char*)pPgHdr->pData)+96, SQLITE_VERSION_NUMBER); /* If running in direct mode, write the contents of page 1 to the file. */ if( DIRECT_MODE ){ const void *zBuf; assert( pPager->dbFileSize>0 ); CODEC2(pPager, pPgHdr->pData, 1, 6, rc=SQLITE_NOMEM, zBuf); if( rc==SQLITE_OK ){ rc = sqlite3OsWrite(pPager->fd, zBuf, pPager->pageSize, 0); } if( rc==SQLITE_OK ){ pPager->changeCountDone = 1; } }else{ pPager->changeCountDone = 1; } } |
︙ | ︙ | |||
4572 4573 4574 4575 4576 4577 4578 | int noSync /* True to omit the xSync on the db file */ ){ int rc = SQLITE_OK; /* Return code */ /* The dbOrigSize is never set if journal_mode=OFF */ assert( pPager->journalMode!=PAGER_JOURNALMODE_OFF || pPager->dbOrigSize==0 ); | | < < | > > > > > > | > > > > | | | | | | | | | | | | | | | | | | | | | | | | | | > > > | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | > | 5103 5104 5105 5106 5107 5108 5109 5110 5111 5112 5113 5114 5115 5116 5117 5118 5119 5120 5121 5122 5123 5124 5125 5126 5127 5128 5129 5130 5131 5132 5133 5134 5135 5136 5137 5138 5139 5140 5141 5142 5143 5144 5145 5146 5147 5148 5149 5150 5151 5152 5153 5154 5155 5156 5157 5158 5159 5160 5161 5162 5163 5164 5165 5166 5167 5168 5169 5170 5171 5172 5173 5174 5175 5176 5177 5178 5179 5180 5181 5182 5183 5184 5185 5186 5187 5188 5189 5190 5191 5192 5193 5194 5195 5196 5197 5198 5199 5200 5201 5202 5203 5204 5205 5206 5207 5208 5209 5210 5211 5212 5213 5214 5215 5216 5217 5218 5219 5220 5221 5222 5223 5224 5225 5226 5227 5228 5229 5230 5231 5232 5233 5234 5235 5236 5237 5238 5239 5240 5241 5242 5243 5244 5245 5246 5247 5248 5249 5250 5251 5252 5253 5254 5255 5256 5257 5258 5259 5260 5261 5262 5263 5264 5265 | int noSync /* True to omit the xSync on the db file */ ){ int rc = SQLITE_OK; /* Return code */ /* The dbOrigSize is never set if journal_mode=OFF */ assert( pPager->journalMode!=PAGER_JOURNALMODE_OFF || pPager->dbOrigSize==0 ); /* If a prior error occurred, report that error again. */ if( pPager->errCode ) return pPager->errCode; PAGERTRACE(("DATABASE SYNC: File=%s zMaster=%s nSize=%d\n", pPager->zFilename, zMaster, pPager->dbSize)); if( MEMDB && pPager->dbModified ){ /* If this is an in-memory db, or no pages have been written to, or this ** function has already been called, it is mostly a no-op. However, any ** backup in progress needs to be restarted. */ sqlite3BackupRestart(pPager->pBackup); }else if( pPager->state!=PAGER_SYNCED && pPager->dbModified ){ if( pagerUseWal(pPager) ){ PgHdr *pList = sqlite3PcacheDirtyList(pPager->pPCache); if( pList ){ rc = pagerWalFrames(pPager, pList, pPager->dbSize, 1, (pPager->fullSync ? pPager->sync_flags : 0) ); } if( rc==SQLITE_OK ){ sqlite3PcacheCleanAll(pPager->pPCache); } }else{ /* The following block updates the change-counter. Exactly how it ** does this depends on whether or not the atomic-update optimization ** was enabled at compile time, and if this transaction meets the ** runtime criteria to use the operation: ** ** * The file-system supports the atomic-write property for ** blocks of size page-size, and ** * This commit is not part of a multi-file transaction, and ** * Exactly one page has been modified and store in the journal file. ** ** If the optimization was not enabled at compile time, then the ** pager_incr_changecounter() function is called to update the change ** counter in 'indirect-mode'. If the optimization is compiled in but ** is not applicable to this transaction, call sqlite3JournalCreate() ** to make sure the journal file has actually been created, then call ** pager_incr_changecounter() to update the change-counter in indirect ** mode. ** ** Otherwise, if the optimization is both enabled and applicable, ** then call pager_incr_changecounter() to update the change-counter ** in 'direct' mode. In this case the journal file will never be ** created for this transaction. */ #ifdef SQLITE_ENABLE_ATOMIC_WRITE PgHdr *pPg; assert( isOpen(pPager->jfd) || pPager->journalMode==PAGER_JOURNALMODE_OFF || pPager->journalMode==PAGER_JOURNALMODE_WAL ); if( !zMaster && isOpen(pPager->jfd) && pPager->journalOff==jrnlBufferSize(pPager) && pPager->dbSize>=pPager->dbFileSize && (0==(pPg = sqlite3PcacheDirtyList(pPager->pPCache)) || 0==pPg->pDirty) ){ /* Update the db file change counter via the direct-write method. The ** following call will modify the in-memory representation of page 1 ** to include the updated change counter and then write page 1 ** directly to the database file. Because of the atomic-write ** property of the host file-system, this is safe. */ rc = pager_incr_changecounter(pPager, 1); }else{ rc = sqlite3JournalCreate(pPager->jfd); if( rc==SQLITE_OK ){ rc = pager_incr_changecounter(pPager, 0); } } #else rc = pager_incr_changecounter(pPager, 0); #endif if( rc!=SQLITE_OK ) goto commit_phase_one_exit; /* If this transaction has made the database smaller, then all pages ** being discarded by the truncation must be written to the journal ** file. This can only happen in auto-vacuum mode. ** ** Before reading the pages with page numbers larger than the ** current value of Pager.dbSize, set dbSize back to the value ** that it took at the start of the transaction. Otherwise, the ** calls to sqlite3PagerGet() return zeroed pages instead of ** reading data from the database file. ** ** When journal_mode==OFF the dbOrigSize is always zero, so this ** block never runs if journal_mode=OFF. */ #ifndef SQLITE_OMIT_AUTOVACUUM if( pPager->dbSize<pPager->dbOrigSize && ALWAYS(pPager->journalMode!=PAGER_JOURNALMODE_OFF) ){ Pgno i; /* Iterator variable */ const Pgno iSkip = PAGER_MJ_PGNO(pPager); /* Pending lock page */ const Pgno dbSize = pPager->dbSize; /* Database image size */ pPager->dbSize = pPager->dbOrigSize; for( i=dbSize+1; i<=pPager->dbOrigSize; i++ ){ if( !sqlite3BitvecTest(pPager->pInJournal, i) && i!=iSkip ){ PgHdr *pPage; /* Page to journal */ rc = sqlite3PagerGet(pPager, i, &pPage); if( rc!=SQLITE_OK ) goto commit_phase_one_exit; rc = sqlite3PagerWrite(pPage); sqlite3PagerUnref(pPage); if( rc!=SQLITE_OK ) goto commit_phase_one_exit; } } pPager->dbSize = dbSize; } #endif /* Write the master journal name into the journal file. If a master ** journal file name has already been written to the journal file, ** or if zMaster is NULL (no master journal), then this call is a no-op. */ rc = writeMasterJournal(pPager, zMaster); if( rc!=SQLITE_OK ) goto commit_phase_one_exit; /* Sync the journal file. If the atomic-update optimization is being ** used, this call will not create the journal file or perform any ** real IO. */ rc = syncJournal(pPager); if( rc!=SQLITE_OK ) goto commit_phase_one_exit; /* Write all dirty pages to the database file. */ rc = pager_write_pagelist(pPager,sqlite3PcacheDirtyList(pPager->pPCache)); if( rc!=SQLITE_OK ){ assert( rc!=SQLITE_IOERR_BLOCKED ); goto commit_phase_one_exit; } sqlite3PcacheCleanAll(pPager->pPCache); /* If the file on disk is not the same size as the database image, ** then use pager_truncate to grow or shrink the file here. */ if( pPager->dbSize!=pPager->dbFileSize ){ Pgno nNew = pPager->dbSize - (pPager->dbSize==PAGER_MJ_PGNO(pPager)); assert( pPager->state>=PAGER_EXCLUSIVE ); rc = pager_truncate(pPager, nNew); if( rc!=SQLITE_OK ) goto commit_phase_one_exit; } /* Finally, sync the database file. */ if( !pPager->noSync && !noSync ){ rc = sqlite3OsSync(pPager->fd, pPager->sync_flags); } IOTRACE(("DBSYNC %p\n", pPager)) } pPager->state = PAGER_SYNCED; } commit_phase_one_exit: return rc; } |
︙ | ︙ | |||
4742 4743 4744 4745 4746 4747 4748 | /* This routine should not be called if a prior error has occurred. ** But if (due to a coding error elsewhere in the system) it does get ** called, just return the same error code without doing anything. */ if( NEVER(pPager->errCode) ) return pPager->errCode; /* This function should not be called if the pager is not in at least | | | > | | 5285 5286 5287 5288 5289 5290 5291 5292 5293 5294 5295 5296 5297 5298 5299 5300 5301 5302 5303 5304 5305 5306 5307 5308 5309 5310 5311 5312 5313 5314 5315 5316 5317 5318 5319 | /* This routine should not be called if a prior error has occurred. ** But if (due to a coding error elsewhere in the system) it does get ** called, just return the same error code without doing anything. */ if( NEVER(pPager->errCode) ) return pPager->errCode; /* This function should not be called if the pager is not in at least ** PAGER_RESERVED state. **FIXME**: Make it so that this test always ** fails - make it so that we never reach this point if we do not hold ** all necessary locks. */ if( NEVER(pPager->state<PAGER_RESERVED) ) return SQLITE_ERROR; /* An optimization. If the database was not actually modified during ** this transaction, the pager is running in exclusive-mode and is ** using persistent journals, then this function is a no-op. ** ** The start of the journal file currently contains a single journal ** header with the nRec field set to 0. If such a journal is used as ** a hot-journal during hot-journal rollback, 0 changes will be made ** to the database file. So there is no need to zero the journal ** header. Since the pager is in exclusive mode, there is no need ** to drop any locks either. */ if( pPager->dbModified==0 && pPager->exclusiveMode && pPager->journalMode==PAGER_JOURNALMODE_PERSIST ){ assert( pPager->journalOff==JOURNAL_HDR_SZ(pPager) || !pPager->journalOff ); return SQLITE_OK; } PAGERTRACE(("COMMIT %d\n", PAGERID(pPager))); assert( pPager->state==PAGER_SYNCED || MEMDB || !pPager->dbModified ); rc = pager_end_transaction(pPager, pPager->setMaster); return pager_error(pPager, rc); |
︙ | ︙ | |||
4794 4795 4796 4797 4798 4799 4800 | ** ** * If in an error state other than SQLITE_FULL, then task (1) is ** performed. If successful, task (2). Regardless of the outcome ** of either, the error state error code is returned to the caller ** (i.e. either SQLITE_IOERR or SQLITE_CORRUPT). ** ** * If the pager is in PAGER_RESERVED state, then attempt (1). Whether | | | 5338 5339 5340 5341 5342 5343 5344 5345 5346 5347 5348 5349 5350 5351 5352 | ** ** * If in an error state other than SQLITE_FULL, then task (1) is ** performed. If successful, task (2). Regardless of the outcome ** of either, the error state error code is returned to the caller ** (i.e. either SQLITE_IOERR or SQLITE_CORRUPT). ** ** * If the pager is in PAGER_RESERVED state, then attempt (1). Whether ** or not (1) is successful, also attempt (2). If successful, return ** SQLITE_OK. Otherwise, enter the error state and return the first ** error code encountered. ** ** In this case there is no chance that the database was written to. ** So is safe to finalize the journal file even if the playback ** (operation 1) failed. However the pager must enter the error state ** as the contents of the in-memory cache are now suspect. |
︙ | ︙ | |||
4817 4818 4819 4820 4821 4822 4823 | ** the journal file. It needs to be left in the file-system so that ** some other process can use it to restore the database state (by ** hot-journal rollback). */ int sqlite3PagerRollback(Pager *pPager){ int rc = SQLITE_OK; /* Return code */ PAGERTRACE(("ROLLBACK %d\n", PAGERID(pPager))); | > > > > > > > | | 5361 5362 5363 5364 5365 5366 5367 5368 5369 5370 5371 5372 5373 5374 5375 5376 5377 5378 5379 5380 5381 5382 | ** the journal file. It needs to be left in the file-system so that ** some other process can use it to restore the database state (by ** hot-journal rollback). */ int sqlite3PagerRollback(Pager *pPager){ int rc = SQLITE_OK; /* Return code */ PAGERTRACE(("ROLLBACK %d\n", PAGERID(pPager))); if( pagerUseWal(pPager) ){ int rc2; rc = sqlite3PagerSavepoint(pPager, SAVEPOINT_ROLLBACK, -1); rc2 = pager_end_transaction(pPager, pPager->setMaster); if( rc==SQLITE_OK ) rc = rc2; rc = pager_error(pPager, rc); }else if( !pPager->dbModified || !isOpen(pPager->jfd) ){ rc = pager_end_transaction(pPager, pPager->setMaster); }else if( pPager->errCode && pPager->errCode!=SQLITE_FULL ){ if( pPager->state>=PAGER_EXCLUSIVE ){ pager_playback(pPager, 0); } rc = pPager->errCode; }else{ |
︙ | ︙ | |||
4863 4864 4865 4866 4867 4868 4869 4870 4871 4872 4873 4874 4875 4876 | /* ** Return the number of references to the pager. */ int sqlite3PagerRefcount(Pager *pPager){ return sqlite3PcacheRefCount(pPager->pPCache); } /* ** Return the number of references to the specified page. */ int sqlite3PagerPageRefcount(DbPage *pPage){ return sqlite3PcachePageRefcount(pPage); } | > > > > > > > > > > | 5414 5415 5416 5417 5418 5419 5420 5421 5422 5423 5424 5425 5426 5427 5428 5429 5430 5431 5432 5433 5434 5435 5436 5437 | /* ** Return the number of references to the pager. */ int sqlite3PagerRefcount(Pager *pPager){ return sqlite3PcacheRefCount(pPager->pPCache); } /* ** Return the approximate number of bytes of memory currently ** used by the pager and its associated cache. */ int sqlite3PagerMemUsed(Pager *pPager){ int perPageSize = pPager->pageSize + pPager->nExtra + 20; return perPageSize*sqlite3PcachePagecount(pPager->pPCache) + sqlite3MallocSize(pPager); } /* ** Return the number of references to the specified page. */ int sqlite3PagerPageRefcount(DbPage *pPage){ return sqlite3PcachePageRefcount(pPage); } |
︙ | ︙ | |||
4916 4917 4918 4919 4920 4921 4922 4923 | int sqlite3PagerOpenSavepoint(Pager *pPager, int nSavepoint){ int rc = SQLITE_OK; /* Return code */ int nCurrent = pPager->nSavepoint; /* Current number of savepoints */ if( nSavepoint>nCurrent && pPager->useJournal ){ int ii; /* Iterator variable */ PagerSavepoint *aNew; /* New Pager.aSavepoint array */ | > < < | < > < < | | | > > | > | < | | 5477 5478 5479 5480 5481 5482 5483 5484 5485 5486 5487 5488 5489 5490 5491 5492 5493 5494 5495 5496 5497 5498 5499 5500 5501 5502 5503 5504 5505 5506 5507 5508 5509 5510 5511 5512 5513 5514 5515 5516 5517 5518 5519 5520 5521 5522 5523 5524 5525 5526 5527 | int sqlite3PagerOpenSavepoint(Pager *pPager, int nSavepoint){ int rc = SQLITE_OK; /* Return code */ int nCurrent = pPager->nSavepoint; /* Current number of savepoints */ if( nSavepoint>nCurrent && pPager->useJournal ){ int ii; /* Iterator variable */ PagerSavepoint *aNew; /* New Pager.aSavepoint array */ int nPage; /* Size of database file */ rc = sqlite3PagerPagecount(pPager, &nPage); if( rc ) return rc; /* Grow the Pager.aSavepoint array using realloc(). Return SQLITE_NOMEM ** if the allocation fails. Otherwise, zero the new portion in case a ** malloc failure occurs while populating it in the for(...) loop below. */ aNew = (PagerSavepoint *)sqlite3Realloc( pPager->aSavepoint, sizeof(PagerSavepoint)*nSavepoint ); if( !aNew ){ return SQLITE_NOMEM; } memset(&aNew[nCurrent], 0, (nSavepoint-nCurrent) * sizeof(PagerSavepoint)); pPager->aSavepoint = aNew; /* Populate the PagerSavepoint structures just allocated. */ for(ii=nCurrent; ii<nSavepoint; ii++){ aNew[ii].nOrig = nPage; if( isOpen(pPager->jfd) && pPager->journalOff>0 ){ aNew[ii].iOffset = pPager->journalOff; }else{ aNew[ii].iOffset = JOURNAL_HDR_SZ(pPager); } aNew[ii].iSubRec = pPager->nSubRec; aNew[ii].pInSavepoint = sqlite3BitvecCreate(nPage); if( !aNew[ii].pInSavepoint ){ return SQLITE_NOMEM; } if( pagerUseWal(pPager) ){ sqlite3WalSavepoint(pPager->pWal, aNew[ii].aWalData); } pPager->nSavepoint = ii+1; } assert( pPager->nSavepoint==nSavepoint ); assertTruncateConstraint(pPager); } return rc; } /* |
︙ | ︙ | |||
5027 5028 5029 5030 5031 5032 5033 | } } /* Else this is a rollback operation, playback the specified savepoint. ** If this is a temp-file, it is possible that the journal file has ** not yet been opened. In this case there have been no changes to ** the database file, so the playback operation can be skipped. */ | | | 5587 5588 5589 5590 5591 5592 5593 5594 5595 5596 5597 5598 5599 5600 5601 | } } /* Else this is a rollback operation, playback the specified savepoint. ** If this is a temp-file, it is possible that the journal file has ** not yet been opened. In this case there have been no changes to ** the database file, so the playback operation can be skipped. */ else if( pagerUseWal(pPager) || isOpen(pPager->jfd) ){ PagerSavepoint *pSavepoint = (nNew==0)?0:&pPager->aSavepoint[nNew-1]; rc = pagerPlaybackSavepoint(pPager, pSavepoint); assert(rc!=SQLITE_DONE); } } return rc; |
︙ | ︙ | |||
5079 5080 5081 5082 5083 5084 5085 | return pPager->noSync; } #ifdef SQLITE_HAS_CODEC /* ** Set or retrieve the codec for this pager */ | | | | 5639 5640 5641 5642 5643 5644 5645 5646 5647 5648 5649 5650 5651 5652 5653 5654 5655 5656 5657 5658 5659 5660 5661 5662 5663 5664 5665 5666 5667 | return pPager->noSync; } #ifdef SQLITE_HAS_CODEC /* ** Set or retrieve the codec for this pager */ void sqlite3PagerSetCodec( Pager *pPager, void *(*xCodec)(void*,void*,Pgno,int), void (*xCodecSizeChng)(void*,int,int), void (*xCodecFree)(void*), void *pCodec ){ if( pPager->xCodecFree ) pPager->xCodecFree(pPager->pCodec); pPager->xCodec = pPager->memDb ? 0 : xCodec; pPager->xCodecSizeChng = xCodecSizeChng; pPager->xCodecFree = xCodecFree; pPager->pCodec = pCodec; pagerReportSize(pPager); } void *sqlite3PagerGetCodec(Pager *pPager){ return pPager->pCodec; } #endif #ifndef SQLITE_OMIT_AUTOVACUUM /* ** Move the page pPg to location pgno in the file. |
︙ | ︙ | |||
5296 5297 5298 5299 5300 5301 5302 | if( eMode>=0 && !pPager->tempFile ){ pPager->exclusiveMode = (u8)eMode; } return (int)pPager->exclusiveMode; } /* | | < > < | | | | > | > | | | | > > > > > > > > > > | | | > > | | > > > > > | > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 5856 5857 5858 5859 5860 5861 5862 5863 5864 5865 5866 5867 5868 5869 5870 5871 5872 5873 5874 5875 5876 5877 5878 5879 5880 5881 5882 5883 5884 5885 5886 5887 5888 5889 5890 5891 5892 5893 5894 5895 5896 5897 5898 5899 5900 5901 5902 5903 5904 5905 5906 5907 5908 5909 5910 5911 5912 5913 5914 5915 5916 5917 5918 5919 5920 5921 5922 5923 5924 5925 5926 5927 5928 5929 5930 5931 5932 5933 5934 5935 5936 5937 5938 5939 5940 5941 5942 5943 5944 5945 5946 5947 5948 5949 5950 5951 5952 5953 5954 5955 5956 5957 5958 5959 5960 5961 5962 5963 5964 5965 5966 5967 5968 5969 5970 5971 5972 5973 5974 5975 5976 5977 5978 5979 5980 5981 5982 5983 5984 5985 5986 5987 5988 5989 5990 5991 5992 | if( eMode>=0 && !pPager->tempFile ){ pPager->exclusiveMode = (u8)eMode; } return (int)pPager->exclusiveMode; } /* ** Set the journal-mode for this pager. Parameter eMode must be one of: ** ** PAGER_JOURNALMODE_DELETE ** PAGER_JOURNALMODE_TRUNCATE ** PAGER_JOURNALMODE_PERSIST ** PAGER_JOURNALMODE_OFF ** PAGER_JOURNALMODE_MEMORY ** PAGER_JOURNALMODE_WAL ** ** The journalmode is set to the value specified if the change is allowed. ** The change may be disallowed for the following reasons: ** ** * An in-memory database can only have its journal_mode set to _OFF ** or _MEMORY. ** ** * Temporary databases cannot have _WAL journalmode. ** ** The returned indicate the current (possibly updated) journal-mode. */ int sqlite3PagerSetJournalMode(Pager *pPager, int eMode){ u8 eOld = pPager->journalMode; /* Prior journalmode */ /* The eMode parameter is always valid */ assert( eMode==PAGER_JOURNALMODE_DELETE || eMode==PAGER_JOURNALMODE_TRUNCATE || eMode==PAGER_JOURNALMODE_PERSIST || eMode==PAGER_JOURNALMODE_OFF || eMode==PAGER_JOURNALMODE_WAL || eMode==PAGER_JOURNALMODE_MEMORY ); /* This routine is only called from the OP_JournalMode opcode, and ** the logic there will never allow a temporary file to be changed ** to WAL mode. */ assert( pPager->tempFile==0 || eMode!=PAGER_JOURNALMODE_WAL ); /* Do allow the journalmode of an in-memory database to be set to ** anything other than MEMORY or OFF */ if( MEMDB ){ assert( eOld==PAGER_JOURNALMODE_MEMORY || eOld==PAGER_JOURNALMODE_OFF ); if( eMode!=PAGER_JOURNALMODE_MEMORY && eMode!=PAGER_JOURNALMODE_OFF ){ eMode = eOld; } } if( eMode!=eOld ){ /* When changing between rollback modes, close the journal file prior ** to the change. But when changing from a rollback mode to WAL, keep ** the journal open since there is a rollback-style transaction in play ** used to convert the version numbers in the btree header. */ if( isOpen(pPager->jfd) && eMode!=PAGER_JOURNALMODE_WAL ){ sqlite3OsClose(pPager->jfd); } /* Change the journal mode. */ pPager->journalMode = (u8)eMode; /* When transistioning from TRUNCATE or PERSIST to any other journal ** mode except WAL (and we are not in locking_mode=EXCLUSIVE) then ** delete the journal file. */ assert( (PAGER_JOURNALMODE_TRUNCATE & 5)==1 ); assert( (PAGER_JOURNALMODE_PERSIST & 5)==1 ); assert( (PAGER_JOURNALMODE_DELETE & 5)==0 ); assert( (PAGER_JOURNALMODE_MEMORY & 5)==4 ); assert( (PAGER_JOURNALMODE_OFF & 5)==0 ); assert( (PAGER_JOURNALMODE_WAL & 5)==5 ); assert( isOpen(pPager->fd) || pPager->exclusiveMode ); if( !pPager->exclusiveMode && (eOld & 5)==1 && (eMode & 1)==0 ){ /* In this case we would like to delete the journal file. If it is ** not possible, then that is not a problem. Deleting the journal file ** here is an optimization only. ** ** Before deleting the journal file, obtain a RESERVED lock on the ** database file. This ensures that the journal file is not deleted ** while it is in use by some other client. */ int rc = SQLITE_OK; int state = pPager->state; if( state<PAGER_SHARED ){ rc = sqlite3PagerSharedLock(pPager); } if( pPager->state==PAGER_SHARED ){ assert( rc==SQLITE_OK ); rc = sqlite3OsLock(pPager->fd, RESERVED_LOCK); } if( rc==SQLITE_OK ){ sqlite3OsDelete(pPager->pVfs, pPager->zJournal, 0); } if( rc==SQLITE_OK && state==PAGER_SHARED ){ sqlite3OsUnlock(pPager->fd, SHARED_LOCK); }else if( state==PAGER_UNLOCK ){ pager_unlock(pPager); } assert( state==pPager->state ); } } /* Return the new journal mode */ return (int)pPager->journalMode; } /* ** Return the current journal mode. */ int sqlite3PagerGetJournalMode(Pager *pPager){ return (int)pPager->journalMode; } /* ** Return TRUE if the pager is in a state where it is OK to change the ** journalmode. Journalmode changes can only happen when the database ** is unmodified. */ int sqlite3PagerOkToChangeJournalMode(Pager *pPager){ if( pPager->dbModified ) return 0; if( NEVER(isOpen(pPager->jfd) && pPager->journalOff>0) ) return 0; return 1; } /* ** Get/set the size-limit used for persistent journal files. ** ** Setting the size limit to -1 means no limit is enforced. ** An attempt to set a limit smaller than -1 is a no-op. */ |
︙ | ︙ | |||
5361 5362 5363 5364 5365 5366 5367 5368 | ** uses it opaquely as an argument to sqlite3BackupRestart() and ** sqlite3BackupUpdate() only. */ sqlite3_backup **sqlite3PagerBackupPtr(Pager *pPager){ return &pPager->pBackup; } #endif /* SQLITE_OMIT_DISKIO */ | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 6003 6004 6005 6006 6007 6008 6009 6010 6011 6012 6013 6014 6015 6016 6017 6018 6019 6020 6021 6022 6023 6024 6025 6026 6027 6028 6029 6030 6031 6032 6033 6034 6035 6036 6037 6038 6039 6040 6041 6042 6043 6044 6045 6046 6047 6048 6049 6050 6051 6052 6053 6054 6055 6056 6057 6058 6059 6060 6061 6062 6063 6064 6065 6066 6067 6068 6069 6070 6071 6072 6073 6074 6075 6076 6077 6078 6079 6080 6081 6082 6083 6084 6085 6086 6087 6088 6089 6090 6091 6092 6093 6094 6095 6096 6097 6098 6099 6100 6101 6102 6103 6104 6105 6106 6107 6108 6109 6110 6111 6112 6113 6114 6115 6116 6117 6118 6119 6120 6121 6122 6123 6124 6125 6126 6127 6128 6129 6130 6131 6132 6133 6134 6135 6136 6137 6138 6139 6140 6141 6142 6143 6144 6145 6146 6147 6148 6149 6150 | ** uses it opaquely as an argument to sqlite3BackupRestart() and ** sqlite3BackupUpdate() only. */ sqlite3_backup **sqlite3PagerBackupPtr(Pager *pPager){ return &pPager->pBackup; } #ifndef SQLITE_OMIT_WAL /* ** This function is called when the user invokes "PRAGMA checkpoint". */ int sqlite3PagerCheckpoint(Pager *pPager){ int rc = SQLITE_OK; if( pPager->pWal ){ u8 *zBuf = (u8 *)pPager->pTmpSpace; rc = sqlite3WalCheckpoint(pPager->pWal, (pPager->noSync ? 0 : pPager->sync_flags), pPager->pageSize, zBuf ); } return rc; } int sqlite3PagerWalCallback(Pager *pPager){ return sqlite3WalCallback(pPager->pWal); } /* ** Return true if the underlying VFS for the given pager supports the ** primitives necessary for write-ahead logging. */ int sqlite3PagerWalSupported(Pager *pPager){ const sqlite3_io_methods *pMethods = pPager->fd->pMethods; return pMethods->iVersion>=2 && pMethods->xShmMap!=0; } /* ** The caller must be holding a SHARED lock on the database file to call ** this function. ** ** If the pager passed as the first argument is open on a real database ** file (not a temp file or an in-memory database), and the WAL file ** is not already open, make an attempt to open it now. If successful, ** return SQLITE_OK. If an error occurs or the VFS used by the pager does ** not support the xShmXXX() methods, return an error code. *pisOpen is ** not modified in either case. ** ** If the pager is open on a temp-file (or in-memory database), or if ** the WAL file is already open, set *pisOpen to 1 and return SQLITE_OK ** without doing anything. */ int sqlite3PagerOpenWal( Pager *pPager, /* Pager object */ int *pisOpen /* OUT: Set to true if call is a no-op */ ){ int rc = SQLITE_OK; /* Return code */ assert( pPager->state>=PAGER_SHARED ); assert( (pisOpen==0 && !pPager->tempFile && !pPager->pWal) || *pisOpen==0 ); if( !pPager->tempFile && !pPager->pWal ){ if( !sqlite3PagerWalSupported(pPager) ) return SQLITE_CANTOPEN; /* Open the connection to the log file. If this operation fails, ** (e.g. due to malloc() failure), unlock the database file and ** return an error code. */ rc = sqlite3WalOpen(pPager->pVfs, pPager->fd, pPager->zWal, &pPager->pWal); if( rc==SQLITE_OK ){ pPager->journalMode = PAGER_JOURNALMODE_WAL; } }else{ *pisOpen = 1; } return rc; } /* ** This function is called to close the connection to the log file prior ** to switching from WAL to rollback mode. ** ** Before closing the log file, this function attempts to take an ** EXCLUSIVE lock on the database file. If this cannot be obtained, an ** error (SQLITE_BUSY) is returned and the log connection is not closed. ** If successful, the EXCLUSIVE lock is not released before returning. */ int sqlite3PagerCloseWal(Pager *pPager){ int rc = SQLITE_OK; assert( pPager->journalMode==PAGER_JOURNALMODE_WAL ); /* If the log file is not already open, but does exist in the file-system, ** it may need to be checkpointed before the connection can switch to ** rollback mode. Open it now so this can happen. */ if( !pPager->pWal ){ int logexists = 0; rc = sqlite3OsLock(pPager->fd, SQLITE_LOCK_SHARED); if( rc==SQLITE_OK ){ rc = sqlite3OsAccess( pPager->pVfs, pPager->zWal, SQLITE_ACCESS_EXISTS, &logexists ); } if( rc==SQLITE_OK && logexists ){ rc = sqlite3WalOpen(pPager->pVfs, pPager->fd, pPager->zWal, &pPager->pWal); } } /* Checkpoint and close the log. Because an EXCLUSIVE lock is held on ** the database file, the log and log-summary files will be deleted. */ if( rc==SQLITE_OK && pPager->pWal ){ rc = sqlite3OsLock(pPager->fd, SQLITE_LOCK_EXCLUSIVE); if( rc==SQLITE_OK ){ rc = sqlite3WalClose(pPager->pWal, (pPager->noSync ? 0 : pPager->sync_flags), pPager->pageSize, (u8*)pPager->pTmpSpace ); pPager->pWal = 0; }else{ /* If we cannot get an EXCLUSIVE lock, downgrade the PENDING lock ** that we did get back to SHARED. */ sqlite3OsUnlock(pPager->fd, SQLITE_LOCK_SHARED); } } return rc; } #ifdef SQLITE_HAS_CODEC /* ** This function is called by the wal module when writing page content ** into the log file. ** ** This function returns a pointer to a buffer containing the encrypted ** page content. If a malloc fails, this function may return NULL. */ void *sqlite3PagerCodec(PgHdr *pPg){ void *aData = 0; CODEC2(pPg->pPager, pPg->pData, pPg->pgno, 6, return 0, aData); return aData; } #endif /* SQLITE_HAS_CODEC */ #endif /* !SQLITE_OMIT_WAL */ #endif /* SQLITE_OMIT_DISKIO */ |
Changes to src/pager.h.
︙ | ︙ | |||
64 65 66 67 68 69 70 | ** Valid values for the second argument to sqlite3PagerLockingMode(). */ #define PAGER_LOCKINGMODE_QUERY -1 #define PAGER_LOCKINGMODE_NORMAL 0 #define PAGER_LOCKINGMODE_EXCLUSIVE 1 /* | | | > | 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 | ** Valid values for the second argument to sqlite3PagerLockingMode(). */ #define PAGER_LOCKINGMODE_QUERY -1 #define PAGER_LOCKINGMODE_NORMAL 0 #define PAGER_LOCKINGMODE_EXCLUSIVE 1 /* ** Numeric constants that encode the journalmode. */ #define PAGER_JOURNALMODE_QUERY (-1) /* Query the value of journalmode */ #define PAGER_JOURNALMODE_DELETE 0 /* Commit by deleting journal file */ #define PAGER_JOURNALMODE_PERSIST 1 /* Commit by zeroing journal header */ #define PAGER_JOURNALMODE_OFF 2 /* Journal omitted. */ #define PAGER_JOURNALMODE_TRUNCATE 3 /* Commit by truncating journal */ #define PAGER_JOURNALMODE_MEMORY 4 /* In-memory journal file */ #define PAGER_JOURNALMODE_WAL 5 /* Use write-ahead logging */ /* ** The remainder of this file contains the declarations of the functions ** that make up the Pager sub-system API. See source code comments for ** a detailed description of each routine. */ |
︙ | ︙ | |||
99 100 101 102 103 104 105 | /* Functions used to configure a Pager object. */ void sqlite3PagerSetBusyhandler(Pager*, int(*)(void *), void *); int sqlite3PagerSetPagesize(Pager*, u16*, int); int sqlite3PagerMaxPageCount(Pager*, int); void sqlite3PagerSetCachesize(Pager*, int); void sqlite3PagerSetSafetyLevel(Pager*,int,int); int sqlite3PagerLockingMode(Pager *, int); | | > > | 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 | /* Functions used to configure a Pager object. */ void sqlite3PagerSetBusyhandler(Pager*, int(*)(void *), void *); int sqlite3PagerSetPagesize(Pager*, u16*, int); int sqlite3PagerMaxPageCount(Pager*, int); void sqlite3PagerSetCachesize(Pager*, int); void sqlite3PagerSetSafetyLevel(Pager*,int,int); int sqlite3PagerLockingMode(Pager *, int); int sqlite3PagerSetJournalMode(Pager *, int); int sqlite3PagerGetJournalMode(Pager*); int sqlite3PagerOkToChangeJournalMode(Pager*); i64 sqlite3PagerJournalSizeLimit(Pager *, i64); sqlite3_backup **sqlite3PagerBackupPtr(Pager*); /* Functions used to obtain and release page references. */ int sqlite3PagerAcquire(Pager *pPager, Pgno pgno, DbPage **ppPage, int clrFlag); #define sqlite3PagerGet(A,B,C) sqlite3PagerAcquire(A,B,C,0) DbPage *sqlite3PagerLookup(Pager *pPager, Pgno pgno); |
︙ | ︙ | |||
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 | int sqlite3PagerCommitPhaseOne(Pager*,const char *zMaster, int); int sqlite3PagerSync(Pager *pPager); int sqlite3PagerCommitPhaseTwo(Pager*); int sqlite3PagerRollback(Pager*); int sqlite3PagerOpenSavepoint(Pager *pPager, int n); int sqlite3PagerSavepoint(Pager *pPager, int op, int iSavepoint); int sqlite3PagerSharedLock(Pager *pPager); /* Functions used to query pager state and configuration. */ u8 sqlite3PagerIsreadonly(Pager*); int sqlite3PagerRefcount(Pager*); const char *sqlite3PagerFilename(Pager*); const sqlite3_vfs *sqlite3PagerVfs(Pager*); sqlite3_file *sqlite3PagerFile(Pager*); const char *sqlite3PagerJournalname(Pager*); int sqlite3PagerNosync(Pager*); void *sqlite3PagerTempSpace(Pager*); int sqlite3PagerIsMemdb(Pager*); /* Functions used to truncate the database file. */ void sqlite3PagerTruncateImage(Pager*,Pgno); /* Functions to support testing and debugging. */ #if !defined(NDEBUG) || defined(SQLITE_TEST) Pgno sqlite3PagerPagenumber(DbPage*); int sqlite3PagerIswriteable(DbPage*); #endif #ifdef SQLITE_TEST | > > > > > > > > > > > | 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 | int sqlite3PagerCommitPhaseOne(Pager*,const char *zMaster, int); int sqlite3PagerSync(Pager *pPager); int sqlite3PagerCommitPhaseTwo(Pager*); int sqlite3PagerRollback(Pager*); int sqlite3PagerOpenSavepoint(Pager *pPager, int n); int sqlite3PagerSavepoint(Pager *pPager, int op, int iSavepoint); int sqlite3PagerSharedLock(Pager *pPager); int sqlite3PagerCheckpoint(Pager *pPager); int sqlite3PagerWalSupported(Pager *pPager); int sqlite3PagerWalCallback(Pager *pPager); int sqlite3PagerOpenWal(Pager *pPager, int *pisOpen); int sqlite3PagerCloseWal(Pager *pPager); /* Functions used to query pager state and configuration. */ u8 sqlite3PagerIsreadonly(Pager*); int sqlite3PagerRefcount(Pager*); int sqlite3PagerMemUsed(Pager*); const char *sqlite3PagerFilename(Pager*); const sqlite3_vfs *sqlite3PagerVfs(Pager*); sqlite3_file *sqlite3PagerFile(Pager*); const char *sqlite3PagerJournalname(Pager*); int sqlite3PagerNosync(Pager*); void *sqlite3PagerTempSpace(Pager*); int sqlite3PagerIsMemdb(Pager*); /* Functions used to truncate the database file. */ void sqlite3PagerTruncateImage(Pager*,Pgno); #if defined(SQLITE_HAS_CODEC) && !defined(SQLITE_OMIT_WAL) void *sqlite3PagerCodec(DbPage *); #endif /* Functions to support testing and debugging. */ #if !defined(NDEBUG) || defined(SQLITE_TEST) Pgno sqlite3PagerPagenumber(DbPage*); int sqlite3PagerIswriteable(DbPage*); #endif #ifdef SQLITE_TEST |
︙ | ︙ |
Changes to src/parse.y.
︙ | ︙ | |||
844 845 846 847 848 849 850 | {spanBinaryExpr(&A,pParse,@OP,&X,&Y);} expr(A) ::= expr(X) CONCAT(OP) expr(Y). {spanBinaryExpr(&A,pParse,@OP,&X,&Y);} %type likeop {struct LikeOp} likeop(A) ::= LIKE_KW(X). {A.eOperator = X; A.not = 0;} likeop(A) ::= NOT LIKE_KW(X). {A.eOperator = X; A.not = 1;} likeop(A) ::= MATCH(X). {A.eOperator = X; A.not = 0;} likeop(A) ::= NOT MATCH(X). {A.eOperator = X; A.not = 1;} | < < < < | < < < > > > > > > > > > > > | 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 | {spanBinaryExpr(&A,pParse,@OP,&X,&Y);} expr(A) ::= expr(X) CONCAT(OP) expr(Y). {spanBinaryExpr(&A,pParse,@OP,&X,&Y);} %type likeop {struct LikeOp} likeop(A) ::= LIKE_KW(X). {A.eOperator = X; A.not = 0;} likeop(A) ::= NOT LIKE_KW(X). {A.eOperator = X; A.not = 1;} likeop(A) ::= MATCH(X). {A.eOperator = X; A.not = 0;} likeop(A) ::= NOT MATCH(X). {A.eOperator = X; A.not = 1;} expr(A) ::= expr(X) likeop(OP) expr(Y). [LIKE_KW] { ExprList *pList; pList = sqlite3ExprListAppend(pParse,0, Y.pExpr); pList = sqlite3ExprListAppend(pParse,pList, X.pExpr); A.pExpr = sqlite3ExprFunction(pParse, pList, &OP.eOperator); if( OP.not ) A.pExpr = sqlite3PExpr(pParse, TK_NOT, A.pExpr, 0, 0); A.zStart = X.zStart; A.zEnd = Y.zEnd; if( A.pExpr ) A.pExpr->flags |= EP_InfixFunc; } expr(A) ::= expr(X) likeop(OP) expr(Y) ESCAPE expr(E). [LIKE_KW] { ExprList *pList; pList = sqlite3ExprListAppend(pParse,0, Y.pExpr); pList = sqlite3ExprListAppend(pParse,pList, X.pExpr); pList = sqlite3ExprListAppend(pParse,pList, E.pExpr); A.pExpr = sqlite3ExprFunction(pParse, pList, &OP.eOperator); if( OP.not ) A.pExpr = sqlite3PExpr(pParse, TK_NOT, A.pExpr, 0, 0); A.zStart = X.zStart; A.zEnd = E.zEnd; if( A.pExpr ) A.pExpr->flags |= EP_InfixFunc; } %include { /* Construct an expression node for a unary postfix operator */ static void spanUnaryPostfix( ExprSpan *pOut, /* Write the new expression node here */ |
︙ | ︙ | |||
955 956 957 958 959 960 961 | A.zEnd = Y.zEnd; } %ifndef SQLITE_OMIT_SUBQUERY %type in_op {int} in_op(A) ::= IN. {A = 0;} in_op(A) ::= NOT IN. {A = 1;} expr(A) ::= expr(X) in_op(N) LP exprlist(Y) RP(E). [IN] { | > > > > > > > > > > > > | | | | | | | | > | 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 | A.zEnd = Y.zEnd; } %ifndef SQLITE_OMIT_SUBQUERY %type in_op {int} in_op(A) ::= IN. {A = 0;} in_op(A) ::= NOT IN. {A = 1;} expr(A) ::= expr(X) in_op(N) LP exprlist(Y) RP(E). [IN] { if( Y==0 ){ /* Expressions of the form ** ** expr1 IN () ** expr1 NOT IN () ** ** simplify to constants 0 (false) and 1 (true), respectively, ** regardless of the value of expr1. */ A.pExpr = sqlite3PExpr(pParse, TK_INTEGER, 0, 0, &sqlite3IntTokens[N]); sqlite3ExprDelete(pParse->db, X.pExpr); }else{ A.pExpr = sqlite3PExpr(pParse, TK_IN, X.pExpr, 0, 0); if( A.pExpr ){ A.pExpr->x.pList = Y; sqlite3ExprSetHeight(pParse, A.pExpr); }else{ sqlite3ExprListDelete(pParse->db, Y); } if( N ) A.pExpr = sqlite3PExpr(pParse, TK_NOT, A.pExpr, 0, 0); } A.zStart = X.zStart; A.zEnd = &E.z[E.n]; } expr(A) ::= LP(B) select(X) RP(E). { A.pExpr = sqlite3PExpr(pParse, TK_SELECT, 0, 0, 0); if( A.pExpr ){ A.pExpr->x.pSelect = X; |
︙ | ︙ |
Changes to src/pcache.c.
︙ | ︙ | |||
256 257 258 259 260 261 262 | } pPage = sqlite3GlobalConfig.pcache.xFetch(pCache->pCache, pgno, 2); } if( pPage ){ if( !pPage->pData ){ | | > | | > | | 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 | } pPage = sqlite3GlobalConfig.pcache.xFetch(pCache->pCache, pgno, 2); } if( pPage ){ if( !pPage->pData ){ memset(pPage, 0, sizeof(PgHdr)); pPage->pData = (void *)&pPage[1]; pPage->pExtra = (void*)&((char *)pPage->pData)[pCache->szPage]; memset(pPage->pExtra, 0, pCache->szExtra); pPage->pCache = pCache; pPage->pgno = pgno; } assert( pPage->pCache==pCache ); assert( pPage->pgno==pgno ); assert( pPage->pData==(void *)&pPage[1] ); assert( pPage->pExtra==(void *)&((char *)&pPage[1])[pCache->szPage] ); if( 0==pPage->nRef ){ pCache->nRef++; } pPage->nRef++; if( pgno==1 ){ pCache->pPage1 = pPage; |
︙ | ︙ | |||
403 404 405 406 407 408 409 | */ void sqlite3PcacheTruncate(PCache *pCache, Pgno pgno){ if( pCache->pCache ){ PgHdr *p; PgHdr *pNext; for(p=pCache->pDirty; p; p=pNext){ pNext = p->pDirtyNext; | > > > > | > | 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 | */ void sqlite3PcacheTruncate(PCache *pCache, Pgno pgno){ if( pCache->pCache ){ PgHdr *p; PgHdr *pNext; for(p=pCache->pDirty; p; p=pNext){ pNext = p->pDirtyNext; /* This routine never gets call with a positive pgno except right ** after sqlite3PcacheCleanAll(). So if there are dirty pages, ** it must be that pgno==0. */ assert( p->pgno>0 ); if( ALWAYS(p->pgno>pgno) ){ assert( p->flags&PGHDR_DIRTY ); sqlite3PcacheMakeClean(p); } } if( pgno==0 && pCache->pPage1 ){ memset(pCache->pPage1->pData, 0, pCache->szPage); pgno = 1; |
︙ | ︙ |
Changes to src/pcache1.c.
︙ | ︙ | |||
169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 | pcache1LeaveMutex(); p = sqlite3Malloc(nByte); pcache1EnterMutex(); if( p ){ int sz = sqlite3MallocSize(p); sqlite3StatusAdd(SQLITE_STATUS_PAGECACHE_OVERFLOW, sz); } } return p; } /* ** Free an allocated buffer obtained from pcache1Alloc(). */ static void pcache1Free(void *p){ assert( sqlite3_mutex_held(pcache1.mutex) ); if( p==0 ) return; if( p>=pcache1.pStart && p<pcache1.pEnd ){ PgFreeslot *pSlot; sqlite3StatusAdd(SQLITE_STATUS_PAGECACHE_USED, -1); pSlot = (PgFreeslot*)p; pSlot->pNext = pcache1.pFree; pcache1.pFree = pSlot; }else{ | > > > > | | 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 | pcache1LeaveMutex(); p = sqlite3Malloc(nByte); pcache1EnterMutex(); if( p ){ int sz = sqlite3MallocSize(p); sqlite3StatusAdd(SQLITE_STATUS_PAGECACHE_OVERFLOW, sz); } sqlite3MemdebugSetType(p, MEMTYPE_PCACHE); } return p; } /* ** Free an allocated buffer obtained from pcache1Alloc(). */ static void pcache1Free(void *p){ assert( sqlite3_mutex_held(pcache1.mutex) ); if( p==0 ) return; if( p>=pcache1.pStart && p<pcache1.pEnd ){ PgFreeslot *pSlot; sqlite3StatusAdd(SQLITE_STATUS_PAGECACHE_USED, -1); pSlot = (PgFreeslot*)p; pSlot->pNext = pcache1.pFree; pcache1.pFree = pSlot; }else{ int iSize; assert( sqlite3MemdebugHasType(p, MEMTYPE_PCACHE) ); sqlite3MemdebugSetType(p, MEMTYPE_HEAP); iSize = sqlite3MallocSize(p); sqlite3StatusAdd(SQLITE_STATUS_PAGECACHE_OVERFLOW, -iSize); sqlite3_free(p); } } /* ** Allocate a new page object initially associated with cache pCache. |
︙ | ︙ | |||
708 709 710 711 712 713 714 | /* ** This function is called during initialization (sqlite3_initialize()) to ** install the default pluggable cache module, assuming the user has not ** already provided an alternative. */ void sqlite3PCacheSetDefault(void){ | | | 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 | /* ** This function is called during initialization (sqlite3_initialize()) to ** install the default pluggable cache module, assuming the user has not ** already provided an alternative. */ void sqlite3PCacheSetDefault(void){ static const sqlite3_pcache_methods defaultMethods = { 0, /* pArg */ pcache1Init, /* xInit */ pcache1Shutdown, /* xShutdown */ pcache1Create, /* xCreate */ pcache1Cachesize, /* xCachesize */ pcache1Pagecount, /* xPagecount */ pcache1Fetch, /* xFetch */ |
︙ | ︙ |
Changes to src/pragma.c.
︙ | ︙ | |||
169 170 171 172 173 174 175 176 177 178 179 180 181 182 | { "full_column_names", SQLITE_FullColNames }, { "short_column_names", SQLITE_ShortColNames }, { "count_changes", SQLITE_CountRows }, { "empty_result_callbacks", SQLITE_NullCallback }, { "legacy_file_format", SQLITE_LegacyFileFmt }, { "fullfsync", SQLITE_FullFSync }, { "reverse_unordered_selects", SQLITE_ReverseOrder }, #ifdef SQLITE_DEBUG { "sql_trace", SQLITE_SqlTrace }, { "vdbe_listing", SQLITE_VdbeListing }, { "vdbe_trace", SQLITE_VdbeTrace }, #endif #ifndef SQLITE_OMIT_CHECK { "ignore_check_constraints", SQLITE_IgnoreChecks }, | > > > | 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 | { "full_column_names", SQLITE_FullColNames }, { "short_column_names", SQLITE_ShortColNames }, { "count_changes", SQLITE_CountRows }, { "empty_result_callbacks", SQLITE_NullCallback }, { "legacy_file_format", SQLITE_LegacyFileFmt }, { "fullfsync", SQLITE_FullFSync }, { "reverse_unordered_selects", SQLITE_ReverseOrder }, #ifndef SQLITE_OMIT_AUTOMATIC_INDEX { "automatic_index", SQLITE_AutoIndex }, #endif #ifdef SQLITE_DEBUG { "sql_trace", SQLITE_SqlTrace }, { "vdbe_listing", SQLITE_VdbeListing }, { "vdbe_trace", SQLITE_VdbeTrace }, #endif #ifndef SQLITE_OMIT_CHECK { "ignore_check_constraints", SQLITE_IgnoreChecks }, |
︙ | ︙ | |||
251 252 253 254 255 256 257 258 259 260 261 262 263 264 | default: zName = "NO ACTION"; assert( action==OE_None ); break; } return zName; } #endif /* ** Process a pragma statement. ** ** Pragmas are of this form: ** ** PRAGMA [database.]id [= value] ** | > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | default: zName = "NO ACTION"; assert( action==OE_None ); break; } return zName; } #endif /* ** Parameter eMode must be one of the PAGER_JOURNALMODE_XXX constants ** defined in pager.h. This function returns the associated lowercase ** journal-mode name. */ const char *sqlite3JournalModename(int eMode){ static char * const azModeName[] = { "delete", "persist", "off", "truncate", "memory" #ifndef SQLITE_OMIT_WAL , "wal" #endif }; assert( PAGER_JOURNALMODE_DELETE==0 ); assert( PAGER_JOURNALMODE_PERSIST==1 ); assert( PAGER_JOURNALMODE_OFF==2 ); assert( PAGER_JOURNALMODE_TRUNCATE==3 ); assert( PAGER_JOURNALMODE_MEMORY==4 ); assert( PAGER_JOURNALMODE_WAL==5 ); assert( eMode>=0 && eMode<=ArraySize(azModeName) ); if( eMode==ArraySize(azModeName) ) return 0; return azModeName[eMode]; } /* ** Process a pragma statement. ** ** Pragmas are of this form: ** ** PRAGMA [database.]id [= value] ** |
︙ | ︙ | |||
323 324 325 326 327 328 329 | ** ** The first form reports the current persistent setting for the ** page cache size. The value returned is the maximum number of ** pages in the page cache. The second form sets both the current ** page cache size value and the persistent page cache size value ** stored in the database file. ** | | | | | | | 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 | ** ** The first form reports the current persistent setting for the ** page cache size. The value returned is the maximum number of ** pages in the page cache. The second form sets both the current ** page cache size value and the persistent page cache size value ** stored in the database file. ** ** Older versions of SQLite would set the default cache size to a ** negative number to indicate synchronous=OFF. These days, synchronous ** is always on by default regardless of the sign of the default cache ** size. But continue to take the absolute value of the default cache ** size of historical compatibility. */ if( sqlite3StrICmp(zLeft,"default_cache_size")==0 ){ static const VdbeOpList getCacheSize[] = { { OP_Transaction, 0, 0, 0}, /* 0 */ { OP_ReadCookie, 0, 1, BTREE_DEFAULT_CACHE_SIZE}, /* 1 */ { OP_IfPos, 1, 7, 0}, { OP_Integer, 0, 2, 0}, |
︙ | ︙ | |||
356 357 358 359 360 361 362 | sqlite3VdbeChangeP1(v, addr+1, iDb); sqlite3VdbeChangeP1(v, addr+6, SQLITE_DEFAULT_CACHE_SIZE); }else{ int size = atoi(zRight); if( size<0 ) size = -size; sqlite3BeginWriteOperation(pParse, 0, iDb); sqlite3VdbeAddOp2(v, OP_Integer, size, 1); | < < < < | 384 385 386 387 388 389 390 391 392 393 394 395 396 397 | sqlite3VdbeChangeP1(v, addr+1, iDb); sqlite3VdbeChangeP1(v, addr+6, SQLITE_DEFAULT_CACHE_SIZE); }else{ int size = atoi(zRight); if( size<0 ) size = -size; sqlite3BeginWriteOperation(pParse, 0, iDb); sqlite3VdbeAddOp2(v, OP_Integer, size, 1); sqlite3VdbeAddOp3(v, OP_SetCookie, iDb, BTREE_DEFAULT_CACHE_SIZE, 1); pDb->pSchema->cache_size = size; sqlite3BtreeSetCacheSize(pDb->pBt, pDb->pSchema->cache_size); } }else /* |
︙ | ︙ | |||
504 505 506 507 508 509 510 | sqlite3VdbeSetColName(v, 0, COLNAME_NAME, "locking_mode", SQLITE_STATIC); sqlite3VdbeAddOp4(v, OP_String8, 0, 1, 0, zRet, 0); sqlite3VdbeAddOp2(v, OP_ResultRow, 1, 1); }else /* ** PRAGMA [database.]journal_mode | | > | > | > > > > > | < > > > > > | | > | > > > | | < < < | < < | < < < < < < < < < < > | | < | < < | < < < < < < < < < < < | 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 | sqlite3VdbeSetColName(v, 0, COLNAME_NAME, "locking_mode", SQLITE_STATIC); sqlite3VdbeAddOp4(v, OP_String8, 0, 1, 0, zRet, 0); sqlite3VdbeAddOp2(v, OP_ResultRow, 1, 1); }else /* ** PRAGMA [database.]journal_mode ** PRAGMA [database.]journal_mode = ** (delete|persist|off|truncate|memory|wal|off) */ if( sqlite3StrICmp(zLeft,"journal_mode")==0 ){ int eMode; /* One of the PAGER_JOURNALMODE_XXX symbols */ int ii; /* Loop counter */ /* Force the schema to be loaded on all databases. This cases all ** database files to be opened and the journal_modes set. */ if( sqlite3ReadSchema(pParse) ){ goto pragma_out; } sqlite3VdbeSetNumCols(v, 1); sqlite3VdbeSetColName(v, 0, COLNAME_NAME, "journal_mode", SQLITE_STATIC); if( zRight==0 ){ /* If there is no "=MODE" part of the pragma, do a query for the ** current mode */ eMode = PAGER_JOURNALMODE_QUERY; }else{ const char *zMode; int n = sqlite3Strlen30(zRight); for(eMode=0; (zMode = sqlite3JournalModename(eMode))!=0; eMode++){ if( sqlite3StrNICmp(zRight, zMode, n)==0 ) break; } if( !zMode ){ /* If the "=MODE" part does not match any known journal mode, ** then do a query */ eMode = PAGER_JOURNALMODE_QUERY; } } if( eMode==PAGER_JOURNALMODE_QUERY && pId2->n==0 ){ /* Convert "PRAGMA journal_mode" into "PRAGMA main.journal_mode" */ iDb = 0; pId2->n = 1; } for(ii=db->nDb-1; ii>=0; ii--){ if( db->aDb[ii].pBt && (ii==iDb || pId2->n==0) ){ sqlite3VdbeUsesBtree(v, ii); sqlite3VdbeAddOp3(v, OP_JournalMode, ii, 1, eMode); } } sqlite3VdbeAddOp2(v, OP_ResultRow, 1, 1); }else /* ** PRAGMA [database.]journal_size_limit ** PRAGMA [database.]journal_size_limit=N ** |
︙ | ︙ | |||
1379 1380 1381 1382 1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 | sqlite3VdbeSetColName(v, 0, COLNAME_NAME, "compile_option", SQLITE_STATIC); while( (zOpt = sqlite3_compileoption_get(i++))!=0 ){ sqlite3VdbeAddOp4(v, OP_String8, 0, 1, 0, zOpt, 0); sqlite3VdbeAddOp2(v, OP_ResultRow, 1, 1); } }else #endif /* SQLITE_OMIT_COMPILEOPTION_DIAGS */ #if defined(SQLITE_DEBUG) || defined(SQLITE_TEST) /* ** Report the current state of file logs for all databases */ if( sqlite3StrICmp(zLeft, "lock_status")==0 ){ static const char *const azLockName[] = { | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | sqlite3VdbeSetColName(v, 0, COLNAME_NAME, "compile_option", SQLITE_STATIC); while( (zOpt = sqlite3_compileoption_get(i++))!=0 ){ sqlite3VdbeAddOp4(v, OP_String8, 0, 1, 0, zOpt, 0); sqlite3VdbeAddOp2(v, OP_ResultRow, 1, 1); } }else #endif /* SQLITE_OMIT_COMPILEOPTION_DIAGS */ #ifndef SQLITE_OMIT_WAL /* ** PRAGMA [database.]wal_checkpoint ** ** Checkpoint the database. */ if( sqlite3StrICmp(zLeft, "wal_checkpoint")==0 ){ if( sqlite3ReadSchema(pParse) ) goto pragma_out; sqlite3VdbeAddOp3(v, OP_Checkpoint, pId2->z?iDb:SQLITE_MAX_ATTACHED, 0, 0); }else /* ** PRAGMA wal_autocheckpoint ** PRAGMA wal_autocheckpoint = N ** ** Configure a database connection to automatically checkpoint a database ** after accumulating N frames in the log. Or query for the current value ** of N. */ if( sqlite3StrICmp(zLeft, "wal_autocheckpoint")==0 ){ sqlite3_int64 ret; if( zRight ){ int nAuto = atoi(zRight); sqlite3_wal_autocheckpoint(db, nAuto); } if( db->xWalCallback==sqlite3WalDefaultHook ){ ret = SQLITE_PTR_TO_INT(db->pWalArg); }else{ ret = 0; } returnSingleInt(pParse, "wal_autocheckpoint", &ret); }else #endif #if defined(SQLITE_DEBUG) || defined(SQLITE_TEST) /* ** Report the current state of file logs for all databases */ if( sqlite3StrICmp(zLeft, "lock_status")==0 ){ static const char *const azLockName[] = { |
︙ | ︙ |
Changes to src/prepare.c.
︙ | ︙ | |||
72 73 74 75 76 77 78 | corruptSchema(pData, argv[0], 0); }else if( argv[2] && argv[2][0] ){ /* Call the parser to process a CREATE TABLE, INDEX or VIEW. ** But because db->init.busy is set to 1, no VDBE code is generated ** or executed. All the parser does is build the internal data ** structures that describe the table, index, or view. */ | < > > > | > > < | | < > | 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 | corruptSchema(pData, argv[0], 0); }else if( argv[2] && argv[2][0] ){ /* Call the parser to process a CREATE TABLE, INDEX or VIEW. ** But because db->init.busy is set to 1, no VDBE code is generated ** or executed. All the parser does is build the internal data ** structures that describe the table, index, or view. */ int rc; sqlite3_stmt *pStmt; TESTONLY(int rcp); /* Return code from sqlite3_prepare() */ assert( db->init.busy ); db->init.iDb = iDb; db->init.newTnum = atoi(argv[1]); db->init.orphanTrigger = 0; TESTONLY(rcp = ) sqlite3_prepare(db, argv[2], -1, &pStmt, 0); rc = db->errCode; assert( (rc&0xFF)==(rcp&0xFF) ); db->init.iDb = 0; if( SQLITE_OK!=rc ){ if( db->init.orphanTrigger ){ assert( iDb==1 ); }else{ pData->rc = rc; if( rc==SQLITE_NOMEM ){ db->mallocFailed = 1; }else if( rc!=SQLITE_INTERRUPT && (rc&0xFF)!=SQLITE_LOCKED ){ corruptSchema(pData, argv[0], sqlite3_errmsg(db)); } } } sqlite3_finalize(pStmt); }else if( argv[0]==0 ){ corruptSchema(pData, 0, 0); }else{ /* If the SQL column is blank it means this is an index that ** was created to be the PRIMARY KEY or to fulfill a UNIQUE ** constraint for a CREATE TABLE. The index should have already ** been created when we processed the CREATE TABLE. All we have |
︙ | ︙ | |||
578 579 580 581 582 583 584 585 586 587 588 589 590 591 | } } } sqlite3VtabUnlockList(db); pParse->db = db; if( nBytes>=0 && (nBytes==0 || zSql[nBytes-1]!=0) ){ char *zSqlCopy; int mxLen = db->aLimit[SQLITE_LIMIT_SQL_LENGTH]; testcase( nBytes==mxLen ); testcase( nBytes==mxLen+1 ); if( nBytes>mxLen ){ sqlite3Error(db, SQLITE_TOOBIG, "statement too long"); | > | 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 | } } } sqlite3VtabUnlockList(db); pParse->db = db; pParse->nQueryLoop = (double)1; if( nBytes>=0 && (nBytes==0 || zSql[nBytes-1]!=0) ){ char *zSqlCopy; int mxLen = db->aLimit[SQLITE_LIMIT_SQL_LENGTH]; testcase( nBytes==mxLen ); testcase( nBytes==mxLen+1 ); if( nBytes>mxLen ){ sqlite3Error(db, SQLITE_TOOBIG, "statement too long"); |
︙ | ︙ | |||
599 600 601 602 603 604 605 606 607 608 609 610 611 612 | pParse->zTail = &zSql[pParse->zTail-zSqlCopy]; }else{ pParse->zTail = &zSql[nBytes]; } }else{ sqlite3RunParser(pParse, zSql, &zErrMsg); } if( db->mallocFailed ){ pParse->rc = SQLITE_NOMEM; } if( pParse->rc==SQLITE_DONE ) pParse->rc = SQLITE_OK; if( pParse->checkSchema ){ schemaIsValid(pParse); | > | 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 | pParse->zTail = &zSql[pParse->zTail-zSqlCopy]; }else{ pParse->zTail = &zSql[nBytes]; } }else{ sqlite3RunParser(pParse, zSql, &zErrMsg); } assert( 1==(int)pParse->nQueryLoop ); if( db->mallocFailed ){ pParse->rc = SQLITE_NOMEM; } if( pParse->rc==SQLITE_DONE ) pParse->rc = SQLITE_OK; if( pParse->checkSchema ){ schemaIsValid(pParse); |
︙ | ︙ |
Changes to src/resolve.c.
︙ | ︙ | |||
353 354 355 356 357 358 359 360 361 362 363 364 365 366 | if( zDb ){ sqlite3ErrorMsg(pParse, "%s: %s.%s.%s", zErr, zDb, zTab, zCol); }else if( zTab ){ sqlite3ErrorMsg(pParse, "%s: %s.%s", zErr, zTab, zCol); }else{ sqlite3ErrorMsg(pParse, "%s: %s", zErr, zCol); } pTopNC->nErr++; } /* If a column from a table in pSrcList is referenced, then record ** this fact in the pSrcList.a[].colUsed bitmask. Column 0 causes ** bit 0 to be set. Column 1 sets bit 1. And so forth. If the ** column number is greater than the number of bits in the bitmask | > | 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 | if( zDb ){ sqlite3ErrorMsg(pParse, "%s: %s.%s.%s", zErr, zDb, zTab, zCol); }else if( zTab ){ sqlite3ErrorMsg(pParse, "%s: %s.%s", zErr, zTab, zCol); }else{ sqlite3ErrorMsg(pParse, "%s: %s", zErr, zCol); } pParse->checkSchema = 1; pTopNC->nErr++; } /* If a column from a table in pSrcList is referenced, then record ** this fact in the pSrcList.a[].colUsed bitmask. Column 0 causes ** bit 0 to be set. Column 1 sets bit 1. And so forth. If the ** column number is greater than the number of bits in the bitmask |
︙ | ︙ | |||
399 400 401 402 403 404 405 | } else { return WRC_Abort; } } /* ** Allocate and return a pointer to an expression to load the column iCol | | > > | 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 | } else { return WRC_Abort; } } /* ** Allocate and return a pointer to an expression to load the column iCol ** from datasource iSrc in SrcList pSrc. */ Expr *sqlite3CreateColumnExpr(sqlite3 *db, SrcList *pSrc, int iSrc, int iCol){ Expr *p = sqlite3ExprAlloc(db, TK_COLUMN, 0, 0); if( p ){ struct SrcList_item *pItem = &pSrc->a[iSrc]; p->pTab = pItem->pTab; p->iTable = pItem->iCursor; if( p->pTab->iPKey==iCol ){ p->iColumn = -1; }else{ p->iColumn = (ynVar)iCol; testcase( iCol==BMS ); testcase( iCol==BMS-1 ); pItem->colUsed |= ((Bitmask)1)<<(iCol>=BMS ? BMS-1 : iCol); } ExprSetProperty(p, EP_Resolved); } return p; } |
︙ | ︙ |
Changes to src/select.c.
︙ | ︙ | |||
2522 2523 2524 2525 2526 2527 2528 | ** a separate restriction deriving from ticket #350. ** ** (13) The subquery and outer query do not both use LIMIT ** ** (14) The subquery does not use OFFSET ** ** (15) The outer query is not part of a compound select or the | | | | 2522 2523 2524 2525 2526 2527 2528 2529 2530 2531 2532 2533 2534 2535 2536 2537 | ** a separate restriction deriving from ticket #350. ** ** (13) The subquery and outer query do not both use LIMIT ** ** (14) The subquery does not use OFFSET ** ** (15) The outer query is not part of a compound select or the ** subquery does not have a LIMIT clause. ** (See ticket #2339 and ticket [02a8e81d44]). ** ** (16) The outer query is not an aggregate or the subquery does ** not contain ORDER BY. (Ticket #2942) This used to not matter ** until we introduced the group_concat() function. ** ** (17) The sub-query is not a compound select, or it is a UNION ALL ** compound clause made up entirely of non-aggregate queries, and |
︙ | ︙ | |||
2606 2607 2608 2609 2610 2611 2612 | /* Prior to version 3.1.2, when LIMIT and OFFSET had to be simple constants, ** not arbitrary expresssions, we allowed some combining of LIMIT and OFFSET ** because they could be computed at compile-time. But when LIMIT and OFFSET ** became arbitrary expressions, we were forced to add restrictions (13) ** and (14). */ if( pSub->pLimit && p->pLimit ) return 0; /* Restriction (13) */ if( pSub->pOffset ) return 0; /* Restriction (14) */ | | | 2606 2607 2608 2609 2610 2611 2612 2613 2614 2615 2616 2617 2618 2619 2620 | /* Prior to version 3.1.2, when LIMIT and OFFSET had to be simple constants, ** not arbitrary expresssions, we allowed some combining of LIMIT and OFFSET ** because they could be computed at compile-time. But when LIMIT and OFFSET ** became arbitrary expressions, we were forced to add restrictions (13) ** and (14). */ if( pSub->pLimit && p->pLimit ) return 0; /* Restriction (13) */ if( pSub->pOffset ) return 0; /* Restriction (14) */ if( p->pRightmost && pSub->pLimit ){ return 0; /* Restriction (15) */ } if( pSubSrc->nSrc==0 ) return 0; /* Restriction (7) */ if( ((pSub->selFlags & SF_Distinct)!=0 || pSub->pLimit) && (pSrc->nSrc>1 || isAgg) ){ /* Restrictions (4)(5)(8)(9) */ return 0; } |
︙ | ︙ | |||
3016 3017 3018 3019 3020 3021 3022 3023 3024 3025 3026 3027 3028 3029 | Index *pIdx; for(pIdx=pTab->pIndex; pIdx && sqlite3StrICmp(pIdx->zName, zIndex); pIdx=pIdx->pNext ); if( !pIdx ){ sqlite3ErrorMsg(pParse, "no such index: %s", zIndex, 0); return SQLITE_ERROR; } pFrom->pIndex = pIdx; } return SQLITE_OK; } | > | 3016 3017 3018 3019 3020 3021 3022 3023 3024 3025 3026 3027 3028 3029 3030 | Index *pIdx; for(pIdx=pTab->pIndex; pIdx && sqlite3StrICmp(pIdx->zName, zIndex); pIdx=pIdx->pNext ); if( !pIdx ){ sqlite3ErrorMsg(pParse, "no such index: %s", zIndex, 0); pParse->checkSchema = 1; return SQLITE_ERROR; } pFrom->pIndex = pIdx; } return SQLITE_OK; } |
︙ | ︙ | |||
3494 3495 3496 3497 3498 3499 3500 3501 3502 3503 3504 3505 3506 3507 | sqlite3ExprCacheAffinityChange(pParse, regAgg, nArg); sqlite3ReleaseTempRange(pParse, regAgg, nArg); if( addrNext ){ sqlite3VdbeResolveLabel(v, addrNext); sqlite3ExprCacheClear(pParse); } } for(i=0, pC=pAggInfo->aCol; i<pAggInfo->nAccumulator; i++, pC++){ sqlite3ExprCode(pParse, pC->pExpr, pC->iMem); } pAggInfo->directMode = 0; sqlite3ExprCacheClear(pParse); } | > > > > > > > > > > > > | 3495 3496 3497 3498 3499 3500 3501 3502 3503 3504 3505 3506 3507 3508 3509 3510 3511 3512 3513 3514 3515 3516 3517 3518 3519 3520 | sqlite3ExprCacheAffinityChange(pParse, regAgg, nArg); sqlite3ReleaseTempRange(pParse, regAgg, nArg); if( addrNext ){ sqlite3VdbeResolveLabel(v, addrNext); sqlite3ExprCacheClear(pParse); } } /* Before populating the accumulator registers, clear the column cache. ** Otherwise, if any of the required column values are already present ** in registers, sqlite3ExprCode() may use OP_SCopy to copy the value ** to pC->iMem. But by the time the value is used, the original register ** may have been used, invalidating the underlying buffer holding the ** text or blob value. See ticket [883034dcb5]. ** ** Another solution would be to change the OP_SCopy used to copy cached ** values to an OP_Copy. */ sqlite3ExprCacheClear(pParse); for(i=0, pC=pAggInfo->aCol; i<pAggInfo->nAccumulator; i++, pC++){ sqlite3ExprCode(pParse, pC->pExpr, pC->iMem); } pAggInfo->directMode = 0; sqlite3ExprCacheClear(pParse); } |
︙ | ︙ | |||
3701 3702 3703 3704 3705 3706 3707 3708 3709 3710 3711 3712 3713 3714 | assert( p->pGroupBy==0 || (p->selFlags & SF_Aggregate)!=0 ); if( (p->selFlags & (SF_Distinct|SF_Aggregate))==SF_Distinct ){ p->pGroupBy = sqlite3ExprListDup(db, p->pEList, 0); pGroupBy = p->pGroupBy; p->selFlags &= ~SF_Distinct; isDistinct = 0; } /* If there is an ORDER BY clause, then this sorting ** index might end up being unused if the data can be ** extracted in pre-sorted order. If that is the case, then the ** OP_OpenEphemeral instruction will be changed to an OP_Noop once ** we figure out that the sorting index is not needed. The addrSortIndex ** variable is used to facilitate that change. | > > > > > > > > > > > > | 3714 3715 3716 3717 3718 3719 3720 3721 3722 3723 3724 3725 3726 3727 3728 3729 3730 3731 3732 3733 3734 3735 3736 3737 3738 3739 | assert( p->pGroupBy==0 || (p->selFlags & SF_Aggregate)!=0 ); if( (p->selFlags & (SF_Distinct|SF_Aggregate))==SF_Distinct ){ p->pGroupBy = sqlite3ExprListDup(db, p->pEList, 0); pGroupBy = p->pGroupBy; p->selFlags &= ~SF_Distinct; isDistinct = 0; } /* If there is both a GROUP BY and an ORDER BY clause and they are ** identical, then disable the ORDER BY clause since the GROUP BY ** will cause elements to come out in the correct order. This is ** an optimization - the correct answer should result regardless. ** Use the SQLITE_GroupByOrder flag with SQLITE_TESTCTRL_OPTIMIZER ** to disable this optimization for testing purposes. */ if( sqlite3ExprListCompare(p->pGroupBy, pOrderBy)==0 && (db->flags & SQLITE_GroupByOrder)==0 ){ pOrderBy = 0; } /* If there is an ORDER BY clause, then this sorting ** index might end up being unused if the data can be ** extracted in pre-sorted order. If that is the case, then the ** OP_OpenEphemeral instruction will be changed to an OP_Noop once ** we figure out that the sorting index is not needed. The addrSortIndex ** variable is used to facilitate that change. |
︙ | ︙ |
Changes to src/shell.c.
︙ | ︙ | |||
2574 2575 2576 2577 2578 2579 2580 | } if( zFirstCmd ){ /* Run just the command that follows the database name */ if( zFirstCmd[0]=='.' ){ rc = do_meta_command(zFirstCmd, &data); | < | 2574 2575 2576 2577 2578 2579 2580 2581 2582 2583 2584 2585 2586 2587 | } if( zFirstCmd ){ /* Run just the command that follows the database name */ if( zFirstCmd[0]=='.' ){ rc = do_meta_command(zFirstCmd, &data); }else{ open_db(&data); rc = shell_exec(data.db, zFirstCmd, shell_callback, &data, &zErrMsg); if( zErrMsg!=0 ){ fprintf(stderr,"Error: %s\n", zErrMsg); return rc!=0 ? rc : 1; }else if( rc!=0 ){ |
︙ | ︙ | |||
2621 2622 2623 2624 2625 2626 2627 | } free(zHome); }else{ rc = process_input(&data, stdin); } } set_table_name(&data, 0); | | | | > | 2620 2621 2622 2623 2624 2625 2626 2627 2628 2629 2630 2631 2632 2633 2634 2635 | } free(zHome); }else{ rc = process_input(&data, stdin); } } set_table_name(&data, 0); if( data.db ){ if( sqlite3_close(data.db)!=SQLITE_OK ){ fprintf(stderr,"Error: cannot close database \"%s\"\n", sqlite3_errmsg(db)); rc++; } } return rc; } |
Changes to src/sqlite.h.in.
︙ | ︙ | |||
137 138 139 140 141 142 143 | ** See also: [sqlite_version()] and [sqlite_source_id()]. */ SQLITE_EXTERN const char sqlite3_version[]; const char *sqlite3_libversion(void); const char *sqlite3_sourceid(void); int sqlite3_libversion_number(void); | < | 137 138 139 140 141 142 143 144 145 146 147 148 149 150 | ** See also: [sqlite_version()] and [sqlite_source_id()]. */ SQLITE_EXTERN const char sqlite3_version[]; const char *sqlite3_libversion(void); const char *sqlite3_sourceid(void); int sqlite3_libversion_number(void); /* ** CAPI3REF: Run-Time Library Compilation Options Diagnostics ** ** ^The sqlite3_compileoption_used() function returns 0 or 1 ** indicating whether the specified option was defined at ** compile time. ^The SQLITE_ prefix may be omitted from the ** option name passed to sqlite3_compileoption_used(). |
︙ | ︙ | |||
160 161 162 163 164 165 166 167 168 | ** ^Support for the diagnostic functions sqlite3_compileoption_used() ** and sqlite3_compileoption_get() may be omitted by specifing the ** [SQLITE_OMIT_COMPILEOPTION_DIAGS] option at compile time. ** ** See also: SQL functions [sqlite_compileoption_used()] and ** [sqlite_compileoption_get()] and the [compile_options pragma]. */ int sqlite3_compileoption_used(const char *zOptName); const char *sqlite3_compileoption_get(int N); | > | | 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 | ** ^Support for the diagnostic functions sqlite3_compileoption_used() ** and sqlite3_compileoption_get() may be omitted by specifing the ** [SQLITE_OMIT_COMPILEOPTION_DIAGS] option at compile time. ** ** See also: SQL functions [sqlite_compileoption_used()] and ** [sqlite_compileoption_get()] and the [compile_options pragma]. */ #ifndef SQLITE_OMIT_COMPILEOPTION_DIAGS int sqlite3_compileoption_used(const char *zOptName); const char *sqlite3_compileoption_get(int N); #endif /* ** CAPI3REF: Test To See If The Library Is Threadsafe ** ** ^The sqlite3_threadsafe() function returns zero if and only if ** SQLite was compiled mutexing code omitted due to the ** [SQLITE_THREADSAFE] compile-time option being set to 0. |
︙ | ︙ | |||
384 385 386 387 388 389 390 | #define SQLITE_READONLY 8 /* Attempt to write a readonly database */ #define SQLITE_INTERRUPT 9 /* Operation terminated by sqlite3_interrupt()*/ #define SQLITE_IOERR 10 /* Some kind of disk I/O error occurred */ #define SQLITE_CORRUPT 11 /* The database disk image is malformed */ #define SQLITE_NOTFOUND 12 /* NOT USED. Table or record not found */ #define SQLITE_FULL 13 /* Insertion failed because database is full */ #define SQLITE_CANTOPEN 14 /* Unable to open the database file */ | | | 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 | #define SQLITE_READONLY 8 /* Attempt to write a readonly database */ #define SQLITE_INTERRUPT 9 /* Operation terminated by sqlite3_interrupt()*/ #define SQLITE_IOERR 10 /* Some kind of disk I/O error occurred */ #define SQLITE_CORRUPT 11 /* The database disk image is malformed */ #define SQLITE_NOTFOUND 12 /* NOT USED. Table or record not found */ #define SQLITE_FULL 13 /* Insertion failed because database is full */ #define SQLITE_CANTOPEN 14 /* Unable to open the database file */ #define SQLITE_PROTOCOL 15 /* Database lock protocol error */ #define SQLITE_EMPTY 16 /* Database is empty */ #define SQLITE_SCHEMA 17 /* The database schema changed */ #define SQLITE_TOOBIG 18 /* String or BLOB exceeds size limit */ #define SQLITE_CONSTRAINT 19 /* Abort due to constraint violation */ #define SQLITE_MISMATCH 20 /* Data type mismatch */ #define SQLITE_MISUSE 21 /* Library used incorrectly */ #define SQLITE_NOLFS 22 /* Uses OS features not supported on host */ |
︙ | ︙ | |||
440 441 442 443 444 445 446 | #define SQLITE_IOERR_BLOCKED (SQLITE_IOERR | (11<<8)) #define SQLITE_IOERR_NOMEM (SQLITE_IOERR | (12<<8)) #define SQLITE_IOERR_ACCESS (SQLITE_IOERR | (13<<8)) #define SQLITE_IOERR_CHECKRESERVEDLOCK (SQLITE_IOERR | (14<<8)) #define SQLITE_IOERR_LOCK (SQLITE_IOERR | (15<<8)) #define SQLITE_IOERR_CLOSE (SQLITE_IOERR | (16<<8)) #define SQLITE_IOERR_DIR_CLOSE (SQLITE_IOERR | (17<<8)) | > > > | > > | 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 | #define SQLITE_IOERR_BLOCKED (SQLITE_IOERR | (11<<8)) #define SQLITE_IOERR_NOMEM (SQLITE_IOERR | (12<<8)) #define SQLITE_IOERR_ACCESS (SQLITE_IOERR | (13<<8)) #define SQLITE_IOERR_CHECKRESERVEDLOCK (SQLITE_IOERR | (14<<8)) #define SQLITE_IOERR_LOCK (SQLITE_IOERR | (15<<8)) #define SQLITE_IOERR_CLOSE (SQLITE_IOERR | (16<<8)) #define SQLITE_IOERR_DIR_CLOSE (SQLITE_IOERR | (17<<8)) #define SQLITE_IOERR_SHMOPEN (SQLITE_IOERR | (18<<8)) #define SQLITE_IOERR_SHMSIZE (SQLITE_IOERR | (19<<8)) #define SQLITE_IOERR_SHMLOCK (SQLITE_IOERR | (20<<8)) #define SQLITE_LOCKED_SHAREDCACHE (SQLITE_LOCKED | (1<<8)) #define SQLITE_BUSY_RECOVERY (SQLITE_BUSY | (1<<8)) #define SQLITE_CANTOPEN_NOTEMPDIR (SQLITE_CANTOPEN | (1<<8)) /* ** CAPI3REF: Flags For File Open Operations ** ** These bit values are intended for use in the ** 3rd parameter to the [sqlite3_open_v2()] interface and ** in the 4th parameter to the xOpen method of the |
︙ | ︙ | |||
467 468 469 470 471 472 473 474 475 476 477 | #define SQLITE_OPEN_TEMP_JOURNAL 0x00001000 /* VFS only */ #define SQLITE_OPEN_SUBJOURNAL 0x00002000 /* VFS only */ #define SQLITE_OPEN_MASTER_JOURNAL 0x00004000 /* VFS only */ #define SQLITE_OPEN_NOMUTEX 0x00008000 /* Ok for sqlite3_open_v2() */ #define SQLITE_OPEN_FULLMUTEX 0x00010000 /* Ok for sqlite3_open_v2() */ #define SQLITE_OPEN_SHAREDCACHE 0x00020000 /* Ok for sqlite3_open_v2() */ #define SQLITE_OPEN_PRIVATECACHE 0x00040000 /* Ok for sqlite3_open_v2() */ /* ** CAPI3REF: Device Characteristics ** | > | | | | | | | | | | | | > | 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 | #define SQLITE_OPEN_TEMP_JOURNAL 0x00001000 /* VFS only */ #define SQLITE_OPEN_SUBJOURNAL 0x00002000 /* VFS only */ #define SQLITE_OPEN_MASTER_JOURNAL 0x00004000 /* VFS only */ #define SQLITE_OPEN_NOMUTEX 0x00008000 /* Ok for sqlite3_open_v2() */ #define SQLITE_OPEN_FULLMUTEX 0x00010000 /* Ok for sqlite3_open_v2() */ #define SQLITE_OPEN_SHAREDCACHE 0x00020000 /* Ok for sqlite3_open_v2() */ #define SQLITE_OPEN_PRIVATECACHE 0x00040000 /* Ok for sqlite3_open_v2() */ #define SQLITE_OPEN_WAL 0x00080000 /* VFS only */ /* ** CAPI3REF: Device Characteristics ** ** The xDeviceCharacteristics method of the [sqlite3_io_methods] ** object returns an integer which is a vector of the these ** bit values expressing I/O characteristics of the mass storage ** device that holds the file that the [sqlite3_io_methods] ** refers to. ** ** The SQLITE_IOCAP_ATOMIC property means that all writes of ** any size are atomic. The SQLITE_IOCAP_ATOMICnnn values ** mean that writes of blocks that are nnn bytes in size and ** are aligned to an address which is an integer multiple of ** nnn are atomic. The SQLITE_IOCAP_SAFE_APPEND value means ** that when data is appended to a file, the data is appended ** first then the size of the file is extended, never the other ** way around. The SQLITE_IOCAP_SEQUENTIAL property means that ** information is written to disk in the same order as calls ** to xWrite(). */ #define SQLITE_IOCAP_ATOMIC 0x00000001 #define SQLITE_IOCAP_ATOMIC512 0x00000002 #define SQLITE_IOCAP_ATOMIC1K 0x00000004 #define SQLITE_IOCAP_ATOMIC2K 0x00000008 #define SQLITE_IOCAP_ATOMIC4K 0x00000010 #define SQLITE_IOCAP_ATOMIC8K 0x00000020 #define SQLITE_IOCAP_ATOMIC16K 0x00000040 #define SQLITE_IOCAP_ATOMIC32K 0x00000080 #define SQLITE_IOCAP_ATOMIC64K 0x00000100 #define SQLITE_IOCAP_SAFE_APPEND 0x00000200 #define SQLITE_IOCAP_SEQUENTIAL 0x00000400 #define SQLITE_IOCAP_UNDELETABLE_WHEN_OPEN 0x00000800 /* ** CAPI3REF: File Locking Levels ** ** SQLite uses one of these integer values as the second ** argument to calls it makes to the xLock() and xUnlock() methods ** of an [sqlite3_io_methods] object. |
︙ | ︙ | |||
649 650 651 652 653 654 655 656 657 658 659 660 661 662 | int (*xFileSize)(sqlite3_file*, sqlite3_int64 *pSize); int (*xLock)(sqlite3_file*, int); int (*xUnlock)(sqlite3_file*, int); int (*xCheckReservedLock)(sqlite3_file*, int *pResOut); int (*xFileControl)(sqlite3_file*, int op, void *pArg); int (*xSectorSize)(sqlite3_file*); int (*xDeviceCharacteristics)(sqlite3_file*); /* Additional methods may be added in future releases */ }; /* ** CAPI3REF: Standard File Control Opcodes ** ** These integer constants are opcodes for the xFileControl method | > > > > > > | 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 | int (*xFileSize)(sqlite3_file*, sqlite3_int64 *pSize); int (*xLock)(sqlite3_file*, int); int (*xUnlock)(sqlite3_file*, int); int (*xCheckReservedLock)(sqlite3_file*, int *pResOut); int (*xFileControl)(sqlite3_file*, int op, void *pArg); int (*xSectorSize)(sqlite3_file*); int (*xDeviceCharacteristics)(sqlite3_file*); /* Methods above are valid for version 1 */ int (*xShmMap)(sqlite3_file*, int iPg, int pgsz, int, void volatile**); int (*xShmLock)(sqlite3_file*, int offset, int n, int flags); void (*xShmBarrier)(sqlite3_file*); int (*xShmUnmap)(sqlite3_file*, int deleteFlag); /* Methods above are valid for version 2 */ /* Additional methods may be added in future releases */ }; /* ** CAPI3REF: Standard File Control Opcodes ** ** These integer constants are opcodes for the xFileControl method |
︙ | ︙ | |||
810 811 812 813 814 815 816 | ** SQLite will always allocate at least mxPathname+1 bytes for the ** output buffer xFullPathname. The exact size of the output buffer ** is also passed as a parameter to both methods. If the output buffer ** is not large enough, [SQLITE_CANTOPEN] should be returned. Since this is ** handled as a fatal error by SQLite, vfs implementations should endeavor ** to prevent this by setting mxPathname to a sufficiently large value. ** | | | | > > > | > > > > | > > > > > > > | | > | > > > > > | > > | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | ** SQLite will always allocate at least mxPathname+1 bytes for the ** output buffer xFullPathname. The exact size of the output buffer ** is also passed as a parameter to both methods. If the output buffer ** is not large enough, [SQLITE_CANTOPEN] should be returned. Since this is ** handled as a fatal error by SQLite, vfs implementations should endeavor ** to prevent this by setting mxPathname to a sufficiently large value. ** ** The xRandomness(), xSleep(), xCurrentTime(), and xCurrentTimeInt64() ** interfaces are not strictly a part of the filesystem, but they are ** included in the VFS structure for completeness. ** The xRandomness() function attempts to return nBytes bytes ** of good-quality randomness into zOut. The return value is ** the actual number of bytes of randomness obtained. ** The xSleep() method causes the calling thread to sleep for at ** least the number of microseconds given. The xCurrentTime() ** method returns a Julian Day Number for the current date and time as ** a floating point value. ** The xCurrentTimeInt64() method returns, as an integer, the Julian ** Day Number multipled by 86400000 (the number of milliseconds in ** a 24-hour day). ** ^SQLite will use the xCurrentTimeInt64() method to get the current ** date and time if that method is available (if iVersion is 2 or ** greater and the function pointer is not NULL) and will fall back ** to xCurrentTime() if xCurrentTimeInt64() is unavailable. */ typedef struct sqlite3_vfs sqlite3_vfs; struct sqlite3_vfs { int iVersion; /* Structure version number (currently 2) */ int szOsFile; /* Size of subclassed sqlite3_file */ int mxPathname; /* Maximum file pathname length */ sqlite3_vfs *pNext; /* Next registered VFS */ const char *zName; /* Name of this virtual file system */ void *pAppData; /* Pointer to application-specific data */ int (*xOpen)(sqlite3_vfs*, const char *zName, sqlite3_file*, int flags, int *pOutFlags); int (*xDelete)(sqlite3_vfs*, const char *zName, int syncDir); int (*xAccess)(sqlite3_vfs*, const char *zName, int flags, int *pResOut); int (*xFullPathname)(sqlite3_vfs*, const char *zName, int nOut, char *zOut); void *(*xDlOpen)(sqlite3_vfs*, const char *zFilename); void (*xDlError)(sqlite3_vfs*, int nByte, char *zErrMsg); void (*(*xDlSym)(sqlite3_vfs*,void*, const char *zSymbol))(void); void (*xDlClose)(sqlite3_vfs*, void*); int (*xRandomness)(sqlite3_vfs*, int nByte, char *zOut); int (*xSleep)(sqlite3_vfs*, int microseconds); int (*xCurrentTime)(sqlite3_vfs*, double*); int (*xGetLastError)(sqlite3_vfs*, int, char *); /* ** The methods above are in version 1 of the sqlite_vfs object ** definition. Those that follow are added in version 2 or later */ int (*xCurrentTimeInt64)(sqlite3_vfs*, sqlite3_int64*); /* ** The methods above are in versions 1 and 2 of the sqlite_vfs object. ** New fields may be appended in figure versions. The iVersion ** value will increment whenever this happens. */ }; /* ** CAPI3REF: Flags for the xAccess VFS method ** ** These integer constants can be used as the third parameter to ** the xAccess method of an [sqlite3_vfs] object. They determine ** what kind of permissions the xAccess method is looking for. ** With SQLITE_ACCESS_EXISTS, the xAccess method ** simply checks whether the file exists. ** With SQLITE_ACCESS_READWRITE, the xAccess method ** checks whether the named directory is both readable and writable ** (in other words, if files can be added, removed, and renamed within ** the directory). ** The SQLITE_ACCESS_READWRITE constant is currently used only by the ** [temp_store_directory pragma], though this could change in a future ** release of SQLite. ** With SQLITE_ACCESS_READ, the xAccess method ** checks whether the file is readable. The SQLITE_ACCESS_READ constant is ** currently unused, though it might be used in a future release of ** SQLite. */ #define SQLITE_ACCESS_EXISTS 0 #define SQLITE_ACCESS_READWRITE 1 /* Used by PRAGMA temp_store_directory */ #define SQLITE_ACCESS_READ 2 /* Unused */ /* ** CAPI3REF: Flags for the xShmLock VFS method ** ** These integer constants define the various locking operations ** allowed by the xShmLock method of [sqlite3_io_methods]. The ** following are the only legal combinations of flags to the ** xShmLock method: ** ** <ul> ** <li> SQLITE_SHM_LOCK | SQLITE_SHM_SHARED ** <li> SQLITE_SHM_LOCK | SQLITE_SHM_EXCLUSIVE ** <li> SQLITE_SHM_UNLOCK | SQLITE_SHM_SHARED ** <li> SQLITE_SHM_UNLOCK | SQLITE_SHM_EXCLUSIVE ** </ul> ** ** When unlocking, the same SHARED or EXCLUSIVE flag must be supplied as ** was given no the corresponding lock. ** ** The xShmLock method can transition between unlocked and SHARED or ** between unlocked and EXCLUSIVE. It cannot transition between SHARED ** and EXCLUSIVE. */ #define SQLITE_SHM_UNLOCK 1 #define SQLITE_SHM_LOCK 2 #define SQLITE_SHM_SHARED 4 #define SQLITE_SHM_EXCLUSIVE 8 /* ** CAPI3REF: Maximum xShmLock index ** ** The xShmLock method on [sqlite3_io_methods] may use values ** between 0 and this upper bound as its "offset" argument. ** The SQLite core will never attempt to acquire or release a ** lock outside of this range */ #define SQLITE_SHM_NLOCK 8 /* ** CAPI3REF: Initialize The SQLite Library ** ** ^The sqlite3_initialize() routine initializes the ** SQLite library. ^The sqlite3_shutdown() routine ** deallocates any resources that were allocated by sqlite3_initialize(). |
︙ | ︙ | |||
972 973 974 975 976 977 978 | ** vary depending on the [SQLITE_CONFIG_SINGLETHREAD | configuration option] ** in the first argument. ** ** ^When a configuration option is set, sqlite3_config() returns [SQLITE_OK]. ** ^If the option is unknown or SQLite is unable to set the option ** then this routine returns a non-zero [error code]. */ | | < | < | 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 | ** vary depending on the [SQLITE_CONFIG_SINGLETHREAD | configuration option] ** in the first argument. ** ** ^When a configuration option is set, sqlite3_config() returns [SQLITE_OK]. ** ^If the option is unknown or SQLite is unable to set the option ** then this routine returns a non-zero [error code]. */ int sqlite3_config(int, ...); /* ** CAPI3REF: Configure database connections ** ** The sqlite3_db_config() interface is used to make configuration ** changes to a [database connection]. The interface is similar to ** [sqlite3_config()] except that the changes apply to a single ** [database connection] (specified in the first argument). The ** sqlite3_db_config() interface should only be used immediately after ** the database connection is created using [sqlite3_open()], ** [sqlite3_open16()], or [sqlite3_open_v2()]. ** ** The second argument to sqlite3_db_config(D,V,...) is the ** configuration verb - an integer code that indicates what ** aspect of the [database connection] is being configured. ** The only choice for this value is [SQLITE_DBCONFIG_LOOKASIDE]. ** New verbs are likely to be added in future releases of SQLite. ** Additional arguments depend on the verb. ** ** ^Calls to sqlite3_db_config() return SQLITE_OK if and only if ** the call is considered successful. */ int sqlite3_db_config(sqlite3*, int op, ...); /* ** CAPI3REF: Memory Allocation Routines ** ** An instance of this object defines the interface between SQLite ** and low-level memory allocation routines. ** ** This object is used in only one place in the SQLite interface. ** A pointer to an instance of this object is the argument to ** [sqlite3_config()] when the configuration option is |
︙ | ︙ | |||
1082 1083 1084 1085 1086 1087 1088 | int (*xInit)(void*); /* Initialize the memory allocator */ void (*xShutdown)(void*); /* Deinitialize the memory allocator */ void *pAppData; /* Argument to xInit() and xShutdown() */ }; /* ** CAPI3REF: Configuration Options | < | 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 | int (*xInit)(void*); /* Initialize the memory allocator */ void (*xShutdown)(void*); /* Deinitialize the memory allocator */ void *pAppData; /* Argument to xInit() and xShutdown() */ }; /* ** CAPI3REF: Configuration Options ** ** These constants are the available integer configuration options that ** can be passed as the first argument to the [sqlite3_config()] interface. ** ** New configuration options may be added in future releases of SQLite. ** Existing configuration options might be discontinued. Applications ** should check the return code from [sqlite3_config()] to make sure that |
︙ | ︙ | |||
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 | ** object and uses it for page cache memory allocations.</dd> ** ** <dt>SQLITE_CONFIG_GETPCACHE</dt> ** <dd> ^(This option takes a single argument which is a pointer to an ** [sqlite3_pcache_methods] object. SQLite copies of the current ** page cache implementation into that object.)^ </dd> ** ** </dl> */ #define SQLITE_CONFIG_SINGLETHREAD 1 /* nil */ #define SQLITE_CONFIG_MULTITHREAD 2 /* nil */ #define SQLITE_CONFIG_SERIALIZED 3 /* nil */ #define SQLITE_CONFIG_MALLOC 4 /* sqlite3_mem_methods* */ #define SQLITE_CONFIG_GETMALLOC 5 /* sqlite3_mem_methods* */ #define SQLITE_CONFIG_SCRATCH 6 /* void*, int sz, int N */ #define SQLITE_CONFIG_PAGECACHE 7 /* void*, int sz, int N */ #define SQLITE_CONFIG_HEAP 8 /* void*, int nByte, int min */ #define SQLITE_CONFIG_MEMSTATUS 9 /* boolean */ #define SQLITE_CONFIG_MUTEX 10 /* sqlite3_mutex_methods* */ #define SQLITE_CONFIG_GETMUTEX 11 /* sqlite3_mutex_methods* */ /* previously SQLITE_CONFIG_CHUNKALLOC 12 which is now unused. */ #define SQLITE_CONFIG_LOOKASIDE 13 /* int int */ #define SQLITE_CONFIG_PCACHE 14 /* sqlite3_pcache_methods* */ #define SQLITE_CONFIG_GETPCACHE 15 /* sqlite3_pcache_methods* */ #define SQLITE_CONFIG_LOG 16 /* xFunc, void* */ /* | > > > > > > > > > > > > > > > > > > | < | 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 | ** object and uses it for page cache memory allocations.</dd> ** ** <dt>SQLITE_CONFIG_GETPCACHE</dt> ** <dd> ^(This option takes a single argument which is a pointer to an ** [sqlite3_pcache_methods] object. SQLite copies of the current ** page cache implementation into that object.)^ </dd> ** ** <dt>SQLITE_CONFIG_LOG</dt> ** <dd> ^The SQLITE_CONFIG_LOG option takes two arguments: a pointer to a ** function with a call signature of void(*)(void*,int,const char*), ** and a pointer to void. ^If the function pointer is not NULL, it is ** invoked by [sqlite3_log()] to process each logging event. ^If the ** function pointer is NULL, the [sqlite3_log()] interface becomes a no-op. ** ^The void pointer that is the second argument to SQLITE_CONFIG_LOG is ** passed through as the first parameter to the application-defined logger ** function whenever that function is invoked. ^The second parameter to ** the logger function is a copy of the first parameter to the corresponding ** [sqlite3_log()] call and is intended to be a [result code] or an ** [extended result code]. ^The third parameter passed to the logger is ** log message after formatting via [sqlite3_snprintf()]. ** The SQLite logging interface is not reentrant; the logger function ** supplied by the application must not invoke any SQLite interface. ** In a multi-threaded application, the application-defined logger ** function must be threadsafe. </dd> ** ** </dl> */ #define SQLITE_CONFIG_SINGLETHREAD 1 /* nil */ #define SQLITE_CONFIG_MULTITHREAD 2 /* nil */ #define SQLITE_CONFIG_SERIALIZED 3 /* nil */ #define SQLITE_CONFIG_MALLOC 4 /* sqlite3_mem_methods* */ #define SQLITE_CONFIG_GETMALLOC 5 /* sqlite3_mem_methods* */ #define SQLITE_CONFIG_SCRATCH 6 /* void*, int sz, int N */ #define SQLITE_CONFIG_PAGECACHE 7 /* void*, int sz, int N */ #define SQLITE_CONFIG_HEAP 8 /* void*, int nByte, int min */ #define SQLITE_CONFIG_MEMSTATUS 9 /* boolean */ #define SQLITE_CONFIG_MUTEX 10 /* sqlite3_mutex_methods* */ #define SQLITE_CONFIG_GETMUTEX 11 /* sqlite3_mutex_methods* */ /* previously SQLITE_CONFIG_CHUNKALLOC 12 which is now unused. */ #define SQLITE_CONFIG_LOOKASIDE 13 /* int int */ #define SQLITE_CONFIG_PCACHE 14 /* sqlite3_pcache_methods* */ #define SQLITE_CONFIG_GETPCACHE 15 /* sqlite3_pcache_methods* */ #define SQLITE_CONFIG_LOG 16 /* xFunc, void* */ /* ** CAPI3REF: Database Connection Configuration Options ** ** These constants are the available integer configuration options that ** can be passed as the second argument to the [sqlite3_db_config()] interface. ** ** New configuration options may be added in future releases of SQLite. ** Existing configuration options might be discontinued. Applications ** should check the return code from [sqlite3_db_config()] to make sure that |
︙ | ︙ | |||
2065 2066 2067 2068 2069 2070 2071 | #define SQLITE_DROP_VTABLE 30 /* Table Name Module Name */ #define SQLITE_FUNCTION 31 /* NULL Function Name */ #define SQLITE_SAVEPOINT 32 /* Operation Savepoint Name */ #define SQLITE_COPY 0 /* No longer used */ /* ** CAPI3REF: Tracing And Profiling Functions | < | | 2152 2153 2154 2155 2156 2157 2158 2159 2160 2161 2162 2163 2164 2165 2166 2167 2168 2169 2170 2171 2172 2173 2174 2175 2176 2177 2178 2179 2180 2181 2182 2183 | #define SQLITE_DROP_VTABLE 30 /* Table Name Module Name */ #define SQLITE_FUNCTION 31 /* NULL Function Name */ #define SQLITE_SAVEPOINT 32 /* Operation Savepoint Name */ #define SQLITE_COPY 0 /* No longer used */ /* ** CAPI3REF: Tracing And Profiling Functions ** ** These routines register callback functions that can be used for ** tracing and profiling the execution of SQL statements. ** ** ^The callback function registered by sqlite3_trace() is invoked at ** various times when an SQL statement is being run by [sqlite3_step()]. ** ^The sqlite3_trace() callback is invoked with a UTF-8 rendering of the ** SQL statement text as the statement first begins executing. ** ^(Additional sqlite3_trace() callbacks might occur ** as each triggered subprogram is entered. The callbacks for triggers ** contain a UTF-8 SQL comment that identifies the trigger.)^ ** ** ^The callback function registered by sqlite3_profile() is invoked ** as each SQL statement finishes. ^The profile callback contains ** the original statement text and an estimate of wall-clock time ** of how long that statement took to run. */ void *sqlite3_trace(sqlite3*, void(*xTrace)(void*,const char*), void*); SQLITE_EXPERIMENTAL void *sqlite3_profile(sqlite3*, void(*xProfile)(void*,const char*,sqlite3_uint64), void*); /* ** CAPI3REF: Query Progress Callbacks ** ** ^This routine configures a callback function - the |
︙ | ︙ | |||
2876 2877 2878 2879 2880 2881 2882 2883 2884 2885 2886 2887 2888 2889 | ** [SQLITE_MISUSE] means that the this routine was called inappropriately. ** Perhaps it was called on a [prepared statement] that has ** already been [sqlite3_finalize | finalized] or on one that had ** previously returned [SQLITE_ERROR] or [SQLITE_DONE]. Or it could ** be the case that the same database connection is being used by two or ** more threads at the same moment in time. ** ** <b>Goofy Interface Alert:</b> In the legacy interface, the sqlite3_step() ** API always returns a generic error code, [SQLITE_ERROR], following any ** error other than [SQLITE_BUSY] and [SQLITE_MISUSE]. You must call ** [sqlite3_reset()] or [sqlite3_finalize()] in order to find one of the ** specific [error codes] that better describes the error. ** We admit that this is a goofy design. The problem has been fixed ** with the "v2" interface. If you prepare all of your SQL statements | > > > > > > > > | 2962 2963 2964 2965 2966 2967 2968 2969 2970 2971 2972 2973 2974 2975 2976 2977 2978 2979 2980 2981 2982 2983 | ** [SQLITE_MISUSE] means that the this routine was called inappropriately. ** Perhaps it was called on a [prepared statement] that has ** already been [sqlite3_finalize | finalized] or on one that had ** previously returned [SQLITE_ERROR] or [SQLITE_DONE]. Or it could ** be the case that the same database connection is being used by two or ** more threads at the same moment in time. ** ** For all versions of SQLite up to and including 3.6.23.1, it was required ** after sqlite3_step() returned anything other than [SQLITE_ROW] that ** [sqlite3_reset()] be called before any subsequent invocation of ** sqlite3_step(). Failure to invoke [sqlite3_reset()] in this way would ** result in an [SQLITE_MISUSE] return from sqlite3_step(). But after ** version 3.6.23.1, sqlite3_step() began calling [sqlite3_reset()] ** automatically in this circumstance rather than returning [SQLITE_MISUSE]. ** ** <b>Goofy Interface Alert:</b> In the legacy interface, the sqlite3_step() ** API always returns a generic error code, [SQLITE_ERROR], following any ** error other than [SQLITE_BUSY] and [SQLITE_MISUSE]. You must call ** [sqlite3_reset()] or [sqlite3_finalize()] in order to find one of the ** specific [error codes] that better describes the error. ** We admit that this is a goofy design. The problem has been fixed ** with the "v2" interface. If you prepare all of your SQL statements |
︙ | ︙ | |||
3688 3689 3690 3691 3692 3693 3694 | ); int sqlite3_collation_needed16( sqlite3*, void*, void(*)(void*,sqlite3*,int eTextRep,const void*) ); | | | 3782 3783 3784 3785 3786 3787 3788 3789 3790 3791 3792 3793 3794 3795 3796 | ); int sqlite3_collation_needed16( sqlite3*, void*, void(*)(void*,sqlite3*,int eTextRep,const void*) ); #ifdef SQLITE_HAS_CODEC /* ** Specify the key for an encrypted database. This routine should be ** called right after sqlite3_open(). ** ** The code to implement this API is not available in the public release ** of SQLite. */ |
︙ | ︙ | |||
3871 3872 3873 3874 3875 3876 3877 | ** hook returning non-zero, just as it would be with any other rollback. ** ** ^For the purposes of this API, a transaction is said to have been ** rolled back if an explicit "ROLLBACK" statement is executed, or ** an error or constraint causes an implicit rollback to occur. ** ^The rollback callback is not invoked if a transaction is ** automatically rolled back because the database connection is closed. | < < | 3965 3966 3967 3968 3969 3970 3971 3972 3973 3974 3975 3976 3977 3978 | ** hook returning non-zero, just as it would be with any other rollback. ** ** ^For the purposes of this API, a transaction is said to have been ** rolled back if an explicit "ROLLBACK" statement is executed, or ** an error or constraint causes an implicit rollback to occur. ** ^The rollback callback is not invoked if a transaction is ** automatically rolled back because the database connection is closed. ** ** See also the [sqlite3_update_hook()] interface. */ void *sqlite3_commit_hook(sqlite3*, int(*)(void*), void*); void *sqlite3_rollback_hook(sqlite3*, void(*)(void *), void*); /* |
︙ | ︙ | |||
4158 4159 4160 4161 4162 4163 4164 | ** [sqlite3_auto_extension()] calls.)^ ** ** ^This function disables automatic extensions in all threads. */ void sqlite3_reset_auto_extension(void); /* | < < < | 4250 4251 4252 4253 4254 4255 4256 4257 4258 4259 4260 4261 4262 4263 4264 4265 4266 4267 4268 4269 4270 4271 4272 4273 4274 4275 4276 4277 4278 4279 4280 4281 4282 | ** [sqlite3_auto_extension()] calls.)^ ** ** ^This function disables automatic extensions in all threads. */ void sqlite3_reset_auto_extension(void); /* ** The interface to the virtual-table mechanism is currently considered ** to be experimental. The interface might change in incompatible ways. ** If this is a problem for you, do not use the interface at this time. ** ** When the virtual-table mechanism stabilizes, we will declare the ** interface fixed, support it indefinitely, and remove this comment. */ /* ** Structures used by the virtual table interface */ typedef struct sqlite3_vtab sqlite3_vtab; typedef struct sqlite3_index_info sqlite3_index_info; typedef struct sqlite3_vtab_cursor sqlite3_vtab_cursor; typedef struct sqlite3_module sqlite3_module; /* ** CAPI3REF: Virtual Table Object ** KEYWORDS: sqlite3_module {virtual table module} ** ** This structure, sometimes called a a "virtual table module", ** defines the implementation of a [virtual tables]. ** This structure consists mostly of methods for the module. ** ** ^A virtual table module is created by filling in a persistent ** instance of this structure and passing a pointer to that instance |
︙ | ︙ | |||
4226 4227 4228 4229 4230 4231 4232 | void **ppArg); int (*xRename)(sqlite3_vtab *pVtab, const char *zNew); }; /* ** CAPI3REF: Virtual Table Indexing Information ** KEYWORDS: sqlite3_index_info | < | 4315 4316 4317 4318 4319 4320 4321 4322 4323 4324 4325 4326 4327 4328 | void **ppArg); int (*xRename)(sqlite3_vtab *pVtab, const char *zNew); }; /* ** CAPI3REF: Virtual Table Indexing Information ** KEYWORDS: sqlite3_index_info ** ** The sqlite3_index_info structure and its substructures is used to ** pass information into and receive the reply from the [xBestIndex] ** method of a [virtual table module]. The fields under **Inputs** are the ** inputs to xBestIndex and are read-only. xBestIndex inserts its ** results into the **Outputs** fields. ** |
︙ | ︙ | |||
4308 4309 4310 4311 4312 4313 4314 | #define SQLITE_INDEX_CONSTRAINT_LE 8 #define SQLITE_INDEX_CONSTRAINT_LT 16 #define SQLITE_INDEX_CONSTRAINT_GE 32 #define SQLITE_INDEX_CONSTRAINT_MATCH 64 /* ** CAPI3REF: Register A Virtual Table Implementation | < | 4396 4397 4398 4399 4400 4401 4402 4403 4404 4405 4406 4407 4408 4409 | #define SQLITE_INDEX_CONSTRAINT_LE 8 #define SQLITE_INDEX_CONSTRAINT_LT 16 #define SQLITE_INDEX_CONSTRAINT_GE 32 #define SQLITE_INDEX_CONSTRAINT_MATCH 64 /* ** CAPI3REF: Register A Virtual Table Implementation ** ** ^These routines are used to register a new [virtual table module] name. ** ^Module names must be registered before ** creating a new [virtual table] using the module and before using a ** preexisting [virtual table] for the module. ** ** ^The module name is registered on the [database connection] specified |
︙ | ︙ | |||
4330 4331 4332 4333 4334 4335 4336 | ** ^The sqlite3_create_module_v2() interface has a fifth parameter which ** is a pointer to a destructor for the pClientData. ^SQLite will ** invoke the destructor function (if it is not NULL) when SQLite ** no longer needs the pClientData pointer. ^The sqlite3_create_module() ** interface is equivalent to sqlite3_create_module_v2() with a NULL ** destructor. */ | | | < | 4417 4418 4419 4420 4421 4422 4423 4424 4425 4426 4427 4428 4429 4430 4431 4432 4433 4434 4435 4436 4437 4438 4439 4440 4441 4442 4443 4444 4445 4446 4447 | ** ^The sqlite3_create_module_v2() interface has a fifth parameter which ** is a pointer to a destructor for the pClientData. ^SQLite will ** invoke the destructor function (if it is not NULL) when SQLite ** no longer needs the pClientData pointer. ^The sqlite3_create_module() ** interface is equivalent to sqlite3_create_module_v2() with a NULL ** destructor. */ int sqlite3_create_module( sqlite3 *db, /* SQLite connection to register module with */ const char *zName, /* Name of the module */ const sqlite3_module *p, /* Methods for the module */ void *pClientData /* Client data for xCreate/xConnect */ ); int sqlite3_create_module_v2( sqlite3 *db, /* SQLite connection to register module with */ const char *zName, /* Name of the module */ const sqlite3_module *p, /* Methods for the module */ void *pClientData, /* Client data for xCreate/xConnect */ void(*xDestroy)(void*) /* Module destructor function */ ); /* ** CAPI3REF: Virtual Table Instance Object ** KEYWORDS: sqlite3_vtab ** ** Every [virtual table module] implementation uses a subclass ** of this object to describe a particular instance ** of the [virtual table]. Each subclass will ** be tailored to the specific needs of the module implementation. ** The purpose of this superclass is to define certain fields that are ** common to all module implementations. |
︙ | ︙ | |||
4373 4374 4375 4376 4377 4378 4379 | char *zErrMsg; /* Error message from sqlite3_mprintf() */ /* Virtual table implementations will typically add additional fields */ }; /* ** CAPI3REF: Virtual Table Cursor Object ** KEYWORDS: sqlite3_vtab_cursor {virtual table cursor} | < | 4459 4460 4461 4462 4463 4464 4465 4466 4467 4468 4469 4470 4471 4472 | char *zErrMsg; /* Error message from sqlite3_mprintf() */ /* Virtual table implementations will typically add additional fields */ }; /* ** CAPI3REF: Virtual Table Cursor Object ** KEYWORDS: sqlite3_vtab_cursor {virtual table cursor} ** ** Every [virtual table module] implementation uses a subclass of the ** following structure to describe cursors that point into the ** [virtual table] and are used ** to loop through the virtual table. Cursors are created using the ** [sqlite3_module.xOpen | xOpen] method of the module and are destroyed ** by the [sqlite3_module.xClose | xClose] method. Cursors are used |
︙ | ︙ | |||
4395 4396 4397 4398 4399 4400 4401 | struct sqlite3_vtab_cursor { sqlite3_vtab *pVtab; /* Virtual table of this cursor */ /* Virtual table implementations will typically add additional fields */ }; /* ** CAPI3REF: Declare The Schema Of A Virtual Table | < | < | < < | 4480 4481 4482 4483 4484 4485 4486 4487 4488 4489 4490 4491 4492 4493 4494 4495 4496 4497 4498 4499 4500 4501 4502 4503 4504 4505 4506 4507 4508 4509 4510 4511 4512 4513 4514 4515 4516 4517 4518 4519 4520 4521 4522 4523 4524 4525 4526 4527 | struct sqlite3_vtab_cursor { sqlite3_vtab *pVtab; /* Virtual table of this cursor */ /* Virtual table implementations will typically add additional fields */ }; /* ** CAPI3REF: Declare The Schema Of A Virtual Table ** ** ^The [xCreate] and [xConnect] methods of a ** [virtual table module] call this interface ** to declare the format (the names and datatypes of the columns) of ** the virtual tables they implement. */ int sqlite3_declare_vtab(sqlite3*, const char *zSQL); /* ** CAPI3REF: Overload A Function For A Virtual Table ** ** ^(Virtual tables can provide alternative implementations of functions ** using the [xFindFunction] method of the [virtual table module]. ** But global versions of those functions ** must exist in order to be overloaded.)^ ** ** ^(This API makes sure a global version of a function with a particular ** name and number of parameters exists. If no such function exists ** before this API is called, a new function is created.)^ ^The implementation ** of the new function always causes an exception to be thrown. So ** the new function is not good for anything by itself. Its only ** purpose is to be a placeholder function that can be overloaded ** by a [virtual table]. */ int sqlite3_overload_function(sqlite3*, const char *zFuncName, int nArg); /* ** The interface to the virtual-table mechanism defined above (back up ** to a comment remarkably similar to this one) is currently considered ** to be experimental. The interface might change in incompatible ways. ** If this is a problem for you, do not use the interface at this time. ** ** When the virtual-table mechanism stabilizes, we will declare the ** interface fixed, support it indefinitely, and remove this comment. */ /* ** CAPI3REF: A Handle To An Open BLOB ** KEYWORDS: {BLOB handle} {BLOB handles} ** ** An instance of this object represents an open BLOB on which |
︙ | ︙ | |||
4775 4776 4777 4778 4779 4780 4781 | void sqlite3_mutex_free(sqlite3_mutex*); void sqlite3_mutex_enter(sqlite3_mutex*); int sqlite3_mutex_try(sqlite3_mutex*); void sqlite3_mutex_leave(sqlite3_mutex*); /* ** CAPI3REF: Mutex Methods Object | < | 4856 4857 4858 4859 4860 4861 4862 4863 4864 4865 4866 4867 4868 4869 | void sqlite3_mutex_free(sqlite3_mutex*); void sqlite3_mutex_enter(sqlite3_mutex*); int sqlite3_mutex_try(sqlite3_mutex*); void sqlite3_mutex_leave(sqlite3_mutex*); /* ** CAPI3REF: Mutex Methods Object ** ** An instance of this structure defines the low-level routines ** used to allocate and use mutexes. ** ** Usually, the default mutex implementations provided by SQLite are ** sufficient, however the user has the option of substituting a custom ** implementation for specialized deployments or systems for which SQLite |
︙ | ︙ | |||
4988 4989 4990 4991 4992 4993 4994 | #define SQLITE_TESTCTRL_BENIGN_MALLOC_HOOKS 10 #define SQLITE_TESTCTRL_PENDING_BYTE 11 #define SQLITE_TESTCTRL_ASSERT 12 #define SQLITE_TESTCTRL_ALWAYS 13 #define SQLITE_TESTCTRL_RESERVE 14 #define SQLITE_TESTCTRL_OPTIMIZATIONS 15 #define SQLITE_TESTCTRL_ISKEYWORD 16 | > | < | 5068 5069 5070 5071 5072 5073 5074 5075 5076 5077 5078 5079 5080 5081 5082 5083 5084 5085 5086 | #define SQLITE_TESTCTRL_BENIGN_MALLOC_HOOKS 10 #define SQLITE_TESTCTRL_PENDING_BYTE 11 #define SQLITE_TESTCTRL_ASSERT 12 #define SQLITE_TESTCTRL_ALWAYS 13 #define SQLITE_TESTCTRL_RESERVE 14 #define SQLITE_TESTCTRL_OPTIMIZATIONS 15 #define SQLITE_TESTCTRL_ISKEYWORD 16 #define SQLITE_TESTCTRL_PGHDRSZ 17 #define SQLITE_TESTCTRL_LAST 17 /* ** CAPI3REF: SQLite Runtime Status ** ** ^This interface is used to retrieve runtime status information ** about the preformance of SQLite, and optionally to reset various ** highwater marks. ^The first argument is an integer code for ** the specific parameter to measure. ^(Recognized integer codes ** are of the form [SQLITE_STATUS_MEMORY_USED | SQLITE_STATUS_...].)^ ** ^The current value of the parameter is returned into *pCurrent. |
︙ | ︙ | |||
5020 5021 5022 5023 5024 5025 5026 | ** interfaces. However the values returned in *pCurrent and ** *pHighwater reflect the status of SQLite at different points in time ** and it is possible that another thread might change the parameter ** in between the times when *pCurrent and *pHighwater are written. ** ** See also: [sqlite3_db_status()] */ | | < | 5100 5101 5102 5103 5104 5105 5106 5107 5108 5109 5110 5111 5112 5113 5114 5115 5116 5117 5118 | ** interfaces. However the values returned in *pCurrent and ** *pHighwater reflect the status of SQLite at different points in time ** and it is possible that another thread might change the parameter ** in between the times when *pCurrent and *pHighwater are written. ** ** See also: [sqlite3_db_status()] */ int sqlite3_status(int op, int *pCurrent, int *pHighwater, int resetFlag); /* ** CAPI3REF: Status Parameters ** ** These integer constants designate various run-time status parameters ** that can be returned by [sqlite3_status()]. ** ** <dl> ** ^(<dt>SQLITE_STATUS_MEMORY_USED</dt> ** <dd>This parameter is the current amount of memory checked out |
︙ | ︙ | |||
5112 5113 5114 5115 5116 5117 5118 | #define SQLITE_STATUS_MALLOC_SIZE 5 #define SQLITE_STATUS_PARSER_STACK 6 #define SQLITE_STATUS_PAGECACHE_SIZE 7 #define SQLITE_STATUS_SCRATCH_SIZE 8 /* ** CAPI3REF: Database Connection Status | < > > | | | | < > > > > > > > > < | 5191 5192 5193 5194 5195 5196 5197 5198 5199 5200 5201 5202 5203 5204 5205 5206 5207 5208 5209 5210 5211 5212 5213 5214 5215 5216 5217 5218 5219 5220 5221 5222 5223 5224 5225 5226 5227 5228 5229 5230 5231 5232 5233 5234 5235 5236 5237 5238 5239 5240 5241 5242 5243 5244 5245 5246 5247 5248 5249 5250 5251 5252 5253 5254 | #define SQLITE_STATUS_MALLOC_SIZE 5 #define SQLITE_STATUS_PARSER_STACK 6 #define SQLITE_STATUS_PAGECACHE_SIZE 7 #define SQLITE_STATUS_SCRATCH_SIZE 8 /* ** CAPI3REF: Database Connection Status ** ** ^This interface is used to retrieve runtime status information ** about a single [database connection]. ^The first argument is the ** database connection object to be interrogated. ^The second argument ** is an integer constant, taken from the set of ** [SQLITE_DBSTATUS_LOOKASIDE_USED | SQLITE_DBSTATUS_*] macros, that ** determiness the parameter to interrogate. The set of ** [SQLITE_DBSTATUS_LOOKASIDE_USED | SQLITE_DBSTATUS_*] macros is likely ** to grow in future releases of SQLite. ** ** ^The current value of the requested parameter is written into *pCur ** and the highest instantaneous value is written into *pHiwtr. ^If ** the resetFlg is true, then the highest instantaneous value is ** reset back down to the current value. ** ** See also: [sqlite3_status()] and [sqlite3_stmt_status()]. */ int sqlite3_db_status(sqlite3*, int op, int *pCur, int *pHiwtr, int resetFlg); /* ** CAPI3REF: Status Parameters for database connections ** ** These constants are the available integer "verbs" that can be passed as ** the second argument to the [sqlite3_db_status()] interface. ** ** New verbs may be added in future releases of SQLite. Existing verbs ** might be discontinued. Applications should check the return code from ** [sqlite3_db_status()] to make sure that the call worked. ** The [sqlite3_db_status()] interface will return a non-zero error code ** if a discontinued or unsupported verb is invoked. ** ** <dl> ** ^(<dt>SQLITE_DBSTATUS_LOOKASIDE_USED</dt> ** <dd>This parameter returns the number of lookaside memory slots currently ** checked out.</dd>)^ ** ** <dt>SQLITE_DBSTATUS_CACHE_USED</dt> ** <dd>^This parameter returns the approximate number of of bytes of heap ** memory used by all pager caches associated with the database connection. ** ^The highwater mark associated with SQLITE_DBSTATUS_CACHE_USED is always 0. ** </dd> ** </dl> */ #define SQLITE_DBSTATUS_LOOKASIDE_USED 0 #define SQLITE_DBSTATUS_CACHE_USED 1 #define SQLITE_DBSTATUS_MAX 1 /* Largest defined DBSTATUS */ /* ** CAPI3REF: Prepared Statement Status ** ** ^(Each prepared statement maintains various ** [SQLITE_STMTSTATUS_SORT | counters] that measure the number ** of times it has performed specific operations.)^ These counters can ** be used to monitor the performance characteristics of the prepared ** statements. For example, if the number of table steps greatly exceeds ** the number of table searches or result rows, that would tend to indicate |
︙ | ︙ | |||
5176 5177 5178 5179 5180 5181 5182 | ** to be interrogated.)^ ** ^The current value of the requested counter is returned. ** ^If the resetFlg is true, then the counter is reset to zero after this ** interface call returns. ** ** See also: [sqlite3_status()] and [sqlite3_db_status()]. */ | | < > > > > > > > > < < | 5262 5263 5264 5265 5266 5267 5268 5269 5270 5271 5272 5273 5274 5275 5276 5277 5278 5279 5280 5281 5282 5283 5284 5285 5286 5287 5288 5289 5290 5291 5292 5293 5294 5295 5296 5297 5298 5299 5300 5301 5302 5303 5304 5305 5306 5307 5308 5309 5310 5311 5312 5313 5314 5315 5316 5317 5318 5319 5320 5321 5322 5323 5324 5325 | ** to be interrogated.)^ ** ^The current value of the requested counter is returned. ** ^If the resetFlg is true, then the counter is reset to zero after this ** interface call returns. ** ** See also: [sqlite3_status()] and [sqlite3_db_status()]. */ int sqlite3_stmt_status(sqlite3_stmt*, int op,int resetFlg); /* ** CAPI3REF: Status Parameters for prepared statements ** ** These preprocessor macros define integer codes that name counter ** values associated with the [sqlite3_stmt_status()] interface. ** The meanings of the various counters are as follows: ** ** <dl> ** <dt>SQLITE_STMTSTATUS_FULLSCAN_STEP</dt> ** <dd>^This is the number of times that SQLite has stepped forward in ** a table as part of a full table scan. Large numbers for this counter ** may indicate opportunities for performance improvement through ** careful use of indices.</dd> ** ** <dt>SQLITE_STMTSTATUS_SORT</dt> ** <dd>^This is the number of sort operations that have occurred. ** A non-zero value in this counter may indicate an opportunity to ** improvement performance through careful use of indices.</dd> ** ** <dt>SQLITE_STMTSTATUS_AUTOINDEX</dt> ** <dd>^This is the number of rows inserted into transient indices that ** were created automatically in order to help joins run faster. ** A non-zero value in this counter may indicate an opportunity to ** improvement performance by adding permanent indices that do not ** need to be reinitialized each time the statement is run.</dd> ** ** </dl> */ #define SQLITE_STMTSTATUS_FULLSCAN_STEP 1 #define SQLITE_STMTSTATUS_SORT 2 #define SQLITE_STMTSTATUS_AUTOINDEX 3 /* ** CAPI3REF: Custom Page Cache Object ** ** The sqlite3_pcache type is opaque. It is implemented by ** the pluggable module. The SQLite core has no knowledge of ** its size or internal structure and never deals with the ** sqlite3_pcache object except by holding and passing pointers ** to the object. ** ** See [sqlite3_pcache_methods] for additional information. */ typedef struct sqlite3_pcache sqlite3_pcache; /* ** CAPI3REF: Application Defined Page Cache. ** KEYWORDS: {page cache} ** ** ^(The [sqlite3_config]([SQLITE_CONFIG_PCACHE], ...) interface can ** register an alternative page cache implementation by passing in an ** instance of the sqlite3_pcache_methods structure.)^ The majority of the ** heap memory used by SQLite is used by the page cache to cache data read ** from, or ready to be written to, the database file. By implementing a ** custom page cache using this API, an application can control more |
︙ | ︙ | |||
5362 5363 5364 5365 5366 5367 5368 | void (*xRekey)(sqlite3_pcache*, void*, unsigned oldKey, unsigned newKey); void (*xTruncate)(sqlite3_pcache*, unsigned iLimit); void (*xDestroy)(sqlite3_pcache*); }; /* ** CAPI3REF: Online Backup Object | < < | 5453 5454 5455 5456 5457 5458 5459 5460 5461 5462 5463 5464 5465 5466 5467 5468 5469 5470 5471 5472 5473 5474 5475 5476 5477 5478 | void (*xRekey)(sqlite3_pcache*, void*, unsigned oldKey, unsigned newKey); void (*xTruncate)(sqlite3_pcache*, unsigned iLimit); void (*xDestroy)(sqlite3_pcache*); }; /* ** CAPI3REF: Online Backup Object ** ** The sqlite3_backup object records state information about an ongoing ** online backup operation. ^The sqlite3_backup object is created by ** a call to [sqlite3_backup_init()] and is destroyed by a call to ** [sqlite3_backup_finish()]. ** ** See Also: [Using the SQLite Online Backup API] */ typedef struct sqlite3_backup sqlite3_backup; /* ** CAPI3REF: Online Backup API. ** ** The backup API copies the content of one database into another. ** It is useful either for creating backups of databases or ** for copying in-memory databases to or from persistent files. ** ** See Also: [Using the SQLite Online Backup API] ** |
︙ | ︙ | |||
5444 5445 5446 5447 5448 5449 5450 | ** from source to destination, then it returns [SQLITE_DONE]. ** ^If an error occurs while running sqlite3_backup_step(B,N), ** then an [error code] is returned. ^As well as [SQLITE_OK] and ** [SQLITE_DONE], a call to sqlite3_backup_step() may return [SQLITE_READONLY], ** [SQLITE_NOMEM], [SQLITE_BUSY], [SQLITE_LOCKED], or an ** [SQLITE_IOERR_ACCESS | SQLITE_IOERR_XXX] extended error code. ** | | > | > | > > | | 5533 5534 5535 5536 5537 5538 5539 5540 5541 5542 5543 5544 5545 5546 5547 5548 5549 5550 5551 5552 5553 5554 | ** from source to destination, then it returns [SQLITE_DONE]. ** ^If an error occurs while running sqlite3_backup_step(B,N), ** then an [error code] is returned. ^As well as [SQLITE_OK] and ** [SQLITE_DONE], a call to sqlite3_backup_step() may return [SQLITE_READONLY], ** [SQLITE_NOMEM], [SQLITE_BUSY], [SQLITE_LOCKED], or an ** [SQLITE_IOERR_ACCESS | SQLITE_IOERR_XXX] extended error code. ** ** ^(The sqlite3_backup_step() might return [SQLITE_READONLY] if ** <ol> ** <li> the destination database was opened read-only, or ** <li> the destination database is using write-ahead-log journaling ** and the destination and source page sizes differ, or ** <li> The destination database is an in-memory database and the ** destination and source page sizes differ. ** </ol>)^ ** ** ^If sqlite3_backup_step() cannot obtain a required file-system lock, then ** the [sqlite3_busy_handler | busy-handler function] ** is invoked (if one is specified). ^If the ** busy-handler returns non-zero before the lock is available, then ** [SQLITE_BUSY] is returned to the caller. ^In this case the call to ** sqlite3_backup_step() can be retried later. ^If the source |
︙ | ︙ | |||
5563 5564 5565 5566 5567 5568 5569 | int sqlite3_backup_step(sqlite3_backup *p, int nPage); int sqlite3_backup_finish(sqlite3_backup *p); int sqlite3_backup_remaining(sqlite3_backup *p); int sqlite3_backup_pagecount(sqlite3_backup *p); /* ** CAPI3REF: Unlock Notification | < | 5656 5657 5658 5659 5660 5661 5662 5663 5664 5665 5666 5667 5668 5669 | int sqlite3_backup_step(sqlite3_backup *p, int nPage); int sqlite3_backup_finish(sqlite3_backup *p); int sqlite3_backup_remaining(sqlite3_backup *p); int sqlite3_backup_pagecount(sqlite3_backup *p); /* ** CAPI3REF: Unlock Notification ** ** ^When running in shared-cache mode, a database operation may fail with ** an [SQLITE_LOCKED] error if the required locks on the shared-cache or ** individual tables within the shared-cache cannot be obtained. See ** [SQLite Shared-Cache Mode] for a description of shared-cache locking. ** ^This API may be used to register a callback that SQLite will invoke ** when the connection currently holding the required lock relinquishes it. |
︙ | ︙ | |||
5685 5686 5687 5688 5689 5690 5691 | void (*xNotify)(void **apArg, int nArg), /* Callback function to invoke */ void *pNotifyArg /* Argument to pass to xNotify */ ); /* ** CAPI3REF: String Comparison | < < | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 5777 5778 5779 5780 5781 5782 5783 5784 5785 5786 5787 5788 5789 5790 5791 5792 5793 5794 5795 5796 5797 5798 5799 5800 5801 5802 5803 5804 5805 5806 5807 5808 5809 5810 5811 5812 5813 5814 5815 5816 5817 5818 5819 5820 5821 5822 5823 5824 5825 5826 5827 5828 5829 5830 5831 5832 5833 5834 5835 5836 5837 5838 5839 5840 5841 5842 5843 5844 5845 5846 5847 5848 5849 5850 5851 5852 5853 5854 5855 5856 5857 5858 5859 5860 5861 5862 5863 5864 5865 5866 5867 5868 5869 5870 5871 5872 5873 5874 5875 5876 5877 5878 5879 5880 5881 5882 5883 5884 5885 5886 5887 5888 5889 5890 5891 5892 5893 5894 5895 5896 5897 5898 5899 5900 5901 5902 5903 5904 5905 5906 5907 5908 5909 | void (*xNotify)(void **apArg, int nArg), /* Callback function to invoke */ void *pNotifyArg /* Argument to pass to xNotify */ ); /* ** CAPI3REF: String Comparison ** ** ^The [sqlite3_strnicmp()] API allows applications and extensions to ** compare the contents of two buffers containing UTF-8 strings in a ** case-indendent fashion, using the same definition of case independence ** that SQLite uses internally when comparing identifiers. */ int sqlite3_strnicmp(const char *, const char *, int); /* ** CAPI3REF: Error Logging Interface ** ** ^The [sqlite3_log()] interface writes a message into the error log ** established by the [SQLITE_CONFIG_LOG] option to [sqlite3_config()]. ** ^If logging is enabled, the zFormat string and subsequent arguments are ** used with [sqlite3_snprintf()] to generate the final output string. ** ** The sqlite3_log() interface is intended for use by extensions such as ** virtual tables, collating functions, and SQL functions. While there is ** nothing to prevent an application from calling sqlite3_log(), doing so ** is considered bad form. ** ** The zFormat string must not be NULL. ** ** To avoid deadlocks and other threading problems, the sqlite3_log() routine ** will not use dynamically allocated memory. The log message is stored in ** a fixed-length buffer on the stack. If the log message is longer than ** a few hundred characters, it will be truncated to the length of the ** buffer. */ void sqlite3_log(int iErrCode, const char *zFormat, ...); /* ** CAPI3REF: Write-Ahead Log Commit Hook ** ** ^The [sqlite3_wal_hook()] function is used to register a callback that ** will be invoked each time a database connection commits data to a ** [write-ahead log] (i.e. whenever a transaction is committed in ** [journal_mode | journal_mode=WAL mode]). ** ** ^The callback is invoked by SQLite after the commit has taken place and ** the associated write-lock on the database released, so the implementation ** may read, write or [checkpoint] the database as required. ** ** ^The first parameter passed to the callback function when it is invoked ** is a copy of the third parameter passed to sqlite3_wal_hook() when ** registering the callback. ^The second is a copy of the database handle. ** ^The third parameter is the name of the database that was written to - ** either "main" or the name of an [ATTACH]-ed database. ^The fourth parameter ** is the number of pages currently in the write-ahead log file, ** including those that were just committed. ** ** The callback function should normally return [SQLITE_OK]. ^If an error ** code is returned, that error will propagate back up through the ** SQLite code base to cause the statement that provoked the callback ** to report an error, though the commit will have still occurred. If the ** callback returns [SQLITE_ROW] or [SQLITE_DONE], or if it returns a value ** that does not correspond to any valid SQLite error code, the results ** are undefined. ** ** A single database handle may have at most a single write-ahead log callback ** registered at one time. ^Calling [sqlite3_wal_hook()] replaces any ** previously registered write-ahead log callback. ^Note that the ** [sqlite3_wal_autocheckpoint()] interface and the ** [wal_autocheckpoint pragma] both invoke [sqlite3_wal_hook()] and will ** those overwrite any prior [sqlite3_wal_hook()] settings. */ void *sqlite3_wal_hook( sqlite3*, int(*)(void *,sqlite3*,const char*,int), void* ); /* ** CAPI3REF: Configure an auto-checkpoint ** ** ^The [sqlite3_wal_autocheckpoint(D,N)] is a wrapper around ** [sqlite3_wal_hook()] that causes any database on [database connection] D ** to automatically [checkpoint] ** after committing a transaction if there are N or ** more frames in the [write-ahead log] file. ^Passing zero or ** a negative value as the nFrame parameter disables automatic ** checkpoints entirely. ** ** ^The callback registered by this function replaces any existing callback ** registered using [sqlite3_wal_hook()]. ^Likewise, registering a callback ** using [sqlite3_wal_hook()] disables the automatic checkpoint mechanism ** configured by this function. ** ** ^The [wal_autocheckpoint pragma] can be used to invoke this interface ** from SQL. ** ** ^Every new [database connection] defaults to having the auto-checkpoint ** enabled with a threshold of 1000 pages. The use of this interface ** is only necessary if the default setting is found to be suboptimal ** for a particular application. */ int sqlite3_wal_autocheckpoint(sqlite3 *db, int N); /* ** CAPI3REF: Checkpoint a database ** ** ^The [sqlite3_wal_checkpoint(D,X)] interface causes database named X ** on [database connection] D to be [checkpointed]. ^If X is NULL or an ** empty string, then a checkpoint is run on all databases of ** connection D. ^If the database connection D is not in ** [WAL | write-ahead log mode] then this interface is a harmless no-op. ** ** ^The [wal_checkpoint pragma] can be used to invoke this interface ** from SQL. ^The [sqlite3_wal_autocheckpoint()] interface and the ** [wal_autocheckpoint pragma] can be used to cause this interface to be ** run whenever the WAL reaches a certain size threshold. */ int sqlite3_wal_checkpoint(sqlite3 *db, const char *zDb); /* ** Undo the hack that converts floating point types to integer for ** builds on processors without floating point support. */ #ifdef SQLITE_OMIT_FLOATING_POINT # undef double #endif #ifdef __cplusplus } /* End of the 'extern "C"' block */ #endif #endif |
Changes to src/sqliteInt.h.
︙ | ︙ | |||
88 89 90 91 92 93 94 | ** integers to pointers. The way you do this varies from one compiler ** to the next, so we have developed the following set of #if statements ** to generate appropriate macros for a wide range of compilers. ** ** The correct "ANSI" way to do this is to use the intptr_t type. ** Unfortunately, that typedef is not available on all compilers, or ** if it is available, it requires an #include of specific headers | | | 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 | ** integers to pointers. The way you do this varies from one compiler ** to the next, so we have developed the following set of #if statements ** to generate appropriate macros for a wide range of compilers. ** ** The correct "ANSI" way to do this is to use the intptr_t type. ** Unfortunately, that typedef is not available on all compilers, or ** if it is available, it requires an #include of specific headers ** that vary from one machine to the next. ** ** Ticket #3860: The llvm-gcc-4.2 compiler from Apple chokes on ** the ((void*)&((char*)0)[X]) construct. But MSVC chokes on ((void*)(X)). ** So we have to define the macros in different ways depending on the ** compiler. */ #if defined(__PTRDIFF_TYPE__) /* This case should work for GCC */ |
︙ | ︙ | |||
268 269 270 271 272 273 274 275 276 277 278 279 280 281 | # define ALWAYS(X) ((X)?1:(assert(0),0)) # define NEVER(X) ((X)?(assert(0),1):0) #else # define ALWAYS(X) (X) # define NEVER(X) (X) #endif /* ** The macro unlikely() is a hint that surrounds a boolean ** expression that is usually false. Macro likely() surrounds ** a boolean expression that is usually true. GCC is able to ** use these hints to generate better code, sometimes. */ #if defined(__GNUC__) && 0 | > > > > > > > | 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 | # define ALWAYS(X) ((X)?1:(assert(0),0)) # define NEVER(X) ((X)?(assert(0),1):0) #else # define ALWAYS(X) (X) # define NEVER(X) (X) #endif /* ** Return true (non-zero) if the input is a integer that is too large ** to fit in 32-bits. This macro is used inside of various testcase() ** macros to verify that we have tested SQLite for large-file support. */ #define IS_BIG_INT(X) (((X)&~(i64)0xffffffff)!=0) /* ** The macro unlikely() is a hint that surrounds a boolean ** expression that is usually false. Macro likely() surrounds ** a boolean expression that is usually true. GCC is able to ** use these hints to generate better code, sometimes. */ #if defined(__GNUC__) && 0 |
︙ | ︙ | |||
297 298 299 300 301 302 303 304 305 306 307 308 309 310 | /* ** If compiling for a processor that lacks floating point support, ** substitute integer for floating-point */ #ifdef SQLITE_OMIT_FLOATING_POINT # define double sqlite_int64 # define LONGDOUBLE_TYPE sqlite_int64 # ifndef SQLITE_BIG_DBL # define SQLITE_BIG_DBL (((sqlite3_int64)1)<<50) # endif # define SQLITE_OMIT_DATETIME_FUNCS 1 # define SQLITE_OMIT_TRACE 1 # undef SQLITE_MIXED_ENDIAN_64BIT_FLOAT | > | 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 | /* ** If compiling for a processor that lacks floating point support, ** substitute integer for floating-point */ #ifdef SQLITE_OMIT_FLOATING_POINT # define double sqlite_int64 # define float sqlite_int64 # define LONGDOUBLE_TYPE sqlite_int64 # ifndef SQLITE_BIG_DBL # define SQLITE_BIG_DBL (((sqlite3_int64)1)<<50) # endif # define SQLITE_OMIT_DATETIME_FUNCS 1 # define SQLITE_OMIT_TRACE 1 # undef SQLITE_MIXED_ENDIAN_64BIT_FLOAT |
︙ | ︙ | |||
785 786 787 788 789 790 791 | int openFlags; /* Flags passed to sqlite3_vfs.xOpen() */ int errCode; /* Most recent error code (SQLITE_*) */ int errMask; /* & result codes with this before returning */ u8 autoCommit; /* The auto-commit flag. */ u8 temp_store; /* 1: file 2: memory 0: default */ u8 mallocFailed; /* True if we have seen a malloc failure */ u8 dfltLockMode; /* Default locking-mode for attached dbs */ | < | 793 794 795 796 797 798 799 800 801 802 803 804 805 806 | int openFlags; /* Flags passed to sqlite3_vfs.xOpen() */ int errCode; /* Most recent error code (SQLITE_*) */ int errMask; /* & result codes with this before returning */ u8 autoCommit; /* The auto-commit flag. */ u8 temp_store; /* 1: file 2: memory 0: default */ u8 mallocFailed; /* True if we have seen a malloc failure */ u8 dfltLockMode; /* Default locking-mode for attached dbs */ signed char nextAutovac; /* Autovac setting after VACUUM if >=0 */ u8 suppressErr; /* Do not issue error messages if true */ int nextPagesize; /* Pagesize after VACUUM if >0 */ int nTable; /* Number of tables in the database */ CollSeq *pDfltColl; /* The default collating sequence (BINARY) */ i64 lastRowid; /* ROWID of most recent insert (see above) */ u32 magic; /* Magic number for detect library misuse */ |
︙ | ︙ | |||
818 819 820 821 822 823 824 825 826 827 828 829 830 831 | void *pProfileArg; /* Argument to profile function */ void *pCommitArg; /* Argument to xCommitCallback() */ int (*xCommitCallback)(void*); /* Invoked at every commit. */ void *pRollbackArg; /* Argument to xRollbackCallback() */ void (*xRollbackCallback)(void*); /* Invoked at every commit. */ void *pUpdateArg; void (*xUpdateCallback)(void*,int, const char*,const char*,sqlite_int64); void(*xCollNeeded)(void*,sqlite3*,int eTextRep,const char*); void(*xCollNeeded16)(void*,sqlite3*,int eTextRep,const void*); void *pCollNeededArg; sqlite3_value *pErr; /* Most recent error message */ char *zErrMsg; /* Most recent error message (UTF-8 encoded) */ char *zErrMsg16; /* Most recent error message (UTF-16 encoded) */ union { | > > > > | 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 | void *pProfileArg; /* Argument to profile function */ void *pCommitArg; /* Argument to xCommitCallback() */ int (*xCommitCallback)(void*); /* Invoked at every commit. */ void *pRollbackArg; /* Argument to xRollbackCallback() */ void (*xRollbackCallback)(void*); /* Invoked at every commit. */ void *pUpdateArg; void (*xUpdateCallback)(void*,int, const char*,const char*,sqlite_int64); #ifndef SQLITE_OMIT_WAL int (*xWalCallback)(void *, sqlite3 *, const char *, int); void *pWalArg; #endif void(*xCollNeeded)(void*,sqlite3*,int eTextRep,const char*); void(*xCollNeeded16)(void*,sqlite3*,int eTextRep,const void*); void *pCollNeededArg; sqlite3_value *pErr; /* Most recent error message */ char *zErrMsg; /* Most recent error message (UTF-8 encoded) */ char *zErrMsg16; /* Most recent error message (UTF-16 encoded) */ union { |
︙ | ︙ | |||
907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 | #define SQLITE_LegacyFileFmt 0x00100000 /* Create new databases in format 1 */ #define SQLITE_FullFSync 0x00200000 /* Use full fsync on the backend */ #define SQLITE_LoadExtension 0x00400000 /* Enable load_extension */ #define SQLITE_RecoveryMode 0x00800000 /* Ignore schema errors */ #define SQLITE_ReverseOrder 0x01000000 /* Reverse unordered SELECTs */ #define SQLITE_RecTriggers 0x02000000 /* Enable recursive triggers */ #define SQLITE_ForeignKeys 0x04000000 /* Enforce foreign key constraints */ /* ** Bits of the sqlite3.flags field that are used by the ** sqlite3_test_control(SQLITE_TESTCTRL_OPTIMIZATIONS,...) interface. ** These must be the low-order bits of the flags field. */ #define SQLITE_QueryFlattener 0x01 /* Disable query flattening */ #define SQLITE_ColumnCache 0x02 /* Disable the column cache */ #define SQLITE_IndexSort 0x04 /* Disable indexes for sorting */ #define SQLITE_IndexSearch 0x08 /* Disable indexes for searching */ #define SQLITE_IndexCover 0x10 /* Disable index covering table */ | > > > | | 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 | #define SQLITE_LegacyFileFmt 0x00100000 /* Create new databases in format 1 */ #define SQLITE_FullFSync 0x00200000 /* Use full fsync on the backend */ #define SQLITE_LoadExtension 0x00400000 /* Enable load_extension */ #define SQLITE_RecoveryMode 0x00800000 /* Ignore schema errors */ #define SQLITE_ReverseOrder 0x01000000 /* Reverse unordered SELECTs */ #define SQLITE_RecTriggers 0x02000000 /* Enable recursive triggers */ #define SQLITE_ForeignKeys 0x04000000 /* Enforce foreign key constraints */ #define SQLITE_AutoIndex 0x08000000 /* Enable automatic indexes */ #define SQLITE_PreferBuiltin 0x10000000 /* Preference to built-in funcs */ /* ** Bits of the sqlite3.flags field that are used by the ** sqlite3_test_control(SQLITE_TESTCTRL_OPTIMIZATIONS,...) interface. ** These must be the low-order bits of the flags field. */ #define SQLITE_QueryFlattener 0x01 /* Disable query flattening */ #define SQLITE_ColumnCache 0x02 /* Disable the column cache */ #define SQLITE_IndexSort 0x04 /* Disable indexes for sorting */ #define SQLITE_IndexSearch 0x08 /* Disable indexes for searching */ #define SQLITE_IndexCover 0x10 /* Disable index covering table */ #define SQLITE_GroupByOrder 0x20 /* Disable GROUPBY cover of ORDERBY */ #define SQLITE_OptMask 0xff /* Mask of all disablable opts */ /* ** Possible values for the sqlite.magic field. ** The numbers are obtained at random and have no special meaning, other ** than being distinct from one another. */ #define SQLITE_MAGIC_OPEN 0xa029a697 /* Database is open */ |
︙ | ︙ | |||
1769 1770 1771 1772 1773 1774 1775 1776 1777 1778 1779 1780 1781 1782 | ** such a table must be a simple name: ID. But in SQLite, the table can ** now be identified by a database name, a dot, then the table name: ID.ID. ** ** The jointype starts out showing the join type between the current table ** and the next table on the list. The parser builds the list this way. ** But sqlite3SrcListShiftJoinType() later shifts the jointypes so that each ** jointype expresses the join between the table and the previous table. */ struct SrcList { i16 nSrc; /* Number of tables or subqueries in the FROM clause */ i16 nAlloc; /* Number of entries allocated in a[] below */ struct SrcList_item { char *zDatabase; /* Name of database holding this table */ char *zName; /* Name of the table */ | > > > | 1783 1784 1785 1786 1787 1788 1789 1790 1791 1792 1793 1794 1795 1796 1797 1798 1799 | ** such a table must be a simple name: ID. But in SQLite, the table can ** now be identified by a database name, a dot, then the table name: ID.ID. ** ** The jointype starts out showing the join type between the current table ** and the next table on the list. The parser builds the list this way. ** But sqlite3SrcListShiftJoinType() later shifts the jointypes so that each ** jointype expresses the join between the table and the previous table. ** ** In the colUsed field, the high-order bit (bit 63) is set if the table ** contains more than 63 columns and the 64-th or later column is used. */ struct SrcList { i16 nSrc; /* Number of tables or subqueries in the FROM clause */ i16 nAlloc; /* Number of entries allocated in a[] below */ struct SrcList_item { char *zDatabase; /* Name of database holding this table */ char *zName; /* Name of the table */ |
︙ | ︙ | |||
1880 1881 1882 1883 1884 1885 1886 | ** and the WhereInfo.wctrlFlags member. */ #define WHERE_ORDERBY_NORMAL 0x0000 /* No-op */ #define WHERE_ORDERBY_MIN 0x0001 /* ORDER BY processing for min() func */ #define WHERE_ORDERBY_MAX 0x0002 /* ORDER BY processing for max() func */ #define WHERE_ONEPASS_DESIRED 0x0004 /* Want to do one-pass UPDATE/DELETE */ #define WHERE_DUPLICATES_OK 0x0008 /* Ok to return a row more than once */ | | | 1897 1898 1899 1900 1901 1902 1903 1904 1905 1906 1907 1908 1909 1910 1911 | ** and the WhereInfo.wctrlFlags member. */ #define WHERE_ORDERBY_NORMAL 0x0000 /* No-op */ #define WHERE_ORDERBY_MIN 0x0001 /* ORDER BY processing for min() func */ #define WHERE_ORDERBY_MAX 0x0002 /* ORDER BY processing for max() func */ #define WHERE_ONEPASS_DESIRED 0x0004 /* Want to do one-pass UPDATE/DELETE */ #define WHERE_DUPLICATES_OK 0x0008 /* Ok to return a row more than once */ #define WHERE_OMIT_OPEN 0x0010 /* Table cursors are already open */ #define WHERE_OMIT_CLOSE 0x0020 /* Omit close of table & index cursors */ #define WHERE_FORCE_TABLE 0x0040 /* Do not use an index-only search */ #define WHERE_ONETABLE_ONLY 0x0080 /* Only code the 1st table in pTabList */ /* ** The WHERE clause processing routine has two halves. The ** first part does the start of the WHERE loop and the second |
︙ | ︙ | |||
1903 1904 1905 1906 1907 1908 1909 1910 1911 1912 1913 1914 1915 1916 | u8 untestedTerms; /* Not all WHERE terms resolved by outer loop */ SrcList *pTabList; /* List of tables in the join */ int iTop; /* The very beginning of the WHERE loop */ int iContinue; /* Jump here to continue with next record */ int iBreak; /* Jump here to break out of the loop */ int nLevel; /* Number of nested loop */ struct WhereClause *pWC; /* Decomposition of the WHERE clause */ WhereLevel a[1]; /* Information about each nest loop in WHERE */ }; /* ** A NameContext defines a context in which to resolve table and column ** names. The context consists of a list of tables (the pSrcList) field and ** a list of named expression (pEList). The named expression list may | > | 1920 1921 1922 1923 1924 1925 1926 1927 1928 1929 1930 1931 1932 1933 1934 | u8 untestedTerms; /* Not all WHERE terms resolved by outer loop */ SrcList *pTabList; /* List of tables in the join */ int iTop; /* The very beginning of the WHERE loop */ int iContinue; /* Jump here to continue with next record */ int iBreak; /* Jump here to break out of the loop */ int nLevel; /* Number of nested loop */ struct WhereClause *pWC; /* Decomposition of the WHERE clause */ double savedNQueryLoop; /* pParse->nQueryLoop outside the WHERE loop */ WhereLevel a[1]; /* Information about each nest loop in WHERE */ }; /* ** A NameContext defines a context in which to resolve table and column ** names. The context consists of a list of tables (the pSrcList) field and ** a list of named expression (pEList). The named expression list may |
︙ | ︙ | |||
2144 2145 2146 2147 2148 2149 2150 2151 2152 2153 2154 2155 2156 2157 | Parse *pToplevel; /* Parse structure for main program (or NULL) */ Table *pTriggerTab; /* Table triggers are being coded for */ u32 oldmask; /* Mask of old.* columns referenced */ u32 newmask; /* Mask of new.* columns referenced */ u8 eTriggerOp; /* TK_UPDATE, TK_INSERT or TK_DELETE */ u8 eOrconf; /* Default ON CONFLICT policy for trigger steps */ u8 disableTriggers; /* True to disable triggers */ /* Above is constant between recursions. Below is reset before and after ** each recursion */ int nVar; /* Number of '?' variables seen in the SQL so far */ int nVarExpr; /* Number of used slots in apVarExpr[] */ int nVarExprAlloc; /* Number of allocated slots in apVarExpr[] */ | > | 2162 2163 2164 2165 2166 2167 2168 2169 2170 2171 2172 2173 2174 2175 2176 | Parse *pToplevel; /* Parse structure for main program (or NULL) */ Table *pTriggerTab; /* Table triggers are being coded for */ u32 oldmask; /* Mask of old.* columns referenced */ u32 newmask; /* Mask of new.* columns referenced */ u8 eTriggerOp; /* TK_UPDATE, TK_INSERT or TK_DELETE */ u8 eOrconf; /* Default ON CONFLICT policy for trigger steps */ u8 disableTriggers; /* True to disable triggers */ double nQueryLoop; /* Estimated number of iterations of a query */ /* Above is constant between recursions. Below is reset before and after ** each recursion */ int nVar; /* Number of '?' variables seen in the SQL so far */ int nVarExpr; /* Number of used slots in apVarExpr[] */ int nVarExprAlloc; /* Number of allocated slots in apVarExpr[] */ |
︙ | ︙ | |||
2519 2520 2521 2522 2523 2524 2525 | #endif #ifdef SQLITE_ENABLE_MEMSYS5 const sqlite3_mem_methods *sqlite3MemGetMemsys5(void); #endif #ifndef SQLITE_MUTEX_OMIT | | > | 2538 2539 2540 2541 2542 2543 2544 2545 2546 2547 2548 2549 2550 2551 2552 2553 | #endif #ifdef SQLITE_ENABLE_MEMSYS5 const sqlite3_mem_methods *sqlite3MemGetMemsys5(void); #endif #ifndef SQLITE_MUTEX_OMIT sqlite3_mutex_methods const *sqlite3DefaultMutex(void); sqlite3_mutex_methods const *sqlite3NoopMutex(void); sqlite3_mutex *sqlite3MutexAlloc(int); int sqlite3MutexInit(void); int sqlite3MutexEnd(void); #endif int sqlite3StatusValue(int); void sqlite3StatusAdd(int, int); |
︙ | ︙ | |||
2651 2652 2653 2654 2655 2656 2657 2658 2659 2660 2661 2662 2663 2664 | Expr *sqlite3LimitWhere(Parse *, SrcList *, Expr *, ExprList *, Expr *, Expr *, char *); #endif void sqlite3DeleteFrom(Parse*, SrcList*, Expr*); void sqlite3Update(Parse*, SrcList*, ExprList*, Expr*, int); WhereInfo *sqlite3WhereBegin(Parse*, SrcList*, Expr*, ExprList**, u16); void sqlite3WhereEnd(WhereInfo*); int sqlite3ExprCodeGetColumn(Parse*, Table*, int, int, int); void sqlite3ExprCodeMove(Parse*, int, int, int); void sqlite3ExprCodeCopy(Parse*, int, int, int); void sqlite3ExprCacheStore(Parse*, int, int, int); void sqlite3ExprCachePush(Parse*); void sqlite3ExprCachePop(Parse*, int); void sqlite3ExprCacheRemove(Parse*, int, int); void sqlite3ExprCacheClear(Parse*); | > | 2671 2672 2673 2674 2675 2676 2677 2678 2679 2680 2681 2682 2683 2684 2685 | Expr *sqlite3LimitWhere(Parse *, SrcList *, Expr *, ExprList *, Expr *, Expr *, char *); #endif void sqlite3DeleteFrom(Parse*, SrcList*, Expr*); void sqlite3Update(Parse*, SrcList*, ExprList*, Expr*, int); WhereInfo *sqlite3WhereBegin(Parse*, SrcList*, Expr*, ExprList**, u16); void sqlite3WhereEnd(WhereInfo*); int sqlite3ExprCodeGetColumn(Parse*, Table*, int, int, int); void sqlite3ExprCodeGetColumnOfTable(Vdbe*, Table*, int, int, int); void sqlite3ExprCodeMove(Parse*, int, int, int); void sqlite3ExprCodeCopy(Parse*, int, int, int); void sqlite3ExprCacheStore(Parse*, int, int, int); void sqlite3ExprCachePush(Parse*); void sqlite3ExprCachePop(Parse*, int); void sqlite3ExprCacheRemove(Parse*, int, int); void sqlite3ExprCacheClear(Parse*); |
︙ | ︙ | |||
2677 2678 2679 2680 2681 2682 2683 2684 2685 2686 2687 2688 2689 2690 | Index *sqlite3FindIndex(sqlite3*,const char*, const char*); void sqlite3UnlinkAndDeleteTable(sqlite3*,int,const char*); void sqlite3UnlinkAndDeleteIndex(sqlite3*,int,const char*); void sqlite3Vacuum(Parse*); int sqlite3RunVacuum(char**, sqlite3*); char *sqlite3NameFromToken(sqlite3*, Token*); int sqlite3ExprCompare(Expr*, Expr*); void sqlite3ExprAnalyzeAggregates(NameContext*, Expr*); void sqlite3ExprAnalyzeAggList(NameContext*,ExprList*); Vdbe *sqlite3GetVdbe(Parse*); void sqlite3PrngSaveState(void); void sqlite3PrngRestoreState(void); void sqlite3PrngResetState(void); void sqlite3RollbackAll(sqlite3*); | > | 2698 2699 2700 2701 2702 2703 2704 2705 2706 2707 2708 2709 2710 2711 2712 | Index *sqlite3FindIndex(sqlite3*,const char*, const char*); void sqlite3UnlinkAndDeleteTable(sqlite3*,int,const char*); void sqlite3UnlinkAndDeleteIndex(sqlite3*,int,const char*); void sqlite3Vacuum(Parse*); int sqlite3RunVacuum(char**, sqlite3*); char *sqlite3NameFromToken(sqlite3*, Token*); int sqlite3ExprCompare(Expr*, Expr*); int sqlite3ExprListCompare(ExprList*, ExprList*); void sqlite3ExprAnalyzeAggregates(NameContext*, Expr*); void sqlite3ExprAnalyzeAggList(NameContext*,ExprList*); Vdbe *sqlite3GetVdbe(Parse*); void sqlite3PrngSaveState(void); void sqlite3PrngRestoreState(void); void sqlite3PrngResetState(void); void sqlite3RollbackAll(sqlite3*); |
︙ | ︙ | |||
2864 2865 2866 2867 2868 2869 2870 2871 2872 2873 2874 2875 2876 | #endif int sqlite3ValueFromExpr(sqlite3 *, Expr *, u8, u8, sqlite3_value **); void sqlite3ValueApplyAffinity(sqlite3_value *, u8, u8); #ifndef SQLITE_AMALGAMATION extern const unsigned char sqlite3OpcodeProperty[]; extern const unsigned char sqlite3UpperToLower[]; extern const unsigned char sqlite3CtypeMap[]; extern SQLITE_WSD struct Sqlite3Config sqlite3Config; extern SQLITE_WSD FuncDefHash sqlite3GlobalFunctions; extern int sqlite3PendingByte; #endif void sqlite3RootPageMoved(Db*, int, int); void sqlite3Reindex(Parse*, Token*, Token*); | > > > | | 2886 2887 2888 2889 2890 2891 2892 2893 2894 2895 2896 2897 2898 2899 2900 2901 2902 2903 2904 2905 2906 2907 2908 2909 | #endif int sqlite3ValueFromExpr(sqlite3 *, Expr *, u8, u8, sqlite3_value **); void sqlite3ValueApplyAffinity(sqlite3_value *, u8, u8); #ifndef SQLITE_AMALGAMATION extern const unsigned char sqlite3OpcodeProperty[]; extern const unsigned char sqlite3UpperToLower[]; extern const unsigned char sqlite3CtypeMap[]; extern const Token sqlite3IntTokens[]; extern SQLITE_WSD struct Sqlite3Config sqlite3Config; extern SQLITE_WSD FuncDefHash sqlite3GlobalFunctions; #ifndef SQLITE_OMIT_WSD extern int sqlite3PendingByte; #endif #endif void sqlite3RootPageMoved(Db*, int, int); void sqlite3Reindex(Parse*, Token*, Token*); void sqlite3AlterFunctions(void); void sqlite3AlterRenameTable(Parse*, SrcList*, Token*); int sqlite3GetToken(const unsigned char *, int *); void sqlite3NestedParse(Parse*, const char*, ...); void sqlite3ExpirePreparedStatements(sqlite3*); int sqlite3CodeSubselect(Parse *, Expr *, int, int); void sqlite3SelectPrep(Parse*, Select*, NameContext*); int sqlite3ResolveExprNames(NameContext*, Expr*); |
︙ | ︙ | |||
2979 2980 2981 2982 2983 2984 2985 2986 2987 2988 2989 2990 2991 2992 | int sqlite3VdbeParameterIndex(Vdbe*, const char*, int); int sqlite3TransferBindings(sqlite3_stmt *, sqlite3_stmt *); int sqlite3Reprepare(Vdbe*); void sqlite3ExprListCheckLength(Parse*, ExprList*, const char*); CollSeq *sqlite3BinaryCompareCollSeq(Parse *, Expr *, Expr *); int sqlite3TempInMemory(const sqlite3*); VTable *sqlite3GetVTable(sqlite3*, Table*); /* Declarations for functions in fkey.c. All of these are replaced by ** no-op macros if OMIT_FOREIGN_KEY is defined. In this case no foreign ** key functionality is available. If OMIT_TRIGGER is defined but ** OMIT_FOREIGN_KEY is not, only some of the functions are no-oped. In ** this case foreign keys are parsed, but no other functionality is ** provided (enforcement of FK constraints requires the triggers sub-system). | > > > | 3004 3005 3006 3007 3008 3009 3010 3011 3012 3013 3014 3015 3016 3017 3018 3019 3020 | int sqlite3VdbeParameterIndex(Vdbe*, const char*, int); int sqlite3TransferBindings(sqlite3_stmt *, sqlite3_stmt *); int sqlite3Reprepare(Vdbe*); void sqlite3ExprListCheckLength(Parse*, ExprList*, const char*); CollSeq *sqlite3BinaryCompareCollSeq(Parse *, Expr *, Expr *); int sqlite3TempInMemory(const sqlite3*); VTable *sqlite3GetVTable(sqlite3*, Table*); const char *sqlite3JournalModename(int); int sqlite3Checkpoint(sqlite3*, int); int sqlite3WalDefaultHook(void*,sqlite3*,const char*,int); /* Declarations for functions in fkey.c. All of these are replaced by ** no-op macros if OMIT_FOREIGN_KEY is defined. In this case no foreign ** key functionality is available. If OMIT_TRIGGER is defined but ** OMIT_FOREIGN_KEY is not, only some of the functions are no-oped. In ** this case foreign keys are parsed, but no other functionality is ** provided (enforcement of FK constraints requires the triggers sub-system). |
︙ | ︙ | |||
3085 3086 3087 3088 3089 3090 3091 3092 | void sqlite3VdbeIOTraceSql(Vdbe*); SQLITE_EXTERN void (*sqlite3IoTrace)(const char*,...); #else # define IOTRACE(A) # define sqlite3VdbeIOTraceSql(X) #endif #endif | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 3113 3114 3115 3116 3117 3118 3119 3120 3121 3122 3123 3124 3125 3126 3127 3128 3129 3130 3131 3132 3133 3134 3135 3136 3137 3138 3139 3140 3141 3142 3143 3144 3145 3146 3147 3148 3149 3150 3151 3152 3153 3154 3155 3156 3157 3158 3159 | void sqlite3VdbeIOTraceSql(Vdbe*); SQLITE_EXTERN void (*sqlite3IoTrace)(const char*,...); #else # define IOTRACE(A) # define sqlite3VdbeIOTraceSql(X) #endif /* ** These routines are available for the mem2.c debugging memory allocator ** only. They are used to verify that different "types" of memory ** allocations are properly tracked by the system. ** ** sqlite3MemdebugSetType() sets the "type" of an allocation to one of ** the MEMTYPE_* macros defined below. The type must be a bitmask with ** a single bit set. ** ** sqlite3MemdebugHasType() returns true if any of the bits in its second ** argument match the type set by the previous sqlite3MemdebugSetType(). ** sqlite3MemdebugHasType() is intended for use inside assert() statements. ** For example: ** ** assert( sqlite3MemdebugHasType(p, MEMTYPE_HEAP) ); ** ** Perhaps the most important point is the difference between MEMTYPE_HEAP ** and MEMTYPE_DB. If an allocation is MEMTYPE_DB, that means it might have ** been allocated by lookaside, except the allocation was too large or ** lookaside was already full. It is important to verify that allocations ** that might have been satisfied by lookaside are not passed back to ** non-lookaside free() routines. Asserts such as the example above are ** placed on the non-lookaside free() routines to verify this constraint. ** ** All of this is no-op for a production build. It only comes into ** play when the SQLITE_MEMDEBUG compile-time option is used. */ #ifdef SQLITE_MEMDEBUG void sqlite3MemdebugSetType(void*,u8); int sqlite3MemdebugHasType(void*,u8); #else # define sqlite3MemdebugSetType(X,Y) /* no-op */ # define sqlite3MemdebugHasType(X,Y) 1 #endif #define MEMTYPE_HEAP 0x01 /* General heap allocations */ #define MEMTYPE_DB 0x02 /* Associated with a database connection */ #define MEMTYPE_SCRATCH 0x04 /* Scratch allocations */ #define MEMTYPE_PCACHE 0x08 /* Page cache allocations */ #endif /* _SQLITEINT_H_ */ |
Changes to src/sqliteLimit.h.
︙ | ︙ | |||
104 105 106 107 108 109 110 111 112 113 114 115 116 117 | #ifndef SQLITE_DEFAULT_CACHE_SIZE # define SQLITE_DEFAULT_CACHE_SIZE 2000 #endif #ifndef SQLITE_DEFAULT_TEMP_CACHE_SIZE # define SQLITE_DEFAULT_TEMP_CACHE_SIZE 500 #endif /* ** The maximum number of attached databases. This must be between 0 ** and 30. The upper bound on 30 is because a 32-bit integer bitmap ** is used internally to track attached databases. */ #ifndef SQLITE_MAX_ATTACHED # define SQLITE_MAX_ATTACHED 10 | > > > > > > > > | 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 | #ifndef SQLITE_DEFAULT_CACHE_SIZE # define SQLITE_DEFAULT_CACHE_SIZE 2000 #endif #ifndef SQLITE_DEFAULT_TEMP_CACHE_SIZE # define SQLITE_DEFAULT_TEMP_CACHE_SIZE 500 #endif /* ** The default number of frames to accumulate in the log file before ** checkpointing the database in WAL mode. */ #ifndef SQLITE_DEFAULT_WAL_AUTOCHECKPOINT # define SQLITE_DEFAULT_WAL_AUTOCHECKPOINT 1000 #endif /* ** The maximum number of attached databases. This must be between 0 ** and 30. The upper bound on 30 is because a 32-bit integer bitmap ** is used internally to track attached databases. */ #ifndef SQLITE_MAX_ATTACHED # define SQLITE_MAX_ATTACHED 10 |
︙ | ︙ |
Changes to src/status.c.
︙ | ︙ | |||
108 109 110 111 112 113 114 115 116 117 118 119 120 | *pCurrent = db->lookaside.nOut; *pHighwater = db->lookaside.mxOut; if( resetFlag ){ db->lookaside.mxOut = db->lookaside.nOut; } break; } default: { return SQLITE_ERROR; } } return SQLITE_OK; } | > > > > > > > > > > > > > > > > > > > > | 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 | *pCurrent = db->lookaside.nOut; *pHighwater = db->lookaside.mxOut; if( resetFlag ){ db->lookaside.mxOut = db->lookaside.nOut; } break; } /* ** Return an approximation for the amount of memory currently used ** by all pagers associated with the given database connection. The ** highwater mark is meaningless and is returned as zero. */ case SQLITE_DBSTATUS_CACHE_USED: { int totalUsed = 0; int i; for(i=0; i<db->nDb; i++){ Btree *pBt = db->aDb[i].pBt; if( pBt ){ Pager *pPager = sqlite3BtreePager(pBt); totalUsed += sqlite3PagerMemUsed(pPager); } } *pCurrent = totalUsed; *pHighwater = 0; break; } default: { return SQLITE_ERROR; } } return SQLITE_OK; } |
Changes to src/tclsqlite.c.
︙ | ︙ | |||
119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 | char *zProgress; /* The progress callback routine */ char *zAuth; /* The authorization callback routine */ int disableAuth; /* Disable the authorizer if it exists */ char *zNull; /* Text to substitute for an SQL NULL value */ SqlFunc *pFunc; /* List of SQL functions */ Tcl_Obj *pUpdateHook; /* Update hook script (if any) */ Tcl_Obj *pRollbackHook; /* Rollback hook script (if any) */ Tcl_Obj *pUnlockNotify; /* Unlock notify script (if any) */ SqlCollate *pCollate; /* List of SQL collation functions */ int rc; /* Return code of most recent sqlite3_exec() */ Tcl_Obj *pCollateNeeded; /* Collation needed script */ SqlPreparedStmt *stmtList; /* List of prepared statements*/ SqlPreparedStmt *stmtLast; /* Last statement in the list */ int maxStmt; /* The next maximum number of stmtList */ int nStmt; /* Number of statements in stmtList */ IncrblobChannel *pIncrblob;/* Linked list of open incrblob channels */ | > | | 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 | char *zProgress; /* The progress callback routine */ char *zAuth; /* The authorization callback routine */ int disableAuth; /* Disable the authorizer if it exists */ char *zNull; /* Text to substitute for an SQL NULL value */ SqlFunc *pFunc; /* List of SQL functions */ Tcl_Obj *pUpdateHook; /* Update hook script (if any) */ Tcl_Obj *pRollbackHook; /* Rollback hook script (if any) */ Tcl_Obj *pWalHook; /* WAL hook script (if any) */ Tcl_Obj *pUnlockNotify; /* Unlock notify script (if any) */ SqlCollate *pCollate; /* List of SQL collation functions */ int rc; /* Return code of most recent sqlite3_exec() */ Tcl_Obj *pCollateNeeded; /* Collation needed script */ SqlPreparedStmt *stmtList; /* List of prepared statements*/ SqlPreparedStmt *stmtLast; /* Last statement in the list */ int maxStmt; /* The next maximum number of stmtList */ int nStmt; /* Number of statements in stmtList */ IncrblobChannel *pIncrblob;/* Linked list of open incrblob channels */ int nStep, nSort, nIndex; /* Statistics for most recent operation */ int nTransaction; /* Number of nested [transaction] methods */ }; struct IncrblobChannel { sqlite3_blob *pBlob; /* sqlite3 blob handle */ SqliteDb *pDb; /* Associated database connection */ int iSeek; /* Current seek offset */ |
︙ | ︙ | |||
481 482 483 484 485 486 487 488 489 490 491 492 493 494 | } if( pDb->pUpdateHook ){ Tcl_DecrRefCount(pDb->pUpdateHook); } if( pDb->pRollbackHook ){ Tcl_DecrRefCount(pDb->pRollbackHook); } if( pDb->pCollateNeeded ){ Tcl_DecrRefCount(pDb->pCollateNeeded); } Tcl_Free((char*)pDb); } /* | > > > | 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 | } if( pDb->pUpdateHook ){ Tcl_DecrRefCount(pDb->pUpdateHook); } if( pDb->pRollbackHook ){ Tcl_DecrRefCount(pDb->pRollbackHook); } if( pDb->pWalHook ){ Tcl_DecrRefCount(pDb->pWalHook); } if( pDb->pCollateNeeded ){ Tcl_DecrRefCount(pDb->pCollateNeeded); } Tcl_Free((char*)pDb); } /* |
︙ | ︙ | |||
584 585 586 587 588 589 590 591 592 593 594 595 596 597 | static void DbRollbackHandler(void *clientData){ SqliteDb *pDb = (SqliteDb*)clientData; assert(pDb->pRollbackHook); if( TCL_OK!=Tcl_EvalObjEx(pDb->interp, pDb->pRollbackHook, 0) ){ Tcl_BackgroundError(pDb->interp); } } #if defined(SQLITE_TEST) && defined(SQLITE_ENABLE_UNLOCK_NOTIFY) static void setTestUnlockNotifyVars(Tcl_Interp *interp, int iArg, int nArg){ char zBuf[64]; sprintf(zBuf, "%d", iArg); Tcl_SetVar(interp, "sqlite_unlock_notify_arg", zBuf, TCL_GLOBAL_ONLY); sprintf(zBuf, "%d", nArg); | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | static void DbRollbackHandler(void *clientData){ SqliteDb *pDb = (SqliteDb*)clientData; assert(pDb->pRollbackHook); if( TCL_OK!=Tcl_EvalObjEx(pDb->interp, pDb->pRollbackHook, 0) ){ Tcl_BackgroundError(pDb->interp); } } /* ** This procedure handles wal_hook callbacks. */ static int DbWalHandler( void *clientData, sqlite3 *db, const char *zDb, int nEntry ){ int ret = SQLITE_OK; Tcl_Obj *p; SqliteDb *pDb = (SqliteDb*)clientData; Tcl_Interp *interp = pDb->interp; assert(pDb->pWalHook); p = Tcl_DuplicateObj(pDb->pWalHook); Tcl_IncrRefCount(p); Tcl_ListObjAppendElement(interp, p, Tcl_NewStringObj(zDb, -1)); Tcl_ListObjAppendElement(interp, p, Tcl_NewIntObj(nEntry)); if( TCL_OK!=Tcl_EvalObjEx(interp, p, 0) || TCL_OK!=Tcl_GetIntFromObj(interp, Tcl_GetObjResult(interp), &ret) ){ Tcl_BackgroundError(interp); } Tcl_DecrRefCount(p); return ret; } #if defined(SQLITE_TEST) && defined(SQLITE_ENABLE_UNLOCK_NOTIFY) static void setTestUnlockNotifyVars(Tcl_Interp *interp, int iArg, int nArg){ char zBuf[64]; sprintf(zBuf, "%d", iArg); Tcl_SetVar(interp, "sqlite_unlock_notify_arg", zBuf, TCL_GLOBAL_ONLY); sprintf(zBuf, "%d", nArg); |
︙ | ︙ | |||
1347 1348 1349 1350 1351 1352 1353 1354 1355 1356 1357 1358 1359 1360 | if( p->pArray ){ dbEvalRowInfo(p, 0, 0); } rcs = sqlite3_reset(pStmt); pDb->nStep = sqlite3_stmt_status(pStmt,SQLITE_STMTSTATUS_FULLSCAN_STEP,1); pDb->nSort = sqlite3_stmt_status(pStmt,SQLITE_STMTSTATUS_SORT,1); dbReleaseColumnNames(p); p->pPreStmt = 0; if( rcs!=SQLITE_OK ){ /* If a run-time error occurs, report the error and stop reading ** the SQL. */ Tcl_SetObjResult(pDb->interp, dbTextToObj(sqlite3_errmsg(pDb->db))); | > | 1380 1381 1382 1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 | if( p->pArray ){ dbEvalRowInfo(p, 0, 0); } rcs = sqlite3_reset(pStmt); pDb->nStep = sqlite3_stmt_status(pStmt,SQLITE_STMTSTATUS_FULLSCAN_STEP,1); pDb->nSort = sqlite3_stmt_status(pStmt,SQLITE_STMTSTATUS_SORT,1); pDb->nIndex = sqlite3_stmt_status(pStmt,SQLITE_STMTSTATUS_AUTOINDEX,1); dbReleaseColumnNames(p); p->pPreStmt = 0; if( rcs!=SQLITE_OK ){ /* If a run-time error occurs, report the error and stop reading ** the SQL. */ Tcl_SetObjResult(pDb->interp, dbTextToObj(sqlite3_errmsg(pDb->db))); |
︙ | ︙ | |||
1540 1541 1542 1543 1544 1545 1546 | "errorcode", "eval", "exists", "function", "incrblob", "interrupt", "last_insert_rowid", "nullvalue", "onecolumn", "profile", "progress", "rekey", "restore", "rollback_hook", "status", "timeout", "total_changes", "trace", "transaction", "unlock_notify", "update_hook", | | | | 1574 1575 1576 1577 1578 1579 1580 1581 1582 1583 1584 1585 1586 1587 1588 1589 1590 1591 1592 1593 1594 1595 1596 1597 1598 1599 1600 1601 1602 | "errorcode", "eval", "exists", "function", "incrblob", "interrupt", "last_insert_rowid", "nullvalue", "onecolumn", "profile", "progress", "rekey", "restore", "rollback_hook", "status", "timeout", "total_changes", "trace", "transaction", "unlock_notify", "update_hook", "version", "wal_hook", 0 }; enum DB_enum { DB_AUTHORIZER, DB_BACKUP, DB_BUSY, DB_CACHE, DB_CHANGES, DB_CLOSE, DB_COLLATE, DB_COLLATION_NEEDED, DB_COMMIT_HOOK, DB_COMPLETE, DB_COPY, DB_ENABLE_LOAD_EXTENSION, DB_ERRORCODE, DB_EVAL, DB_EXISTS, DB_FUNCTION, DB_INCRBLOB, DB_INTERRUPT, DB_LAST_INSERT_ROWID, DB_NULLVALUE, DB_ONECOLUMN, DB_PROFILE, DB_PROGRESS, DB_REKEY, DB_RESTORE, DB_ROLLBACK_HOOK, DB_STATUS, DB_TIMEOUT, DB_TOTAL_CHANGES, DB_TRACE, DB_TRANSACTION, DB_UNLOCK_NOTIFY, DB_UPDATE_HOOK, DB_VERSION, DB_WAL_HOOK }; /* don't leave trailing commas on DB_enum, it confuses the AIX xlc compiler */ if( objc<2 ){ Tcl_WrongNumArgs(interp, 1, objv, "SUBCOMMAND ..."); return TCL_ERROR; } |
︙ | ︙ | |||
2524 2525 2526 2527 2528 2529 2530 | rc = TCL_ERROR; } sqlite3_close(pSrc); break; } /* | | > > | > | 2558 2559 2560 2561 2562 2563 2564 2565 2566 2567 2568 2569 2570 2571 2572 2573 2574 2575 2576 2577 2578 2579 2580 2581 2582 2583 2584 2585 2586 2587 2588 2589 2590 2591 2592 2593 | rc = TCL_ERROR; } sqlite3_close(pSrc); break; } /* ** $db status (step|sort|autoindex) ** ** Display SQLITE_STMTSTATUS_FULLSCAN_STEP or ** SQLITE_STMTSTATUS_SORT for the most recent eval. */ case DB_STATUS: { int v; const char *zOp; if( objc!=3 ){ Tcl_WrongNumArgs(interp, 2, objv, "(step|sort)"); return TCL_ERROR; } zOp = Tcl_GetString(objv[2]); if( strcmp(zOp, "step")==0 ){ v = pDb->nStep; }else if( strcmp(zOp, "sort")==0 ){ v = pDb->nSort; }else if( strcmp(zOp, "autoindex")==0 ){ v = pDb->nIndex; }else{ Tcl_AppendResult(interp, "bad argument: should be autoindex, step, or sort", (char*)0); return TCL_ERROR; } Tcl_SetObjResult(interp, Tcl_NewIntObj(v)); break; } |
︙ | ︙ | |||
2722 2723 2724 2725 2726 2727 2728 2729 2730 2731 2732 2733 2734 2735 2736 2737 2738 2739 2740 2741 2742 2743 2744 2745 2746 2747 | } } #endif break; } /* ** $db update_hook ?script? ** $db rollback_hook ?script? */ case DB_UPDATE_HOOK: case DB_ROLLBACK_HOOK: { /* set ppHook to point at pUpdateHook or pRollbackHook, depending on ** whether [$db update_hook] or [$db rollback_hook] was invoked. */ Tcl_Obj **ppHook; if( choice==DB_UPDATE_HOOK ){ ppHook = &pDb->pUpdateHook; }else{ ppHook = &pDb->pRollbackHook; } if( objc!=2 && objc!=3 ){ Tcl_WrongNumArgs(interp, 2, objv, "?SCRIPT?"); return TCL_ERROR; | > > > > | 2759 2760 2761 2762 2763 2764 2765 2766 2767 2768 2769 2770 2771 2772 2773 2774 2775 2776 2777 2778 2779 2780 2781 2782 2783 2784 2785 2786 2787 2788 | } } #endif break; } /* ** $db wal_hook ?script? ** $db update_hook ?script? ** $db rollback_hook ?script? */ case DB_WAL_HOOK: case DB_UPDATE_HOOK: case DB_ROLLBACK_HOOK: { /* set ppHook to point at pUpdateHook or pRollbackHook, depending on ** whether [$db update_hook] or [$db rollback_hook] was invoked. */ Tcl_Obj **ppHook; if( choice==DB_UPDATE_HOOK ){ ppHook = &pDb->pUpdateHook; }else if( choice==DB_WAL_HOOK ){ ppHook = &pDb->pWalHook; }else{ ppHook = &pDb->pRollbackHook; } if( objc!=2 && objc!=3 ){ Tcl_WrongNumArgs(interp, 2, objv, "?SCRIPT?"); return TCL_ERROR; |
︙ | ︙ | |||
2759 2760 2761 2762 2763 2764 2765 2766 2767 2768 2769 2770 2771 2772 | *ppHook = objv[2]; Tcl_IncrRefCount(*ppHook); } } sqlite3_update_hook(pDb->db, (pDb->pUpdateHook?DbUpdateHandler:0), pDb); sqlite3_rollback_hook(pDb->db,(pDb->pRollbackHook?DbRollbackHandler:0),pDb); break; } /* $db version ** ** Return the version string for this database. | > | 2800 2801 2802 2803 2804 2805 2806 2807 2808 2809 2810 2811 2812 2813 2814 | *ppHook = objv[2]; Tcl_IncrRefCount(*ppHook); } } sqlite3_update_hook(pDb->db, (pDb->pUpdateHook?DbUpdateHandler:0), pDb); sqlite3_rollback_hook(pDb->db,(pDb->pRollbackHook?DbRollbackHandler:0),pDb); sqlite3_wal_hook(pDb->db,(pDb->pWalHook?DbWalHandler:0),pDb); break; } /* $db version ** ** Return the version string for this database. |
︙ | ︙ | |||
2851 2852 2853 2854 2855 2856 2857 | } } for(i=3; i+1<objc; i+=2){ zArg = Tcl_GetString(objv[i]); if( strcmp(zArg,"-key")==0 ){ pKey = Tcl_GetByteArrayFromObj(objv[i+1], &nKey); }else if( strcmp(zArg, "-vfs")==0 ){ | < | | 2893 2894 2895 2896 2897 2898 2899 2900 2901 2902 2903 2904 2905 2906 2907 | } } for(i=3; i+1<objc; i+=2){ zArg = Tcl_GetString(objv[i]); if( strcmp(zArg,"-key")==0 ){ pKey = Tcl_GetByteArrayFromObj(objv[i+1], &nKey); }else if( strcmp(zArg, "-vfs")==0 ){ zVfs = Tcl_GetString(objv[i+1]); }else if( strcmp(zArg, "-readonly")==0 ){ int b; if( Tcl_GetBooleanFromObj(interp, objv[i+1], &b) ) return TCL_ERROR; if( b ){ flags &= ~(SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE); flags |= SQLITE_OPEN_READONLY; }else{ |
︙ | ︙ | |||
3443 3444 3445 3446 3447 3448 3449 | "set line {}\n" "} else {\n" "append line \\n\n" "}\n" "}\n" ; #endif | > > > | > | > > > > > > > > | | > > > | | > > > > | | < > | > | > > > > > > > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | | 3484 3485 3486 3487 3488 3489 3490 3491 3492 3493 3494 3495 3496 3497 3498 3499 3500 3501 3502 3503 3504 3505 3506 3507 3508 3509 3510 3511 3512 3513 3514 3515 3516 3517 3518 3519 3520 3521 3522 3523 3524 3525 3526 3527 3528 3529 3530 3531 3532 3533 3534 3535 3536 3537 3538 3539 3540 3541 3542 3543 3544 3545 3546 3547 3548 3549 3550 3551 3552 3553 3554 3555 3556 3557 3558 3559 3560 3561 3562 3563 3564 3565 3566 3567 3568 3569 3570 3571 3572 3573 3574 3575 3576 3577 3578 3579 3580 3581 3582 3583 3584 3585 3586 3587 3588 3589 3590 3591 3592 3593 3594 3595 3596 3597 3598 3599 3600 3601 3602 3603 3604 3605 3606 3607 3608 3609 3610 3611 3612 3613 3614 3615 3616 3617 3618 3619 3620 3621 3622 3623 3624 3625 3626 3627 3628 3629 3630 3631 3632 3633 3634 3635 3636 3637 3638 3639 3640 3641 3642 3643 3644 3645 3646 3647 | "set line {}\n" "} else {\n" "append line \\n\n" "}\n" "}\n" ; #endif #if TCLSH==2 static char zMainloop[] = #include "spaceanal_tcl.h" ; #endif #ifdef SQLITE_TEST static void init_all(Tcl_Interp *); static int init_all_cmd( ClientData cd, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[] ){ Tcl_Interp *slave; if( objc!=2 ){ Tcl_WrongNumArgs(interp, 1, objv, "SLAVE"); return TCL_ERROR; } slave = Tcl_GetSlave(interp, Tcl_GetString(objv[1])); if( !slave ){ return TCL_ERROR; } init_all(slave); return TCL_OK; } #endif /* ** Configure the interpreter passed as the first argument to have access ** to the commands and linked variables that make up: ** ** * the [sqlite3] extension itself, ** ** * If SQLITE_TCLMD5 or SQLITE_TEST is defined, the Md5 commands, and ** ** * If SQLITE_TEST is set, the various test interfaces used by the Tcl ** test suite. */ static void init_all(Tcl_Interp *interp){ Sqlite3_Init(interp); #if defined(SQLITE_TEST) || defined(SQLITE_TCLMD5) Md5_Init(interp); #endif #ifdef SQLITE_TEST { extern int Sqliteconfig_Init(Tcl_Interp*); extern int Sqlitetest1_Init(Tcl_Interp*); extern int Sqlitetest2_Init(Tcl_Interp*); extern int Sqlitetest3_Init(Tcl_Interp*); extern int Sqlitetest4_Init(Tcl_Interp*); extern int Sqlitetest5_Init(Tcl_Interp*); extern int Sqlitetest6_Init(Tcl_Interp*); extern int Sqlitetest7_Init(Tcl_Interp*); extern int Sqlitetest8_Init(Tcl_Interp*); extern int Sqlitetest9_Init(Tcl_Interp*); extern int Sqlitetestasync_Init(Tcl_Interp*); extern int Sqlitetest_autoext_Init(Tcl_Interp*); extern int Sqlitetest_demovfs_Init(Tcl_Interp *); extern int Sqlitetest_func_Init(Tcl_Interp*); extern int Sqlitetest_hexio_Init(Tcl_Interp*); extern int Sqlitetest_init_Init(Tcl_Interp*); extern int Sqlitetest_malloc_Init(Tcl_Interp*); extern int Sqlitetest_mutex_Init(Tcl_Interp*); extern int Sqlitetestschema_Init(Tcl_Interp*); extern int Sqlitetestsse_Init(Tcl_Interp*); extern int Sqlitetesttclvar_Init(Tcl_Interp*); extern int SqlitetestThread_Init(Tcl_Interp*); extern int SqlitetestOnefile_Init(); extern int SqlitetestOsinst_Init(Tcl_Interp*); extern int Sqlitetestbackup_Init(Tcl_Interp*); extern int Sqlitetestintarray_Init(Tcl_Interp*); extern int Sqlitetestvfs_Init(Tcl_Interp *); extern int SqlitetestStat_Init(Tcl_Interp*); Sqliteconfig_Init(interp); Sqlitetest1_Init(interp); Sqlitetest2_Init(interp); Sqlitetest3_Init(interp); Sqlitetest4_Init(interp); Sqlitetest5_Init(interp); Sqlitetest6_Init(interp); Sqlitetest7_Init(interp); Sqlitetest8_Init(interp); Sqlitetest9_Init(interp); Sqlitetestasync_Init(interp); Sqlitetest_autoext_Init(interp); Sqlitetest_demovfs_Init(interp); Sqlitetest_func_Init(interp); Sqlitetest_hexio_Init(interp); Sqlitetest_init_Init(interp); Sqlitetest_malloc_Init(interp); Sqlitetest_mutex_Init(interp); Sqlitetestschema_Init(interp); Sqlitetesttclvar_Init(interp); SqlitetestThread_Init(interp); SqlitetestOnefile_Init(interp); SqlitetestOsinst_Init(interp); Sqlitetestbackup_Init(interp); Sqlitetestintarray_Init(interp); Sqlitetestvfs_Init(interp); SqlitetestStat_Init(interp); Tcl_CreateObjCommand(interp,"load_testfixture_extensions",init_all_cmd,0,0); #ifdef SQLITE_SSE Sqlitetestsse_Init(interp); #endif } #endif } #define TCLSH_MAIN main /* Needed to fake out mktclapp */ int TCLSH_MAIN(int argc, char **argv){ Tcl_Interp *interp; /* Call sqlite3_shutdown() once before doing anything else. This is to ** test that sqlite3_shutdown() can be safely called by a process before ** sqlite3_initialize() is. */ sqlite3_shutdown(); #if TCLSH==2 sqlite3_config(SQLITE_CONFIG_SINGLETHREAD); #endif Tcl_FindExecutable(argv[0]); interp = Tcl_CreateInterp(); init_all(interp); if( argc>=2 ){ int i; char zArgc[32]; sqlite3_snprintf(sizeof(zArgc), zArgc, "%d", argc-(3-TCLSH)); Tcl_SetVar(interp,"argc", zArgc, TCL_GLOBAL_ONLY); Tcl_SetVar(interp,"argv0",argv[1],TCL_GLOBAL_ONLY); Tcl_SetVar(interp,"argv", "", TCL_GLOBAL_ONLY); for(i=3-TCLSH; i<argc; i++){ Tcl_SetVar(interp, "argv", argv[i], TCL_GLOBAL_ONLY | TCL_LIST_ELEMENT | TCL_APPEND_VALUE); } if( TCLSH==1 && Tcl_EvalFile(interp, argv[1])!=TCL_OK ){ const char *zInfo = Tcl_GetVar(interp, "errorInfo", TCL_GLOBAL_ONLY); if( zInfo==0 ) zInfo = Tcl_GetStringResult(interp); fprintf(stderr,"%s: %s\n", *argv, zInfo); return 1; } } if( TCLSH==2 || argc<=1 ){ Tcl_GlobalEval(interp, zMainloop); } return 0; } #endif /* TCLSH */ |
Changes to src/test1.c.
︙ | ︙ | |||
1897 1898 1899 1900 1901 1902 1903 1904 1905 1906 1907 1908 1909 1910 | */ static int sqlite_abort( void *NotUsed, Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ int argc, /* Number of arguments */ char **argv /* Text of each argument */ ){ assert( interp==0 ); /* This will always fail */ return TCL_OK; } /* ** The following routine is a user-defined SQL function whose purpose ** is to test the sqlite_set_result() API. | > > > > > > > | 1897 1898 1899 1900 1901 1902 1903 1904 1905 1906 1907 1908 1909 1910 1911 1912 1913 1914 1915 1916 1917 | */ static int sqlite_abort( void *NotUsed, Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ int argc, /* Number of arguments */ char **argv /* Text of each argument */ ){ #if defined(_MSC_VER) /* We do this, otherwise the test will halt with a popup message * that we have to click away before the test will continue. */ _set_abort_behavior( 0, _CALL_REPORTFAULT ); #endif exit(255); assert( interp==0 ); /* This will always fail */ return TCL_OK; } /* ** The following routine is a user-defined SQL function whose purpose ** is to test the sqlite_set_result() API. |
︙ | ︙ | |||
2021 2022 2023 2024 2025 2026 2027 2028 2029 2030 2031 2032 2033 2034 | static const struct { const char *zName; int op; } aOp[] = { { "SQLITE_STMTSTATUS_FULLSCAN_STEP", SQLITE_STMTSTATUS_FULLSCAN_STEP }, { "SQLITE_STMTSTATUS_SORT", SQLITE_STMTSTATUS_SORT }, }; if( objc!=4 ){ Tcl_WrongNumArgs(interp, 1, objv, "STMT PARAMETER RESETFLAG"); return TCL_ERROR; } if( getStmtPointer(interp, Tcl_GetString(objv[1]), &pStmt) ) return TCL_ERROR; zOpName = Tcl_GetString(objv[2]); | > | 2028 2029 2030 2031 2032 2033 2034 2035 2036 2037 2038 2039 2040 2041 2042 | static const struct { const char *zName; int op; } aOp[] = { { "SQLITE_STMTSTATUS_FULLSCAN_STEP", SQLITE_STMTSTATUS_FULLSCAN_STEP }, { "SQLITE_STMTSTATUS_SORT", SQLITE_STMTSTATUS_SORT }, { "SQLITE_STMTSTATUS_AUTOINDEX", SQLITE_STMTSTATUS_AUTOINDEX }, }; if( objc!=4 ){ Tcl_WrongNumArgs(interp, 1, objv, "STMT PARAMETER RESETFLAG"); return TCL_ERROR; } if( getStmtPointer(interp, Tcl_GetString(objv[1]), &pStmt) ) return TCL_ERROR; zOpName = Tcl_GetString(objv[2]); |
︙ | ︙ | |||
2621 2622 2623 2624 2625 2626 2627 | Tcl_AppendResult(interp, "wrong # args: should be \"", Tcl_GetStringFromObj(objv[0], 0), " <DB> <utf8> <utf16le> <utf16be>", 0); #endif /* SQLITE_OMIT_UTF16 */ return TCL_ERROR; } /* | | | 2629 2630 2631 2632 2633 2634 2635 2636 2637 2638 2639 2640 2641 2642 2643 | Tcl_AppendResult(interp, "wrong # args: should be \"", Tcl_GetStringFromObj(objv[0], 0), " <DB> <utf8> <utf16le> <utf16be>", 0); #endif /* SQLITE_OMIT_UTF16 */ return TCL_ERROR; } /* ** Usage: sqlite3_test_errstr <err code> ** ** Test that the english language string equivalents for sqlite error codes ** are sane. The parameter is an integer representing an sqlite error code. ** The result is a list of two elements, the string representation of the ** error code and the english language explanation. */ static int test_errstr( |
︙ | ︙ | |||
3896 3897 3898 3899 3900 3901 3902 | static int test_global_recover( void * clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[] ){ | < < | 3904 3905 3906 3907 3908 3909 3910 3911 3912 3913 3914 3915 3916 3917 3918 3919 3920 3921 3922 3923 3924 3925 | static int test_global_recover( void * clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[] ){ #ifndef SQLITE_OMIT_DEPRECATED int rc; if( objc!=1 ){ Tcl_WrongNumArgs(interp, 1, objv, ""); return TCL_ERROR; } rc = sqlite3_global_recover(); Tcl_SetResult(interp, (char *)t1ErrorName(rc), TCL_STATIC); #endif return TCL_OK; } /* ** Usage: sqlite3_column_text STMT column ** |
︙ | ︙ | |||
4605 4606 4607 4608 4609 4610 4611 | Tcl_GetStringFromObj(Tcl_NewIntObj(iArg), 0), " ", 0); return TCL_ERROR; } return TCL_OK; } /* | | > > | | > < > > > > > > > | 4611 4612 4613 4614 4615 4616 4617 4618 4619 4620 4621 4622 4623 4624 4625 4626 4627 4628 4629 4630 4631 4632 4633 4634 4635 4636 4637 4638 4639 4640 4641 4642 4643 4644 4645 4646 4647 4648 4649 4650 4651 4652 4653 4654 4655 4656 4657 4658 4659 4660 4661 4662 4663 4664 4665 4666 4667 4668 | Tcl_GetStringFromObj(Tcl_NewIntObj(iArg), 0), " ", 0); return TCL_ERROR; } return TCL_OK; } /* ** tclcmd: file_control_lockproxy_test DB PWD ** ** This TCL command runs the sqlite3_file_control interface and ** verifies correct operation of the SQLITE_GET_LOCKPROXYFILE and ** SQLITE_SET_LOCKPROXYFILE verbs. */ static int file_control_lockproxy_test( ClientData clientData, /* Pointer to sqlite3_enable_XXX function */ Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ int objc, /* Number of arguments */ Tcl_Obj *CONST objv[] /* Command arguments */ ){ sqlite3 *db; const char *zPwd; int nPwd; if( objc!=3 ){ Tcl_AppendResult(interp, "wrong # args: should be \"", Tcl_GetStringFromObj(objv[0], 0), " DB PWD", 0); return TCL_ERROR; } if( getDbPointer(interp, Tcl_GetString(objv[1]), &db) ){ return TCL_ERROR; } zPwd = Tcl_GetStringFromObj(objv[2], &nPwd); #if !defined(SQLITE_ENABLE_LOCKING_STYLE) # if defined(__APPLE__) # define SQLITE_ENABLE_LOCKING_STYLE 1 # else # define SQLITE_ENABLE_LOCKING_STYLE 0 # endif #endif #if SQLITE_ENABLE_LOCKING_STYLE && defined(__APPLE__) { char *testPath; int rc; char proxyPath[400]; if( sizeof(proxyPath)<nPwd+20 ){ Tcl_AppendResult(interp, "PWD too big", (void*)0); return TCL_ERROR; } sprintf(proxyPath, "%s/test.proxy", zPwd); rc = sqlite3_file_control(db, NULL, SQLITE_SET_LOCKPROXYFILE, proxyPath); if( rc ){ Tcl_SetObjResult(interp, Tcl_NewIntObj(rc)); return TCL_ERROR; } rc = sqlite3_file_control(db, NULL, SQLITE_GET_LOCKPROXYFILE, &testPath); if( strncmp(proxyPath,testPath,11) ){ |
︙ | ︙ | |||
4864 4865 4866 4867 4868 4869 4870 4871 4872 4873 4874 4875 4876 4877 | } rc = sqlite3_unlock_notify(db, test_unlock_notify_cb, (void *)interp); Tcl_SetResult(interp, (char *)t1ErrorName(rc), TCL_STATIC); return TCL_OK; } #endif /* ** tcl_objproc COMMANDNAME ARGS... ** ** Run a TCL command using its objProc interface. Throw an error if ** the command has no objProc interface. */ | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 4879 4880 4881 4882 4883 4884 4885 4886 4887 4888 4889 4890 4891 4892 4893 4894 4895 4896 4897 4898 4899 4900 4901 4902 4903 4904 4905 4906 4907 4908 4909 4910 4911 4912 4913 4914 4915 4916 4917 4918 4919 4920 4921 | } rc = sqlite3_unlock_notify(db, test_unlock_notify_cb, (void *)interp); Tcl_SetResult(interp, (char *)t1ErrorName(rc), TCL_STATIC); return TCL_OK; } #endif /* ** tclcmd: sqlite3_wal_checkpoint db ?NAME? */ static int test_wal_checkpoint( ClientData clientData, /* Unused */ Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ int objc, /* Number of arguments */ Tcl_Obj *CONST objv[] /* Command arguments */ ){ char *zDb = 0; sqlite3 *db; int rc; if( objc!=3 && objc!=2 ){ Tcl_WrongNumArgs(interp, 1, objv, "DB ?NAME?"); return TCL_ERROR; } if( getDbPointer(interp, Tcl_GetString(objv[1]), &db) ){ return TCL_ERROR; } if( objc==3 ){ zDb = Tcl_GetString(objv[2]); } rc = sqlite3_wal_checkpoint(db, zDb); Tcl_SetResult(interp, (char *)t1ErrorName(rc), TCL_STATIC); return TCL_OK; } /* ** tcl_objproc COMMANDNAME ARGS... ** ** Run a TCL command using its objProc interface. Throw an error if ** the command has no objProc interface. */ |
︙ | ︙ | |||
5085 5086 5087 5088 5089 5090 5091 5092 5093 5094 5095 5096 5097 5098 5099 5100 5101 | { "sqlite3_blob_read", test_blob_read, 0 }, { "sqlite3_blob_write", test_blob_write, 0 }, #endif { "pcache_stats", test_pcache_stats, 0 }, #ifdef SQLITE_ENABLE_UNLOCK_NOTIFY { "sqlite3_unlock_notify", test_unlock_notify, 0 }, #endif }; static int bitmask_size = sizeof(Bitmask)*8; int i; extern int sqlite3_sync_count, sqlite3_fullsync_count; extern int sqlite3_opentemp_count; extern int sqlite3_like_count; extern int sqlite3_xferopt_count; extern int sqlite3_pager_readdb_count; extern int sqlite3_pager_writedb_count; extern int sqlite3_pager_writej_count; | > < < < > | 5129 5130 5131 5132 5133 5134 5135 5136 5137 5138 5139 5140 5141 5142 5143 5144 5145 5146 5147 5148 5149 5150 5151 5152 5153 5154 5155 5156 5157 5158 5159 5160 5161 | { "sqlite3_blob_read", test_blob_read, 0 }, { "sqlite3_blob_write", test_blob_write, 0 }, #endif { "pcache_stats", test_pcache_stats, 0 }, #ifdef SQLITE_ENABLE_UNLOCK_NOTIFY { "sqlite3_unlock_notify", test_unlock_notify, 0 }, #endif { "sqlite3_wal_checkpoint", test_wal_checkpoint, 0 }, }; static int bitmask_size = sizeof(Bitmask)*8; int i; extern int sqlite3_sync_count, sqlite3_fullsync_count; extern int sqlite3_opentemp_count; extern int sqlite3_like_count; extern int sqlite3_xferopt_count; extern int sqlite3_pager_readdb_count; extern int sqlite3_pager_writedb_count; extern int sqlite3_pager_writej_count; #if SQLITE_OS_WIN extern int sqlite3_os_type; #endif #ifdef SQLITE_DEBUG extern int sqlite3WhereTrace; extern int sqlite3OSTrace; extern int sqlite3VdbeAddopTrace; extern int sqlite3WalTrace; #endif #ifdef SQLITE_TEST extern char sqlite3_query_plan[]; static char *query_plan = sqlite3_query_plan; #ifdef SQLITE_ENABLE_FTS3 extern int sqlite3_fts3_enable_parentheses; #endif |
︙ | ︙ | |||
5153 5154 5155 5156 5157 5158 5159 | (char*)&sqlite3_pager_writedb_count, TCL_LINK_INT); Tcl_LinkVar(interp, "sqlite3_pager_writej_count", (char*)&sqlite3_pager_writej_count, TCL_LINK_INT); #ifndef SQLITE_OMIT_UTF16 Tcl_LinkVar(interp, "unaligned_string_counter", (char*)&unaligned_string_counter, TCL_LINK_INT); #endif | < < < < > > > > | 5196 5197 5198 5199 5200 5201 5202 5203 5204 5205 5206 5207 5208 5209 5210 5211 5212 5213 5214 5215 5216 5217 5218 5219 5220 5221 5222 5223 5224 5225 5226 5227 5228 5229 5230 5231 5232 | (char*)&sqlite3_pager_writedb_count, TCL_LINK_INT); Tcl_LinkVar(interp, "sqlite3_pager_writej_count", (char*)&sqlite3_pager_writej_count, TCL_LINK_INT); #ifndef SQLITE_OMIT_UTF16 Tcl_LinkVar(interp, "unaligned_string_counter", (char*)&unaligned_string_counter, TCL_LINK_INT); #endif #ifndef SQLITE_OMIT_UTF16 Tcl_LinkVar(interp, "sqlite_last_needed_collation", (char*)&pzNeededCollation, TCL_LINK_STRING|TCL_LINK_READ_ONLY); #endif #if SQLITE_OS_WIN Tcl_LinkVar(interp, "sqlite_os_type", (char*)&sqlite3_os_type, TCL_LINK_INT); #endif #ifdef SQLITE_TEST Tcl_LinkVar(interp, "sqlite_query_plan", (char*)&query_plan, TCL_LINK_STRING|TCL_LINK_READ_ONLY); #endif #ifdef SQLITE_DEBUG Tcl_LinkVar(interp, "sqlite_addop_trace", (char*)&sqlite3VdbeAddopTrace, TCL_LINK_INT); Tcl_LinkVar(interp, "sqlite_where_trace", (char*)&sqlite3WhereTrace, TCL_LINK_INT); Tcl_LinkVar(interp, "sqlite_os_trace", (char*)&sqlite3OSTrace, TCL_LINK_INT); #ifndef SQLITE_OMIT_WAL Tcl_LinkVar(interp, "sqlite_wal_trace", (char*)&sqlite3WalTrace, TCL_LINK_INT); #endif #endif #ifndef SQLITE_OMIT_DISKIO Tcl_LinkVar(interp, "sqlite_opentemp_count", (char*)&sqlite3_opentemp_count, TCL_LINK_INT); #endif Tcl_LinkVar(interp, "sqlite_static_bind_value", (char*)&sqlite_static_bind_value, TCL_LINK_STRING); |
︙ | ︙ |
Changes to src/test2.c.
︙ | ︙ | |||
577 578 579 580 581 582 583 584 585 586 587 588 589 590 | const char **argv /* Text of each argument */ ){ int pbyte; int rc; if( argc!=2 ){ Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], " PENDING-BYTE\"", (void*)0); } if( Tcl_GetInt(interp, argv[1], &pbyte) ) return TCL_ERROR; rc = sqlite3_test_control(SQLITE_TESTCTRL_PENDING_BYTE, pbyte); Tcl_SetObjResult(interp, Tcl_NewIntObj(rc)); return TCL_OK; } | > | 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 | const char **argv /* Text of each argument */ ){ int pbyte; int rc; if( argc!=2 ){ Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], " PENDING-BYTE\"", (void*)0); return TCL_ERROR; } if( Tcl_GetInt(interp, argv[1], &pbyte) ) return TCL_ERROR; rc = sqlite3_test_control(SQLITE_TESTCTRL_PENDING_BYTE, pbyte); Tcl_SetObjResult(interp, Tcl_NewIntObj(rc)); return TCL_OK; } |
︙ | ︙ | |||
670 671 672 673 674 675 676 677 678 679 680 | (char*)&sqlite3_io_error_hit, TCL_LINK_INT); Tcl_LinkVar(interp, "sqlite_io_error_hardhit", (char*)&sqlite3_io_error_hardhit, TCL_LINK_INT); Tcl_LinkVar(interp, "sqlite_diskfull_pending", (char*)&sqlite3_diskfull_pending, TCL_LINK_INT); Tcl_LinkVar(interp, "sqlite_diskfull", (char*)&sqlite3_diskfull, TCL_LINK_INT); Tcl_LinkVar(interp, "sqlite_pending_byte", (char*)&sqlite3PendingByte, TCL_LINK_INT | TCL_LINK_READ_ONLY); return TCL_OK; } | > > | 671 672 673 674 675 676 677 678 679 680 681 682 683 | (char*)&sqlite3_io_error_hit, TCL_LINK_INT); Tcl_LinkVar(interp, "sqlite_io_error_hardhit", (char*)&sqlite3_io_error_hardhit, TCL_LINK_INT); Tcl_LinkVar(interp, "sqlite_diskfull_pending", (char*)&sqlite3_diskfull_pending, TCL_LINK_INT); Tcl_LinkVar(interp, "sqlite_diskfull", (char*)&sqlite3_diskfull, TCL_LINK_INT); #ifndef SQLITE_OMIT_WSD Tcl_LinkVar(interp, "sqlite_pending_byte", (char*)&sqlite3PendingByte, TCL_LINK_INT | TCL_LINK_READ_ONLY); #endif return TCL_OK; } |
Changes to src/test6.c.
︙ | ︙ | |||
167 168 169 170 171 172 173 | } /* ** Wrapper around the sqlite3OsWrite() function that avoids writing to the ** 512 byte block begining at offset PENDING_BYTE. */ static int writeDbFile(CrashFile *p, u8 *z, i64 iAmt, i64 iOff){ | | | 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 | } /* ** Wrapper around the sqlite3OsWrite() function that avoids writing to the ** 512 byte block begining at offset PENDING_BYTE. */ static int writeDbFile(CrashFile *p, u8 *z, i64 iAmt, i64 iOff){ int rc = SQLITE_OK; int iSkip = 0; if( iOff==PENDING_BYTE && (p->flags&SQLITE_OPEN_MAIN_DB) ){ iSkip = 512; } if( (iAmt-iSkip)>0 ){ rc = sqlite3OsWrite(p->pRealFile, &z[iSkip], iAmt-iSkip, iOff+iSkip); } |
︙ | ︙ | |||
515 516 517 518 519 520 521 522 523 | */ static int cfSectorSize(sqlite3_file *pFile){ return g.iSectorSize; } static int cfDeviceCharacteristics(sqlite3_file *pFile){ return g.iDeviceCharacteristics; } static const sqlite3_io_methods CrashFileVtab = { | > > > > > > > > > > > > > > > > > > > > > > | | > > > > | 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 | */ static int cfSectorSize(sqlite3_file *pFile){ return g.iSectorSize; } static int cfDeviceCharacteristics(sqlite3_file *pFile){ return g.iDeviceCharacteristics; } /* ** Pass-throughs for WAL support. */ static int cfShmLock(sqlite3_file *pFile, int ofst, int n, int flags){ return sqlite3OsShmLock(((CrashFile*)pFile)->pRealFile, ofst, n, flags); } static void cfShmBarrier(sqlite3_file *pFile){ sqlite3OsShmBarrier(((CrashFile*)pFile)->pRealFile); } static int cfShmUnmap(sqlite3_file *pFile, int delFlag){ return sqlite3OsShmUnmap(((CrashFile*)pFile)->pRealFile, delFlag); } static int cfShmMap( sqlite3_file *pFile, /* Handle open on database file */ int iRegion, /* Region to retrieve */ int sz, /* Size of regions */ int w, /* True to extend file if necessary */ void volatile **pp /* OUT: Mapped memory */ ){ return sqlite3OsShmMap(((CrashFile*)pFile)->pRealFile, iRegion, sz, w, pp); } static const sqlite3_io_methods CrashFileVtab = { 2, /* iVersion */ cfClose, /* xClose */ cfRead, /* xRead */ cfWrite, /* xWrite */ cfTruncate, /* xTruncate */ cfSync, /* xSync */ cfFileSize, /* xFileSize */ cfLock, /* xLock */ cfUnlock, /* xUnlock */ cfCheckReservedLock, /* xCheckReservedLock */ cfFileControl, /* xFileControl */ cfSectorSize, /* xSectorSize */ cfDeviceCharacteristics, /* xDeviceCharacteristics */ cfShmMap, /* xShmMap */ cfShmLock, /* xShmLock */ cfShmBarrier, /* xShmBarrier */ cfShmUnmap /* xShmUnmap */ }; /* ** Application data for the crash VFS */ struct crashAppData { sqlite3_vfs *pOrig; /* Wrapped vfs structure */ |
︙ | ︙ | |||
760 761 762 763 764 765 766 | void * clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[] ){ int isEnable; static sqlite3_vfs crashVfs = { | | | > > | 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 | void * clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[] ){ int isEnable; static sqlite3_vfs crashVfs = { 2, /* iVersion */ 0, /* szOsFile */ 0, /* mxPathname */ 0, /* pNext */ "crash", /* zName */ 0, /* pAppData */ cfOpen, /* xOpen */ cfDelete, /* xDelete */ cfAccess, /* xAccess */ cfFullPathname, /* xFullPathname */ cfDlOpen, /* xDlOpen */ cfDlError, /* xDlError */ cfDlSym, /* xDlSym */ cfDlClose, /* xDlClose */ cfRandomness, /* xRandomness */ cfSleep, /* xSleep */ cfCurrentTime, /* xCurrentTime */ 0, /* xGetlastError */ 0, /* xCurrentTimeInt64 */ }; if( objc!=2 ){ Tcl_WrongNumArgs(interp, 1, objv, "ENABLE"); return TCL_ERROR; } |
︙ | ︙ |
Changes to src/test_async.c.
︙ | ︙ | |||
80 81 82 83 84 85 86 87 88 89 90 91 92 93 | } static Tcl_ThreadCreateType tclWriterThread(ClientData pIsStarted){ Tcl_MutexLock(&testasync_g_writerMutex); *((int *)pIsStarted) = 1; sqlite3async_run(); Tcl_MutexUnlock(&testasync_g_writerMutex); TCL_THREAD_CREATE_RETURN; } /* ** sqlite3async_start ** ** Start a new writer thread. | > | 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 | } static Tcl_ThreadCreateType tclWriterThread(ClientData pIsStarted){ Tcl_MutexLock(&testasync_g_writerMutex); *((int *)pIsStarted) = 1; sqlite3async_run(); Tcl_MutexUnlock(&testasync_g_writerMutex); Tcl_ExitThread(0); TCL_THREAD_CREATE_RETURN; } /* ** sqlite3async_start ** ** Start a new writer thread. |
︙ | ︙ | |||
224 225 226 227 228 229 230 | /* ** This routine registers the custom TCL commands defined in this ** module. This should be the only procedure visible from outside ** of this module. */ int Sqlitetestasync_Init(Tcl_Interp *interp){ | | | 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 | /* ** This routine registers the custom TCL commands defined in this ** module. This should be the only procedure visible from outside ** of this module. */ int Sqlitetestasync_Init(Tcl_Interp *interp){ #ifdef SQLITE_ENABLE_ASYNCIO Tcl_CreateObjCommand(interp,"sqlite3async_start",testAsyncStart,0,0); Tcl_CreateObjCommand(interp,"sqlite3async_wait",testAsyncWait,0,0); Tcl_CreateObjCommand(interp,"sqlite3async_control",testAsyncControl,0,0); Tcl_CreateObjCommand(interp,"sqlite3async_initialize",testAsyncInit,0,0); Tcl_CreateObjCommand(interp,"sqlite3async_shutdown",testAsyncShutdown,0,0); #endif /* SQLITE_ENABLE_ASYNCIO */ return TCL_OK; } |
Changes to src/test_config.c.
︙ | ︙ | |||
122 123 124 125 126 127 128 129 130 131 132 133 134 135 | #endif #ifdef SQLITE_OMIT_AUTOINCREMENT Tcl_SetVar2(interp, "sqlite_options", "autoinc", "0", TCL_GLOBAL_ONLY); #else Tcl_SetVar2(interp, "sqlite_options", "autoinc", "1", TCL_GLOBAL_ONLY); #endif #ifdef SQLITE_OMIT_AUTOVACUUM Tcl_SetVar2(interp, "sqlite_options", "autovacuum", "0", TCL_GLOBAL_ONLY); #else Tcl_SetVar2(interp, "sqlite_options", "autovacuum", "1", TCL_GLOBAL_ONLY); #endif /* SQLITE_OMIT_AUTOVACUUM */ #if !defined(SQLITE_DEFAULT_AUTOVACUUM) | > > > > > > | 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 | #endif #ifdef SQLITE_OMIT_AUTOINCREMENT Tcl_SetVar2(interp, "sqlite_options", "autoinc", "0", TCL_GLOBAL_ONLY); #else Tcl_SetVar2(interp, "sqlite_options", "autoinc", "1", TCL_GLOBAL_ONLY); #endif #ifdef SQLITE_OMIT_AUTOMATIC_INDEX Tcl_SetVar2(interp, "sqlite_options", "autoindex", "0", TCL_GLOBAL_ONLY); #else Tcl_SetVar2(interp, "sqlite_options", "autoindex", "1", TCL_GLOBAL_ONLY); #endif #ifdef SQLITE_OMIT_AUTOVACUUM Tcl_SetVar2(interp, "sqlite_options", "autovacuum", "0", TCL_GLOBAL_ONLY); #else Tcl_SetVar2(interp, "sqlite_options", "autovacuum", "1", TCL_GLOBAL_ONLY); #endif /* SQLITE_OMIT_AUTOVACUUM */ #if !defined(SQLITE_DEFAULT_AUTOVACUUM) |
︙ | ︙ | |||
275 276 277 278 279 280 281 | #ifdef SQLITE_OMIT_GET_TABLE Tcl_SetVar2(interp, "sqlite_options", "gettable", "0", TCL_GLOBAL_ONLY); #else Tcl_SetVar2(interp, "sqlite_options", "gettable", "1", TCL_GLOBAL_ONLY); #endif | < < < < < < | 281 282 283 284 285 286 287 288 289 290 291 292 293 294 | #ifdef SQLITE_OMIT_GET_TABLE Tcl_SetVar2(interp, "sqlite_options", "gettable", "0", TCL_GLOBAL_ONLY); #else Tcl_SetVar2(interp, "sqlite_options", "gettable", "1", TCL_GLOBAL_ONLY); #endif #ifdef SQLITE_ENABLE_ICU Tcl_SetVar2(interp, "sqlite_options", "icu", "1", TCL_GLOBAL_ONLY); #else Tcl_SetVar2(interp, "sqlite_options", "icu", "0", TCL_GLOBAL_ONLY); #endif #ifdef SQLITE_OMIT_INCRBLOB |
︙ | ︙ | |||
459 460 461 462 463 464 465 | #ifdef SQLITE_OMIT_TRIGGER Tcl_SetVar2(interp, "sqlite_options", "trigger", "0", TCL_GLOBAL_ONLY); #else Tcl_SetVar2(interp, "sqlite_options", "trigger", "1", TCL_GLOBAL_ONLY); #endif | | | 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 | #ifdef SQLITE_OMIT_TRIGGER Tcl_SetVar2(interp, "sqlite_options", "trigger", "0", TCL_GLOBAL_ONLY); #else Tcl_SetVar2(interp, "sqlite_options", "trigger", "1", TCL_GLOBAL_ONLY); #endif #ifdef SQLITE_OMIT_TRUNCATE_OPTIMIZATION Tcl_SetVar2(interp, "sqlite_options", "truncate_opt", "0", TCL_GLOBAL_ONLY); #else Tcl_SetVar2(interp, "sqlite_options", "truncate_opt", "1", TCL_GLOBAL_ONLY); #endif #ifdef SQLITE_OMIT_UTF16 Tcl_SetVar2(interp, "sqlite_options", "utf16", "0", TCL_GLOBAL_ONLY); |
︙ | ︙ | |||
488 489 490 491 492 493 494 495 496 497 498 499 500 501 | #endif #ifdef SQLITE_OMIT_VIRTUALTABLE Tcl_SetVar2(interp, "sqlite_options", "vtab", "0", TCL_GLOBAL_ONLY); #else Tcl_SetVar2(interp, "sqlite_options", "vtab", "1", TCL_GLOBAL_ONLY); #endif #ifdef SQLITE_OMIT_WSD Tcl_SetVar2(interp, "sqlite_options", "wsd", "0", TCL_GLOBAL_ONLY); #else Tcl_SetVar2(interp, "sqlite_options", "wsd", "1", TCL_GLOBAL_ONLY); #endif | > > > > > > | 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 | #endif #ifdef SQLITE_OMIT_VIRTUALTABLE Tcl_SetVar2(interp, "sqlite_options", "vtab", "0", TCL_GLOBAL_ONLY); #else Tcl_SetVar2(interp, "sqlite_options", "vtab", "1", TCL_GLOBAL_ONLY); #endif #ifdef SQLITE_OMIT_WAL Tcl_SetVar2(interp, "sqlite_options", "wal", "0", TCL_GLOBAL_ONLY); #else Tcl_SetVar2(interp, "sqlite_options", "wal", "1", TCL_GLOBAL_ONLY); #endif #ifdef SQLITE_OMIT_WSD Tcl_SetVar2(interp, "sqlite_options", "wsd", "0", TCL_GLOBAL_ONLY); #else Tcl_SetVar2(interp, "sqlite_options", "wsd", "1", TCL_GLOBAL_ONLY); #endif |
︙ | ︙ |
Added src/test_demovfs.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 | /* ** 2010 April 7 ** ** The 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. ** ************************************************************************* ** ** An example of a simple VFS implementation that omits complex features ** often not required or not possible on embedded platforms. Also includes ** code to buffer writes to the journal file, which can be a significant ** performance improvement on some embedded platforms. ** */ /* ** OVERVIEW ** ** The code in this file implements a minimal SQLite VFS that can be ** used on Linux and other posix-like operating systems. The following ** system calls are used: ** ** File-system: access(), unlink(), getcwd() ** File IO: open(), read(), write(), fsync(), close(), fstat() ** Other: sleep(), usleep(), time() ** ** The following VFS features are omitted: ** ** 1. File locking. The user must ensure that there is at most one ** connection to each database when using this VFS. Multiple ** connections to a single shared-cache count as a single connection ** for the purposes of the previous statement. ** ** 2. The loading of dynamic extensions (shared libraries). ** ** 3. Temporary files. The user must configure SQLite to use in-memory ** temp files when using this VFS. The easiest way to do this is to ** compile with: ** ** -DSQLITE_TEMP_STORE=3 ** ** 4. File truncation. As of version 3.6.24, SQLite may run without ** a working xTruncate() call, providing the user does not configure ** SQLite to use "journal_mode=truncate", or use both ** "journal_mode=persist" and ATTACHed databases. ** ** It is assumed that the system uses UNIX-like path-names. Specifically, ** that '/' characters are used to separate path components and that ** a path-name is a relative path unless it begins with a '/'. And that ** no UTF-8 encoded paths are greater than 512 bytes in length. ** ** JOURNAL WRITE-BUFFERING ** ** To commit a transaction to the database, SQLite first writes rollback ** information into the journal file. This usually consists of 4 steps: ** ** 1. The rollback information is sequentially written into the journal ** file, starting at the start of the file. ** 2. The journal file is synced to disk. ** 3. A modification is made to the first few bytes of the journal file. ** 4. The journal file is synced to disk again. ** ** Most of the data is written in step 1 using a series of calls to the ** VFS xWrite() method. The buffers passed to the xWrite() calls are of ** various sizes. For example, as of version 3.6.24, when committing a ** transaction that modifies 3 pages of a database file that uses 4096 ** byte pages residing on a media with 512 byte sectors, SQLite makes ** eleven calls to the xWrite() method to create the rollback journal, ** as follows: ** ** Write offset | Bytes written ** ---------------------------- ** 0 512 ** 512 4 ** 516 4096 ** 4612 4 ** 4616 4 ** 4620 4096 ** 8716 4 ** 8720 4 ** 8724 4096 ** 12820 4 ** ++++++++++++SYNC+++++++++++ ** 0 12 ** ++++++++++++SYNC+++++++++++ ** ** On many operating systems, this is an efficient way to write to a file. ** However, on some embedded systems that do not cache writes in OS ** buffers it is much more efficient to write data in blocks that are ** an integer multiple of the sector-size in size and aligned at the ** start of a sector. ** ** To work around this, the code in this file allocates a fixed size ** buffer of SQLITE_DEMOVFS_BUFFERSZ using sqlite3_malloc() whenever a ** journal file is opened. It uses the buffer to coalesce sequential ** writes into aligned SQLITE_DEMOVFS_BUFFERSZ blocks. When SQLite ** invokes the xSync() method to sync the contents of the file to disk, ** all accumulated data is written out, even if it does not constitute ** a complete block. This means the actual IO to create the rollback ** journal for the example transaction above is this: ** ** Write offset | Bytes written ** ---------------------------- ** 0 8192 ** 8192 4632 ** ++++++++++++SYNC+++++++++++ ** 0 12 ** ++++++++++++SYNC+++++++++++ ** ** Much more efficient if the underlying OS is not caching write ** operations. */ #if !defined(SQLITE_TEST) || defined(SQLITE_OS_UNIX) #include <sqlite3.h> #include <assert.h> #include <string.h> #include <sys/types.h> #include <sys/stat.h> #include <sys/file.h> #include <sys/param.h> #include <unistd.h> #include <time.h> /* ** Size of the write buffer used by journal files in bytes. */ #ifndef SQLITE_DEMOVFS_BUFFERSZ # define SQLITE_DEMOVFS_BUFFERSZ 8192 #endif /* ** When using this VFS, the sqlite3_file* handles that SQLite uses are ** actually pointers to instances of type DemoFile. */ typedef struct DemoFile DemoFile; struct DemoFile { sqlite3_file base; /* Base class. Must be first. */ int fd; /* File descriptor */ char *aBuffer; /* Pointer to malloc'd buffer */ int nBuffer; /* Valid bytes of data in zBuffer */ sqlite3_int64 iBufferOfst; /* Offset in file of zBuffer[0] */ }; /* ** Write directly to the file passed as the first argument. Even if the ** file has a write-buffer (DemoFile.aBuffer), ignore it. */ static int demoDirectWrite( DemoFile *p, /* File handle */ const void *zBuf, /* Buffer containing data to write */ int iAmt, /* Size of data to write in bytes */ sqlite_int64 iOfst /* File offset to write to */ ){ off_t ofst; /* Return value from lseek() */ size_t nWrite; /* Return value from write() */ ofst = lseek(p->fd, iOfst, SEEK_SET); if( ofst!=iOfst ){ return SQLITE_IOERR_WRITE; } nWrite = write(p->fd, zBuf, iAmt); if( nWrite!=iAmt ){ return SQLITE_IOERR_WRITE; } return SQLITE_OK; } /* ** Flush the contents of the DemoFile.aBuffer buffer to disk. This is a ** no-op if this particular file does not have a buffer (i.e. it is not ** a journal file) or if the buffer is currently empty. */ static int demoFlushBuffer(DemoFile *p){ int rc = SQLITE_OK; if( p->nBuffer ){ rc = demoDirectWrite(p, p->aBuffer, p->nBuffer, p->iBufferOfst); p->nBuffer = 0; } return rc; } /* ** Close a file. */ static int demoClose(sqlite3_file *pFile){ int rc; DemoFile *p = (DemoFile*)pFile; rc = demoFlushBuffer(p); sqlite3_free(p->aBuffer); close(p->fd); return rc; } /* ** Read data from a file. */ static int demoRead( sqlite3_file *pFile, void *zBuf, int iAmt, sqlite_int64 iOfst ){ DemoFile *p = (DemoFile*)pFile; off_t ofst; /* Return value from lseek() */ int nRead; /* Return value from read() */ int rc; /* Return code from demoFlushBuffer() */ /* Flush any data in the write buffer to disk in case this operation ** is trying to read data the file-region currently cached in the buffer. ** It would be possible to detect this case and possibly save an ** unnecessary write here, but in practice SQLite will rarely read from ** a journal file when there is data cached in the write-buffer. */ rc = demoFlushBuffer(p); if( rc!=SQLITE_OK ){ return rc; } ofst = lseek(p->fd, iOfst, SEEK_SET); if( ofst!=iOfst ){ return SQLITE_IOERR_READ; } nRead = read(p->fd, zBuf, iAmt); if( nRead==iAmt ){ return SQLITE_OK; }else if( nRead>=0 ){ return SQLITE_IOERR_SHORT_READ; } return SQLITE_IOERR_READ; } /* ** Write data to a crash-file. */ static int demoWrite( sqlite3_file *pFile, const void *zBuf, int iAmt, sqlite_int64 iOfst ){ DemoFile *p = (DemoFile*)pFile; if( p->aBuffer ){ char *z = (char *)zBuf; /* Pointer to remaining data to write */ int n = iAmt; /* Number of bytes at z */ sqlite3_int64 i = iOfst; /* File offset to write to */ while( n>0 ){ int nCopy; /* Number of bytes to copy into buffer */ /* If the buffer is full, or if this data is not being written directly ** following the data already buffered, flush the buffer. Flushing ** the buffer is a no-op if it is empty. */ if( p->nBuffer==SQLITE_DEMOVFS_BUFFERSZ || p->iBufferOfst+p->nBuffer!=i ){ int rc = demoFlushBuffer(p); if( rc!=SQLITE_OK ){ return rc; } } assert( p->nBuffer==0 || p->iBufferOfst+p->nBuffer==i ); p->iBufferOfst = i - p->nBuffer; /* Copy as much data as possible into the buffer. */ nCopy = SQLITE_DEMOVFS_BUFFERSZ - p->nBuffer; if( nCopy>n ){ nCopy = n; } memcpy(&p->aBuffer[p->nBuffer], z, nCopy); p->nBuffer += nCopy; n -= nCopy; i += nCopy; z += nCopy; } }else{ return demoDirectWrite(p, zBuf, iAmt, iOfst); } return SQLITE_OK; } /* ** Truncate a file. This is a no-op for this VFS (see header comments at ** the top of the file). */ static int demoTruncate(sqlite3_file *pFile, sqlite_int64 size){ #if 0 if( ftruncate(((DemoFile *)pFile)->fd, size) ) return SQLITE_IOERR_TRUNCATE; #endif return SQLITE_OK; } /* ** Sync the contents of the file to the persistent media. */ static int demoSync(sqlite3_file *pFile, int flags){ DemoFile *p = (DemoFile*)pFile; int rc; rc = demoFlushBuffer(p); if( rc!=SQLITE_OK ){ return rc; } rc = fsync(p->fd); return (rc==0 ? SQLITE_OK : SQLITE_IOERR_FSYNC); } /* ** Write the size of the file in bytes to *pSize. */ static int demoFileSize(sqlite3_file *pFile, sqlite_int64 *pSize){ DemoFile *p = (DemoFile*)pFile; int rc; /* Return code from fstat() call */ struct stat sStat; /* Output of fstat() call */ /* Flush the contents of the buffer to disk. As with the flush in the ** demoRead() method, it would be possible to avoid this and save a write ** here and there. But in practice this comes up so infrequently it is ** not worth the trouble. */ rc = demoFlushBuffer(p); if( rc!=SQLITE_OK ){ return rc; } rc = fstat(p->fd, &sStat); if( rc!=0 ) return SQLITE_IOERR_FSTAT; *pSize = sStat.st_size; return SQLITE_OK; } /* ** Locking functions. The xLock() and xUnlock() methods are both no-ops. ** The xCheckReservedLock() always indicates that no other process holds ** a reserved lock on the database file. This ensures that if a hot-journal ** file is found in the file-system it is rolled back. */ static int demoLock(sqlite3_file *pFile, int eLock){ return SQLITE_OK; } static int demoUnlock(sqlite3_file *pFile, int eLock){ return SQLITE_OK; } static int demoCheckReservedLock(sqlite3_file *pFile, int *pResOut){ *pResOut = 0; return SQLITE_OK; } /* ** No xFileControl() verbs are implemented by this VFS. */ static int demoFileControl(sqlite3_file *pFile, int op, void *pArg){ return SQLITE_OK; } /* ** The xSectorSize() and xDeviceCharacteristics() methods. These two ** may return special values allowing SQLite to optimize file-system ** access to some extent. But it is also safe to simply return 0. */ static int demoSectorSize(sqlite3_file *pFile){ return 0; } static int demoDeviceCharacteristics(sqlite3_file *pFile){ return 0; } /* ** Open a file handle. */ static int demoOpen( sqlite3_vfs *pVfs, /* VFS */ const char *zName, /* File to open, or 0 for a temp file */ sqlite3_file *pFile, /* Pointer to DemoFile struct to populate */ int flags, /* Input SQLITE_OPEN_XXX flags */ int *pOutFlags /* Output SQLITE_OPEN_XXX flags (or NULL) */ ){ static const sqlite3_io_methods demoio = { 1, /* iVersion */ demoClose, /* xClose */ demoRead, /* xRead */ demoWrite, /* xWrite */ demoTruncate, /* xTruncate */ demoSync, /* xSync */ demoFileSize, /* xFileSize */ demoLock, /* xLock */ demoUnlock, /* xUnlock */ demoCheckReservedLock, /* xCheckReservedLock */ demoFileControl, /* xFileControl */ demoSectorSize, /* xSectorSize */ demoDeviceCharacteristics /* xDeviceCharacteristics */ }; DemoFile *p = (DemoFile*)pFile; /* Populate this structure */ int oflags = 0; /* flags to pass to open() call */ char *aBuf = 0; if( zName==0 ){ return SQLITE_IOERR; } if( flags&SQLITE_OPEN_MAIN_JOURNAL ){ aBuf = (char *)sqlite3_malloc(SQLITE_DEMOVFS_BUFFERSZ); if( !aBuf ){ return SQLITE_NOMEM; } } if( flags&SQLITE_OPEN_EXCLUSIVE ) oflags |= O_EXCL; if( flags&SQLITE_OPEN_CREATE ) oflags |= O_CREAT; if( flags&SQLITE_OPEN_READONLY ) oflags |= O_RDONLY; if( flags&SQLITE_OPEN_READWRITE ) oflags |= O_RDWR; memset(p, 0, sizeof(DemoFile)); p->fd = open(zName, oflags, 0600); if( p->fd<0 ){ sqlite3_free(aBuf); return SQLITE_CANTOPEN; } p->aBuffer = aBuf; if( pOutFlags ){ *pOutFlags = flags; } p->base.pMethods = &demoio; return SQLITE_OK; } /* ** Delete the file identified by argument zPath. If the dirSync parameter ** is non-zero, then ensure the file-system modification to delete the ** file has been synced to disk before returning. */ static int demoDelete(sqlite3_vfs *pVfs, const char *zPath, int dirSync){ int rc; rc = unlink(zPath); if( rc==0 && dirSync ){ int dfd; /* File descriptor open on directory */ int i; /* Iterator variable */ char zDir[pVfs->mxPathname+1];/* Name of directory containing file zPath */ /* Figure out the directory name from the path of the file deleted. */ sqlite3_snprintf(pVfs->mxPathname, zDir, "%s", zPath); zDir[pVfs->mxPathname] = '\0'; for(i=strlen(zDir); i>1 && zDir[i]!='/'; i++); zDir[i] = '\0'; /* Open a file-descriptor on the directory. Sync. Close. */ dfd = open(zDir, O_RDONLY, 0); if( dfd<0 ){ rc = -1; }else{ rc = fsync(dfd); close(dfd); } } return (rc==0 ? SQLITE_OK : SQLITE_IOERR_DELETE); } #ifndef F_OK # define F_OK 0 #endif #ifndef R_OK # define R_OK 4 #endif #ifndef W_OK # define W_OK 2 #endif /* ** Query the file-system to see if the named file exists, is readable or ** is both readable and writable. */ static int demoAccess( sqlite3_vfs *pVfs, const char *zPath, int flags, int *pResOut ){ int rc; /* access() return code */ int eAccess = F_OK; /* Second argument to access() */ assert( flags==SQLITE_ACCESS_EXISTS /* access(zPath, F_OK) */ || flags==SQLITE_ACCESS_READ /* access(zPath, R_OK) */ || flags==SQLITE_ACCESS_READWRITE /* access(zPath, R_OK|W_OK) */ ); if( flags==SQLITE_ACCESS_READWRITE ) eAccess = R_OK|W_OK; if( flags==SQLITE_ACCESS_READ ) eAccess = R_OK; rc = access(zPath, eAccess); *pResOut = (rc==0); return SQLITE_OK; } /* ** Argument zPath points to a nul-terminated string containing a file path. ** If zPath is an absolute path, then it is copied as is into the output ** buffer. Otherwise, if it is a relative path, then the equivalent full ** path is written to the output buffer. ** ** This function assumes that paths are UNIX style. Specifically, that: ** ** 1. Path components are separated by a '/'. and ** 2. Full paths begin with a '/' character. */ static int demoFullPathname( sqlite3_vfs *pVfs, /* VFS */ const char *zPath, /* Input path (possibly a relative path) */ int nPathOut, /* Size of output buffer in bytes */ char *zPathOut /* Pointer to output buffer */ ){ char zDir[pVfs->mxPathname+1]; if( zPath[0]=='/' ){ zDir[0] = '\0'; }else{ getcwd(zDir, sizeof(zDir)); } zDir[pVfs->mxPathname] = '\0'; sqlite3_snprintf(nPathOut, zPathOut, "%s/%s", zDir, zPath); zPathOut[nPathOut-1] = '\0'; return SQLITE_OK; } /* ** The following four VFS methods: ** ** xDlOpen ** xDlError ** xDlSym ** xDlClose ** ** are supposed to implement the functionality needed by SQLite to load ** extensions compiled as shared objects. This simple VFS does not support ** this functionality, so the following functions are no-ops. */ static void *demoDlOpen(sqlite3_vfs *pVfs, const char *zPath){ return 0; } static void demoDlError(sqlite3_vfs *pVfs, int nByte, char *zErrMsg){ sqlite3_snprintf(nByte, zErrMsg, "Loadable extensions are not supported"); zErrMsg[nByte-1] = '\0'; } static void (*demoDlSym(sqlite3_vfs *pVfs, void *pH, const char *z))(void){ return 0; } static void demoDlClose(sqlite3_vfs *pVfs, void *pHandle){ return; } /* ** Parameter zByte points to a buffer nByte bytes in size. Populate this ** buffer with pseudo-random data. */ static int demoRandomness(sqlite3_vfs *pVfs, int nByte, char *zByte){ return SQLITE_OK; } /* ** Sleep for at least nMicro microseconds. Return the (approximate) number ** of microseconds slept for. */ static int demoSleep(sqlite3_vfs *pVfs, int nMicro){ sleep(nMicro / 1000000); usleep(nMicro % 1000000); return nMicro; } /* ** Set *pTime to the current UTC time expressed as a Julian day. Return ** SQLITE_OK if successful, or an error code otherwise. ** ** http://en.wikipedia.org/wiki/Julian_day ** ** This implementation is not very good. The current time is rounded to ** an integer number of seconds. Also, assuming time_t is a signed 32-bit ** value, it will stop working some time in the year 2038 AD (the so-called ** "year 2038" problem that afflicts systems that store time this way). */ static int demoCurrentTime(sqlite3_vfs *pVfs, double *pTime){ time_t t = time(0); *pTime = t/86400.0 + 2440587.5; return SQLITE_OK; } /* ** This function returns a pointer to the VFS implemented in this file. ** To make the VFS available to SQLite: ** ** sqlite3_vfs_register(sqlite3_demovfs(), 0); */ sqlite3_vfs *sqlite3_demovfs(void){ static sqlite3_vfs demovfs = { 1, /* iVersion */ sizeof(DemoFile), /* szOsFile */ 512, /* mxPathname */ 0, /* pNext */ "demo", /* zName */ 0, /* pAppData */ demoOpen, /* xOpen */ demoDelete, /* xDelete */ demoAccess, /* xAccess */ demoFullPathname, /* xFullPathname */ demoDlOpen, /* xDlOpen */ demoDlError, /* xDlError */ demoDlSym, /* xDlSym */ demoDlClose, /* xDlClose */ demoRandomness, /* xRandomness */ demoSleep, /* xSleep */ demoCurrentTime, /* xCurrentTime */ }; return &demovfs; } #endif /* !defined(SQLITE_TEST) || defined(SQLITE_OS_UNIX) */ #ifdef SQLITE_TEST #include <tcl.h> #ifdef SQLITE_OS_UNIX static int register_demovfs( ClientData clientData, /* Pointer to sqlite3_enable_XXX function */ Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ int objc, /* Number of arguments */ Tcl_Obj *CONST objv[] /* Command arguments */ ){ sqlite3_vfs_register(sqlite3_demovfs(), 1); return TCL_OK; } static int unregister_demovfs( ClientData clientData, /* Pointer to sqlite3_enable_XXX function */ Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ int objc, /* Number of arguments */ Tcl_Obj *CONST objv[] /* Command arguments */ ){ sqlite3_vfs_unregister(sqlite3_demovfs()); return TCL_OK; } /* ** Register commands with the TCL interpreter. */ int Sqlitetest_demovfs_Init(Tcl_Interp *interp){ Tcl_CreateObjCommand(interp, "register_demovfs", register_demovfs, 0, 0); Tcl_CreateObjCommand(interp, "unregister_demovfs", unregister_demovfs, 0, 0); return TCL_OK; } #else int Sqlitetest_demovfs_Init(Tcl_Interp *interp){ return TCL_OK; } #endif #endif /* SQLITE_TEST */ |
Changes to src/test_devsym.c.
︙ | ︙ | |||
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 | static int devsymFileSize(sqlite3_file*, sqlite3_int64 *pSize); static int devsymLock(sqlite3_file*, int); static int devsymUnlock(sqlite3_file*, int); static int devsymCheckReservedLock(sqlite3_file*, int *); static int devsymFileControl(sqlite3_file*, int op, void *pArg); static int devsymSectorSize(sqlite3_file*); static int devsymDeviceCharacteristics(sqlite3_file*); /* ** Method declarations for devsym_vfs. */ static int devsymOpen(sqlite3_vfs*, const char *, sqlite3_file*, int , int *); static int devsymDelete(sqlite3_vfs*, const char *zName, int syncDir); static int devsymAccess(sqlite3_vfs*, const char *zName, int flags, int *); static int devsymFullPathname(sqlite3_vfs*, const char *zName, int, char *zOut); #ifndef SQLITE_OMIT_LOAD_EXTENSION static void *devsymDlOpen(sqlite3_vfs*, const char *zFilename); static void devsymDlError(sqlite3_vfs*, int nByte, char *zErrMsg); static void (*devsymDlSym(sqlite3_vfs*,void*, const char *zSymbol))(void); static void devsymDlClose(sqlite3_vfs*, void*); #endif /* SQLITE_OMIT_LOAD_EXTENSION */ static int devsymRandomness(sqlite3_vfs*, int nByte, char *zOut); static int devsymSleep(sqlite3_vfs*, int microseconds); static int devsymCurrentTime(sqlite3_vfs*, double*); static sqlite3_vfs devsym_vfs = { | > > > > | | 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 | static int devsymFileSize(sqlite3_file*, sqlite3_int64 *pSize); static int devsymLock(sqlite3_file*, int); static int devsymUnlock(sqlite3_file*, int); static int devsymCheckReservedLock(sqlite3_file*, int *); static int devsymFileControl(sqlite3_file*, int op, void *pArg); static int devsymSectorSize(sqlite3_file*); static int devsymDeviceCharacteristics(sqlite3_file*); static int devsymShmLock(sqlite3_file*,int,int,int); static int devsymShmMap(sqlite3_file*,int,int,int, void volatile **); static void devsymShmBarrier(sqlite3_file*); static int devsymShmUnmap(sqlite3_file*,int); /* ** Method declarations for devsym_vfs. */ static int devsymOpen(sqlite3_vfs*, const char *, sqlite3_file*, int , int *); static int devsymDelete(sqlite3_vfs*, const char *zName, int syncDir); static int devsymAccess(sqlite3_vfs*, const char *zName, int flags, int *); static int devsymFullPathname(sqlite3_vfs*, const char *zName, int, char *zOut); #ifndef SQLITE_OMIT_LOAD_EXTENSION static void *devsymDlOpen(sqlite3_vfs*, const char *zFilename); static void devsymDlError(sqlite3_vfs*, int nByte, char *zErrMsg); static void (*devsymDlSym(sqlite3_vfs*,void*, const char *zSymbol))(void); static void devsymDlClose(sqlite3_vfs*, void*); #endif /* SQLITE_OMIT_LOAD_EXTENSION */ static int devsymRandomness(sqlite3_vfs*, int nByte, char *zOut); static int devsymSleep(sqlite3_vfs*, int microseconds); static int devsymCurrentTime(sqlite3_vfs*, double*); static sqlite3_vfs devsym_vfs = { 2, /* iVersion */ sizeof(devsym_file), /* szOsFile */ DEVSYM_MAX_PATHNAME, /* mxPathname */ 0, /* pNext */ DEVSYM_VFS_NAME, /* zName */ 0, /* pAppData */ devsymOpen, /* xOpen */ devsymDelete, /* xDelete */ |
︙ | ︙ | |||
88 89 90 91 92 93 94 | 0, /* xDlOpen */ 0, /* xDlError */ 0, /* xDlSym */ 0, /* xDlClose */ #endif /* SQLITE_OMIT_LOAD_EXTENSION */ devsymRandomness, /* xRandomness */ devsymSleep, /* xSleep */ | | > > | | > > > > | 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 | 0, /* xDlOpen */ 0, /* xDlError */ 0, /* xDlSym */ 0, /* xDlClose */ #endif /* SQLITE_OMIT_LOAD_EXTENSION */ devsymRandomness, /* xRandomness */ devsymSleep, /* xSleep */ devsymCurrentTime, /* xCurrentTime */ 0, /* xGetLastError */ 0 /* xCurrentTimeInt64 */ }; static sqlite3_io_methods devsym_io_methods = { 2, /* iVersion */ devsymClose, /* xClose */ devsymRead, /* xRead */ devsymWrite, /* xWrite */ devsymTruncate, /* xTruncate */ devsymSync, /* xSync */ devsymFileSize, /* xFileSize */ devsymLock, /* xLock */ devsymUnlock, /* xUnlock */ devsymCheckReservedLock, /* xCheckReservedLock */ devsymFileControl, /* xFileControl */ devsymSectorSize, /* xSectorSize */ devsymDeviceCharacteristics, /* xDeviceCharacteristics */ devsymShmMap, /* xShmMap */ devsymShmLock, /* xShmLock */ devsymShmBarrier, /* xShmBarrier */ devsymShmUnmap /* xShmUnmap */ }; struct DevsymGlobal { sqlite3_vfs *pVfs; int iDeviceChar; int iSectorSize; }; |
︙ | ︙ | |||
217 218 219 220 221 222 223 224 225 226 227 228 229 230 | /* ** Return the device characteristic flags supported by an devsym-file. */ static int devsymDeviceCharacteristics(sqlite3_file *pFile){ return g.iDeviceChar; } /* ** Open an devsym file handle. */ static int devsymOpen( sqlite3_vfs *pVfs, const char *zName, | > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | /* ** Return the device characteristic flags supported by an devsym-file. */ static int devsymDeviceCharacteristics(sqlite3_file *pFile){ return g.iDeviceChar; } /* ** Shared-memory methods are all pass-thrus. */ static int devsymShmLock(sqlite3_file *pFile, int ofst, int n, int flags){ devsym_file *p = (devsym_file *)pFile; return sqlite3OsShmLock(p->pReal, ofst, n, flags); } static int devsymShmMap( sqlite3_file *pFile, int iRegion, int szRegion, int isWrite, void volatile **pp ){ devsym_file *p = (devsym_file *)pFile; return sqlite3OsShmMap(p->pReal, iRegion, szRegion, isWrite, pp); } static void devsymShmBarrier(sqlite3_file *pFile){ devsym_file *p = (devsym_file *)pFile; sqlite3OsShmBarrier(p->pReal); } static int devsymShmUnmap(sqlite3_file *pFile, int delFlag){ devsym_file *p = (devsym_file *)pFile; return sqlite3OsShmUnmap(p->pReal, delFlag); } /* ** Open an devsym file handle. */ static int devsymOpen( sqlite3_vfs *pVfs, const char *zName, |
︙ | ︙ | |||
326 327 328 329 330 331 332 | return sqlite3OsSleep(g.pVfs, nMicro); } /* ** Return the current time as a Julian Day number in *pTimeOut. */ static int devsymCurrentTime(sqlite3_vfs *pVfs, double *pTimeOut){ | | > > > > > | 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 | return sqlite3OsSleep(g.pVfs, nMicro); } /* ** Return the current time as a Julian Day number in *pTimeOut. */ static int devsymCurrentTime(sqlite3_vfs *pVfs, double *pTimeOut){ return g.pVfs->xCurrentTime(g.pVfs, pTimeOut); } /* ** This procedure registers the devsym vfs with SQLite. If the argument is ** true, the devsym vfs becomes the new default vfs. It is the only publicly ** available function in this file. */ void devsym_register(int iDeviceChar, int iSectorSize){ if( g.pVfs==0 ){ g.pVfs = sqlite3_vfs_find(0); devsym_vfs.szOsFile += g.pVfs->szOsFile; sqlite3_vfs_register(&devsym_vfs, 0); } if( iDeviceChar>=0 ){ g.iDeviceChar = iDeviceChar; }else{ g.iDeviceChar = 0; } if( iSectorSize>=0 ){ g.iSectorSize = iSectorSize; }else{ g.iSectorSize = 512; } } #endif |
Changes to src/test_journal.c.
︙ | ︙ | |||
157 158 159 160 161 162 163 164 165 | static void *jtDlOpen(sqlite3_vfs*, const char *zFilename); static void jtDlError(sqlite3_vfs*, int nByte, char *zErrMsg); static void (*jtDlSym(sqlite3_vfs*,void*, const char *zSymbol))(void); static void jtDlClose(sqlite3_vfs*, void*); static int jtRandomness(sqlite3_vfs*, int nByte, char *zOut); static int jtSleep(sqlite3_vfs*, int microseconds); static int jtCurrentTime(sqlite3_vfs*, double*); static sqlite3_vfs jt_vfs = { | > | | > > | 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 | static void *jtDlOpen(sqlite3_vfs*, const char *zFilename); static void jtDlError(sqlite3_vfs*, int nByte, char *zErrMsg); static void (*jtDlSym(sqlite3_vfs*,void*, const char *zSymbol))(void); static void jtDlClose(sqlite3_vfs*, void*); static int jtRandomness(sqlite3_vfs*, int nByte, char *zOut); static int jtSleep(sqlite3_vfs*, int microseconds); static int jtCurrentTime(sqlite3_vfs*, double*); static int jtCurrentTimeInt64(sqlite3_vfs*, sqlite3_int64*); static sqlite3_vfs jt_vfs = { 2, /* iVersion */ sizeof(jt_file), /* szOsFile */ JT_MAX_PATHNAME, /* mxPathname */ 0, /* pNext */ JT_VFS_NAME, /* zName */ 0, /* pAppData */ jtOpen, /* xOpen */ jtDelete, /* xDelete */ jtAccess, /* xAccess */ jtFullPathname, /* xFullPathname */ jtDlOpen, /* xDlOpen */ jtDlError, /* xDlError */ jtDlSym, /* xDlSym */ jtDlClose, /* xDlClose */ jtRandomness, /* xRandomness */ jtSleep, /* xSleep */ jtCurrentTime, /* xCurrentTime */ 0, /* xGetLastError */ jtCurrentTimeInt64 /* xCurrentTimeInt64 */ }; static sqlite3_io_methods jt_io_methods = { 1, /* iVersion */ jtClose, /* xClose */ jtRead, /* xRead */ jtWrite, /* xWrite */ |
︙ | ︙ | |||
797 798 799 800 801 802 803 | return sqlite3OsSleep(g.pVfs, nMicro); } /* ** Return the current time as a Julian Day number in *pTimeOut. */ static int jtCurrentTime(sqlite3_vfs *pVfs, double *pTimeOut){ | | > > > > > > > > > > > | 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 | return sqlite3OsSleep(g.pVfs, nMicro); } /* ** Return the current time as a Julian Day number in *pTimeOut. */ static int jtCurrentTime(sqlite3_vfs *pVfs, double *pTimeOut){ return g.pVfs->xCurrentTime(g.pVfs, pTimeOut); } /* ** Return the current time as a Julian Day number in *pTimeOut. */ static int jtCurrentTimeInt64(sqlite3_vfs *pVfs, sqlite3_int64 *pTimeOut){ return g.pVfs->xCurrentTimeInt64(g.pVfs, pTimeOut); } /************************************************************************** ** Start of public API. */ /* ** Configure the jt VFS as a wrapper around the VFS named by parameter ** zWrap. If the isDefault parameter is true, then the jt VFS is installed ** as the new default VFS for SQLite connections. If isDefault is not ** true, then the jt VFS is installed as non-default. In this case it ** is available via its name, "jt". */ int jt_register(char *zWrap, int isDefault){ g.pVfs = sqlite3_vfs_find(zWrap); if( g.pVfs==0 ){ return SQLITE_ERROR; } jt_vfs.szOsFile = sizeof(jt_file) + g.pVfs->szOsFile; if( g.pVfs->iVersion==1 ){ jt_vfs.iVersion = 1; }else if( g.pVfs->xCurrentTimeInt64==0 ){ jt_vfs.xCurrentTimeInt64 = 0; } sqlite3_vfs_register(&jt_vfs, isDefault); return SQLITE_OK; } /* ** Uninstall the jt VFS, if it is installed. */ void jt_unregister(void){ sqlite3_vfs_unregister(&jt_vfs); } #endif |
Changes to src/test_malloc.c.
︙ | ︙ | |||
1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 | sqlite3 *db; int getDbPointer(Tcl_Interp*, const char*, sqlite3**); static const struct { const char *zName; int op; } aOp[] = { { "SQLITE_DBSTATUS_LOOKASIDE_USED", SQLITE_DBSTATUS_LOOKASIDE_USED }, }; Tcl_Obj *pResult; if( objc!=4 ){ Tcl_WrongNumArgs(interp, 1, objv, "PARAMETER RESETFLAG"); return TCL_ERROR; } if( getDbPointer(interp, Tcl_GetString(objv[1]), &db) ) return TCL_ERROR; | > | 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 | sqlite3 *db; int getDbPointer(Tcl_Interp*, const char*, sqlite3**); static const struct { const char *zName; int op; } aOp[] = { { "SQLITE_DBSTATUS_LOOKASIDE_USED", SQLITE_DBSTATUS_LOOKASIDE_USED }, { "SQLITE_DBSTATUS_CACHE_USED", SQLITE_DBSTATUS_CACHE_USED }, }; Tcl_Obj *pResult; if( objc!=4 ){ Tcl_WrongNumArgs(interp, 1, objv, "PARAMETER RESETFLAG"); return TCL_ERROR; } if( getDbPointer(interp, Tcl_GetString(objv[1]), &db) ) return TCL_ERROR; |
︙ | ︙ | |||
1353 1354 1355 1356 1357 1358 1359 1360 1361 1362 1363 1364 1365 1366 | #ifdef SQLITE_ENABLE_MEMSYS3 const sqlite3_mem_methods *sqlite3MemGetMemsys3(void); rc = sqlite3_config(SQLITE_CONFIG_MALLOC, sqlite3MemGetMemsys3()); #endif Tcl_SetResult(interp, (char *)sqlite3TestErrorName(rc), TCL_VOLATILE); return TCL_OK; } /* ** Register commands with the TCL interpreter. */ int Sqlitetest_malloc_Init(Tcl_Interp *interp){ static struct { char *zName; | > > > > > > > > > > > > > > > > > > > | 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 | #ifdef SQLITE_ENABLE_MEMSYS3 const sqlite3_mem_methods *sqlite3MemGetMemsys3(void); rc = sqlite3_config(SQLITE_CONFIG_MALLOC, sqlite3MemGetMemsys3()); #endif Tcl_SetResult(interp, (char *)sqlite3TestErrorName(rc), TCL_VOLATILE); return TCL_OK; } static int test_vfs_oom_test( void * clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[] ){ extern int sqlite3_memdebug_vfs_oom_test; if( objc>2 ){ Tcl_WrongNumArgs(interp, 1, objv, "?INTEGER?"); return TCL_ERROR; }else if( objc==2 ){ int iNew; if( Tcl_GetIntFromObj(interp, objv[1], &iNew) ) return TCL_ERROR; sqlite3_memdebug_vfs_oom_test = iNew; } Tcl_SetObjResult(interp, Tcl_NewIntObj(sqlite3_memdebug_vfs_oom_test)); return TCL_OK; } /* ** Register commands with the TCL interpreter. */ int Sqlitetest_malloc_Init(Tcl_Interp *interp){ static struct { char *zName; |
︙ | ︙ | |||
1391 1392 1393 1394 1395 1396 1397 1398 1399 1400 1401 1402 1403 1404 1405 1406 | { "sqlite3_config_memstatus", test_config_memstatus ,0 }, { "sqlite3_config_lookaside", test_config_lookaside ,0 }, { "sqlite3_config_error", test_config_error ,0 }, { "sqlite3_db_config_lookaside",test_db_config_lookaside ,0 }, { "sqlite3_dump_memsys3", test_dump_memsys3 ,3 }, { "sqlite3_dump_memsys5", test_dump_memsys3 ,5 }, { "sqlite3_install_memsys3", test_install_memsys3 ,0 }, }; int i; for(i=0; i<sizeof(aObjCmd)/sizeof(aObjCmd[0]); i++){ ClientData c = (ClientData)aObjCmd[i].clientData; Tcl_CreateObjCommand(interp, aObjCmd[i].zName, aObjCmd[i].xProc, c, 0); } return TCL_OK; } #endif | > | 1411 1412 1413 1414 1415 1416 1417 1418 1419 1420 1421 1422 1423 1424 1425 1426 1427 | { "sqlite3_config_memstatus", test_config_memstatus ,0 }, { "sqlite3_config_lookaside", test_config_lookaside ,0 }, { "sqlite3_config_error", test_config_error ,0 }, { "sqlite3_db_config_lookaside",test_db_config_lookaside ,0 }, { "sqlite3_dump_memsys3", test_dump_memsys3 ,3 }, { "sqlite3_dump_memsys5", test_dump_memsys3 ,5 }, { "sqlite3_install_memsys3", test_install_memsys3 ,0 }, { "sqlite3_memdebug_vfs_oom_test", test_vfs_oom_test ,0 }, }; int i; for(i=0; i<sizeof(aObjCmd)/sizeof(aObjCmd[0]); i++){ ClientData c = (ClientData)aObjCmd[i].clientData; Tcl_CreateObjCommand(interp, aObjCmd[i].zName, aObjCmd[i].xProc, c, 0); } return TCL_OK; } #endif |
Changes to src/test_onefile.c.
︙ | ︙ | |||
194 195 196 197 198 199 200 | fsFullPathname, /* xFullPathname */ fsDlOpen, /* xDlOpen */ fsDlError, /* xDlError */ fsDlSym, /* xDlSym */ fsDlClose, /* xDlClose */ fsRandomness, /* xRandomness */ fsSleep, /* xSleep */ | | > | > > > > | > > > > | 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 | fsFullPathname, /* xFullPathname */ fsDlOpen, /* xDlOpen */ fsDlError, /* xDlError */ fsDlSym, /* xDlSym */ fsDlClose, /* xDlClose */ fsRandomness, /* xRandomness */ fsSleep, /* xSleep */ fsCurrentTime, /* xCurrentTime */ 0 /* xCurrentTimeInt64 */ }, 0, /* pFileList */ 0 /* pParent */ }; static sqlite3_io_methods fs_io_methods = { 1, /* iVersion */ fsClose, /* xClose */ fsRead, /* xRead */ fsWrite, /* xWrite */ fsTruncate, /* xTruncate */ fsSync, /* xSync */ fsFileSize, /* xFileSize */ fsLock, /* xLock */ fsUnlock, /* xUnlock */ fsCheckReservedLock, /* xCheckReservedLock */ fsFileControl, /* xFileControl */ fsSectorSize, /* xSectorSize */ fsDeviceCharacteristics, /* xDeviceCharacteristics */ 0, /* xShmMap */ 0, /* xShmLock */ 0, /* xShmBarrier */ 0 /* xShmUnmap */ }; static sqlite3_io_methods tmp_io_methods = { 1, /* iVersion */ tmpClose, /* xClose */ tmpRead, /* xRead */ tmpWrite, /* xWrite */ tmpTruncate, /* xTruncate */ tmpSync, /* xSync */ tmpFileSize, /* xFileSize */ tmpLock, /* xLock */ tmpUnlock, /* xUnlock */ tmpCheckReservedLock, /* xCheckReservedLock */ tmpFileControl, /* xFileControl */ tmpSectorSize, /* xSectorSize */ tmpDeviceCharacteristics, /* xDeviceCharacteristics */ 0, /* xShmMap */ 0, /* xShmLock */ 0, /* xShmBarrier */ 0 /* xShmUnmap */ }; /* Useful macros used in several places */ #define MIN(x,y) ((x)<(y)?(x):(y)) #define MAX(x,y) ((x)>(y)?(x):(y)) |
︙ | ︙ |
Changes to src/test_osinst.c.
︙ | ︙ | |||
11 12 13 14 15 16 17 | ****************************************************************************** ** ** This file contains the implementation of an SQLite vfs wrapper that ** adds instrumentation to all vfs and file methods. C and Tcl interfaces ** are provided to control the instrumentation. */ | < > | > > | > | < > | < < | < | < | < | < < < | < | | < < < | < < < | | | < < < < > | < < | | > < < | | < < | > | | | > > | > > | > | < < < > > > > > | | < < < < > > | | | | < < < | < | < | < > < < < < | | | | < | < > > > > | | | | | | | | | | | | | > > > > > | | | | | | | | | | | | | > | | | | | | | | | | | | | | | | | | > > | | | | | | | | | | | | | | > > > > < > | > | < > > > > > | > > > | < | | < | < | | | > | < > | | < | > | < < | < < < < < < < < | < | | > > > | > > | < | > > > | > | | > > | < > | < > > > | | > > | | < | < > > > | | | > > > | < > > > | | > > > > | > > > > > > > > > > > > > > > > | > | | | > > > | < > > > | | > > > > | > > > > > > > > > > > > > > > > | > | | > | < > | < > > > > | < > > > < > > > > > > > > > > > > | > > > > | > > > > > > > > > > > > > > > > > > | < < < | > > > | > > > > > > > > > > > | < < < < < | | | > > > > | | < | < | | < | > | > > > | > > | < | > > | > | > > | < | > > | > | < | < | | | | | < | < | < | < | < | < < < < < < < < < < < < < < < < < < < < < < < < < < < < | < < < < < < < < < < < < < < < < < < < < < < < | < < < < < < < < < < < < < < | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | < < < < < < < < < < < < < | < < < < | < < < < < < < < < < < < < < < < < < < < > | | | > < < < < < < < < < < < < | < < < < | < < < | | < | < < < < < < < < < | < < < < < < | | > | > | > < | > > > | < > > > > | > > > | | < > > | | > > | > > | < > | | | | > > | > | > | | > | < < < < | > > | > > > < | < < < | > > < < | < < < < < < < < < < < < < < < < < < < < < < < < > > | > | | | > | > > | > | > | > | | | > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > > | > > > > > > > > > > | > > > > > > > | > > > > > > > > > > > > > > > > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | < < | < < < < < < < < < < < < < < < < < < < < < | < < < < < < < < < < < < < < < < < < < | | > > > > > | < | > | | | > | | < | < < < | < | < < < < < < < < < < < < < < < < < < < < < < | | < | < | > | < < < | | < | > > | | < < | < | | | < < < < | < < < < < < | < < < | < < | < < | < < | | | | | > > > > | | < < < < < < < < < < < < | < < | | | | | < | > | < | < < < < < < < < | | | | < < < < < < < < < | < | | > < > < < < < < < < < < < < < < < < < < < | | 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 | ****************************************************************************** ** ** This file contains the implementation of an SQLite vfs wrapper that ** adds instrumentation to all vfs and file methods. C and Tcl interfaces ** are provided to control the instrumentation. */ /* ** This module contains code for a wrapper VFS that causes a log of ** most VFS calls to be written into a nominated file on disk. The log ** is stored in a compressed binary format to reduce the amount of IO ** overhead introduced into the application by logging. ** ** All calls on sqlite3_file objects except xFileControl() are logged. ** Additionally, calls to the xAccess(), xOpen(), and xDelete() ** methods are logged. The other sqlite3_vfs object methods (xDlXXX, ** xRandomness, xSleep, xCurrentTime, xGetLastError and xCurrentTimeInt64) ** are not logged. ** ** The binary log files are read using a virtual table implementation ** also contained in this file. ** ** CREATING LOG FILES: ** ** int sqlite3_vfslog_new( ** const char *zVfs, // Name of new VFS ** const char *zParentVfs, // Name of parent VFS (or NULL) ** const char *zLog // Name of log file to write to ** ); ** ** int sqlite3_vfslog_finalize(const char *zVfs); ** ** ANNOTATING LOG FILES: ** ** To write an arbitrary message into a log file: ** ** int sqlite3_vfslog_annotate(const char *zVfs, const char *zMsg); ** ** READING LOG FILES: ** ** Log files are read using the "vfslog" virtual table implementation ** in this file. To register the virtual table with SQLite, use: ** ** int sqlite3_vfslog_register(sqlite3 *db); ** ** Then, if the log file is named "vfs.log", the following SQL command: ** ** CREATE VIRTUAL TABLE v USING vfslog('vfs.log'); ** ** creates a virtual table with 6 columns, as follows: ** ** CREATE TABLE v( ** event TEXT, // "xOpen", "xRead" etc. ** file TEXT, // Name of file this call applies to ** clicks INTEGER, // Time spent in call ** rc INTEGER, // Return value ** size INTEGER, // Bytes read or written ** offset INTEGER // File offset read or written ** ); */ #include "sqlite3.h" #include <string.h> #include <assert.h> /* ** Maximum pathname length supported by the vfslog backend. */ #define INST_MAX_PATHNAME 512 #define OS_ACCESS 1 #define OS_CHECKRESERVEDLOCK 2 #define OS_CLOSE 3 #define OS_CURRENTTIME 4 #define OS_DELETE 5 #define OS_DEVCHAR 6 #define OS_FILECONTROL 7 #define OS_FILESIZE 8 #define OS_FULLPATHNAME 9 #define OS_LOCK 11 #define OS_OPEN 12 #define OS_RANDOMNESS 13 #define OS_READ 14 #define OS_SECTORSIZE 15 #define OS_SLEEP 16 #define OS_SYNC 17 #define OS_TRUNCATE 18 #define OS_UNLOCK 19 #define OS_WRITE 20 #define OS_SHMUNMAP 22 #define OS_SHMMAP 23 #define OS_SHMLOCK 25 #define OS_SHMBARRIER 26 #define OS_ANNOTATE 28 #define OS_NUMEVENTS 29 #define VFSLOG_BUFFERSIZE 8192 typedef struct VfslogVfs VfslogVfs; typedef struct VfslogFile VfslogFile; struct VfslogVfs { sqlite3_vfs base; /* VFS methods */ sqlite3_vfs *pVfs; /* Parent VFS */ int iNextFileId; /* Next file id */ sqlite3_file *pLog; /* Log file handle */ sqlite3_int64 iOffset; /* Log file offset of start of write buffer */ int nBuf; /* Number of valid bytes in aBuf[] */ char aBuf[VFSLOG_BUFFERSIZE]; /* Write buffer */ }; struct VfslogFile { sqlite3_file base; /* IO methods */ sqlite3_file *pReal; /* Underlying file handle */ sqlite3_vfs *pVfslog; /* Associated VsflogVfs object */ int iFileId; /* File id number */ }; #define REALVFS(p) (((VfslogVfs *)(p))->pVfs) /* ** Method declarations for vfslog_file. */ static int vfslogClose(sqlite3_file*); static int vfslogRead(sqlite3_file*, void*, int iAmt, sqlite3_int64 iOfst); static int vfslogWrite(sqlite3_file*,const void*,int iAmt, sqlite3_int64 iOfst); static int vfslogTruncate(sqlite3_file*, sqlite3_int64 size); static int vfslogSync(sqlite3_file*, int flags); static int vfslogFileSize(sqlite3_file*, sqlite3_int64 *pSize); static int vfslogLock(sqlite3_file*, int); static int vfslogUnlock(sqlite3_file*, int); static int vfslogCheckReservedLock(sqlite3_file*, int *pResOut); static int vfslogFileControl(sqlite3_file*, int op, void *pArg); static int vfslogSectorSize(sqlite3_file*); static int vfslogDeviceCharacteristics(sqlite3_file*); static int vfslogShmLock(sqlite3_file *pFile, int ofst, int n, int flags); static int vfslogShmMap(sqlite3_file *pFile,int,int,int,volatile void **); static void vfslogShmBarrier(sqlite3_file*); static int vfslogShmUnmap(sqlite3_file *pFile, int deleteFlag); /* ** Method declarations for vfslog_vfs. */ static int vfslogOpen(sqlite3_vfs*, const char *, sqlite3_file*, int , int *); static int vfslogDelete(sqlite3_vfs*, const char *zName, int syncDir); static int vfslogAccess(sqlite3_vfs*, const char *zName, int flags, int *); static int vfslogFullPathname(sqlite3_vfs*, const char *zName, int, char *zOut); static void *vfslogDlOpen(sqlite3_vfs*, const char *zFilename); static void vfslogDlError(sqlite3_vfs*, int nByte, char *zErrMsg); static void (*vfslogDlSym(sqlite3_vfs *pVfs, void *p, const char*zSym))(void); static void vfslogDlClose(sqlite3_vfs*, void*); static int vfslogRandomness(sqlite3_vfs*, int nByte, char *zOut); static int vfslogSleep(sqlite3_vfs*, int microseconds); static int vfslogCurrentTime(sqlite3_vfs*, double*); static int vfslogGetLastError(sqlite3_vfs*, int, char *); static int vfslogCurrentTimeInt64(sqlite3_vfs*, sqlite3_int64*); static sqlite3_vfs vfslog_vfs = { 1, /* iVersion */ sizeof(VfslogFile), /* szOsFile */ INST_MAX_PATHNAME, /* mxPathname */ 0, /* pNext */ 0, /* zName */ 0, /* pAppData */ vfslogOpen, /* xOpen */ vfslogDelete, /* xDelete */ vfslogAccess, /* xAccess */ vfslogFullPathname, /* xFullPathname */ vfslogDlOpen, /* xDlOpen */ vfslogDlError, /* xDlError */ vfslogDlSym, /* xDlSym */ vfslogDlClose, /* xDlClose */ vfslogRandomness, /* xRandomness */ vfslogSleep, /* xSleep */ vfslogCurrentTime, /* xCurrentTime */ vfslogGetLastError, /* xGetLastError */ vfslogCurrentTimeInt64 /* xCurrentTime */ }; static sqlite3_io_methods vfslog_io_methods = { 2, /* iVersion */ vfslogClose, /* xClose */ vfslogRead, /* xRead */ vfslogWrite, /* xWrite */ vfslogTruncate, /* xTruncate */ vfslogSync, /* xSync */ vfslogFileSize, /* xFileSize */ vfslogLock, /* xLock */ vfslogUnlock, /* xUnlock */ vfslogCheckReservedLock, /* xCheckReservedLock */ vfslogFileControl, /* xFileControl */ vfslogSectorSize, /* xSectorSize */ vfslogDeviceCharacteristics, /* xDeviceCharacteristics */ vfslogShmMap, /* xShmMap */ vfslogShmLock, /* xShmLock */ vfslogShmBarrier, /* xShmBarrier */ vfslogShmUnmap /* xShmUnmap */ }; #if defined(SQLITE_OS_UNIX) && !defined(NO_GETTOD) #include <sys/time.h> static sqlite3_uint64 vfslog_time(){ struct timeval sTime; gettimeofday(&sTime, 0); return sTime.tv_usec + (sqlite3_uint64)sTime.tv_sec * 1000000; } #elif defined(SQLITE_OS_WIN) #include <windows.h> #include <time.h> static sqlite3_uint64 vfslog_time(){ FILETIME ft; sqlite3_uint64 u64time = 0; GetSystemTimeAsFileTime(&ft); u64time |= ft.dwHighDateTime; u64time <<= 32; u64time |= ft.dwLowDateTime; /* ft is 100-nanosecond intervals, we want microseconds */ return u64time /(sqlite3_uint64)10; } #else static sqlite3_uint64 vfslog_time(){ return 0; } #endif static void vfslog_call(sqlite3_vfs *, int, int, int, int, int, int); static void vfslog_string(sqlite3_vfs *, const char *); /* ** Close an vfslog-file. */ static int vfslogClose(sqlite3_file *pFile){ sqlite3_uint64 t; int rc = SQLITE_OK; VfslogFile *p = (VfslogFile *)pFile; t = vfslog_time(); if( p->pReal->pMethods ){ rc = p->pReal->pMethods->xClose(p->pReal); } t = vfslog_time() - t; vfslog_call(p->pVfslog, OS_CLOSE, p->iFileId, t, rc, 0, 0); return rc; } /* ** Read data from an vfslog-file. */ static int vfslogRead( sqlite3_file *pFile, void *zBuf, int iAmt, sqlite_int64 iOfst ){ int rc; sqlite3_uint64 t; VfslogFile *p = (VfslogFile *)pFile; t = vfslog_time(); rc = p->pReal->pMethods->xRead(p->pReal, zBuf, iAmt, iOfst); t = vfslog_time() - t; vfslog_call(p->pVfslog, OS_READ, p->iFileId, t, rc, iAmt, (int)iOfst); return rc; } /* ** Write data to an vfslog-file. */ static int vfslogWrite( sqlite3_file *pFile, const void *z, int iAmt, sqlite_int64 iOfst ){ int rc; sqlite3_uint64 t; VfslogFile *p = (VfslogFile *)pFile; t = vfslog_time(); rc = p->pReal->pMethods->xWrite(p->pReal, z, iAmt, iOfst); t = vfslog_time() - t; vfslog_call(p->pVfslog, OS_WRITE, p->iFileId, t, rc, iAmt, (int)iOfst); return rc; } /* ** Truncate an vfslog-file. */ static int vfslogTruncate(sqlite3_file *pFile, sqlite_int64 size){ int rc; sqlite3_uint64 t; VfslogFile *p = (VfslogFile *)pFile; t = vfslog_time(); rc = p->pReal->pMethods->xTruncate(p->pReal, size); t = vfslog_time() - t; vfslog_call(p->pVfslog, OS_TRUNCATE, p->iFileId, t, rc, 0, (int)size); return rc; } /* ** Sync an vfslog-file. */ static int vfslogSync(sqlite3_file *pFile, int flags){ int rc; sqlite3_uint64 t; VfslogFile *p = (VfslogFile *)pFile; t = vfslog_time(); rc = p->pReal->pMethods->xSync(p->pReal, flags); t = vfslog_time() - t; vfslog_call(p->pVfslog, OS_SYNC, p->iFileId, t, rc, flags, 0); return rc; } /* ** Return the current file-size of an vfslog-file. */ static int vfslogFileSize(sqlite3_file *pFile, sqlite_int64 *pSize){ int rc; sqlite3_uint64 t; VfslogFile *p = (VfslogFile *)pFile; t = vfslog_time(); rc = p->pReal->pMethods->xFileSize(p->pReal, pSize); t = vfslog_time() - t; vfslog_call(p->pVfslog, OS_FILESIZE, p->iFileId, t, rc, 0, (int)*pSize); return rc; } /* ** Lock an vfslog-file. */ static int vfslogLock(sqlite3_file *pFile, int eLock){ int rc; sqlite3_uint64 t; VfslogFile *p = (VfslogFile *)pFile; t = vfslog_time(); rc = p->pReal->pMethods->xLock(p->pReal, eLock); t = vfslog_time() - t; vfslog_call(p->pVfslog, OS_LOCK, p->iFileId, t, rc, eLock, 0); return rc; } /* ** Unlock an vfslog-file. */ static int vfslogUnlock(sqlite3_file *pFile, int eLock){ int rc; sqlite3_uint64 t; VfslogFile *p = (VfslogFile *)pFile; t = vfslog_time(); rc = p->pReal->pMethods->xUnlock(p->pReal, eLock); t = vfslog_time() - t; vfslog_call(p->pVfslog, OS_UNLOCK, p->iFileId, t, rc, eLock, 0); return rc; } /* ** Check if another file-handle holds a RESERVED lock on an vfslog-file. */ static int vfslogCheckReservedLock(sqlite3_file *pFile, int *pResOut){ int rc; sqlite3_uint64 t; VfslogFile *p = (VfslogFile *)pFile; t = vfslog_time(); rc = p->pReal->pMethods->xCheckReservedLock(p->pReal, pResOut); t = vfslog_time() - t; vfslog_call(p->pVfslog, OS_CHECKRESERVEDLOCK, p->iFileId, t, rc, *pResOut, 0); return rc; } /* ** File control method. For custom operations on an vfslog-file. */ static int vfslogFileControl(sqlite3_file *pFile, int op, void *pArg){ VfslogFile *p = (VfslogFile *)pFile; return p->pReal->pMethods->xFileControl(p->pReal, op, pArg); } /* ** Return the sector-size in bytes for an vfslog-file. */ static int vfslogSectorSize(sqlite3_file *pFile){ int rc; sqlite3_uint64 t; VfslogFile *p = (VfslogFile *)pFile; t = vfslog_time(); rc = p->pReal->pMethods->xSectorSize(p->pReal); t = vfslog_time() - t; vfslog_call(p->pVfslog, OS_SECTORSIZE, p->iFileId, t, rc, 0, 0); return rc; } /* ** Return the device characteristic flags supported by an vfslog-file. */ static int vfslogDeviceCharacteristics(sqlite3_file *pFile){ int rc; sqlite3_uint64 t; VfslogFile *p = (VfslogFile *)pFile; t = vfslog_time(); rc = p->pReal->pMethods->xDeviceCharacteristics(p->pReal); t = vfslog_time() - t; vfslog_call(p->pVfslog, OS_DEVCHAR, p->iFileId, t, rc, 0, 0); return rc; } static int vfslogShmLock(sqlite3_file *pFile, int ofst, int n, int flags){ int rc; sqlite3_uint64 t; VfslogFile *p = (VfslogFile *)pFile; t = vfslog_time(); rc = p->pReal->pMethods->xShmLock(p->pReal, ofst, n, flags); t = vfslog_time() - t; vfslog_call(p->pVfslog, OS_SHMLOCK, p->iFileId, t, rc, 0, 0); return rc; } static int vfslogShmMap( sqlite3_file *pFile, int iRegion, int szRegion, int isWrite, volatile void **pp ){ int rc; sqlite3_uint64 t; VfslogFile *p = (VfslogFile *)pFile; t = vfslog_time(); rc = p->pReal->pMethods->xShmMap(p->pReal, iRegion, szRegion, isWrite, pp); t = vfslog_time() - t; vfslog_call(p->pVfslog, OS_SHMMAP, p->iFileId, t, rc, 0, 0); return rc; } static void vfslogShmBarrier(sqlite3_file *pFile){ sqlite3_uint64 t; VfslogFile *p = (VfslogFile *)pFile; t = vfslog_time(); p->pReal->pMethods->xShmBarrier(p->pReal); t = vfslog_time() - t; vfslog_call(p->pVfslog, OS_SHMBARRIER, p->iFileId, t, SQLITE_OK, 0, 0); } static int vfslogShmUnmap(sqlite3_file *pFile, int deleteFlag){ int rc; sqlite3_uint64 t; VfslogFile *p = (VfslogFile *)pFile; t = vfslog_time(); rc = p->pReal->pMethods->xShmUnmap(p->pReal, deleteFlag); t = vfslog_time() - t; vfslog_call(p->pVfslog, OS_SHMUNMAP, p->iFileId, t, rc, 0, 0); return rc; } /* ** Open an vfslog file handle. */ static int vfslogOpen( sqlite3_vfs *pVfs, const char *zName, sqlite3_file *pFile, int flags, int *pOutFlags ){ int rc; sqlite3_uint64 t; VfslogFile *p = (VfslogFile *)pFile; VfslogVfs *pLog = (VfslogVfs *)pVfs; pFile->pMethods = &vfslog_io_methods; p->pReal = (sqlite3_file *)&p[1]; p->pVfslog = pVfs; p->iFileId = ++pLog->iNextFileId; t = vfslog_time(); rc = REALVFS(pVfs)->xOpen(REALVFS(pVfs), zName, p->pReal, flags, pOutFlags); t = vfslog_time() - t; vfslog_call(pVfs, OS_OPEN, p->iFileId, t, rc, 0, 0); vfslog_string(pVfs, zName); return rc; } /* ** Delete the file located at zPath. If the dirSync argument is true, ** ensure the file-system modifications are synced to disk before ** returning. */ static int vfslogDelete(sqlite3_vfs *pVfs, const char *zPath, int dirSync){ int rc; sqlite3_uint64 t; t = vfslog_time(); rc = REALVFS(pVfs)->xDelete(REALVFS(pVfs), zPath, dirSync); t = vfslog_time() - t; vfslog_call(pVfs, OS_DELETE, 0, t, rc, dirSync, 0); vfslog_string(pVfs, zPath); return rc; } /* ** Test for access permissions. Return true if the requested permission ** is available, or false otherwise. */ static int vfslogAccess( sqlite3_vfs *pVfs, const char *zPath, int flags, int *pResOut ){ int rc; sqlite3_uint64 t; t = vfslog_time(); rc = REALVFS(pVfs)->xAccess(REALVFS(pVfs), zPath, flags, pResOut); t = vfslog_time() - t; vfslog_call(pVfs, OS_ACCESS, 0, t, rc, flags, *pResOut); vfslog_string(pVfs, zPath); return rc; } /* ** Populate buffer zOut with the full canonical pathname corresponding ** to the pathname in zPath. zOut is guaranteed to point to a buffer ** of at least (INST_MAX_PATHNAME+1) bytes. */ static int vfslogFullPathname( sqlite3_vfs *pVfs, const char *zPath, int nOut, char *zOut ){ return REALVFS(pVfs)->xFullPathname(REALVFS(pVfs), zPath, nOut, zOut); } /* ** Open the dynamic library located at zPath and return a handle. */ static void *vfslogDlOpen(sqlite3_vfs *pVfs, const char *zPath){ return REALVFS(pVfs)->xDlOpen(REALVFS(pVfs), zPath); } /* ** Populate the buffer zErrMsg (size nByte bytes) with a human readable ** utf-8 string describing the most recent error encountered associated ** with dynamic libraries. */ static void vfslogDlError(sqlite3_vfs *pVfs, int nByte, char *zErrMsg){ REALVFS(pVfs)->xDlError(REALVFS(pVfs), nByte, zErrMsg); } /* ** Return a pointer to the symbol zSymbol in the dynamic library pHandle. */ static void (*vfslogDlSym(sqlite3_vfs *pVfs, void *p, const char *zSym))(void){ return REALVFS(pVfs)->xDlSym(REALVFS(pVfs), p, zSym); } /* ** Close the dynamic library handle pHandle. */ static void vfslogDlClose(sqlite3_vfs *pVfs, void *pHandle){ REALVFS(pVfs)->xDlClose(REALVFS(pVfs), pHandle); } /* ** Populate the buffer pointed to by zBufOut with nByte bytes of ** random data. */ static int vfslogRandomness(sqlite3_vfs *pVfs, int nByte, char *zBufOut){ return REALVFS(pVfs)->xRandomness(REALVFS(pVfs), nByte, zBufOut); } /* ** Sleep for nMicro microseconds. Return the number of microseconds ** actually slept. */ static int vfslogSleep(sqlite3_vfs *pVfs, int nMicro){ return REALVFS(pVfs)->xSleep(REALVFS(pVfs), nMicro); } /* ** Return the current time as a Julian Day number in *pTimeOut. */ static int vfslogCurrentTime(sqlite3_vfs *pVfs, double *pTimeOut){ return REALVFS(pVfs)->xCurrentTime(REALVFS(pVfs), pTimeOut); } static int vfslogGetLastError(sqlite3_vfs *pVfs, int a, char *b){ return REALVFS(pVfs)->xGetLastError(REALVFS(pVfs), a, b); } static int vfslogCurrentTimeInt64(sqlite3_vfs *pVfs, sqlite3_int64 *p){ return REALVFS(pVfs)->xCurrentTimeInt64(REALVFS(pVfs), p); } static void vfslog_flush(VfslogVfs *p){ #ifdef SQLITE_TEST extern int sqlite3_io_error_pending; extern int sqlite3_io_error_persist; extern int sqlite3_diskfull_pending; int pending = sqlite3_io_error_pending; int persist = sqlite3_io_error_persist; int diskfull = sqlite3_diskfull_pending; sqlite3_io_error_pending = 0; sqlite3_io_error_persist = 0; sqlite3_diskfull_pending = 0; #endif if( p->nBuf ){ p->pLog->pMethods->xWrite(p->pLog, p->aBuf, p->nBuf, p->iOffset); p->iOffset += p->nBuf; p->nBuf = 0; } #ifdef SQLITE_TEST sqlite3_io_error_pending = pending; sqlite3_io_error_persist = persist; sqlite3_diskfull_pending = diskfull; #endif } static void put32bits(unsigned char *p, unsigned int v){ p[0] = v>>24; p[1] = v>>16; p[2] = v>>8; p[3] = v; } static void vfslog_call( sqlite3_vfs *pVfs, int eEvent, int iFileid, int nClick, int return_code, int size, int offset ){ VfslogVfs *p = (VfslogVfs *)pVfs; unsigned char *zRec; if( (24+p->nBuf)>sizeof(p->aBuf) ){ vfslog_flush(p); } zRec = (unsigned char *)&p->aBuf[p->nBuf]; put32bits(&zRec[0], eEvent); put32bits(&zRec[4], iFileid); put32bits(&zRec[8], nClick); put32bits(&zRec[12], return_code); put32bits(&zRec[16], size); put32bits(&zRec[20], offset); p->nBuf += 24; } static void vfslog_string(sqlite3_vfs *pVfs, const char *zStr){ VfslogVfs *p = (VfslogVfs *)pVfs; unsigned char *zRec; int nStr = zStr ? strlen(zStr) : 0; if( (4+nStr+p->nBuf)>sizeof(p->aBuf) ){ vfslog_flush(p); } zRec = (unsigned char *)&p->aBuf[p->nBuf]; put32bits(&zRec[0], nStr); if( zStr ){ memcpy(&zRec[4], zStr, nStr); } p->nBuf += (4 + nStr); } static void vfslog_finalize(VfslogVfs *p){ if( p->pLog->pMethods ){ vfslog_flush(p); p->pLog->pMethods->xClose(p->pLog); } sqlite3_free(p); } int sqlite3_vfslog_finalize(const char *zVfs){ sqlite3_vfs *pVfs; pVfs = sqlite3_vfs_find(zVfs); if( !pVfs || pVfs->xOpen!=vfslogOpen ){ return SQLITE_ERROR; } sqlite3_vfs_unregister(pVfs); vfslog_finalize((VfslogVfs *)pVfs); return SQLITE_OK; } int sqlite3_vfslog_new( const char *zVfs, /* New VFS name */ const char *zParentVfs, /* Parent VFS name (or NULL) */ const char *zLog /* Log file name */ ){ VfslogVfs *p; sqlite3_vfs *pParent; int nByte; int flags; int rc; char *zFile; int nVfs; pParent = sqlite3_vfs_find(zParentVfs); if( !pParent ){ return SQLITE_ERROR; } nVfs = strlen(zVfs); nByte = sizeof(VfslogVfs) + pParent->szOsFile + nVfs+1+pParent->mxPathname+1; p = (VfslogVfs *)sqlite3_malloc(nByte); memset(p, 0, nByte); p->pVfs = pParent; p->pLog = (sqlite3_file *)&p[1]; memcpy(&p->base, &vfslog_vfs, sizeof(sqlite3_vfs)); p->base.zName = &((char *)p->pLog)[pParent->szOsFile]; p->base.szOsFile += pParent->szOsFile; memcpy((char *)p->base.zName, zVfs, nVfs); zFile = (char *)&p->base.zName[nVfs+1]; pParent->xFullPathname(pParent, zLog, pParent->mxPathname, zFile); flags = SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE|SQLITE_OPEN_MASTER_JOURNAL; pParent->xDelete(pParent, zFile, 0); rc = pParent->xOpen(pParent, zFile, p->pLog, flags, &flags); if( rc==SQLITE_OK ){ memcpy(p->aBuf, "sqlite_ostrace1.....", 20); p->iOffset = 0; p->nBuf = 20; rc = sqlite3_vfs_register((sqlite3_vfs *)p, 1); } if( rc ){ vfslog_finalize(p); } return rc; } int sqlite3_vfslog_annotate(const char *zVfs, const char *zMsg){ sqlite3_vfs *pVfs; pVfs = sqlite3_vfs_find(zVfs); if( !pVfs || pVfs->xOpen!=vfslogOpen ){ return SQLITE_ERROR; } vfslog_call(pVfs, OS_ANNOTATE, 0, 0, 0, 0, 0); vfslog_string(pVfs, zMsg); return SQLITE_OK; } static const char *vfslog_eventname(int eEvent){ const char *zEvent = 0; switch( eEvent ){ case OS_CLOSE: zEvent = "xClose"; break; case OS_READ: zEvent = "xRead"; break; case OS_WRITE: zEvent = "xWrite"; break; case OS_TRUNCATE: zEvent = "xTruncate"; break; case OS_SYNC: zEvent = "xSync"; break; case OS_FILESIZE: zEvent = "xFilesize"; break; case OS_LOCK: zEvent = "xLock"; break; case OS_UNLOCK: zEvent = "xUnlock"; break; case OS_CHECKRESERVEDLOCK: zEvent = "xCheckResLock"; break; case OS_FILECONTROL: zEvent = "xFileControl"; break; case OS_SECTORSIZE: zEvent = "xSectorSize"; break; case OS_DEVCHAR: zEvent = "xDeviceChar"; break; case OS_OPEN: zEvent = "xOpen"; break; case OS_DELETE: zEvent = "xDelete"; break; case OS_ACCESS: zEvent = "xAccess"; break; case OS_FULLPATHNAME: zEvent = "xFullPathname"; break; case OS_RANDOMNESS: zEvent = "xRandomness"; break; case OS_SLEEP: zEvent = "xSleep"; break; case OS_CURRENTTIME: zEvent = "xCurrentTime"; break; case OS_SHMUNMAP: zEvent = "xShmUnmap"; break; case OS_SHMLOCK: zEvent = "xShmLock"; break; case OS_SHMBARRIER: zEvent = "xShmBarrier"; break; case OS_SHMMAP: zEvent = "xShmMap"; break; case OS_ANNOTATE: zEvent = "annotation"; break; } return zEvent; } typedef struct VfslogVtab VfslogVtab; typedef struct VfslogCsr VfslogCsr; /* ** Virtual table type for the vfslog reader module. */ struct VfslogVtab { sqlite3_vtab base; /* Base class */ sqlite3_file *pFd; /* File descriptor open on vfslog file */ sqlite3_int64 nByte; /* Size of file in bytes */ char *zFile; /* File name for pFd */ }; /* ** Virtual table cursor type for the vfslog reader module. */ struct VfslogCsr { sqlite3_vtab_cursor base; /* Base class */ sqlite3_int64 iRowid; /* Current rowid. */ sqlite3_int64 iOffset; /* Offset of next record in file */ char *zTransient; /* Transient 'file' string */ int nFile; /* Size of array azFile[] */ char **azFile; /* File strings */ unsigned char aBuf[1024]; /* Current vfs log entry (read from file) */ }; static unsigned int get32bits(unsigned char *p){ return (p[0]<<24) + (p[1]<<16) + (p[2]<<8) + p[3]; } /* ** The argument must point to a buffer containing a nul-terminated string. ** If the string begins with an SQL quote character it is overwritten by ** the dequoted version. Otherwise the buffer is left unmodified. */ static void dequote(char *z){ char quote; /* Quote character (if any ) */ quote = z[0]; if( quote=='[' || quote=='\'' || quote=='"' || quote=='`' ){ int iIn = 1; /* Index of next byte to read from input */ int iOut = 0; /* Index of next byte to write to output */ if( quote=='[' ) quote = ']'; while( z[iIn] ){ if( z[iIn]==quote ){ if( z[iIn+1]!=quote ) break; z[iOut++] = quote; iIn += 2; }else{ z[iOut++] = z[iIn++]; } } z[iOut] = '\0'; } } #ifndef SQLITE_OMIT_VIRTUALTABLE /* ** Connect to or create a vfslog virtual table. */ static int vlogConnect( sqlite3 *db, void *pAux, int argc, const char *const*argv, sqlite3_vtab **ppVtab, char **pzErr ){ sqlite3_vfs *pVfs; /* VFS used to read log file */ int flags; /* flags passed to pVfs->xOpen() */ VfslogVtab *p; int rc; int nByte; char *zFile; *ppVtab = 0; pVfs = sqlite3_vfs_find(0); nByte = sizeof(VfslogVtab) + pVfs->szOsFile + pVfs->mxPathname; p = sqlite3_malloc(nByte); if( p==0 ) return SQLITE_NOMEM; memset(p, 0, nByte); p->pFd = (sqlite3_file *)&p[1]; p->zFile = &((char *)p->pFd)[pVfs->szOsFile]; zFile = sqlite3_mprintf("%s", argv[3]); if( !zFile ){ sqlite3_free(p); return SQLITE_NOMEM; } dequote(zFile); pVfs->xFullPathname(pVfs, zFile, pVfs->mxPathname, p->zFile); sqlite3_free(zFile); flags = SQLITE_OPEN_READWRITE|SQLITE_OPEN_MASTER_JOURNAL; rc = pVfs->xOpen(pVfs, p->zFile, p->pFd, flags, &flags); if( rc==SQLITE_OK ){ p->pFd->pMethods->xFileSize(p->pFd, &p->nByte); sqlite3_declare_vtab(db, "CREATE TABLE xxx(event, file, click, rc, size, offset)" ); *ppVtab = &p->base; }else{ sqlite3_free(p); } return rc; } /* ** There is no "best-index". This virtual table always does a linear ** scan of the binary VFS log file. */ static int vlogBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){ pIdxInfo->estimatedCost = 10.0; return SQLITE_OK; } /* ** Disconnect from or destroy a vfslog virtual table. */ static int vlogDisconnect(sqlite3_vtab *pVtab){ VfslogVtab *p = (VfslogVtab *)pVtab; if( p->pFd->pMethods ){ p->pFd->pMethods->xClose(p->pFd); p->pFd->pMethods = 0; } sqlite3_free(p); return SQLITE_OK; } /* ** Open a new vfslog cursor. */ static int vlogOpen(sqlite3_vtab *pVTab, sqlite3_vtab_cursor **ppCursor){ VfslogCsr *pCsr; /* Newly allocated cursor object */ pCsr = sqlite3_malloc(sizeof(VfslogCsr)); if( !pCsr ) return SQLITE_NOMEM; memset(pCsr, 0, sizeof(VfslogCsr)); *ppCursor = &pCsr->base; return SQLITE_OK; } /* ** Close a vfslog cursor. */ static int vlogClose(sqlite3_vtab_cursor *pCursor){ VfslogCsr *p = (VfslogCsr *)pCursor; int i; for(i=0; i<p->nFile; i++){ sqlite3_free(p->azFile[i]); } sqlite3_free(p->azFile); sqlite3_free(p->zTransient); sqlite3_free(p); return SQLITE_OK; } /* ** Move a vfslog cursor to the next entry in the file. */ static int vlogNext(sqlite3_vtab_cursor *pCursor){ VfslogCsr *pCsr = (VfslogCsr *)pCursor; VfslogVtab *p = (VfslogVtab *)pCursor->pVtab; int rc = SQLITE_OK; int nRead; sqlite3_free(pCsr->zTransient); pCsr->zTransient = 0; nRead = 24; if( pCsr->iOffset+nRead<=p->nByte ){ int eEvent; rc = p->pFd->pMethods->xRead(p->pFd, pCsr->aBuf, nRead, pCsr->iOffset); eEvent = get32bits(pCsr->aBuf); if( (rc==SQLITE_OK) && (eEvent==OS_OPEN || eEvent==OS_DELETE || eEvent==OS_ACCESS) ){ char buf[4]; rc = p->pFd->pMethods->xRead(p->pFd, buf, 4, pCsr->iOffset+nRead); nRead += 4; if( rc==SQLITE_OK ){ int nStr = get32bits((unsigned char *)buf); char *zStr = sqlite3_malloc(nStr+1); rc = p->pFd->pMethods->xRead(p->pFd, zStr, nStr, pCsr->iOffset+nRead); zStr[nStr] = '\0'; nRead += nStr; if( eEvent==OS_OPEN ){ int iFileid = get32bits(&pCsr->aBuf[4]); if( iFileid>=pCsr->nFile ){ int nNew = sizeof(pCsr->azFile[0])*(iFileid+1); pCsr->azFile = (char **)sqlite3_realloc(pCsr->azFile, nNew); nNew -= sizeof(pCsr->azFile[0])*pCsr->nFile; memset(&pCsr->azFile[pCsr->nFile], 0, nNew); pCsr->nFile = iFileid+1; } sqlite3_free(pCsr->azFile[iFileid]); pCsr->azFile[iFileid] = zStr; }else{ pCsr->zTransient = zStr; } } } } pCsr->iRowid += 1; pCsr->iOffset += nRead; return rc; } static int vlogEof(sqlite3_vtab_cursor *pCursor){ VfslogCsr *pCsr = (VfslogCsr *)pCursor; VfslogVtab *p = (VfslogVtab *)pCursor->pVtab; return (pCsr->iOffset>=p->nByte); } static int vlogFilter( sqlite3_vtab_cursor *pCursor, int idxNum, const char *idxStr, int argc, sqlite3_value **argv ){ VfslogCsr *pCsr = (VfslogCsr *)pCursor; pCsr->iRowid = 0; pCsr->iOffset = 20; return vlogNext(pCursor); } static int vlogColumn( sqlite3_vtab_cursor *pCursor, sqlite3_context *ctx, int i ){ unsigned int val; VfslogCsr *pCsr = (VfslogCsr *)pCursor; assert( i<7 ); val = get32bits(&pCsr->aBuf[4*i]); switch( i ){ case 0: { sqlite3_result_text(ctx, vfslog_eventname(val), -1, SQLITE_STATIC); break; } case 1: { char *zStr = pCsr->zTransient; if( val!=0 && val<pCsr->nFile ){ zStr = pCsr->azFile[val]; } sqlite3_result_text(ctx, zStr, -1, SQLITE_TRANSIENT); break; } default: sqlite3_result_int(ctx, val); break; } return SQLITE_OK; } static int vlogRowid(sqlite3_vtab_cursor *pCursor, sqlite_int64 *pRowid){ VfslogCsr *pCsr = (VfslogCsr *)pCursor; *pRowid = pCsr->iRowid; return SQLITE_OK; } int sqlite3_vfslog_register(sqlite3 *db){ static sqlite3_module vfslog_module = { 0, /* iVersion */ vlogConnect, /* xCreate */ vlogConnect, /* xConnect */ vlogBestIndex, /* xBestIndex */ vlogDisconnect, /* xDisconnect */ vlogDisconnect, /* xDestroy */ vlogOpen, /* xOpen - open a cursor */ vlogClose, /* xClose - close a cursor */ vlogFilter, /* xFilter - configure scan constraints */ vlogNext, /* xNext - advance a cursor */ vlogEof, /* xEof - check for end of scan */ vlogColumn, /* xColumn - read data */ vlogRowid, /* xRowid - read data */ 0, /* xUpdate */ 0, /* xBegin */ 0, /* xSync */ 0, /* xCommit */ 0, /* xRollback */ 0, /* xFindMethod */ 0, /* xRename */ }; sqlite3_create_module(db, "vfslog", &vfslog_module, 0); return SQLITE_OK; } #endif /* SQLITE_OMIT_VIRTUALTABLE */ /************************************************************************** *************************************************************************** ** Tcl interface starts here. */ #if defined(SQLITE_TEST) || defined(TCLSH) #include <tcl.h> static int test_vfslog( void *clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[] ){ struct SqliteDb { sqlite3 *db; }; sqlite3 *db; Tcl_CmdInfo cmdInfo; int rc = SQLITE_ERROR; static const char *strs[] = { "annotate", "finalize", "new", "register", 0 }; enum VL_enum { VL_ANNOTATE, VL_FINALIZE, VL_NEW, VL_REGISTER }; int iSub; if( objc<2 ){ Tcl_WrongNumArgs(interp, 1, objv, "SUB-COMMAND ..."); return TCL_ERROR; } if( Tcl_GetIndexFromObj(interp, objv[1], strs, "sub-command", 0, &iSub) ){ return TCL_ERROR; } switch( (enum VL_enum)iSub ){ case VL_ANNOTATE: { int rc; char *zVfs; char *zMsg; if( objc!=4 ){ Tcl_WrongNumArgs(interp, 3, objv, "VFS"); return TCL_ERROR; } zVfs = Tcl_GetString(objv[2]); zMsg = Tcl_GetString(objv[3]); rc = sqlite3_vfslog_annotate(zVfs, zMsg); if( rc!=SQLITE_OK ){ Tcl_AppendResult(interp, "failed", 0); return TCL_ERROR; } break; } case VL_FINALIZE: { int rc; char *zVfs; if( objc!=3 ){ Tcl_WrongNumArgs(interp, 2, objv, "VFS"); return TCL_ERROR; } zVfs = Tcl_GetString(objv[2]); rc = sqlite3_vfslog_finalize(zVfs); if( rc!=SQLITE_OK ){ Tcl_AppendResult(interp, "failed", 0); return TCL_ERROR; } break; }; case VL_NEW: { int rc; char *zVfs; char *zParent; char *zLog; if( objc!=5 ){ Tcl_WrongNumArgs(interp, 2, objv, "VFS PARENT LOGFILE"); return TCL_ERROR; } zVfs = Tcl_GetString(objv[2]); zParent = Tcl_GetString(objv[3]); zLog = Tcl_GetString(objv[4]); if( *zParent=='\0' ) zParent = 0; rc = sqlite3_vfslog_new(zVfs, zParent, zLog); if( rc!=SQLITE_OK ){ Tcl_AppendResult(interp, "failed", 0); return TCL_ERROR; } break; }; case VL_REGISTER: { char *zDb; if( objc!=3 ){ Tcl_WrongNumArgs(interp, 2, objv, "DB"); return TCL_ERROR; } #ifdef SQLITE_OMIT_VIRTUALTABLE Tcl_AppendResult(interp, "vfslog not available because of " "SQLITE_OMIT_VIRTUALTABLE", (void*)0); return TCL_ERROR; #else zDb = Tcl_GetString(objv[2]); if( Tcl_GetCommandInfo(interp, zDb, &cmdInfo) ){ db = ((struct SqliteDb*)cmdInfo.objClientData)->db; rc = sqlite3_vfslog_register(db); } if( rc!=SQLITE_OK ){ Tcl_AppendResult(interp, "bad sqlite3 handle: ", zDb, (void*)0); return TCL_ERROR; } break; #endif } } return TCL_OK; } int SqlitetestOsinst_Init(Tcl_Interp *interp){ Tcl_CreateObjCommand(interp, "vfslog", test_vfslog, 0, 0); return TCL_OK; } #endif /* SQLITE_TEST */ |
Added src/test_stat.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 | /* ** 2010 July 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 contains an implementation of the "dbstat" virtual table. ** ** The dbstat virtual table is used to extract low-level formatting ** information from an SQLite database in order to implement the ** "sqlite3_analyzer" utility. See the ../tool/spaceanal.tcl script ** for an example implementation. */ #include "sqliteInt.h" #ifndef SQLITE_OMIT_VIRTUALTABLE /* ** Page paths: ** ** The value of the 'path' column describes the path taken from the ** root-node of the b-tree structure to each page. The value of the ** root-node path is '/'. ** ** The value of the path for the left-most child page of the root of ** a b-tree is '/000/'. (Btrees store content ordered from left to right ** so the pages to the left have smaller keys than the pages to the right.) ** The next to left-most child of the root page is ** '/001', and so on, each sibling page identified by a 3-digit hex ** value. The children of the 451st left-most sibling have paths such ** as '/1c2/000/, '/1c2/001/' etc. ** ** Overflow pages are specified by appending a '+' character and a ** six-digit hexadecimal value to the path to the cell they are linked ** from. For example, the three overflow pages in a chain linked from ** the left-most cell of the 450th child of the root page are identified ** by the paths: ** ** '/1c2/000+000000' // First page in overflow chain ** '/1c2/000+000001' // Second page in overflow chain ** '/1c2/000+000002' // Third page in overflow chain ** ** If the paths are sorted using the BINARY collation sequence, then ** the overflow pages associated with a cell will appear earlier in the ** sort-order than its child page: ** ** '/1c2/000/' // Left-most child of 451st child of root */ #define VTAB_SCHEMA \ "CREATE TABLE xx( " \ " name STRING, /* Name of table or index */" \ " path INTEGER, /* Path to page from root */" \ " pageno INTEGER, /* Page number */" \ " pagetype STRING, /* 'internal', 'leaf' or 'overflow' */" \ " ncell INTEGER, /* Cells on page (0 for overflow) */" \ " payload INTEGER, /* Bytes of payload on this page */" \ " unused INTEGER, /* Bytes of unused space on this page */" \ " mx_payload INTEGER /* Largest payload size of all cells */" \ ");" #if 0 #define VTAB_SCHEMA2 \ "CREATE TABLE yy( " \ " pageno INTEGER, /* B-tree page number */" \ " cellno INTEGER, /* Cell number within page */" \ " local INTEGER, /* Bytes of content stored locally */" \ " payload INTEGER, /* Total cell payload size */" \ " novfl INTEGER /* Number of overflow pages */" \ ");" #endif typedef struct StatTable StatTable; typedef struct StatCursor StatCursor; typedef struct StatPage StatPage; typedef struct StatCell StatCell; struct StatCell { int nLocal; /* Bytes of local payload */ u32 iChildPg; /* Child node (or 0 if this is a leaf) */ int nOvfl; /* Entries in aOvfl[] */ u32 *aOvfl; /* Array of overflow page numbers */ int nLastOvfl; /* Bytes of payload on final overflow page */ int iOvfl; /* Iterates through aOvfl[] */ }; struct StatPage { u32 iPgno; DbPage *pPg; int iCell; char *zPath; /* Path to this page */ /* Variables populated by statDecodePage(): */ u8 flags; /* Copy of flags byte */ int nCell; /* Number of cells on page */ int nUnused; /* Number of unused bytes on page */ StatCell *aCell; /* Array of parsed cells */ u32 iRightChildPg; /* Right-child page number (or 0) */ int nMxPayload; /* Largest payload of any cell on this page */ }; struct StatCursor { sqlite3_vtab_cursor base; sqlite3_stmt *pStmt; /* Iterates through set of root pages */ int isEof; /* After pStmt has returned SQLITE_DONE */ StatPage aPage[32]; int iPage; /* Current entry in aPage[] */ /* Values to return. */ char *zName; /* Value of 'name' column */ char *zPath; /* Value of 'path' column */ u32 iPageno; /* Value of 'pageno' column */ char *zPagetype; /* Value of 'pagetype' column */ int nCell; /* Value of 'ncell' column */ int nPayload; /* Value of 'payload' column */ int nUnused; /* Value of 'unused' column */ int nMxPayload; /* Value of 'mx_payload' column */ }; struct StatTable { sqlite3_vtab base; sqlite3 *db; }; #ifndef get2byte # define get2byte(x) ((x)[0]<<8 | (x)[1]) #endif /* ** Connect to or create a statvfs virtual table. */ static int statConnect( sqlite3 *db, void *pAux, int argc, const char *const*argv, sqlite3_vtab **ppVtab, char **pzErr ){ StatTable *pTab; pTab = (StatTable *)sqlite3_malloc(sizeof(StatTable)); memset(pTab, 0, sizeof(StatTable)); pTab->db = db; sqlite3_declare_vtab(db, VTAB_SCHEMA); *ppVtab = &pTab->base; return SQLITE_OK; } /* ** Disconnect from or destroy a statvfs virtual table. */ static int statDisconnect(sqlite3_vtab *pVtab){ sqlite3_free(pVtab); return SQLITE_OK; } /* ** There is no "best-index". This virtual table always does a linear ** scan of the binary VFS log file. */ static int statBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){ /* Records are always returned in ascending order of (name, path). ** If this will satisfy the client, set the orderByConsumed flag so that ** SQLite does not do an external sort. */ if( ( pIdxInfo->nOrderBy==1 && pIdxInfo->aOrderBy[0].iColumn==0 && pIdxInfo->aOrderBy[0].desc==0 ) || ( pIdxInfo->nOrderBy==2 && pIdxInfo->aOrderBy[0].iColumn==0 && pIdxInfo->aOrderBy[0].desc==0 && pIdxInfo->aOrderBy[1].iColumn==1 && pIdxInfo->aOrderBy[1].desc==0 ) ){ pIdxInfo->orderByConsumed = 1; } pIdxInfo->estimatedCost = 10.0; return SQLITE_OK; } /* ** Open a new statvfs cursor. */ static int statOpen(sqlite3_vtab *pVTab, sqlite3_vtab_cursor **ppCursor){ StatTable *pTab = (StatTable *)pVTab; StatCursor *pCsr; int rc; pCsr = (StatCursor *)sqlite3_malloc(sizeof(StatCursor)); memset(pCsr, 0, sizeof(StatCursor)); pCsr->base.pVtab = pVTab; rc = sqlite3_prepare_v2(pTab->db, "SELECT 'sqlite_master' AS name, 1 AS rootpage, 'table' AS type" " UNION ALL " "SELECT name, rootpage, type FROM sqlite_master WHERE rootpage!=0" " ORDER BY name", -1, &pCsr->pStmt, 0 ); if( rc!=SQLITE_OK ){ sqlite3_free(pCsr); return rc; } *ppCursor = (sqlite3_vtab_cursor *)pCsr; return SQLITE_OK; } static void statClearPage(StatPage *p){ int i; for(i=0; i<p->nCell; i++){ sqlite3_free(p->aCell[i].aOvfl); } sqlite3PagerUnref(p->pPg); sqlite3_free(p->aCell); sqlite3_free(p->zPath); memset(p, 0, sizeof(StatPage)); } static void statResetCsr(StatCursor *pCsr){ int i; sqlite3_reset(pCsr->pStmt); for(i=0; i<ArraySize(pCsr->aPage); i++){ statClearPage(&pCsr->aPage[i]); } pCsr->iPage = 0; sqlite3_free(pCsr->zPath); pCsr->zPath = 0; } /* ** Close a statvfs cursor. */ static int statClose(sqlite3_vtab_cursor *pCursor){ StatCursor *pCsr = (StatCursor *)pCursor; statResetCsr(pCsr); sqlite3_finalize(pCsr->pStmt); sqlite3_free(pCsr); return SQLITE_OK; } static void getLocalPayload( int nUsable, /* Usable bytes per page */ u8 flags, /* Page flags */ int nTotal, /* Total record (payload) size */ int *pnLocal /* OUT: Bytes stored locally */ ){ int nLocal; int nMinLocal; int nMaxLocal; if( flags==0x0D ){ /* Table leaf node */ nMinLocal = (nUsable - 12) * 32 / 255 - 23; nMaxLocal = nUsable - 35; }else{ /* Index interior and leaf nodes */ nMinLocal = (nUsable - 12) * 32 / 255 - 23; nMaxLocal = (nUsable - 12) * 64 / 255 - 23; } nLocal = nMinLocal + (nTotal - nMinLocal) % (nUsable - 4); if( nLocal>nMaxLocal ) nLocal = nMinLocal; *pnLocal = nLocal; } static int statDecodePage(Btree *pBt, StatPage *p){ int nUnused; int iOff; int nHdr; int isLeaf; u8 *aData = sqlite3PagerGetData(p->pPg); u8 *aHdr = &aData[p->iPgno==1 ? 100 : 0]; p->flags = aHdr[0]; p->nCell = get2byte(&aHdr[3]); p->nMxPayload = 0; isLeaf = (p->flags==0x0A || p->flags==0x0D); nHdr = 12 - isLeaf*4 + (p->iPgno==1)*100; nUnused = get2byte(&aHdr[5]) - nHdr - 2*p->nCell; nUnused += (int)aHdr[7]; iOff = get2byte(&aHdr[1]); while( iOff ){ nUnused += get2byte(&aData[iOff+2]); iOff = get2byte(&aData[iOff]); } p->nUnused = nUnused; p->iRightChildPg = isLeaf ? 0 : sqlite3Get4byte(&aHdr[8]); if( p->nCell ){ int i; /* Used to iterate through cells */ int nUsable = sqlite3BtreeGetPageSize(pBt) - sqlite3BtreeGetReserve(pBt); p->aCell = sqlite3_malloc((p->nCell+1) * sizeof(StatCell)); memset(p->aCell, 0, (p->nCell+1) * sizeof(StatCell)); for(i=0; i<p->nCell; i++){ StatCell *pCell = &p->aCell[i]; iOff = get2byte(&aData[nHdr+i*2]); if( !isLeaf ){ pCell->iChildPg = sqlite3Get4byte(&aData[iOff]); iOff += 4; } if( p->flags==0x05 ){ /* A table interior node. nPayload==0. */ }else{ u32 nPayload; /* Bytes of payload total (local+overflow) */ int nLocal; /* Bytes of payload stored locally */ iOff += getVarint32(&aData[iOff], nPayload); if( p->flags==0x0D ){ u64 dummy; iOff += sqlite3GetVarint(&aData[iOff], &dummy); } if( nPayload>p->nMxPayload ) p->nMxPayload = nPayload; getLocalPayload(nUsable, p->flags, nPayload, &nLocal); pCell->nLocal = nLocal; assert( nPayload>=nLocal ); assert( nLocal<=(nUsable-35) ); if( nPayload>nLocal ){ int j; int nOvfl = ((nPayload - nLocal) + nUsable-4 - 1) / (nUsable - 4); pCell->nLastOvfl = (nPayload-nLocal) - (nOvfl-1) * (nUsable-4); pCell->nOvfl = nOvfl; pCell->aOvfl = sqlite3_malloc(sizeof(u32)*nOvfl); pCell->aOvfl[0] = sqlite3Get4byte(&aData[iOff+nLocal]); for(j=1; j<nOvfl; j++){ int rc; u32 iPrev = pCell->aOvfl[j-1]; DbPage *pPg = 0; rc = sqlite3PagerGet(sqlite3BtreePager(pBt), iPrev, &pPg); if( rc!=SQLITE_OK ){ assert( pPg==0 ); return rc; } pCell->aOvfl[j] = sqlite3Get4byte(sqlite3PagerGetData(pPg)); sqlite3PagerUnref(pPg); } } } } } return SQLITE_OK; } /* ** Move a statvfs cursor to the next entry in the file. */ static int statNext(sqlite3_vtab_cursor *pCursor){ int rc; int nPayload; StatCursor *pCsr = (StatCursor *)pCursor; StatTable *pTab = (StatTable *)pCursor->pVtab; Btree *pBt = pTab->db->aDb[0].pBt; Pager *pPager = sqlite3BtreePager(pBt); sqlite3_free(pCsr->zPath); pCsr->zPath = 0; if( pCsr->aPage[0].pPg==0 ){ rc = sqlite3_step(pCsr->pStmt); if( rc==SQLITE_ROW ){ u32 iRoot = sqlite3_column_int64(pCsr->pStmt, 1); rc = sqlite3PagerGet(pPager, iRoot, &pCsr->aPage[0].pPg); pCsr->aPage[0].iPgno = iRoot; pCsr->aPage[0].iCell = 0; pCsr->aPage[0].zPath = sqlite3_mprintf("/"); pCsr->iPage = 0; }else{ pCsr->isEof = 1; return sqlite3_reset(pCsr->pStmt); } }else{ /* Page p itself has already been visited. */ StatPage *p = &pCsr->aPage[pCsr->iPage]; while( p->iCell<p->nCell ){ StatCell *pCell = &p->aCell[p->iCell]; if( pCell->iOvfl<pCell->nOvfl ){ int nUsable = sqlite3BtreeGetPageSize(pBt)-sqlite3BtreeGetReserve(pBt); pCsr->zName = (char *)sqlite3_column_text(pCsr->pStmt, 0); pCsr->iPageno = pCell->aOvfl[pCell->iOvfl]; pCsr->zPagetype = "overflow"; pCsr->nCell = 0; pCsr->nMxPayload = 0; pCsr->zPath = sqlite3_mprintf( "%s%.3x+%.6x", p->zPath, p->iCell, pCell->iOvfl ); if( pCell->iOvfl<pCell->nOvfl-1 ){ pCsr->nUnused = 0; pCsr->nPayload = nUsable - 4; }else{ pCsr->nPayload = pCell->nLastOvfl; pCsr->nUnused = nUsable - 4 - pCsr->nPayload; } pCell->iOvfl++; return SQLITE_OK; } if( p->iRightChildPg ) break; p->iCell++; } while( !p->iRightChildPg || p->iCell>p->nCell ){ statClearPage(p); if( pCsr->iPage==0 ) return statNext(pCursor); pCsr->iPage--; p = &pCsr->aPage[pCsr->iPage]; } pCsr->iPage++; assert( p==&pCsr->aPage[pCsr->iPage-1] ); if( p->iCell==p->nCell ){ p[1].iPgno = p->iRightChildPg; }else{ p[1].iPgno = p->aCell[p->iCell].iChildPg; } rc = sqlite3PagerGet(pPager, p[1].iPgno, &p[1].pPg); p[1].iCell = 0; p[1].zPath = sqlite3_mprintf("%s%.3x/", p->zPath, p->iCell); p->iCell++; } /* Populate the StatCursor fields with the values to be returned ** by the xColumn() and xRowid() methods. */ if( rc==SQLITE_OK ){ int i; StatPage *p = &pCsr->aPage[pCsr->iPage]; pCsr->zName = (char *)sqlite3_column_text(pCsr->pStmt, 0); pCsr->iPageno = p->iPgno; statDecodePage(pBt, p); switch( p->flags ){ case 0x05: /* table internal */ case 0x02: /* index internal */ pCsr->zPagetype = "internal"; break; case 0x0D: /* table leaf */ case 0x0A: /* index leaf */ pCsr->zPagetype = "leaf"; break; default: pCsr->zPagetype = "corrupted"; break; } pCsr->nCell = p->nCell; pCsr->nUnused = p->nUnused; pCsr->nMxPayload = p->nMxPayload; pCsr->zPath = sqlite3_mprintf("%s", p->zPath); nPayload = 0; for(i=0; i<p->nCell; i++){ nPayload += p->aCell[i].nLocal; } pCsr->nPayload = nPayload; } return rc; } static int statEof(sqlite3_vtab_cursor *pCursor){ StatCursor *pCsr = (StatCursor *)pCursor; return pCsr->isEof; } static int statFilter( sqlite3_vtab_cursor *pCursor, int idxNum, const char *idxStr, int argc, sqlite3_value **argv ){ sqlite3 *db = ((StatTable *)(pCursor->pVtab))->db; StatCursor *pCsr = (StatCursor *)pCursor; int nPage = 0; statResetCsr((StatCursor *)pCursor); sqlite3PagerPagecount(sqlite3BtreePager(db->aDb[0].pBt), &nPage); if( nPage==0 ){ pCsr->isEof = 1; return SQLITE_OK; } return statNext(pCursor); } static int statColumn( sqlite3_vtab_cursor *pCursor, sqlite3_context *ctx, int i ){ StatCursor *pCsr = (StatCursor *)pCursor; switch( i ){ case 0: /* name */ sqlite3_result_text(ctx, pCsr->zName, -1, SQLITE_STATIC); break; case 1: /* path */ sqlite3_result_text(ctx, pCsr->zPath, -1, SQLITE_TRANSIENT); break; case 2: /* pageno */ sqlite3_result_int64(ctx, pCsr->iPageno); break; case 3: /* pagetype */ sqlite3_result_text(ctx, pCsr->zPagetype, -1, SQLITE_STATIC); break; case 4: /* ncell */ sqlite3_result_int(ctx, pCsr->nCell); break; case 5: /* payload */ sqlite3_result_int(ctx, pCsr->nPayload); break; case 6: /* unused */ sqlite3_result_int(ctx, pCsr->nUnused); break; case 7: /* mx_payload */ sqlite3_result_int(ctx, pCsr->nMxPayload); break; } return SQLITE_OK; } static int statRowid(sqlite3_vtab_cursor *pCursor, sqlite_int64 *pRowid){ StatCursor *pCsr = (StatCursor *)pCursor; *pRowid = pCsr->iPageno; return SQLITE_OK; } int sqlite3_dbstat_register(sqlite3 *db){ static sqlite3_module dbstat_module = { 0, /* iVersion */ statConnect, /* xCreate */ statConnect, /* xConnect */ statBestIndex, /* xBestIndex */ statDisconnect, /* xDisconnect */ statDisconnect, /* xDestroy */ statOpen, /* xOpen - open a cursor */ statClose, /* xClose - close a cursor */ statFilter, /* xFilter - configure scan constraints */ statNext, /* xNext - advance a cursor */ statEof, /* xEof - check for end of scan */ statColumn, /* xColumn - read data */ statRowid, /* xRowid - read data */ 0, /* xUpdate */ 0, /* xBegin */ 0, /* xSync */ 0, /* xCommit */ 0, /* xRollback */ 0, /* xFindMethod */ 0, /* xRename */ }; sqlite3_create_module(db, "dbstat", &dbstat_module, 0); return SQLITE_OK; } #endif #ifdef SQLITE_TEST #include <tcl.h> static int test_dbstat( void *clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[] ){ #ifdef SQLITE_OMIT_VIRTUALTABLE Tcl_AppendResult(interp, "dbstat not available because of " "SQLITE_OMIT_VIRTUALTABLE", (void*)0); return TCL_ERROR; #else struct SqliteDb { sqlite3 *db; }; char *zDb; Tcl_CmdInfo cmdInfo; if( objc!=2 ){ Tcl_WrongNumArgs(interp, 1, objv, "DB"); return TCL_ERROR; } zDb = Tcl_GetString(objv[1]); if( Tcl_GetCommandInfo(interp, zDb, &cmdInfo) ){ sqlite3* db = ((struct SqliteDb*)cmdInfo.objClientData)->db; sqlite3_dbstat_register(db); } return TCL_OK; #endif } int SqlitetestStat_Init(Tcl_Interp *interp){ Tcl_CreateObjCommand(interp, "register_dbstat_vtab", test_dbstat, 0, 0); return TCL_OK; } #endif |
Changes to src/test_thread.c.
︙ | ︙ | |||
54 55 56 57 58 59 60 61 62 63 64 65 66 67 | static Tcl_ObjCmdProc sqlthread_proc; static Tcl_ObjCmdProc clock_seconds_proc; #if defined(SQLITE_OS_UNIX) && defined(SQLITE_ENABLE_UNLOCK_NOTIFY) static Tcl_ObjCmdProc blocking_step_proc; static Tcl_ObjCmdProc blocking_prepare_v2_proc; #endif int Sqlitetest1_Init(Tcl_Interp *); /* Functions from test1.c */ void *sqlite3TestTextToPtr(const char *); const char *sqlite3TestErrorName(int); int getDbPointer(Tcl_Interp *, const char *, sqlite3 **); int sqlite3TestMakePointerStr(Tcl_Interp *, char *, void *); int sqlite3TestErrCode(Tcl_Interp *, sqlite3 *, int); | > | 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 | static Tcl_ObjCmdProc sqlthread_proc; static Tcl_ObjCmdProc clock_seconds_proc; #if defined(SQLITE_OS_UNIX) && defined(SQLITE_ENABLE_UNLOCK_NOTIFY) static Tcl_ObjCmdProc blocking_step_proc; static Tcl_ObjCmdProc blocking_prepare_v2_proc; #endif int Sqlitetest1_Init(Tcl_Interp *); int Sqlite3_Init(Tcl_Interp *); /* Functions from test1.c */ void *sqlite3TestTextToPtr(const char *); const char *sqlite3TestErrorName(int); int getDbPointer(Tcl_Interp *, const char *, sqlite3 **); int sqlite3TestMakePointerStr(Tcl_Interp *, char *, void *); int sqlite3TestErrCode(Tcl_Interp *, sqlite3 *, int); |
︙ | ︙ | |||
120 121 122 123 124 125 126 127 128 129 130 131 132 133 | Tcl_CreateObjCommand(interp, "sqlite3_blocking_prepare_v2", blocking_prepare_v2_proc, (void *)1, 0); Tcl_CreateObjCommand(interp, "sqlite3_nonblocking_prepare_v2", blocking_prepare_v2_proc, 0, 0); #endif Sqlitetest1_Init(interp); Sqlitetest_mutex_Init(interp); rc = Tcl_Eval(interp, p->zScript); pRes = Tcl_GetObjResult(interp); pList = Tcl_NewObj(); Tcl_IncrRefCount(pList); Tcl_IncrRefCount(pRes); | > | 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 | Tcl_CreateObjCommand(interp, "sqlite3_blocking_prepare_v2", blocking_prepare_v2_proc, (void *)1, 0); Tcl_CreateObjCommand(interp, "sqlite3_nonblocking_prepare_v2", blocking_prepare_v2_proc, 0, 0); #endif Sqlitetest1_Init(interp); Sqlitetest_mutex_Init(interp); Sqlite3_Init(interp); rc = Tcl_Eval(interp, p->zScript); pRes = Tcl_GetObjResult(interp); pList = Tcl_NewObj(); Tcl_IncrRefCount(pList); Tcl_IncrRefCount(pRes); |
︙ | ︙ | |||
144 145 146 147 148 149 150 151 152 153 154 155 156 157 | Tcl_ListObjAppendElement(interp, pList, pRes); postToParent(p, pList); ckfree((void *)p); Tcl_DecrRefCount(pList); Tcl_DecrRefCount(pRes); Tcl_DeleteInterp(interp); TCL_THREAD_CREATE_RETURN; } /* ** sqlthread spawn VARNAME SCRIPT ** ** Spawn a new thread with its own Tcl interpreter and run the | > > | 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 | Tcl_ListObjAppendElement(interp, pList, pRes); postToParent(p, pList); ckfree((void *)p); Tcl_DecrRefCount(pList); Tcl_DecrRefCount(pRes); Tcl_DeleteInterp(interp); while( Tcl_DoOneEvent(TCL_ALL_EVENTS|TCL_DONT_WAIT) ); Tcl_ExitThread(0); TCL_THREAD_CREATE_RETURN; } /* ** sqlthread spawn VARNAME SCRIPT ** ** Spawn a new thread with its own Tcl interpreter and run the |
︙ | ︙ |
Added src/test_vfs.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 | /* ** 2010 May 05 ** ** The author disclaims copyright to this source code. In place of ** a legal notice, here is a blessing: ** ** May you do good and not evil. ** May you find forgiveness for yourself and forgive others. ** May you share freely, never taking more than you give. ** ****************************************************************************** ** */ #if SQLITE_TEST /* This file is used for testing only */ /* ** This file contains the implementation of the Tcl [testvfs] command, ** used to create SQLite VFS implementations with various properties and ** instrumentation to support testing SQLite. ** ** testvfs VFSNAME ?OPTIONS? ** ** Available options are: ** ** -noshm BOOLEAN (True to omit shm methods. Default false) ** -default BOOLEAN (True to make the vfs default. Default false) ** -szosfile INTEGER (Value for sqlite3_vfs.szOsFile) ** -mxpathname INTEGER (Value for sqlite3_vfs.mxPathname) */ #include "sqlite3.h" #include "sqliteInt.h" typedef struct Testvfs Testvfs; typedef struct TestvfsShm TestvfsShm; typedef struct TestvfsBuffer TestvfsBuffer; typedef struct TestvfsFile TestvfsFile; typedef struct TestvfsFd TestvfsFd; /* ** An open file handle. */ struct TestvfsFile { sqlite3_file base; /* Base class. Must be first */ TestvfsFd *pFd; /* File data */ }; #define tvfsGetFd(pFile) (((TestvfsFile *)pFile)->pFd) struct TestvfsFd { sqlite3_vfs *pVfs; /* The VFS */ const char *zFilename; /* Filename as passed to xOpen() */ sqlite3_file *pReal; /* The real, underlying file descriptor */ Tcl_Obj *pShmId; /* Shared memory id for Tcl callbacks */ TestvfsBuffer *pShm; /* Shared memory buffer */ u32 excllock; /* Mask of exclusive locks */ u32 sharedlock; /* Mask of shared locks */ TestvfsFd *pNext; /* Next handle opened on the same file */ }; #define FAULT_INJECT_NONE 0 #define FAULT_INJECT_TRANSIENT 1 #define FAULT_INJECT_PERSISTENT 2 typedef struct TestFaultInject TestFaultInject; struct TestFaultInject { int iCnt; /* Remaining calls before fault injection */ int eFault; /* A FAULT_INJECT_* value */ int nFail; /* Number of faults injected */ }; /* ** An instance of this structure is allocated for each VFS created. The ** sqlite3_vfs.pAppData field of the VFS structure registered with SQLite ** is set to point to it. */ struct Testvfs { char *zName; /* Name of this VFS */ sqlite3_vfs *pParent; /* The VFS to use for file IO */ sqlite3_vfs *pVfs; /* The testvfs registered with SQLite */ Tcl_Interp *interp; /* Interpreter to run script in */ Tcl_Obj *pScript; /* Script to execute */ int nScript; /* Number of elements in array apScript */ Tcl_Obj **apScript; /* Array version of pScript */ TestvfsBuffer *pBuffer; /* List of shared buffers */ int isNoshm; int mask; /* Mask controlling [script] and [ioerr] */ TestFaultInject ioerr_err; TestFaultInject full_err; TestFaultInject cantopen_err; #if 0 int iIoerrCnt; int ioerr; int nIoerrFail; int iFullCnt; int fullerr; int nFullFail; #endif int iDevchar; int iSectorsize; }; /* ** The Testvfs.mask variable is set to a combination of the following. ** If a bit is clear in Testvfs.mask, then calls made by SQLite to the ** corresponding VFS method is ignored for purposes of: ** ** + Simulating IO errors, and ** + Invoking the Tcl callback script. */ #define TESTVFS_SHMOPEN_MASK 0x00000001 #define TESTVFS_SHMLOCK_MASK 0x00000010 #define TESTVFS_SHMMAP_MASK 0x00000020 #define TESTVFS_SHMBARRIER_MASK 0x00000040 #define TESTVFS_SHMCLOSE_MASK 0x00000080 #define TESTVFS_OPEN_MASK 0x00000100 #define TESTVFS_SYNC_MASK 0x00000200 #define TESTVFS_DELETE_MASK 0x00000400 #define TESTVFS_CLOSE_MASK 0x00000800 #define TESTVFS_WRITE_MASK 0x00001000 #define TESTVFS_TRUNCATE_MASK 0x00002000 #define TESTVFS_ACCESS_MASK 0x00004000 #define TESTVFS_ALL_MASK 0x00007FFF #define TESTVFS_MAX_PAGES 1024 /* ** A shared-memory buffer. There is one of these objects for each shared ** memory region opened by clients. If two clients open the same file, ** there are two TestvfsFile structures but only one TestvfsBuffer structure. */ struct TestvfsBuffer { char *zFile; /* Associated file name */ int pgsz; /* Page size */ u8 *aPage[TESTVFS_MAX_PAGES]; /* Array of ckalloc'd pages */ TestvfsFd *pFile; /* List of open handles */ TestvfsBuffer *pNext; /* Next in linked list of all buffers */ }; #define PARENTVFS(x) (((Testvfs *)((x)->pAppData))->pParent) #define TESTVFS_MAX_ARGS 12 /* ** Method declarations for TestvfsFile. */ static int tvfsClose(sqlite3_file*); static int tvfsRead(sqlite3_file*, void*, int iAmt, sqlite3_int64 iOfst); static int tvfsWrite(sqlite3_file*,const void*,int iAmt, sqlite3_int64 iOfst); static int tvfsTruncate(sqlite3_file*, sqlite3_int64 size); static int tvfsSync(sqlite3_file*, int flags); static int tvfsFileSize(sqlite3_file*, sqlite3_int64 *pSize); static int tvfsLock(sqlite3_file*, int); static int tvfsUnlock(sqlite3_file*, int); static int tvfsCheckReservedLock(sqlite3_file*, int *); static int tvfsFileControl(sqlite3_file*, int op, void *pArg); static int tvfsSectorSize(sqlite3_file*); static int tvfsDeviceCharacteristics(sqlite3_file*); /* ** Method declarations for tvfs_vfs. */ static int tvfsOpen(sqlite3_vfs*, const char *, sqlite3_file*, int , int *); static int tvfsDelete(sqlite3_vfs*, const char *zName, int syncDir); static int tvfsAccess(sqlite3_vfs*, const char *zName, int flags, int *); static int tvfsFullPathname(sqlite3_vfs*, const char *zName, int, char *zOut); #ifndef SQLITE_OMIT_LOAD_EXTENSION static void *tvfsDlOpen(sqlite3_vfs*, const char *zFilename); static void tvfsDlError(sqlite3_vfs*, int nByte, char *zErrMsg); static void (*tvfsDlSym(sqlite3_vfs*,void*, const char *zSymbol))(void); static void tvfsDlClose(sqlite3_vfs*, void*); #endif /* SQLITE_OMIT_LOAD_EXTENSION */ static int tvfsRandomness(sqlite3_vfs*, int nByte, char *zOut); static int tvfsSleep(sqlite3_vfs*, int microseconds); static int tvfsCurrentTime(sqlite3_vfs*, double*); static int tvfsShmOpen(sqlite3_file*); static int tvfsShmLock(sqlite3_file*, int , int, int); static int tvfsShmMap(sqlite3_file*,int,int,int, void volatile **); static void tvfsShmBarrier(sqlite3_file*); static int tvfsShmUnmap(sqlite3_file*, int); static sqlite3_io_methods tvfs_io_methods = { 2, /* iVersion */ tvfsClose, /* xClose */ tvfsRead, /* xRead */ tvfsWrite, /* xWrite */ tvfsTruncate, /* xTruncate */ tvfsSync, /* xSync */ tvfsFileSize, /* xFileSize */ tvfsLock, /* xLock */ tvfsUnlock, /* xUnlock */ tvfsCheckReservedLock, /* xCheckReservedLock */ tvfsFileControl, /* xFileControl */ tvfsSectorSize, /* xSectorSize */ tvfsDeviceCharacteristics, /* xDeviceCharacteristics */ tvfsShmMap, /* xShmMap */ tvfsShmLock, /* xShmLock */ tvfsShmBarrier, /* xShmBarrier */ tvfsShmUnmap /* xShmUnmap */ }; static int tvfsResultCode(Testvfs *p, int *pRc){ struct errcode { int eCode; const char *zCode; } aCode[] = { { SQLITE_OK, "SQLITE_OK" }, { SQLITE_ERROR, "SQLITE_ERROR" }, { SQLITE_IOERR, "SQLITE_IOERR" }, { SQLITE_LOCKED, "SQLITE_LOCKED" }, { SQLITE_BUSY, "SQLITE_BUSY" }, }; const char *z; int i; z = Tcl_GetStringResult(p->interp); for(i=0; i<ArraySize(aCode); i++){ if( 0==strcmp(z, aCode[i].zCode) ){ *pRc = aCode[i].eCode; return 1; } } return 0; } static int tvfsInjectFault(TestFaultInject *p){ int ret = 0; if( p->eFault ){ p->iCnt--; if( p->iCnt==0 || (p->iCnt<0 && p->eFault==FAULT_INJECT_PERSISTENT ) ){ ret = 1; p->nFail++; } } return ret; } static int tvfsInjectIoerr(Testvfs *p){ return tvfsInjectFault(&p->ioerr_err); } static int tvfsInjectFullerr(Testvfs *p){ return tvfsInjectFault(&p->full_err); } static int tvfsInjectCantopenerr(Testvfs *p){ return tvfsInjectFault(&p->cantopen_err); } static void tvfsExecTcl( Testvfs *p, const char *zMethod, Tcl_Obj *arg1, Tcl_Obj *arg2, Tcl_Obj *arg3 ){ int rc; /* Return code from Tcl_EvalObj() */ int nArg; /* Elements in eval'd list */ int nScript; Tcl_Obj ** ap; assert( p->pScript ); if( !p->apScript ){ int nByte; int i; if( TCL_OK!=Tcl_ListObjGetElements(p->interp, p->pScript, &nScript, &ap) ){ Tcl_BackgroundError(p->interp); Tcl_ResetResult(p->interp); return; } p->nScript = nScript; nByte = (nScript+TESTVFS_MAX_ARGS)*sizeof(Tcl_Obj *); p->apScript = (Tcl_Obj **)ckalloc(nByte); memset(p->apScript, 0, nByte); for(i=0; i<nScript; i++){ p->apScript[i] = ap[i]; } } p->apScript[p->nScript] = Tcl_NewStringObj(zMethod, -1); p->apScript[p->nScript+1] = arg1; p->apScript[p->nScript+2] = arg2; p->apScript[p->nScript+3] = arg3; for(nArg=p->nScript; p->apScript[nArg]; nArg++){ Tcl_IncrRefCount(p->apScript[nArg]); } rc = Tcl_EvalObjv(p->interp, nArg, p->apScript, TCL_EVAL_GLOBAL); if( rc!=TCL_OK ){ Tcl_BackgroundError(p->interp); Tcl_ResetResult(p->interp); } for(nArg=p->nScript; p->apScript[nArg]; nArg++){ Tcl_DecrRefCount(p->apScript[nArg]); p->apScript[nArg] = 0; } } /* ** Close an tvfs-file. */ static int tvfsClose(sqlite3_file *pFile){ int rc; TestvfsFile *pTestfile = (TestvfsFile *)pFile; TestvfsFd *pFd = pTestfile->pFd; Testvfs *p = (Testvfs *)pFd->pVfs->pAppData; if( p->pScript && p->mask&TESTVFS_CLOSE_MASK ){ tvfsExecTcl(p, "xClose", Tcl_NewStringObj(pFd->zFilename, -1), pFd->pShmId, 0 ); } if( pFd->pShmId ){ Tcl_DecrRefCount(pFd->pShmId); pFd->pShmId = 0; } if( pFile->pMethods ){ ckfree((char *)pFile->pMethods); } rc = sqlite3OsClose(pFd->pReal); ckfree((char *)pFd); pTestfile->pFd = 0; return rc; } /* ** Read data from an tvfs-file. */ static int tvfsRead( sqlite3_file *pFile, void *zBuf, int iAmt, sqlite_int64 iOfst ){ TestvfsFd *p = tvfsGetFd(pFile); return sqlite3OsRead(p->pReal, zBuf, iAmt, iOfst); } /* ** Write data to an tvfs-file. */ static int tvfsWrite( sqlite3_file *pFile, const void *zBuf, int iAmt, sqlite_int64 iOfst ){ int rc = SQLITE_OK; TestvfsFd *pFd = tvfsGetFd(pFile); Testvfs *p = (Testvfs *)pFd->pVfs->pAppData; if( p->pScript && p->mask&TESTVFS_WRITE_MASK ){ tvfsExecTcl(p, "xWrite", Tcl_NewStringObj(pFd->zFilename, -1), pFd->pShmId, 0 ); tvfsResultCode(p, &rc); } if( rc==SQLITE_OK && tvfsInjectFullerr(p) ){ rc = SQLITE_FULL; } if( rc==SQLITE_OK && p->mask&TESTVFS_WRITE_MASK && tvfsInjectIoerr(p) ){ rc = SQLITE_IOERR; } if( rc==SQLITE_OK ){ rc = sqlite3OsWrite(pFd->pReal, zBuf, iAmt, iOfst); } return rc; } /* ** Truncate an tvfs-file. */ static int tvfsTruncate(sqlite3_file *pFile, sqlite_int64 size){ int rc = SQLITE_OK; TestvfsFd *pFd = tvfsGetFd(pFile); Testvfs *p = (Testvfs *)pFd->pVfs->pAppData; if( p->pScript && p->mask&TESTVFS_TRUNCATE_MASK ){ tvfsExecTcl(p, "xTruncate", Tcl_NewStringObj(pFd->zFilename, -1), pFd->pShmId, 0 ); tvfsResultCode(p, &rc); } if( rc==SQLITE_OK ){ rc = sqlite3OsTruncate(pFd->pReal, size); } return rc; } /* ** Sync an tvfs-file. */ static int tvfsSync(sqlite3_file *pFile, int flags){ int rc = SQLITE_OK; TestvfsFd *pFd = tvfsGetFd(pFile); Testvfs *p = (Testvfs *)pFd->pVfs->pAppData; if( p->pScript && p->mask&TESTVFS_SYNC_MASK ){ char *zFlags; switch( flags ){ case SQLITE_SYNC_NORMAL: zFlags = "normal"; break; case SQLITE_SYNC_FULL: zFlags = "full"; break; case SQLITE_SYNC_NORMAL|SQLITE_SYNC_DATAONLY: zFlags = "normal|dataonly"; break; case SQLITE_SYNC_FULL|SQLITE_SYNC_DATAONLY: zFlags = "full|dataonly"; break; default: assert(0); } tvfsExecTcl(p, "xSync", Tcl_NewStringObj(pFd->zFilename, -1), pFd->pShmId, Tcl_NewStringObj(zFlags, -1) ); tvfsResultCode(p, &rc); } if( rc==SQLITE_OK && tvfsInjectFullerr(p) ) rc = SQLITE_FULL; if( rc==SQLITE_OK ){ rc = sqlite3OsSync(pFd->pReal, flags); } return rc; } /* ** Return the current file-size of an tvfs-file. */ static int tvfsFileSize(sqlite3_file *pFile, sqlite_int64 *pSize){ TestvfsFd *p = tvfsGetFd(pFile); return sqlite3OsFileSize(p->pReal, pSize); } /* ** Lock an tvfs-file. */ static int tvfsLock(sqlite3_file *pFile, int eLock){ TestvfsFd *p = tvfsGetFd(pFile); return sqlite3OsLock(p->pReal, eLock); } /* ** Unlock an tvfs-file. */ static int tvfsUnlock(sqlite3_file *pFile, int eLock){ TestvfsFd *p = tvfsGetFd(pFile); return sqlite3OsUnlock(p->pReal, eLock); } /* ** Check if another file-handle holds a RESERVED lock on an tvfs-file. */ static int tvfsCheckReservedLock(sqlite3_file *pFile, int *pResOut){ TestvfsFd *p = tvfsGetFd(pFile); return sqlite3OsCheckReservedLock(p->pReal, pResOut); } /* ** File control method. For custom operations on an tvfs-file. */ static int tvfsFileControl(sqlite3_file *pFile, int op, void *pArg){ TestvfsFd *p = tvfsGetFd(pFile); return sqlite3OsFileControl(p->pReal, op, pArg); } /* ** Return the sector-size in bytes for an tvfs-file. */ static int tvfsSectorSize(sqlite3_file *pFile){ TestvfsFd *pFd = tvfsGetFd(pFile); Testvfs *p = (Testvfs *)pFd->pVfs->pAppData; if( p->iSectorsize>=0 ){ return p->iSectorsize; } return sqlite3OsSectorSize(pFd->pReal); } /* ** Return the device characteristic flags supported by an tvfs-file. */ static int tvfsDeviceCharacteristics(sqlite3_file *pFile){ TestvfsFd *pFd = tvfsGetFd(pFile); Testvfs *p = (Testvfs *)pFd->pVfs->pAppData; if( p->iDevchar>=0 ){ return p->iDevchar; } return sqlite3OsDeviceCharacteristics(pFd->pReal); } /* ** Open an tvfs file handle. */ static int tvfsOpen( sqlite3_vfs *pVfs, const char *zName, sqlite3_file *pFile, int flags, int *pOutFlags ){ int rc; TestvfsFile *pTestfile = (TestvfsFile *)pFile; TestvfsFd *pFd; Tcl_Obj *pId = 0; Testvfs *p = (Testvfs *)pVfs->pAppData; pFd = (TestvfsFd *)ckalloc(sizeof(TestvfsFd) + PARENTVFS(pVfs)->szOsFile); memset(pFd, 0, sizeof(TestvfsFd) + PARENTVFS(pVfs)->szOsFile); pFd->pShm = 0; pFd->pShmId = 0; pFd->zFilename = zName; pFd->pVfs = pVfs; pFd->pReal = (sqlite3_file *)&pFd[1]; pTestfile->pFd = pFd; /* Evaluate the Tcl script: ** ** SCRIPT xOpen FILENAME ** ** If the script returns an SQLite error code other than SQLITE_OK, an ** error is returned to the caller. If it returns SQLITE_OK, the new ** connection is named "anon". Otherwise, the value returned by the ** script is used as the connection name. */ Tcl_ResetResult(p->interp); if( p->pScript && p->mask&TESTVFS_OPEN_MASK ){ tvfsExecTcl(p, "xOpen", Tcl_NewStringObj(pFd->zFilename, -1), 0, 0); if( tvfsResultCode(p, &rc) ){ if( rc!=SQLITE_OK ) return rc; }else{ pId = Tcl_GetObjResult(p->interp); } } if( (p->mask&TESTVFS_OPEN_MASK) && tvfsInjectIoerr(p) ) return SQLITE_IOERR; if( tvfsInjectCantopenerr(p) ) return SQLITE_CANTOPEN; if( tvfsInjectFullerr(p) ) return SQLITE_FULL; if( !pId ){ pId = Tcl_NewStringObj("anon", -1); } Tcl_IncrRefCount(pId); pFd->pShmId = pId; Tcl_ResetResult(p->interp); rc = sqlite3OsOpen(PARENTVFS(pVfs), zName, pFd->pReal, flags, pOutFlags); if( pFd->pReal->pMethods ){ sqlite3_io_methods *pMethods; int nByte; if( pVfs->iVersion>1 ){ nByte = sizeof(sqlite3_io_methods); }else{ nByte = offsetof(sqlite3_io_methods, xShmMap); } pMethods = (sqlite3_io_methods *)ckalloc(nByte); memcpy(pMethods, &tvfs_io_methods, nByte); pMethods->iVersion = pVfs->iVersion; if( pVfs->iVersion>1 && ((Testvfs *)pVfs->pAppData)->isNoshm ){ pMethods->xShmUnmap = 0; pMethods->xShmLock = 0; pMethods->xShmBarrier = 0; pMethods->xShmMap = 0; } pFile->pMethods = pMethods; } return rc; } /* ** Delete the file located at zPath. If the dirSync argument is true, ** ensure the file-system modifications are synced to disk before ** returning. */ static int tvfsDelete(sqlite3_vfs *pVfs, const char *zPath, int dirSync){ int rc = SQLITE_OK; Testvfs *p = (Testvfs *)pVfs->pAppData; if( p->pScript && p->mask&TESTVFS_DELETE_MASK ){ tvfsExecTcl(p, "xDelete", Tcl_NewStringObj(zPath, -1), Tcl_NewIntObj(dirSync), 0 ); tvfsResultCode(p, &rc); } if( rc==SQLITE_OK ){ rc = sqlite3OsDelete(PARENTVFS(pVfs), zPath, dirSync); } return rc; } /* ** Test for access permissions. Return true if the requested permission ** is available, or false otherwise. */ static int tvfsAccess( sqlite3_vfs *pVfs, const char *zPath, int flags, int *pResOut ){ Testvfs *p = (Testvfs *)pVfs->pAppData; if( p->pScript && p->mask&TESTVFS_ACCESS_MASK ){ int rc; char *zArg = 0; if( flags==SQLITE_ACCESS_EXISTS ) zArg = "SQLITE_ACCESS_EXISTS"; if( flags==SQLITE_ACCESS_READWRITE ) zArg = "SQLITE_ACCESS_READWRITE"; if( flags==SQLITE_ACCESS_READ ) zArg = "SQLITE_ACCESS_READ"; tvfsExecTcl(p, "xAccess", Tcl_NewStringObj(zPath, -1), Tcl_NewStringObj(zArg, -1), 0 ); if( tvfsResultCode(p, &rc) ){ if( rc!=SQLITE_OK ) return rc; }else{ Tcl_Interp *interp = p->interp; if( TCL_OK==Tcl_GetBooleanFromObj(0, Tcl_GetObjResult(interp), pResOut) ){ return SQLITE_OK; } } } return sqlite3OsAccess(PARENTVFS(pVfs), zPath, flags, pResOut); } /* ** Populate buffer zOut with the full canonical pathname corresponding ** to the pathname in zPath. zOut is guaranteed to point to a buffer ** of at least (DEVSYM_MAX_PATHNAME+1) bytes. */ static int tvfsFullPathname( sqlite3_vfs *pVfs, const char *zPath, int nOut, char *zOut ){ return sqlite3OsFullPathname(PARENTVFS(pVfs), zPath, nOut, zOut); } #ifndef SQLITE_OMIT_LOAD_EXTENSION /* ** Open the dynamic library located at zPath and return a handle. */ static void *tvfsDlOpen(sqlite3_vfs *pVfs, const char *zPath){ return sqlite3OsDlOpen(PARENTVFS(pVfs), zPath); } /* ** Populate the buffer zErrMsg (size nByte bytes) with a human readable ** utf-8 string describing the most recent error encountered associated ** with dynamic libraries. */ static void tvfsDlError(sqlite3_vfs *pVfs, int nByte, char *zErrMsg){ sqlite3OsDlError(PARENTVFS(pVfs), nByte, zErrMsg); } /* ** Return a pointer to the symbol zSymbol in the dynamic library pHandle. */ static void (*tvfsDlSym(sqlite3_vfs *pVfs, void *p, const char *zSym))(void){ return sqlite3OsDlSym(PARENTVFS(pVfs), p, zSym); } /* ** Close the dynamic library handle pHandle. */ static void tvfsDlClose(sqlite3_vfs *pVfs, void *pHandle){ sqlite3OsDlClose(PARENTVFS(pVfs), pHandle); } #endif /* SQLITE_OMIT_LOAD_EXTENSION */ /* ** Populate the buffer pointed to by zBufOut with nByte bytes of ** random data. */ static int tvfsRandomness(sqlite3_vfs *pVfs, int nByte, char *zBufOut){ return sqlite3OsRandomness(PARENTVFS(pVfs), nByte, zBufOut); } /* ** Sleep for nMicro microseconds. Return the number of microseconds ** actually slept. */ static int tvfsSleep(sqlite3_vfs *pVfs, int nMicro){ return sqlite3OsSleep(PARENTVFS(pVfs), nMicro); } /* ** Return the current time as a Julian Day number in *pTimeOut. */ static int tvfsCurrentTime(sqlite3_vfs *pVfs, double *pTimeOut){ return PARENTVFS(pVfs)->xCurrentTime(PARENTVFS(pVfs), pTimeOut); } static int tvfsShmOpen(sqlite3_file *pFile){ Testvfs *p; int rc = SQLITE_OK; /* Return code */ TestvfsBuffer *pBuffer; /* Buffer to open connection to */ TestvfsFd *pFd; /* The testvfs file structure */ pFd = tvfsGetFd(pFile); p = (Testvfs *)pFd->pVfs->pAppData; assert( pFd->pShmId && pFd->pShm==0 && pFd->pNext==0 ); /* Evaluate the Tcl script: ** ** SCRIPT xShmOpen FILENAME */ Tcl_ResetResult(p->interp); if( p->pScript && p->mask&TESTVFS_SHMOPEN_MASK ){ tvfsExecTcl(p, "xShmOpen", Tcl_NewStringObj(pFd->zFilename, -1), 0, 0); if( tvfsResultCode(p, &rc) ){ if( rc!=SQLITE_OK ) return rc; } } assert( rc==SQLITE_OK ); if( p->mask&TESTVFS_SHMOPEN_MASK && tvfsInjectIoerr(p) ){ return SQLITE_IOERR; } /* Search for a TestvfsBuffer. Create a new one if required. */ for(pBuffer=p->pBuffer; pBuffer; pBuffer=pBuffer->pNext){ if( 0==strcmp(pFd->zFilename, pBuffer->zFile) ) break; } if( !pBuffer ){ int nByte = sizeof(TestvfsBuffer) + strlen(pFd->zFilename) + 1; pBuffer = (TestvfsBuffer *)ckalloc(nByte); memset(pBuffer, 0, nByte); pBuffer->zFile = (char *)&pBuffer[1]; strcpy(pBuffer->zFile, pFd->zFilename); pBuffer->pNext = p->pBuffer; p->pBuffer = pBuffer; } /* Connect the TestvfsBuffer to the new TestvfsShm handle and return. */ pFd->pNext = pBuffer->pFile; pBuffer->pFile = pFd; pFd->pShm = pBuffer; return SQLITE_OK; } static void tvfsAllocPage(TestvfsBuffer *p, int iPage, int pgsz){ assert( iPage<TESTVFS_MAX_PAGES ); if( p->aPage[iPage]==0 ){ p->aPage[iPage] = (u8 *)ckalloc(pgsz); memset(p->aPage[iPage], 0, pgsz); p->pgsz = pgsz; } } static int tvfsShmMap( sqlite3_file *pFile, /* Handle open on database file */ int iPage, /* Page to retrieve */ int pgsz, /* Size of pages */ int isWrite, /* True to extend file if necessary */ void volatile **pp /* OUT: Mapped memory */ ){ int rc = SQLITE_OK; TestvfsFd *pFd = tvfsGetFd(pFile); Testvfs *p = (Testvfs *)(pFd->pVfs->pAppData); if( 0==pFd->pShm ){ rc = tvfsShmOpen(pFile); if( rc!=SQLITE_OK ){ return rc; } } if( p->pScript && p->mask&TESTVFS_SHMMAP_MASK ){ Tcl_Obj *pArg = Tcl_NewObj(); Tcl_IncrRefCount(pArg); Tcl_ListObjAppendElement(p->interp, pArg, Tcl_NewIntObj(iPage)); Tcl_ListObjAppendElement(p->interp, pArg, Tcl_NewIntObj(pgsz)); Tcl_ListObjAppendElement(p->interp, pArg, Tcl_NewIntObj(isWrite)); tvfsExecTcl(p, "xShmMap", Tcl_NewStringObj(pFd->pShm->zFile, -1), pFd->pShmId, pArg ); tvfsResultCode(p, &rc); Tcl_DecrRefCount(pArg); } if( rc==SQLITE_OK && p->mask&TESTVFS_SHMMAP_MASK && tvfsInjectIoerr(p) ){ rc = SQLITE_IOERR; } if( rc==SQLITE_OK && isWrite && !pFd->pShm->aPage[iPage] ){ tvfsAllocPage(pFd->pShm, iPage, pgsz); } *pp = (void volatile *)pFd->pShm->aPage[iPage]; return rc; } static int tvfsShmLock( sqlite3_file *pFile, int ofst, int n, int flags ){ int rc = SQLITE_OK; TestvfsFd *pFd = tvfsGetFd(pFile); Testvfs *p = (Testvfs *)(pFd->pVfs->pAppData); int nLock; char zLock[80]; if( p->pScript && p->mask&TESTVFS_SHMLOCK_MASK ){ sqlite3_snprintf(sizeof(zLock), zLock, "%d %d", ofst, n); nLock = strlen(zLock); if( flags & SQLITE_SHM_LOCK ){ strcpy(&zLock[nLock], " lock"); }else{ strcpy(&zLock[nLock], " unlock"); } nLock += strlen(&zLock[nLock]); if( flags & SQLITE_SHM_SHARED ){ strcpy(&zLock[nLock], " shared"); }else{ strcpy(&zLock[nLock], " exclusive"); } tvfsExecTcl(p, "xShmLock", Tcl_NewStringObj(pFd->pShm->zFile, -1), pFd->pShmId, Tcl_NewStringObj(zLock, -1) ); tvfsResultCode(p, &rc); } if( rc==SQLITE_OK && p->mask&TESTVFS_SHMLOCK_MASK && tvfsInjectIoerr(p) ){ rc = SQLITE_IOERR; } if( rc==SQLITE_OK ){ int isLock = (flags & SQLITE_SHM_LOCK); int isExcl = (flags & SQLITE_SHM_EXCLUSIVE); u32 mask = (((1<<n)-1) << ofst); if( isLock ){ TestvfsFd *p2; for(p2=pFd->pShm->pFile; p2; p2=p2->pNext){ if( p2==pFd ) continue; if( (p2->excllock&mask) || (isExcl && p2->sharedlock&mask) ){ rc = SQLITE_BUSY; break; } } if( rc==SQLITE_OK ){ if( isExcl ) pFd->excllock |= mask; if( !isExcl ) pFd->sharedlock |= mask; } }else{ if( isExcl ) pFd->excllock &= (~mask); if( !isExcl ) pFd->sharedlock &= (~mask); } } return rc; } static void tvfsShmBarrier(sqlite3_file *pFile){ TestvfsFd *pFd = tvfsGetFd(pFile); Testvfs *p = (Testvfs *)(pFd->pVfs->pAppData); if( p->pScript && p->mask&TESTVFS_SHMBARRIER_MASK ){ tvfsExecTcl(p, "xShmBarrier", Tcl_NewStringObj(pFd->pShm->zFile, -1), pFd->pShmId, 0 ); } } static int tvfsShmUnmap( sqlite3_file *pFile, int deleteFlag ){ int rc = SQLITE_OK; TestvfsFd *pFd = tvfsGetFd(pFile); Testvfs *p = (Testvfs *)(pFd->pVfs->pAppData); TestvfsBuffer *pBuffer = pFd->pShm; TestvfsFd **ppFd; if( !pBuffer ) return SQLITE_OK; assert( pFd->pShmId && pFd->pShm ); if( p->pScript && p->mask&TESTVFS_SHMCLOSE_MASK ){ tvfsExecTcl(p, "xShmUnmap", Tcl_NewStringObj(pFd->pShm->zFile, -1), pFd->pShmId, 0 ); tvfsResultCode(p, &rc); } for(ppFd=&pBuffer->pFile; *ppFd!=pFd; ppFd=&((*ppFd)->pNext)); assert( (*ppFd)==pFd ); *ppFd = pFd->pNext; pFd->pNext = 0; if( pBuffer->pFile==0 ){ int i; TestvfsBuffer **pp; for(pp=&p->pBuffer; *pp!=pBuffer; pp=&((*pp)->pNext)); *pp = (*pp)->pNext; for(i=0; pBuffer->aPage[i]; i++){ ckfree((char *)pBuffer->aPage[i]); } ckfree((char *)pBuffer); } pFd->pShm = 0; return rc; } static int testvfs_obj_cmd( ClientData cd, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[] ){ Testvfs *p = (Testvfs *)cd; enum DB_enum { CMD_SHM, CMD_DELETE, CMD_FILTER, CMD_IOERR, CMD_SCRIPT, CMD_DEVCHAR, CMD_SECTORSIZE, CMD_FULLERR, CMD_CANTOPENERR }; struct TestvfsSubcmd { char *zName; enum DB_enum eCmd; } aSubcmd[] = { { "shm", CMD_SHM }, { "delete", CMD_DELETE }, { "filter", CMD_FILTER }, { "ioerr", CMD_IOERR }, { "fullerr", CMD_FULLERR }, { "cantopenerr", CMD_CANTOPENERR }, { "script", CMD_SCRIPT }, { "devchar", CMD_DEVCHAR }, { "sectorsize", CMD_SECTORSIZE }, { 0, 0 } }; int i; if( objc<2 ){ Tcl_WrongNumArgs(interp, 1, objv, "SUBCOMMAND ..."); return TCL_ERROR; } if( Tcl_GetIndexFromObjStruct( interp, objv[1], aSubcmd, sizeof(aSubcmd[0]), "subcommand", 0, &i) ){ return TCL_ERROR; } Tcl_ResetResult(interp); switch( aSubcmd[i].eCmd ){ case CMD_SHM: { Tcl_Obj *pObj; int i; TestvfsBuffer *pBuffer; char *zName; if( objc!=3 && objc!=4 ){ Tcl_WrongNumArgs(interp, 2, objv, "FILE ?VALUE?"); return TCL_ERROR; } zName = ckalloc(p->pParent->mxPathname); p->pParent->xFullPathname( p->pParent, Tcl_GetString(objv[2]), p->pParent->mxPathname, zName ); for(pBuffer=p->pBuffer; pBuffer; pBuffer=pBuffer->pNext){ if( 0==strcmp(pBuffer->zFile, zName) ) break; } ckfree(zName); if( !pBuffer ){ Tcl_AppendResult(interp, "no such file: ", Tcl_GetString(objv[2]), 0); return TCL_ERROR; } if( objc==4 ){ int n; u8 *a = Tcl_GetByteArrayFromObj(objv[3], &n); int pgsz = pBuffer->pgsz; if( pgsz==0 ) pgsz = 32768; for(i=0; i*pgsz<n; i++){ int nByte = pgsz; tvfsAllocPage(pBuffer, i, pgsz); if( n-i*pgsz<pgsz ){ nByte = n; } memcpy(pBuffer->aPage[i], &a[i*pgsz], nByte); } } pObj = Tcl_NewObj(); for(i=0; pBuffer->aPage[i]; i++){ int pgsz = pBuffer->pgsz; if( pgsz==0 ) pgsz = 32768; Tcl_AppendObjToObj(pObj, Tcl_NewByteArrayObj(pBuffer->aPage[i], pgsz)); } Tcl_SetObjResult(interp, pObj); break; } case CMD_FILTER: { static struct VfsMethod { char *zName; int mask; } vfsmethod [] = { { "xShmOpen", TESTVFS_SHMOPEN_MASK }, { "xShmLock", TESTVFS_SHMLOCK_MASK }, { "xShmBarrier", TESTVFS_SHMBARRIER_MASK }, { "xShmUnmap", TESTVFS_SHMCLOSE_MASK }, { "xShmMap", TESTVFS_SHMMAP_MASK }, { "xSync", TESTVFS_SYNC_MASK }, { "xDelete", TESTVFS_DELETE_MASK }, { "xWrite", TESTVFS_WRITE_MASK }, { "xTruncate", TESTVFS_TRUNCATE_MASK }, { "xOpen", TESTVFS_OPEN_MASK }, { "xClose", TESTVFS_CLOSE_MASK }, { "xAccess", TESTVFS_ACCESS_MASK }, }; Tcl_Obj **apElem = 0; int nElem = 0; int i; int mask = 0; if( objc!=3 ){ Tcl_WrongNumArgs(interp, 2, objv, "LIST"); return TCL_ERROR; } if( Tcl_ListObjGetElements(interp, objv[2], &nElem, &apElem) ){ return TCL_ERROR; } Tcl_ResetResult(interp); for(i=0; i<nElem; i++){ int iMethod; char *zElem = Tcl_GetString(apElem[i]); for(iMethod=0; iMethod<ArraySize(vfsmethod); iMethod++){ if( strcmp(zElem, vfsmethod[iMethod].zName)==0 ){ mask |= vfsmethod[iMethod].mask; break; } } if( iMethod==ArraySize(vfsmethod) ){ Tcl_AppendResult(interp, "unknown method: ", zElem, 0); return TCL_ERROR; } } p->mask = mask; break; } case CMD_SCRIPT: { if( objc==3 ){ int nByte; if( p->pScript ){ Tcl_DecrRefCount(p->pScript); ckfree((char *)p->apScript); p->apScript = 0; p->nScript = 0; p->pScript = 0; } Tcl_GetStringFromObj(objv[2], &nByte); if( nByte>0 ){ p->pScript = Tcl_DuplicateObj(objv[2]); Tcl_IncrRefCount(p->pScript); } }else if( objc!=2 ){ Tcl_WrongNumArgs(interp, 2, objv, "?SCRIPT?"); return TCL_ERROR; } Tcl_ResetResult(interp); if( p->pScript ) Tcl_SetObjResult(interp, p->pScript); break; } /* ** TESTVFS ioerr ?IFAIL PERSIST? ** ** Where IFAIL is an integer and PERSIST is boolean. */ case CMD_CANTOPENERR: case CMD_IOERR: case CMD_FULLERR: { TestFaultInject *pTest; int iRet; switch( aSubcmd[i].eCmd ){ case CMD_IOERR: pTest = &p->ioerr_err; break; case CMD_FULLERR: pTest = &p->full_err; break; case CMD_CANTOPENERR: pTest = &p->cantopen_err; break; default: assert(0); } iRet = pTest->nFail; pTest->nFail = 0; pTest->eFault = 0; pTest->iCnt = 0; if( objc==4 ){ int iCnt, iPersist; if( TCL_OK!=Tcl_GetIntFromObj(interp, objv[2], &iCnt) || TCL_OK!=Tcl_GetBooleanFromObj(interp, objv[3], &iPersist) ){ return TCL_ERROR; } pTest->eFault = iPersist?FAULT_INJECT_PERSISTENT:FAULT_INJECT_TRANSIENT; pTest->iCnt = iCnt; }else if( objc!=2 ){ Tcl_WrongNumArgs(interp, 2, objv, "?CNT PERSIST?"); return TCL_ERROR; } Tcl_SetObjResult(interp, Tcl_NewIntObj(iRet)); break; } case CMD_DELETE: { Tcl_DeleteCommand(interp, Tcl_GetString(objv[0])); break; } case CMD_DEVCHAR: { struct DeviceFlag { char *zName; int iValue; } aFlag[] = { { "default", -1 }, { "atomic", SQLITE_IOCAP_ATOMIC }, { "atomic512", SQLITE_IOCAP_ATOMIC512 }, { "atomic1k", SQLITE_IOCAP_ATOMIC1K }, { "atomic2k", SQLITE_IOCAP_ATOMIC2K }, { "atomic4k", SQLITE_IOCAP_ATOMIC4K }, { "atomic8k", SQLITE_IOCAP_ATOMIC8K }, { "atomic16k", SQLITE_IOCAP_ATOMIC16K }, { "atomic32k", SQLITE_IOCAP_ATOMIC32K }, { "atomic64k", SQLITE_IOCAP_ATOMIC64K }, { "sequential", SQLITE_IOCAP_SEQUENTIAL }, { "safe_append", SQLITE_IOCAP_SAFE_APPEND }, { "undeletable_when_open", SQLITE_IOCAP_UNDELETABLE_WHEN_OPEN }, { 0, 0 } }; Tcl_Obj *pRet; int iFlag; if( objc>3 ){ Tcl_WrongNumArgs(interp, 2, objv, "?ATTR-LIST?"); return TCL_ERROR; } if( objc==3 ){ int j; int iNew = 0; Tcl_Obj **flags = 0; int nFlags = 0; if( Tcl_ListObjGetElements(interp, objv[2], &nFlags, &flags) ){ return TCL_ERROR; } for(j=0; j<nFlags; j++){ int idx = 0; if( Tcl_GetIndexFromObjStruct(interp, flags[j], aFlag, sizeof(aFlag[0]), "flag", 0, &idx) ){ return TCL_ERROR; } if( aFlag[idx].iValue<0 && nFlags>1 ){ Tcl_AppendResult(interp, "bad flags: ", Tcl_GetString(objv[2]), 0); return TCL_ERROR; } iNew |= aFlag[idx].iValue; } p->iDevchar = iNew; } pRet = Tcl_NewObj(); for(iFlag=0; iFlag<sizeof(aFlag)/sizeof(aFlag[0]); iFlag++){ if( p->iDevchar & aFlag[iFlag].iValue ){ Tcl_ListObjAppendElement( interp, pRet, Tcl_NewStringObj(aFlag[iFlag].zName, -1) ); } } Tcl_SetObjResult(interp, pRet); break; } case CMD_SECTORSIZE: { if( objc>3 ){ Tcl_WrongNumArgs(interp, 2, objv, "?VALUE?"); return TCL_ERROR; } if( objc==3 ){ int iNew = 0; if( Tcl_GetIntFromObj(interp, objv[2], &iNew) ){ return TCL_ERROR; } p->iSectorsize = iNew; } Tcl_SetObjResult(interp, Tcl_NewIntObj(p->iSectorsize)); break; } } return TCL_OK; } static void testvfs_obj_del(ClientData cd){ Testvfs *p = (Testvfs *)cd; if( p->pScript ) Tcl_DecrRefCount(p->pScript); sqlite3_vfs_unregister(p->pVfs); ckfree((char *)p->apScript); ckfree((char *)p->pVfs); ckfree((char *)p); } /* ** Usage: testvfs VFSNAME ?SWITCHES? ** ** Switches are: ** ** -noshm BOOLEAN (True to omit shm methods. Default false) ** -default BOOLEAN (True to make the vfs default. Default false) ** ** This command creates two things when it is invoked: an SQLite VFS, and ** a Tcl command. Both are named VFSNAME. The VFS is installed. It is not ** installed as the default VFS. ** ** The VFS passes all file I/O calls through to the underlying VFS. ** ** Whenever the xShmMap method of the VFS ** is invoked, the SCRIPT is executed as follows: ** ** SCRIPT xShmMap FILENAME ID ** ** The value returned by the invocation of SCRIPT above is interpreted as ** an SQLite error code and returned to SQLite. Either a symbolic ** "SQLITE_OK" or numeric "0" value may be returned. ** ** The contents of the shared-memory buffer associated with a given file ** may be read and set using the following command: ** ** VFSNAME shm FILENAME ?NEWVALUE? ** ** When the xShmLock method is invoked by SQLite, the following script is ** run: ** ** SCRIPT xShmLock FILENAME ID LOCK ** ** where LOCK is of the form "OFFSET NBYTE lock/unlock shared/exclusive" */ static int testvfs_cmd( ClientData cd, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[] ){ static sqlite3_vfs tvfs_vfs = { 2, /* iVersion */ 0, /* szOsFile */ 0, /* mxPathname */ 0, /* pNext */ 0, /* zName */ 0, /* pAppData */ tvfsOpen, /* xOpen */ tvfsDelete, /* xDelete */ tvfsAccess, /* xAccess */ tvfsFullPathname, /* xFullPathname */ #ifndef SQLITE_OMIT_LOAD_EXTENSION tvfsDlOpen, /* xDlOpen */ tvfsDlError, /* xDlError */ tvfsDlSym, /* xDlSym */ tvfsDlClose, /* xDlClose */ #else 0, /* xDlOpen */ 0, /* xDlError */ 0, /* xDlSym */ 0, /* xDlClose */ #endif /* SQLITE_OMIT_LOAD_EXTENSION */ tvfsRandomness, /* xRandomness */ tvfsSleep, /* xSleep */ tvfsCurrentTime, /* xCurrentTime */ 0, /* xGetLastError */ 0, /* xCurrentTimeInt64 */ }; Testvfs *p; /* New object */ sqlite3_vfs *pVfs; /* New VFS */ char *zVfs; int nByte; /* Bytes of space to allocate at p */ int i; int isNoshm = 0; /* True if -noshm is passed */ int isDefault = 0; /* True if -default is passed */ int szOsFile = 0; /* Value passed to -szosfile */ int mxPathname = -1; /* Value passed to -mxpathname */ int iVersion = 2; /* Value passed to -iversion */ if( objc<2 || 0!=(objc%2) ) goto bad_args; for(i=2; i<objc; i += 2){ int nSwitch; char *zSwitch; zSwitch = Tcl_GetStringFromObj(objv[i], &nSwitch); if( nSwitch>2 && 0==strncmp("-noshm", zSwitch, nSwitch) ){ if( Tcl_GetBooleanFromObj(interp, objv[i+1], &isNoshm) ){ return TCL_ERROR; } } else if( nSwitch>2 && 0==strncmp("-default", zSwitch, nSwitch) ){ if( Tcl_GetBooleanFromObj(interp, objv[i+1], &isDefault) ){ return TCL_ERROR; } } else if( nSwitch>2 && 0==strncmp("-szosfile", zSwitch, nSwitch) ){ if( Tcl_GetIntFromObj(interp, objv[i+1], &szOsFile) ){ return TCL_ERROR; } } else if( nSwitch>2 && 0==strncmp("-mxpathname", zSwitch, nSwitch) ){ if( Tcl_GetIntFromObj(interp, objv[i+1], &mxPathname) ){ return TCL_ERROR; } } else if( nSwitch>2 && 0==strncmp("-iversion", zSwitch, nSwitch) ){ if( Tcl_GetIntFromObj(interp, objv[i+1], &iVersion) ){ return TCL_ERROR; } } else{ goto bad_args; } } if( szOsFile<sizeof(TestvfsFile) ){ szOsFile = sizeof(TestvfsFile); } zVfs = Tcl_GetString(objv[1]); nByte = sizeof(Testvfs) + strlen(zVfs)+1; p = (Testvfs *)ckalloc(nByte); memset(p, 0, nByte); p->iDevchar = -1; p->iSectorsize = -1; /* Create the new object command before querying SQLite for a default VFS ** to use for 'real' IO operations. This is because creating the new VFS ** may delete an existing [testvfs] VFS of the same name. If such a VFS ** is currently the default, the new [testvfs] may end up calling the ** methods of a deleted object. */ Tcl_CreateObjCommand(interp, zVfs, testvfs_obj_cmd, p, testvfs_obj_del); p->pParent = sqlite3_vfs_find(0); p->interp = interp; p->zName = (char *)&p[1]; memcpy(p->zName, zVfs, strlen(zVfs)+1); pVfs = (sqlite3_vfs *)ckalloc(sizeof(sqlite3_vfs)); memcpy(pVfs, &tvfs_vfs, sizeof(sqlite3_vfs)); pVfs->pAppData = (void *)p; pVfs->iVersion = iVersion; pVfs->zName = p->zName; pVfs->mxPathname = p->pParent->mxPathname; if( mxPathname>=0 && mxPathname<pVfs->mxPathname ){ pVfs->mxPathname = mxPathname; } pVfs->szOsFile = szOsFile; p->pVfs = pVfs; p->isNoshm = isNoshm; p->mask = TESTVFS_ALL_MASK; sqlite3_vfs_register(pVfs, isDefault); return TCL_OK; bad_args: Tcl_WrongNumArgs(interp, 1, objv, "VFSNAME ?-noshm BOOL? ?-default BOOL? ?-mxpathname INT? ?-szosfile INT? ?-iversion INT?"); return TCL_ERROR; } int Sqlitetestvfs_Init(Tcl_Interp *interp){ Tcl_CreateObjCommand(interp, "testvfs", testvfs_cmd, 0, 0); return TCL_OK; } #endif |
Changes to src/trigger.c.
︙ | ︙ | |||
492 493 494 495 496 497 498 499 500 501 502 503 504 505 | pTrigger = sqlite3HashFind(&(db->aDb[j].pSchema->trigHash), zName, nName); if( pTrigger ) break; } if( !pTrigger ){ if( !noErr ){ sqlite3ErrorMsg(pParse, "no such trigger: %S", pName, 0); } goto drop_trigger_cleanup; } sqlite3DropTriggerPtr(pParse, pTrigger); drop_trigger_cleanup: sqlite3SrcListDelete(db, pName); } | > | 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 | pTrigger = sqlite3HashFind(&(db->aDb[j].pSchema->trigHash), zName, nName); if( pTrigger ) break; } if( !pTrigger ){ if( !noErr ){ sqlite3ErrorMsg(pParse, "no such trigger: %S", pName, 0); } pParse->checkSchema = 1; goto drop_trigger_cleanup; } sqlite3DropTriggerPtr(pParse, pTrigger); drop_trigger_cleanup: sqlite3SrcListDelete(db, pName); } |
︙ | ︙ | |||
822 823 824 825 826 827 828 829 830 831 832 833 834 835 | memset(&sNC, 0, sizeof(sNC)); sNC.pParse = pSubParse; pSubParse->db = db; pSubParse->pTriggerTab = pTab; pSubParse->pToplevel = pTop; pSubParse->zAuthContext = pTrigger->zName; pSubParse->eTriggerOp = pTrigger->op; v = sqlite3GetVdbe(pSubParse); if( v ){ VdbeComment((v, "Start: %s.%s (%s %s%s%s ON %s)", pTrigger->zName, onErrorText(orconf), (pTrigger->tr_tm==TRIGGER_BEFORE ? "BEFORE" : "AFTER"), (pTrigger->op==TK_UPDATE ? "UPDATE" : ""), | > | 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 | memset(&sNC, 0, sizeof(sNC)); sNC.pParse = pSubParse; pSubParse->db = db; pSubParse->pTriggerTab = pTab; pSubParse->pToplevel = pTop; pSubParse->zAuthContext = pTrigger->zName; pSubParse->eTriggerOp = pTrigger->op; pSubParse->nQueryLoop = pParse->nQueryLoop; v = sqlite3GetVdbe(pSubParse); if( v ){ VdbeComment((v, "Start: %s.%s (%s %s%s%s ON %s)", pTrigger->zName, onErrorText(orconf), (pTrigger->tr_tm==TRIGGER_BEFORE ? "BEFORE" : "AFTER"), (pTrigger->op==TK_UPDATE ? "UPDATE" : ""), |
︙ | ︙ |
Changes to src/update.c.
1 2 3 4 5 6 7 8 9 10 | /* ** 2001 September 15 ** ** The author disclaims copyright to this source code. In place of ** a legal notice, here is a blessing: ** ** May you do good and not evil. ** May you find forgiveness for yourself and forgive others. ** May you share freely, never taking more than you give. ** | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | /* ** 2001 September 15 ** ** The author disclaims copyright to this source code. In place of ** a legal notice, here is a blessing: ** ** May you do good and not evil. ** May you find forgiveness for yourself and forgive others. ** May you share freely, never taking more than you give. ** sqlite************************************************************************* ** This file contains C code routines that are called by the parser ** to handle UPDATE statements. */ #include "sqliteInt.h" #ifndef SQLITE_OMIT_VIRTUALTABLE /* Forward declaration */ |
︙ | ︙ | |||
208 209 210 211 212 213 214 215 216 217 218 219 220 221 | } if( j>=pTab->nCol ){ if( sqlite3IsRowid(pChanges->a[i].zName) ){ chngRowid = 1; pRowidExpr = pChanges->a[i].pExpr; }else{ sqlite3ErrorMsg(pParse, "no such column: %s", pChanges->a[i].zName); goto update_cleanup; } } #ifndef SQLITE_OMIT_AUTHORIZATION { int rc; rc = sqlite3AuthCheck(pParse, SQLITE_UPDATE, pTab->zName, | > | 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 | } if( j>=pTab->nCol ){ if( sqlite3IsRowid(pChanges->a[i].zName) ){ chngRowid = 1; pRowidExpr = pChanges->a[i].pExpr; }else{ sqlite3ErrorMsg(pParse, "no such column: %s", pChanges->a[i].zName); pParse->checkSchema = 1; goto update_cleanup; } } #ifndef SQLITE_OMIT_AUTHORIZATION { int rc; rc = sqlite3AuthCheck(pParse, SQLITE_UPDATE, pTab->zName, |
︙ | ︙ | |||
392 393 394 395 396 397 398 | if( hasFK || pTrigger ){ u32 oldmask = (hasFK ? sqlite3FkOldmask(pParse, pTab) : 0); oldmask |= sqlite3TriggerColmask(pParse, pTrigger, pChanges, 0, TRIGGER_BEFORE|TRIGGER_AFTER, pTab, onError ); for(i=0; i<pTab->nCol; i++){ if( aXRef[i]<0 || oldmask==0xffffffff || (oldmask & (1<<i)) ){ | < | | 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 | if( hasFK || pTrigger ){ u32 oldmask = (hasFK ? sqlite3FkOldmask(pParse, pTab) : 0); oldmask |= sqlite3TriggerColmask(pParse, pTrigger, pChanges, 0, TRIGGER_BEFORE|TRIGGER_AFTER, pTab, onError ); for(i=0; i<pTab->nCol; i++){ if( aXRef[i]<0 || oldmask==0xffffffff || (oldmask & (1<<i)) ){ sqlite3ExprCodeGetColumnOfTable(v, pTab, iCur, i, regOld+i); }else{ sqlite3VdbeAddOp2(v, OP_Null, 0, regOld+i); } } if( chngRowid==0 ){ sqlite3VdbeAddOp2(v, OP_Copy, regOldRowid, regNewRowid); } |
︙ | ︙ |
Changes to src/vacuum.c.
︙ | ︙ | |||
99 100 101 102 103 104 105 | char *zSql = 0; /* SQL statements */ int saved_flags; /* Saved value of the db->flags */ int saved_nChange; /* Saved value of db->nChange */ int saved_nTotalChange; /* Saved value of db->nTotalChange */ void (*saved_xTrace)(void*,const char*); /* Saved db->xTrace */ Db *pDb = 0; /* Database to detach at end of vacuum */ int isMemDb; /* True if vacuuming a :memory: database */ | | > | > | | | > > > > > > > > | 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 | char *zSql = 0; /* SQL statements */ int saved_flags; /* Saved value of the db->flags */ int saved_nChange; /* Saved value of db->nChange */ int saved_nTotalChange; /* Saved value of db->nTotalChange */ void (*saved_xTrace)(void*,const char*); /* Saved db->xTrace */ Db *pDb = 0; /* Database to detach at end of vacuum */ int isMemDb; /* True if vacuuming a :memory: database */ int nRes; /* Bytes of reserved space at the end of each page */ int nDb; /* Number of attached databases */ if( !db->autoCommit ){ sqlite3SetString(pzErrMsg, db, "cannot VACUUM from within a transaction"); return SQLITE_ERROR; } /* Save the current value of the database flags so that it can be ** restored before returning. Then set the writable-schema flag, and ** disable CHECK and foreign key constraints. */ saved_flags = db->flags; saved_nChange = db->nChange; saved_nTotalChange = db->nTotalChange; saved_xTrace = db->xTrace; db->flags |= SQLITE_WriteSchema | SQLITE_IgnoreChecks | SQLITE_PreferBuiltin; db->flags &= ~(SQLITE_ForeignKeys | SQLITE_ReverseOrder); db->xTrace = 0; pMain = db->aDb[0].pBt; isMemDb = sqlite3PagerIsMemdb(sqlite3BtreePager(pMain)); /* Attach the temporary database as 'vacuum_db'. The synchronous pragma ** can be set to 'off' for this file, as it is not recovered if a crash ** occurs anyway. The integrity of the database is maintained by a ** (possibly synchronous) transaction opened on the main database before ** sqlite3BtreeCopyFile() is called. ** ** An optimisation would be to use a non-journaled pager. ** (Later:) I tried setting "PRAGMA vacuum_db.journal_mode=OFF" but ** that actually made the VACUUM run slower. Very little journalling ** actually occurs when doing a vacuum since the vacuum_db is initially ** empty. Only the journal header is written. Apparently it takes more ** time to parse and run the PRAGMA to turn journalling off than it does ** to write the journal header file. */ nDb = db->nDb; if( sqlite3TempInMemory(db) ){ zSql = "ATTACH ':memory:' AS vacuum_db;"; }else{ zSql = "ATTACH '' AS vacuum_db;"; } rc = execSql(db, pzErrMsg, zSql); if( db->nDb>nDb ){ pDb = &db->aDb[db->nDb-1]; assert( strcmp(pDb->zName,"vacuum_db")==0 ); } if( rc!=SQLITE_OK ) goto end_of_vacuum; pTemp = db->aDb[db->nDb-1].pBt; /* The call to execSql() to attach the temp database has left the file ** locked (as there was more than one active statement when the transaction ** to read the schema was concluded. Unlock it here so that this doesn't ** cause problems for the call to BtreeSetPageSize() below. */ sqlite3BtreeCommit(pTemp); nRes = sqlite3BtreeGetReserve(pMain); /* A VACUUM cannot change the pagesize of an encrypted database. */ #ifdef SQLITE_HAS_CODEC if( db->nextPagesize ){ extern void sqlite3CodecGetKey(sqlite3*, int, void**, int*); int nKey; char *zKey; sqlite3CodecGetKey(db, 0, (void**)&zKey, &nKey); if( nKey ) db->nextPagesize = 0; } #endif /* Do not attempt to change the page size for a WAL database */ if( sqlite3PagerGetJournalMode(sqlite3BtreePager(pMain)) ==PAGER_JOURNALMODE_WAL ){ db->nextPagesize = 0; } if( sqlite3BtreeSetPageSize(pTemp, sqlite3BtreeGetPageSize(pMain), nRes, 0) || (!isMemDb && sqlite3BtreeSetPageSize(pTemp, db->nextPagesize, nRes, 0)) || NEVER(db->mallocFailed) ){ rc = SQLITE_NOMEM; goto end_of_vacuum; |
︙ | ︙ | |||
300 301 302 303 304 305 306 307 308 309 310 311 312 313 | end_of_vacuum: /* Restore the original value of db->flags */ db->flags = saved_flags; db->nChange = saved_nChange; db->nTotalChange = saved_nTotalChange; db->xTrace = saved_xTrace; /* Currently there is an SQL level transaction open on the vacuum ** database. No locks are held on any other files (since the main file ** was committed at the btree level). So it safe to end the transaction ** by manually setting the autoCommit flag to true and detaching the ** vacuum database. The vacuum_db journal file is deleted when the pager ** is closed by the DETACH. | > | 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 | end_of_vacuum: /* Restore the original value of db->flags */ db->flags = saved_flags; db->nChange = saved_nChange; db->nTotalChange = saved_nTotalChange; db->xTrace = saved_xTrace; sqlite3BtreeSetPageSize(pMain, -1, -1, 1); /* Currently there is an SQL level transaction open on the vacuum ** database. No locks are held on any other files (since the main file ** was committed at the btree level). So it safe to end the transaction ** by manually setting the autoCommit flag to true and detaching the ** vacuum database. The vacuum_db journal file is deleted when the pager ** is closed by the DETACH. |
︙ | ︙ |
Changes to src/vdbe.c.
︙ | ︙ | |||
476 477 478 479 480 481 482 | ** implement a loop. This test used to be on every single instruction, ** but that meant we more testing that we needed. By only testing the ** flag on jump instructions, we get a (small) speed improvement. */ #define CHECK_FOR_INTERRUPT \ if( db->u1.isInterrupted ) goto abort_due_to_interrupt; | < < < < < < < < < < < < < < < < | 476 477 478 479 480 481 482 483 484 485 486 487 488 489 | ** implement a loop. This test used to be on every single instruction, ** but that meant we more testing that we needed. By only testing the ** flag on jump instructions, we get a (small) speed improvement. */ #define CHECK_FOR_INTERRUPT \ if( db->u1.isInterrupted ) goto abort_due_to_interrupt; #ifndef NDEBUG /* ** This function is only called from within an assert() expression. It ** checks that the sqlite3.nTransaction variable is correctly set to ** the number of non-transaction savepoints currently in the ** linked list starting at sqlite3.pSavepoint. |
︙ | ︙ | |||
590 591 592 593 594 595 596 | CHECK_FOR_INTERRUPT; sqlite3VdbeIOTraceSql(p); #ifndef SQLITE_OMIT_PROGRESS_CALLBACK checkProgress = db->xProgress!=0; #endif #ifdef SQLITE_DEBUG sqlite3BeginBenignMalloc(); | | < < < < < | 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 | CHECK_FOR_INTERRUPT; sqlite3VdbeIOTraceSql(p); #ifndef SQLITE_OMIT_PROGRESS_CALLBACK checkProgress = db->xProgress!=0; #endif #ifdef SQLITE_DEBUG sqlite3BeginBenignMalloc(); if( p->pc==0 && (p->db->flags & SQLITE_VdbeListing)!=0 ){ int i; printf("VDBE Program Listing:\n"); sqlite3VdbePrintSql(p); for(i=0; i<p->nOp; i++){ sqlite3VdbePrintOp(stdout, i, &aOp[i]); } } sqlite3EndBenignMalloc(); #endif for(pc=p->pc; rc==SQLITE_OK; pc++){ assert( pc>=0 && pc<p->nOp ); if( db->mallocFailed ) goto no_mem; #ifdef VDBE_PROFILE origPc = pc; |
︙ | ︙ | |||
624 625 626 627 628 629 630 | if( p->trace ){ if( pc==0 ){ printf("VDBE Execution Trace:\n"); sqlite3VdbePrintSql(p); } sqlite3VdbePrintOp(p->trace, pc, pOp); } | < < < < < < < | 603 604 605 606 607 608 609 610 611 612 613 614 615 616 | if( p->trace ){ if( pc==0 ){ printf("VDBE Execution Trace:\n"); sqlite3VdbePrintSql(p); } sqlite3VdbePrintOp(p->trace, pc, pOp); } #endif /* Check to see if we need to simulate an interrupt. This only happens ** if we have a special test build. */ #ifdef SQLITE_TEST |
︙ | ︙ | |||
985 986 987 988 989 990 991 | assert( pOp->p1 <= SQLITE_MAX_LENGTH ); sqlite3VdbeMemSetStr(pOut, pOp->p4.z, pOp->p1, 0, 0); pOut->enc = encoding; UPDATE_MAX_BLOBSIZE(pOut); break; } | | | < | < < < < < < | < < < < | | | | < < < | | < | 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 | assert( pOp->p1 <= SQLITE_MAX_LENGTH ); sqlite3VdbeMemSetStr(pOut, pOp->p4.z, pOp->p1, 0, 0); pOut->enc = encoding; UPDATE_MAX_BLOBSIZE(pOut); break; } /* Opcode: Variable P1 P2 * P4 * ** ** Transfer the values of bound parameter P1 into register P2 ** ** If the parameter is named, then its name appears in P4 and P3==1. ** The P4 value is used by sqlite3_bind_parameter_name(). */ case OP_Variable: { /* out2-prerelease */ Mem *pVar; /* Value being transferred */ assert( pOp->p1>0 && pOp->p1<=p->nVar ); pVar = &p->aVar[pOp->p1 - 1]; if( sqlite3VdbeMemTooBig(pVar) ){ goto too_big; } sqlite3VdbeMemShallowCopy(pOut, pVar, MEM_Static); UPDATE_MAX_BLOBSIZE(pOut); break; } /* Opcode: Move P1 P2 P3 * * ** ** Move the values in register P1..P1+P3-1 over into ** registers P2..P2+P3-1. Registers P1..P1+P1-1 are |
︙ | ︙ | |||
1379 1380 1381 1382 1383 1384 1385 | assert( n==0 || (pOp->p2>0 && pOp->p2+n<=p->nMem+1) ); assert( pOp->p3<pOp->p2 || pOp->p3>=pOp->p2+n ); pArg = &aMem[pOp->p2]; for(i=0; i<n; i++, pArg++){ apVal[i] = pArg; sqlite3VdbeMemStoreType(pArg); | | | 1336 1337 1338 1339 1340 1341 1342 1343 1344 1345 1346 1347 1348 1349 1350 | assert( n==0 || (pOp->p2>0 && pOp->p2+n<=p->nMem+1) ); assert( pOp->p3<pOp->p2 || pOp->p3>=pOp->p2+n ); pArg = &aMem[pOp->p2]; for(i=0; i<n; i++, pArg++){ apVal[i] = pArg; sqlite3VdbeMemStoreType(pArg); REGISTER_TRACE(pOp->p2+i, pArg); } assert( pOp->p4type==P4_FUNCDEF || pOp->p4type==P4_VDBEFUNC ); if( pOp->p4type==P4_FUNCDEF ){ ctx.pFunc = pOp->p4.pFunc; ctx.pVdbeFunc = 0; }else{ |
︙ | ︙ | |||
3064 3065 3066 3067 3068 3069 3070 | break; } /* Opcode: OpenEphemeral P1 P2 * P4 * ** ** Open a new cursor P1 to a transient table. ** The cursor is always opened read/write even if | | | > > > > > > > > | 3021 3022 3023 3024 3025 3026 3027 3028 3029 3030 3031 3032 3033 3034 3035 3036 3037 3038 3039 3040 3041 3042 3043 3044 3045 3046 3047 3048 3049 3050 3051 3052 3053 3054 3055 3056 | break; } /* Opcode: OpenEphemeral P1 P2 * P4 * ** ** Open a new cursor P1 to a transient table. ** The cursor is always opened read/write even if ** the main database is read-only. The ephemeral ** table is deleted automatically when the cursor is closed. ** ** P2 is the number of columns in the ephemeral table. ** The cursor points to a BTree table if P4==0 and to a BTree index ** if P4 is not 0. If P4 is not NULL, it points to a KeyInfo structure ** that defines the format of keys in the index. ** ** This opcode was once called OpenTemp. But that created ** confusion because the term "temp table", might refer either ** to a TEMP table at the SQL level, or to a table opened by ** this opcode. Then this opcode was call OpenVirtual. But ** that created confusion with the whole virtual-table idea. */ /* Opcode: OpenAutoindex P1 P2 * P4 * ** ** This opcode works the same as OP_OpenEphemeral. It has a ** different name to distinguish its use. Tables created using ** by this opcode will be used for automatically created transient ** indices in joins. */ case OP_OpenAutoindex: case OP_OpenEphemeral: { VdbeCursor *pCx; static const int openFlags = SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE | SQLITE_OPEN_EXCLUSIVE | SQLITE_OPEN_DELETEONCLOSE | |
︙ | ︙ | |||
4169 4170 4171 4172 4173 4174 4175 4176 4177 4178 4179 4180 4181 | VdbeCursor *pC; BtCursor *pCrsr; int res; assert( pOp->p1>=0 && pOp->p1<p->nCursor ); pC = p->apCsr[pOp->p1]; assert( pC!=0 ); if( (pCrsr = pC->pCursor)!=0 ){ rc = sqlite3BtreeFirst(pCrsr, &res); pC->atFirst = res==0 ?1:0; pC->deferredMoveto = 0; pC->cacheStatus = CACHE_STALE; pC->rowidIsValid = 0; | > < < | > > > | > > > > | 4134 4135 4136 4137 4138 4139 4140 4141 4142 4143 4144 4145 4146 4147 4148 4149 4150 4151 4152 4153 4154 4155 4156 4157 4158 4159 4160 4161 4162 4163 4164 4165 4166 4167 4168 4169 4170 4171 4172 4173 4174 4175 4176 4177 4178 4179 4180 4181 4182 4183 4184 4185 4186 4187 4188 4189 4190 4191 4192 4193 4194 4195 4196 4197 4198 | VdbeCursor *pC; BtCursor *pCrsr; int res; assert( pOp->p1>=0 && pOp->p1<p->nCursor ); pC = p->apCsr[pOp->p1]; assert( pC!=0 ); res = 1; if( (pCrsr = pC->pCursor)!=0 ){ rc = sqlite3BtreeFirst(pCrsr, &res); pC->atFirst = res==0 ?1:0; pC->deferredMoveto = 0; pC->cacheStatus = CACHE_STALE; pC->rowidIsValid = 0; } pC->nullRow = (u8)res; assert( pOp->p2>0 && pOp->p2<p->nOp ); if( res ){ pc = pOp->p2 - 1; } break; } /* Opcode: Next P1 P2 * * P5 ** ** Advance cursor P1 so that it points to the next key/data pair in its ** table or index. If there are no more key/value pairs then fall through ** to the following instruction. But if the cursor advance was successful, ** jump immediately to P2. ** ** The P1 cursor must be for a real table, not a pseudo-table. ** ** If P5 is positive and the jump is taken, then event counter ** number P5-1 in the prepared statement is incremented. ** ** See also: Prev */ /* Opcode: Prev P1 P2 * * P5 ** ** Back up cursor P1 so that it points to the previous key/data pair in its ** table or index. If there is no previous key/value pairs then fall through ** to the following instruction. But if the cursor backup was successful, ** jump immediately to P2. ** ** The P1 cursor must be for a real table, not a pseudo-table. ** ** If P5 is positive and the jump is taken, then event counter ** number P5-1 in the prepared statement is incremented. */ case OP_Prev: /* jump */ case OP_Next: { /* jump */ VdbeCursor *pC; BtCursor *pCrsr; int res; CHECK_FOR_INTERRUPT; assert( pOp->p1>=0 && pOp->p1<p->nCursor ); assert( pOp->p5<=ArraySize(p->aCounter) ); pC = p->apCsr[pOp->p1]; if( pC==0 ){ break; /* See ticket #2273 */ } pCrsr = pC->pCursor; if( pCrsr==0 ){ pC->nullRow = 1; |
︙ | ︙ | |||
5167 5168 5169 5170 5171 5172 5173 5174 5175 5176 5177 5178 5179 5180 | UPDATE_MAX_BLOBSIZE(pMem); if( sqlite3VdbeMemTooBig(pMem) ){ goto too_big; } break; } #if !defined(SQLITE_OMIT_VACUUM) && !defined(SQLITE_OMIT_ATTACH) /* Opcode: Vacuum * * * * * ** ** Vacuum the entire database. This opcode will cause other virtual ** machines to be created and run. It may not be called from within ** a transaction. | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 5138 5139 5140 5141 5142 5143 5144 5145 5146 5147 5148 5149 5150 5151 5152 5153 5154 5155 5156 5157 5158 5159 5160 5161 5162 5163 5164 5165 5166 5167 5168 5169 5170 5171 5172 5173 5174 5175 5176 5177 5178 5179 5180 5181 5182 5183 5184 5185 5186 5187 5188 5189 5190 5191 5192 5193 5194 5195 5196 5197 5198 5199 5200 5201 5202 5203 5204 5205 5206 5207 5208 5209 5210 5211 5212 5213 5214 5215 5216 5217 5218 5219 5220 5221 5222 5223 5224 5225 5226 5227 5228 5229 5230 5231 5232 5233 5234 5235 5236 5237 5238 5239 5240 5241 5242 5243 5244 5245 5246 5247 5248 5249 5250 5251 5252 5253 5254 5255 5256 5257 5258 5259 5260 5261 5262 5263 5264 5265 5266 5267 5268 5269 5270 5271 5272 5273 5274 5275 5276 5277 5278 5279 5280 5281 5282 5283 5284 | UPDATE_MAX_BLOBSIZE(pMem); if( sqlite3VdbeMemTooBig(pMem) ){ goto too_big; } break; } #ifndef SQLITE_OMIT_WAL /* Opcode: Checkpoint P1 * * * * ** ** Checkpoint database P1. This is a no-op if P1 is not currently in ** WAL mode. */ case OP_Checkpoint: { rc = sqlite3Checkpoint(db, pOp->p1); break; }; #endif #ifndef SQLITE_OMIT_PRAGMA /* Opcode: JournalMode P1 P2 P3 * P5 ** ** Change the journal mode of database P1 to P3. P3 must be one of the ** PAGER_JOURNALMODE_XXX values. If changing between the various rollback ** modes (delete, truncate, persist, off and memory), this is a simple ** operation. No IO is required. ** ** If changing into or out of WAL mode the procedure is more complicated. ** ** Write a string containing the final journal-mode to register P2. */ case OP_JournalMode: { /* out2-prerelease */ Btree *pBt; /* Btree to change journal mode of */ Pager *pPager; /* Pager associated with pBt */ int eNew; /* New journal mode */ int eOld; /* The old journal mode */ const char *zFilename; /* Name of database file for pPager */ eNew = pOp->p3; assert( eNew==PAGER_JOURNALMODE_DELETE || eNew==PAGER_JOURNALMODE_TRUNCATE || eNew==PAGER_JOURNALMODE_PERSIST || eNew==PAGER_JOURNALMODE_OFF || eNew==PAGER_JOURNALMODE_MEMORY || eNew==PAGER_JOURNALMODE_WAL || eNew==PAGER_JOURNALMODE_QUERY ); assert( pOp->p1>=0 && pOp->p1<db->nDb ); /* This opcode is used in two places: PRAGMA journal_mode and ATTACH. ** In PRAGMA journal_mode, the sqlite3VdbeUsesBtree() routine is called ** when the statment is prepared and so p->aMutex.nMutex>0. All mutexes ** are already acquired. But when used in ATTACH, sqlite3VdbeUsesBtree() ** is not called when the statement is prepared because it requires the ** iDb index of the database as a parameter, and the database has not ** yet been attached so that index is unavailable. We have to wait ** until runtime (now) to get the mutex on the newly attached database. ** No other mutexes are required by the ATTACH command so this is safe ** to do. */ assert( (p->btreeMask & (1<<pOp->p1))!=0 || p->aMutex.nMutex==0 ); if( p->aMutex.nMutex==0 ){ /* This occurs right after ATTACH. Get a mutex on the newly ATTACHed ** database. */ sqlite3VdbeUsesBtree(p, pOp->p1); sqlite3VdbeMutexArrayEnter(p); } pBt = db->aDb[pOp->p1].pBt; pPager = sqlite3BtreePager(pBt); eOld = sqlite3PagerGetJournalMode(pPager); if( eNew==PAGER_JOURNALMODE_QUERY ) eNew = eOld; if( !sqlite3PagerOkToChangeJournalMode(pPager) ) eNew = eOld; #ifndef SQLITE_OMIT_WAL zFilename = sqlite3PagerFilename(pPager); /* Do not allow a transition to journal_mode=WAL for a database ** in temporary storage or if the VFS does not support shared memory */ if( eNew==PAGER_JOURNALMODE_WAL && (zFilename[0]==0 /* Temp file */ || !sqlite3PagerWalSupported(pPager)) /* No shared-memory support */ ){ eNew = eOld; } if( (eNew!=eOld) && (eOld==PAGER_JOURNALMODE_WAL || eNew==PAGER_JOURNALMODE_WAL) ){ if( !db->autoCommit || db->activeVdbeCnt>1 ){ rc = SQLITE_ERROR; sqlite3SetString(&p->zErrMsg, db, "cannot change %s wal mode from within a transaction", (eNew==PAGER_JOURNALMODE_WAL ? "into" : "out of") ); break; }else{ if( eOld==PAGER_JOURNALMODE_WAL ){ /* If leaving WAL mode, close the log file. If successful, the call ** to PagerCloseWal() checkpoints and deletes the write-ahead-log ** file. An EXCLUSIVE lock may still be held on the database file ** after a successful return. */ rc = sqlite3PagerCloseWal(pPager); if( rc==SQLITE_OK ){ sqlite3PagerSetJournalMode(pPager, eNew); } }else if( eOld==PAGER_JOURNALMODE_MEMORY ){ /* Cannot transition directly from MEMORY to WAL. Use mode OFF ** as an intermediate */ sqlite3PagerSetJournalMode(pPager, PAGER_JOURNALMODE_OFF); } /* Open a transaction on the database file. Regardless of the journal ** mode, this transaction always uses a rollback journal. */ assert( sqlite3BtreeIsInTrans(pBt)==0 ); if( rc==SQLITE_OK ){ rc = sqlite3BtreeSetVersion(pBt, (eNew==PAGER_JOURNALMODE_WAL ? 2 : 1)); } } } #endif /* ifndef SQLITE_OMIT_WAL */ if( rc ){ eNew = eOld; } eNew = sqlite3PagerSetJournalMode(pPager, eNew); pOut = &aMem[pOp->p2]; pOut->flags = MEM_Str|MEM_Static|MEM_Term; pOut->z = (char *)sqlite3JournalModename(eNew); pOut->n = sqlite3Strlen30(pOut->z); pOut->enc = SQLITE_UTF8; sqlite3VdbeChangeEncoding(pOut, encoding); break; }; #endif /* SQLITE_OMIT_PRAGMA */ #if !defined(SQLITE_OMIT_VACUUM) && !defined(SQLITE_OMIT_ATTACH) /* Opcode: Vacuum * * * * * ** ** Vacuum the entire database. This opcode will cause other virtual ** machines to be created and run. It may not be called from within ** a transaction. |
︙ | ︙ | |||
5612 5613 5614 5615 5616 5617 5618 | #ifndef SQLITE_OMIT_PAGER_PRAGMAS /* Opcode: Pagecount P1 P2 * * * ** ** Write the current number of pages in database P1 to memory cell P2. */ case OP_Pagecount: { /* out2-prerelease */ | < < < < < < < < < < < | < | 5716 5717 5718 5719 5720 5721 5722 5723 5724 5725 5726 5727 5728 5729 5730 | #ifndef SQLITE_OMIT_PAGER_PRAGMAS /* Opcode: Pagecount P1 P2 * * * ** ** Write the current number of pages in database P1 to memory cell P2. */ case OP_Pagecount: { /* out2-prerelease */ pOut->u.i = sqlite3BtreeLastPage(db->aDb[pOp->p1].pBt); break; } #endif #ifndef SQLITE_OMIT_TRACE /* Opcode: Trace * * * P4 * ** |
︙ | ︙ |
Changes to src/vdbeInt.h.
︙ | ︙ | |||
307 308 309 310 311 312 313 | u8 usesStmtJournal; /* True if uses a statement journal */ u8 readOnly; /* True for read-only statements */ u8 isPrepareV2; /* True if prepared with prepare_v2() */ int nChange; /* Number of db changes made since last reset */ int btreeMask; /* Bitmask of db->aDb[] entries referenced */ i64 startTime; /* Time when query started - used for profiling */ BtreeMutexArray aMutex; /* An array of Btree used here and needing locks */ | | | 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 | u8 usesStmtJournal; /* True if uses a statement journal */ u8 readOnly; /* True for read-only statements */ u8 isPrepareV2; /* True if prepared with prepare_v2() */ int nChange; /* Number of db changes made since last reset */ int btreeMask; /* Bitmask of db->aDb[] entries referenced */ i64 startTime; /* Time when query started - used for profiling */ BtreeMutexArray aMutex; /* An array of Btree used here and needing locks */ int aCounter[3]; /* Counters used by sqlite3_stmt_status() */ char *zSql; /* Text of the SQL statement that generated this */ void *pFree; /* Free this when deleting the vdbe */ i64 nFkConstraint; /* Number of imm. FK constraints this VM */ i64 nStmtDefCons; /* Number of def. constraints when stmt started */ int iStatement; /* Statement number (or 0 if has not opened stmt) */ #ifdef SQLITE_DEBUG FILE *trace; /* Write an execution trace here, if not NULL */ |
︙ | ︙ |
Changes to src/vdbeapi.c.
︙ | ︙ | |||
317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 | /* An SQLITE_NOMEM error. */ void sqlite3_result_error_nomem(sqlite3_context *pCtx){ assert( sqlite3_mutex_held(pCtx->s.db->mutex) ); sqlite3VdbeMemSetNull(&pCtx->s); pCtx->isError = SQLITE_NOMEM; pCtx->s.db->mallocFailed = 1; } /* ** Execute the statement pStmt, either until a row of data is ready, the ** statement is completely executed or an error occurs. ** ** This routine implements the bulk of the logic behind the sqlite_step() ** API. The only thing omitted is the automatic recompile if a ** schema change has occurred. That detail is handled by the ** outer sqlite3_step() wrapper procedure. */ static int sqlite3Step(Vdbe *p){ sqlite3 *db; int rc; assert(p); if( p->magic!=VDBE_MAGIC_RUN ){ | > > > > > > > > > > > > > > > > > > > > > | | | > > > | 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 | /* An SQLITE_NOMEM error. */ void sqlite3_result_error_nomem(sqlite3_context *pCtx){ assert( sqlite3_mutex_held(pCtx->s.db->mutex) ); sqlite3VdbeMemSetNull(&pCtx->s); pCtx->isError = SQLITE_NOMEM; pCtx->s.db->mallocFailed = 1; } /* ** This function is called after a transaction has been committed. It ** invokes callbacks registered with sqlite3_wal_hook() as required. */ static int doWalCallbacks(sqlite3 *db){ int rc = SQLITE_OK; #ifndef SQLITE_OMIT_WAL int i; for(i=0; i<db->nDb; i++){ Btree *pBt = db->aDb[i].pBt; if( pBt ){ int nEntry = sqlite3PagerWalCallback(sqlite3BtreePager(pBt)); if( db->xWalCallback && nEntry>0 && rc==SQLITE_OK ){ rc = db->xWalCallback(db->pWalArg, db, db->aDb[i].zName, nEntry); } } } #endif return rc; } /* ** Execute the statement pStmt, either until a row of data is ready, the ** statement is completely executed or an error occurs. ** ** This routine implements the bulk of the logic behind the sqlite_step() ** API. The only thing omitted is the automatic recompile if a ** schema change has occurred. That detail is handled by the ** outer sqlite3_step() wrapper procedure. */ static int sqlite3Step(Vdbe *p){ sqlite3 *db; int rc; assert(p); if( p->magic!=VDBE_MAGIC_RUN ){ /* We used to require that sqlite3_reset() be called before retrying ** sqlite3_step() after any error. But after 3.6.23, we changed this ** so that sqlite3_reset() would be called automatically instead of ** throwing the error. */ sqlite3_reset((sqlite3_stmt*)p); } /* Check that malloc() has not failed. If it has, return early. */ db = p->db; if( db->mallocFailed ){ p->rc = SQLITE_NOMEM; return SQLITE_NOMEM; |
︙ | ︙ | |||
363 364 365 366 367 368 369 | db->u1.isInterrupted = 0; } assert( db->writeVdbeCnt>0 || db->autoCommit==0 || db->nDeferredCons==0 ); #ifndef SQLITE_OMIT_TRACE if( db->xProfile && !db->init.busy ){ | < | < < < | | < < | > > > > > > > > | 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 | db->u1.isInterrupted = 0; } assert( db->writeVdbeCnt>0 || db->autoCommit==0 || db->nDeferredCons==0 ); #ifndef SQLITE_OMIT_TRACE if( db->xProfile && !db->init.busy ){ sqlite3OsCurrentTimeInt64(db->pVfs, &p->startTime); } #endif db->activeVdbeCnt++; if( p->readOnly==0 ) db->writeVdbeCnt++; p->pc = 0; } #ifndef SQLITE_OMIT_EXPLAIN if( p->explain ){ rc = sqlite3VdbeList(p); }else #endif /* SQLITE_OMIT_EXPLAIN */ { rc = sqlite3VdbeExec(p); } #ifndef SQLITE_OMIT_TRACE /* Invoke the profile callback if there is one */ if( rc!=SQLITE_ROW && db->xProfile && !db->init.busy && p->zSql ){ sqlite3_int64 iNow; sqlite3OsCurrentTimeInt64(db->pVfs, &iNow); db->xProfile(db->pProfileArg, p->zSql, iNow - p->startTime); } #endif if( rc==SQLITE_DONE ){ assert( p->rc==SQLITE_OK ); p->rc = doWalCallbacks(db); if( p->rc!=SQLITE_OK ){ rc = SQLITE_ERROR; } } db->errCode = rc; if( SQLITE_NOMEM==sqlite3ApiExit(p->db, p->rc) ){ p->rc = SQLITE_NOMEM; } end_of_step: /* At this point local variable rc holds the value that should be |
︙ | ︙ |
Changes to src/vdbeaux.c.
︙ | ︙ | |||
739 740 741 742 743 744 745 | nField = ((KeyInfo*)zP4)->nField; nByte = sizeof(*pKeyInfo) + (nField-1)*sizeof(pKeyInfo->aColl[0]) + nField; pKeyInfo = sqlite3Malloc( nByte ); pOp->p4.pKeyInfo = pKeyInfo; if( pKeyInfo ){ u8 *aSortOrder; | | | 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 | nField = ((KeyInfo*)zP4)->nField; nByte = sizeof(*pKeyInfo) + (nField-1)*sizeof(pKeyInfo->aColl[0]) + nField; pKeyInfo = sqlite3Malloc( nByte ); pOp->p4.pKeyInfo = pKeyInfo; if( pKeyInfo ){ u8 *aSortOrder; memcpy((char*)pKeyInfo, zP4, nByte - nField); aSortOrder = pKeyInfo->aSortOrder; if( aSortOrder ){ pKeyInfo->aSortOrder = (unsigned char*)&pKeyInfo->aColl[nField]; memcpy(pKeyInfo->aSortOrder, aSortOrder, nField); } pOp->p4type = P4_KEYINFO; }else{ |
︙ | ︙ | |||
810 811 812 813 814 815 816 | /* ** Return the opcode for a given address. If the address is -1, then ** return the most recently inserted opcode. ** ** If a memory allocation error has occurred prior to the calling of this ** routine, then a pointer to a dummy VdbeOp will be returned. That opcode | | | | > > > > > | | | | 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 | /* ** Return the opcode for a given address. If the address is -1, then ** return the most recently inserted opcode. ** ** If a memory allocation error has occurred prior to the calling of this ** routine, then a pointer to a dummy VdbeOp will be returned. That opcode ** is readable but not writable, though it is cast to a writable value. ** The return of a dummy opcode allows the call to continue functioning ** after a OOM fault without having to check to see if the return from ** this routine is a valid pointer. But because the dummy.opcode is 0, ** dummy will never be written to. This is verified by code inspection and ** by running with Valgrind. ** ** About the #ifdef SQLITE_OMIT_TRACE: Normally, this routine is never called ** unless p->nOp>0. This is because in the absense of SQLITE_OMIT_TRACE, ** an OP_Trace instruction is always inserted by sqlite3VdbeGet() as soon as ** a new VDBE is created. So we are free to set addr to p->nOp-1 without ** having to double-check to make sure that the result is non-negative. But ** if SQLITE_OMIT_TRACE is defined, the OP_Trace is omitted and we do need to ** check the value of p->nOp-1 before continuing. */ VdbeOp *sqlite3VdbeGetOp(Vdbe *p, int addr){ /* C89 specifies that the constant "dummy" will be initialized to all ** zeros, which is correct. MSVC generates a warning, nevertheless. */ static const VdbeOp dummy; /* Ignore the MSVC warning about no initializer */ assert( p->magic==VDBE_MAGIC_INIT ); if( addr<0 ){ #ifdef SQLITE_OMIT_TRACE if( p->nOp==0 ) return (VdbeOp*)&dummy; #endif addr = p->nOp - 1; } assert( (addr>=0 && addr<p->nOp) || p->db->mallocFailed ); if( p->db->mallocFailed ){ return (VdbeOp*)&dummy; }else{ return &p->aOp[addr]; } } #if !defined(SQLITE_OMIT_EXPLAIN) || !defined(NDEBUG) \ || defined(VDBE_PROFILE) || defined(SQLITE_DEBUG) |
︙ | ︙ | |||
946 947 948 949 950 951 952 953 954 955 956 957 958 959 | assert( zP4!=0 ); return zP4; } #endif /* ** Declare to the Vdbe that the BTree object at db->aDb[i] is used. */ void sqlite3VdbeUsesBtree(Vdbe *p, int i){ int mask; assert( i>=0 && i<p->db->nDb && i<sizeof(u32)*8 ); assert( i<(int)sizeof(p->btreeMask)*8 ); mask = ((u32)1)<<i; if( (p->btreeMask & mask)==0 ){ | > > > > > | 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 | assert( zP4!=0 ); return zP4; } #endif /* ** Declare to the Vdbe that the BTree object at db->aDb[i] is used. ** ** The prepared statement has to know in advance which Btree objects ** will be used so that it can acquire mutexes on them all in sorted ** order (via sqlite3VdbeMutexArrayEnter(). Mutexes are acquired ** in order (and released in reverse order) to avoid deadlocks. */ void sqlite3VdbeUsesBtree(Vdbe *p, int i){ int mask; assert( i>=0 && i<p->db->nDb && i<sizeof(u32)*8 ); assert( i<(int)sizeof(p->btreeMask)*8 ); mask = ((u32)1)<<i; if( (p->btreeMask & mask)==0 ){ |
︙ | ︙ | |||
1445 1446 1447 1448 1449 1450 1451 1452 1453 1454 1455 1456 1457 1458 | p->errorAction = OE_Abort; p->explain |= isExplain; p->magic = VDBE_MAGIC_RUN; p->nChange = 0; p->cacheCtr = 1; p->minWriteFileFormat = 255; p->iStatement = 0; #ifdef VDBE_PROFILE { int i; for(i=0; i<p->nOp; i++){ p->aOp[i].cnt = 0; p->aOp[i].cycles = 0; } | > | 1455 1456 1457 1458 1459 1460 1461 1462 1463 1464 1465 1466 1467 1468 1469 | p->errorAction = OE_Abort; p->explain |= isExplain; p->magic = VDBE_MAGIC_RUN; p->nChange = 0; p->cacheCtr = 1; p->minWriteFileFormat = 255; p->iStatement = 0; p->nFkConstraint = 0; #ifdef VDBE_PROFILE { int i; for(i=0; i<p->nOp; i++){ p->aOp[i].cnt = 0; p->aOp[i].cycles = 0; } |
︙ | ︙ | |||
2133 2134 2135 2136 2137 2138 2139 | ** Note that sqlite3VdbeCloseStatement() can only fail if eStatementOp ** is SAVEPOINT_ROLLBACK. But if p->rc==SQLITE_OK then eStatementOp ** must be SAVEPOINT_RELEASE. Hence the NEVER(p->rc==SQLITE_OK) in ** the following code. */ if( eStatementOp ){ rc = sqlite3VdbeCloseStatement(p, eStatementOp); | > > | | | | > > > > > | 2144 2145 2146 2147 2148 2149 2150 2151 2152 2153 2154 2155 2156 2157 2158 2159 2160 2161 2162 2163 2164 2165 2166 2167 2168 | ** Note that sqlite3VdbeCloseStatement() can only fail if eStatementOp ** is SAVEPOINT_ROLLBACK. But if p->rc==SQLITE_OK then eStatementOp ** must be SAVEPOINT_RELEASE. Hence the NEVER(p->rc==SQLITE_OK) in ** the following code. */ if( eStatementOp ){ rc = sqlite3VdbeCloseStatement(p, eStatementOp); if( rc ){ assert( eStatementOp==SAVEPOINT_ROLLBACK ); if( NEVER(p->rc==SQLITE_OK) || p->rc==SQLITE_CONSTRAINT ){ p->rc = rc; sqlite3DbFree(db, p->zErrMsg); p->zErrMsg = 0; } invalidateCursorsOnModifiedBtrees(db); sqlite3RollbackAll(db); sqlite3CloseSavepoints(db); db->autoCommit = 1; } } /* If this was an INSERT, UPDATE or DELETE and no statement transaction ** has been rolled back, update the database connection change-counter. */ if( p->changeCntOn ){ |
︙ | ︙ |
Changes to src/vdbeblob.c.
︙ | ︙ | |||
187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 | sqlite3VdbeChangeP1(v, 1, iDb); sqlite3VdbeChangeP2(v, 1, pTab->pSchema->schema_cookie); /* Make sure a mutex is held on the table to be accessed */ sqlite3VdbeUsesBtree(v, iDb); /* Configure the OP_TableLock instruction */ sqlite3VdbeChangeP1(v, 2, iDb); sqlite3VdbeChangeP2(v, 2, pTab->tnum); sqlite3VdbeChangeP3(v, 2, flags); sqlite3VdbeChangeP4(v, 2, pTab->zName, P4_TRANSIENT); /* Remove either the OP_OpenWrite or OpenRead. Set the P2 ** parameter of the other to pTab->tnum. */ sqlite3VdbeChangeToNoop(v, 4 - flags, 1); sqlite3VdbeChangeP2(v, 3 + flags, pTab->tnum); sqlite3VdbeChangeP3(v, 3 + flags, iDb); | > > > > | 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 | sqlite3VdbeChangeP1(v, 1, iDb); sqlite3VdbeChangeP2(v, 1, pTab->pSchema->schema_cookie); /* Make sure a mutex is held on the table to be accessed */ sqlite3VdbeUsesBtree(v, iDb); /* Configure the OP_TableLock instruction */ #ifdef SQLITE_OMIT_SHARED_CACHE sqlite3VdbeChangeToNoop(v, 2, 1); #else sqlite3VdbeChangeP1(v, 2, iDb); sqlite3VdbeChangeP2(v, 2, pTab->tnum); sqlite3VdbeChangeP3(v, 2, flags); sqlite3VdbeChangeP4(v, 2, pTab->zName, P4_TRANSIENT); #endif /* Remove either the OP_OpenWrite or OpenRead. Set the P2 ** parameter of the other to pTab->tnum. */ sqlite3VdbeChangeToNoop(v, 4 - flags, 1); sqlite3VdbeChangeP2(v, 3 + flags, pTab->tnum); sqlite3VdbeChangeP3(v, 3 + flags, iDb); |
︙ | ︙ |
Changes to src/vdbemem.c.
︙ | ︙ | |||
1011 1012 1013 1014 1015 1016 1017 | sqlite3_value *pVal = 0; if( !pExpr ){ *ppVal = 0; return SQLITE_OK; } op = pExpr->op; | > > > > > > | > | < > | 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 | sqlite3_value *pVal = 0; if( !pExpr ){ *ppVal = 0; return SQLITE_OK; } op = pExpr->op; /* op can only be TK_REGISTER is we have compiled with SQLITE_ENABLE_STAT2. ** The ifdef here is to enable us to achieve 100% branch test coverage even ** when SQLITE_ENABLE_STAT2 is omitted. */ #ifdef SQLITE_ENABLE_STAT2 if( op==TK_REGISTER ) op = pExpr->op2; #else if( NEVER(op==TK_REGISTER) ) op = pExpr->op2; #endif if( op==TK_STRING || op==TK_FLOAT || op==TK_INTEGER ){ pVal = sqlite3ValueNew(db); if( pVal==0 ) goto no_mem; if( ExprHasProperty(pExpr, EP_IntValue) ){ sqlite3VdbeMemSetInt64(pVal, (i64)pExpr->u.iValue); }else{ |
︙ | ︙ |
Changes to src/vtab.c.
︙ | ︙ | |||
653 654 655 656 657 658 659 660 661 662 663 664 665 666 | pParse = sqlite3StackAllocZero(db, sizeof(*pParse)); if( pParse==0 ){ rc = SQLITE_NOMEM; }else{ pParse->declareVtab = 1; pParse->db = db; if( SQLITE_OK==sqlite3RunParser(pParse, zCreateTable, &zErr) && pParse->pNewTable && !db->mallocFailed && !pParse->pNewTable->pSelect && (pParse->pNewTable->tabFlags & TF_Virtual)==0 ){ | > | 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 | pParse = sqlite3StackAllocZero(db, sizeof(*pParse)); if( pParse==0 ){ rc = SQLITE_NOMEM; }else{ pParse->declareVtab = 1; pParse->db = db; pParse->nQueryLoop = 1; if( SQLITE_OK==sqlite3RunParser(pParse, zCreateTable, &zErr) && pParse->pNewTable && !db->mallocFailed && !pParse->pNewTable->pSelect && (pParse->pNewTable->tabFlags & TF_Virtual)==0 ){ |
︙ | ︙ |
Added src/wal.c.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334 1335 1336 1337 1338 1339 1340 1341 1342 1343 1344 1345 1346 1347 1348 1349 1350 1351 1352 1353 1354 1355 1356 1357 1358 1359 1360 1361 1362 1363 1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 1375 1376 1377 1378 1379 1380 1381 1382 1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398 1399 1400 1401 1402 1403 1404 1405 1406 1407 1408 1409 1410 1411 1412 1413 1414 1415 1416 1417 1418 1419 1420 1421 1422 1423 1424 1425 1426 1427 1428 1429 1430 1431 1432 1433 1434 1435 1436 1437 1438 1439 1440 1441 1442 1443 1444 1445 1446 1447 1448 1449 1450 1451 1452 1453 1454 1455 1456 1457 1458 1459 1460 1461 1462 1463 1464 1465 1466 1467 1468 1469 1470 1471 1472 1473 1474 1475 1476 1477 1478 1479 1480 1481 1482 1483 1484 1485 1486 1487 1488 1489 1490 1491 1492 1493 1494 1495 1496 1497 1498 1499 1500 1501 1502 1503 1504 1505 1506 1507 1508 1509 1510 1511 1512 1513 1514 1515 1516 1517 1518 1519 1520 1521 1522 1523 1524 1525 1526 1527 1528 1529 1530 1531 1532 1533 1534 1535 1536 1537 1538 1539 1540 1541 1542 1543 1544 1545 1546 1547 1548 1549 1550 1551 1552 1553 1554 1555 1556 1557 1558 1559 1560 1561 1562 1563 1564 1565 1566 1567 1568 1569 1570 1571 1572 1573 1574 1575 1576 1577 1578 1579 1580 1581 1582 1583 1584 1585 1586 1587 1588 1589 1590 1591 1592 1593 1594 1595 1596 1597 1598 1599 1600 1601 1602 1603 1604 1605 1606 1607 1608 1609 1610 1611 1612 1613 1614 1615 1616 1617 1618 1619 1620 1621 1622 1623 1624 1625 1626 1627 1628 1629 1630 1631 1632 1633 1634 1635 1636 1637 1638 1639 1640 1641 1642 1643 1644 1645 1646 1647 1648 1649 1650 1651 1652 1653 1654 1655 1656 1657 1658 1659 1660 1661 1662 1663 1664 1665 1666 1667 1668 1669 1670 1671 1672 1673 1674 1675 1676 1677 1678 1679 1680 1681 1682 1683 1684 1685 1686 1687 1688 1689 1690 1691 1692 1693 1694 1695 1696 1697 1698 1699 1700 1701 1702 1703 1704 1705 1706 1707 1708 1709 1710 1711 1712 1713 1714 1715 1716 1717 1718 1719 1720 1721 1722 1723 1724 1725 1726 1727 1728 1729 1730 1731 1732 1733 1734 1735 1736 1737 1738 1739 1740 1741 1742 1743 1744 1745 1746 1747 1748 1749 1750 1751 1752 1753 1754 1755 1756 1757 1758 1759 1760 1761 1762 1763 1764 1765 1766 1767 1768 1769 1770 1771 1772 1773 1774 1775 1776 1777 1778 1779 1780 1781 1782 1783 1784 1785 1786 1787 1788 1789 1790 1791 1792 1793 1794 1795 1796 1797 1798 1799 1800 1801 1802 1803 1804 1805 1806 1807 1808 1809 1810 1811 1812 1813 1814 1815 1816 1817 1818 1819 1820 1821 1822 1823 1824 1825 1826 1827 1828 1829 1830 1831 1832 1833 1834 1835 1836 1837 1838 1839 1840 1841 1842 1843 1844 1845 1846 1847 1848 1849 1850 1851 1852 1853 1854 1855 1856 1857 1858 1859 1860 1861 1862 1863 1864 1865 1866 1867 1868 1869 1870 1871 1872 1873 1874 1875 1876 1877 1878 1879 1880 1881 1882 1883 1884 1885 1886 1887 1888 1889 1890 1891 1892 1893 1894 1895 1896 1897 1898 1899 1900 1901 1902 1903 1904 1905 1906 1907 1908 1909 1910 1911 1912 1913 1914 1915 1916 1917 1918 1919 1920 1921 1922 1923 1924 1925 1926 1927 1928 1929 1930 1931 1932 1933 1934 1935 1936 1937 1938 1939 1940 1941 1942 1943 1944 1945 1946 1947 1948 1949 1950 1951 1952 1953 1954 1955 1956 1957 1958 1959 1960 1961 1962 1963 1964 1965 1966 1967 1968 1969 1970 1971 1972 1973 1974 1975 1976 1977 1978 1979 1980 1981 1982 1983 1984 1985 1986 1987 1988 1989 1990 1991 1992 1993 1994 1995 1996 1997 1998 1999 2000 2001 2002 2003 2004 2005 2006 2007 2008 2009 2010 2011 2012 2013 2014 2015 2016 2017 2018 2019 2020 2021 2022 2023 2024 2025 2026 2027 2028 2029 2030 2031 2032 2033 2034 2035 2036 2037 2038 2039 2040 2041 2042 2043 2044 2045 2046 2047 2048 2049 2050 2051 2052 2053 2054 2055 2056 2057 2058 2059 2060 2061 2062 2063 2064 2065 2066 2067 2068 2069 2070 2071 2072 2073 2074 2075 2076 2077 2078 2079 2080 2081 2082 2083 2084 2085 2086 2087 2088 2089 2090 2091 2092 2093 2094 2095 2096 2097 2098 2099 2100 2101 2102 2103 2104 2105 2106 2107 2108 2109 2110 2111 2112 2113 2114 2115 2116 2117 2118 2119 2120 2121 2122 2123 2124 2125 2126 2127 2128 2129 2130 2131 2132 2133 2134 2135 2136 2137 2138 2139 2140 2141 2142 2143 2144 2145 2146 2147 2148 2149 2150 2151 2152 2153 2154 2155 2156 2157 2158 2159 2160 2161 2162 2163 2164 2165 2166 2167 2168 2169 2170 2171 2172 2173 2174 2175 2176 2177 2178 2179 2180 2181 2182 2183 2184 2185 2186 2187 2188 2189 2190 2191 2192 2193 2194 2195 2196 2197 2198 2199 2200 2201 2202 2203 2204 2205 2206 2207 2208 2209 2210 2211 2212 2213 2214 2215 2216 2217 2218 2219 2220 2221 2222 2223 2224 2225 2226 2227 2228 2229 2230 2231 2232 2233 2234 2235 2236 2237 2238 2239 2240 2241 2242 2243 2244 2245 2246 2247 2248 2249 2250 2251 2252 2253 2254 2255 2256 2257 2258 2259 2260 2261 2262 2263 2264 2265 2266 2267 2268 2269 2270 2271 2272 2273 2274 2275 2276 2277 2278 2279 2280 2281 2282 2283 2284 2285 2286 2287 2288 2289 2290 2291 2292 2293 2294 2295 2296 2297 2298 2299 2300 2301 2302 2303 2304 2305 2306 2307 2308 2309 2310 2311 2312 2313 2314 2315 2316 2317 2318 2319 2320 2321 2322 2323 2324 2325 2326 2327 2328 2329 2330 2331 2332 2333 2334 2335 2336 2337 2338 2339 2340 2341 2342 2343 2344 2345 2346 2347 2348 2349 2350 2351 2352 2353 2354 2355 2356 2357 2358 2359 2360 2361 2362 2363 2364 2365 2366 2367 2368 2369 2370 2371 2372 2373 2374 2375 2376 2377 2378 2379 2380 2381 2382 2383 2384 2385 2386 2387 2388 2389 2390 2391 2392 2393 2394 2395 2396 2397 2398 2399 2400 2401 2402 2403 2404 2405 2406 2407 2408 2409 2410 2411 2412 2413 2414 2415 2416 2417 2418 2419 2420 2421 2422 2423 2424 2425 2426 2427 2428 2429 2430 2431 2432 2433 2434 2435 2436 2437 2438 2439 2440 2441 2442 2443 2444 2445 2446 2447 2448 2449 2450 2451 2452 2453 2454 2455 2456 2457 2458 2459 2460 2461 2462 2463 2464 2465 2466 2467 2468 2469 2470 2471 2472 2473 2474 2475 2476 2477 2478 2479 2480 2481 2482 2483 2484 2485 2486 2487 2488 2489 2490 2491 2492 2493 2494 2495 2496 2497 2498 2499 2500 2501 2502 2503 2504 2505 2506 2507 2508 2509 2510 2511 2512 2513 2514 2515 2516 2517 2518 2519 2520 2521 2522 2523 2524 2525 2526 2527 2528 2529 2530 2531 2532 2533 2534 2535 2536 2537 2538 2539 2540 2541 2542 2543 2544 2545 2546 2547 2548 2549 2550 2551 2552 2553 2554 2555 2556 2557 2558 2559 2560 2561 2562 2563 2564 2565 2566 2567 2568 2569 2570 2571 2572 2573 2574 2575 2576 2577 2578 2579 2580 2581 2582 2583 2584 2585 2586 2587 2588 2589 2590 2591 2592 2593 2594 2595 2596 2597 2598 2599 2600 2601 2602 2603 2604 2605 2606 2607 2608 2609 2610 2611 2612 2613 2614 2615 2616 2617 2618 2619 2620 2621 2622 2623 2624 2625 2626 2627 2628 2629 2630 2631 2632 2633 2634 2635 2636 2637 2638 2639 2640 2641 2642 2643 2644 2645 2646 2647 2648 2649 2650 2651 2652 2653 2654 2655 2656 2657 2658 2659 2660 2661 | /* ** 2010 February 1 ** ** The author disclaims copyright to this source code. In place of ** a legal notice, here is a blessing: ** ** May you do good and not evil. ** May you find forgiveness for yourself and forgive others. ** May you share freely, never taking more than you give. ** ************************************************************************* ** ** This file contains the implementation of a write-ahead log (WAL) used in ** "journal_mode=WAL" mode. ** ** WRITE-AHEAD LOG (WAL) FILE FORMAT ** ** A WAL file consists of a header followed by zero or more "frames". ** Each frame records the revised content of a single page from the ** database file. All changes to the database are recorded by writing ** frames into the WAL. Transactions commit when a frame is written that ** contains a commit marker. A single WAL can and usually does record ** multiple transactions. Periodically, the content of the WAL is ** transferred back into the database file in an operation called a ** "checkpoint". ** ** A single WAL file can be used multiple times. In other words, the ** WAL can fill up with frames and then be checkpointed and then new ** frames can overwrite the old ones. A WAL always grows from beginning ** toward the end. Checksums and counters attached to each frame are ** used to determine which frames within the WAL are valid and which ** are leftovers from prior checkpoints. ** ** The WAL header is 32 bytes in size and consists of the following eight ** big-endian 32-bit unsigned integer values: ** ** 0: Magic number. 0x377f0682 or 0x377f0683 ** 4: File format version. Currently 3007000 ** 8: Database page size. Example: 1024 ** 12: Checkpoint sequence number ** 16: Salt-1, random integer incremented with each checkpoint ** 20: Salt-2, a different random integer changing with each ckpt ** 24: Checksum-1 (first part of checksum for first 24 bytes of header). ** 28: Checksum-2 (second part of checksum for first 24 bytes of header). ** ** Immediately following the wal-header are zero or more frames. Each ** frame consists of a 24-byte frame-header followed by a <page-size> bytes ** of page data. The frame-header is six big-endian 32-bit unsigned ** integer values, as follows: ** ** 0: Page number. ** 4: For commit records, the size of the database image in pages ** after the commit. For all other records, zero. ** 8: Salt-1 (copied from the header) ** 12: Salt-2 (copied from the header) ** 16: Checksum-1. ** 20: Checksum-2. ** ** A frame is considered valid if and only if the following conditions are ** true: ** ** (1) The salt-1 and salt-2 values in the frame-header match ** salt values in the wal-header ** ** (2) The checksum values in the final 8 bytes of the frame-header ** exactly match the checksum computed consecutively on the ** WAL header and the first 8 bytes and the content of all frames ** up to and including the current frame. ** ** The checksum is computed using 32-bit big-endian integers if the ** magic number in the first 4 bytes of the WAL is 0x377f0683 and it ** is computed using little-endian if the magic number is 0x377f0682. ** The checksum values are always stored in the frame header in a ** big-endian format regardless of which byte order is used to compute ** the checksum. The checksum is computed by interpreting the input as ** an even number of unsigned 32-bit integers: x[0] through x[N]. The ** algorithm used for the checksum is as follows: ** ** for i from 0 to n-1 step 2: ** s0 += x[i] + s1; ** s1 += x[i+1] + s0; ** endfor ** ** Note that s0 and s1 are both weighted checksums using fibonacci weights ** in reverse order (the largest fibonacci weight occurs on the first element ** of the sequence being summed.) The s1 value spans all 32-bit ** terms of the sequence whereas s0 omits the final term. ** ** On a checkpoint, the WAL is first VFS.xSync-ed, then valid content of the ** WAL is transferred into the database, then the database is VFS.xSync-ed. ** The VFS.xSync operations serve as write barriers - all writes launched ** before the xSync must complete before any write that launches after the ** xSync begins. ** ** After each checkpoint, the salt-1 value is incremented and the salt-2 ** value is randomized. This prevents old and new frames in the WAL from ** being considered valid at the same time and being checkpointing together ** following a crash. ** ** READER ALGORITHM ** ** To read a page from the database (call it page number P), a reader ** first checks the WAL to see if it contains page P. If so, then the ** last valid instance of page P that is a followed by a commit frame ** or is a commit frame itself becomes the value read. If the WAL ** contains no copies of page P that are valid and which are a commit ** frame or are followed by a commit frame, then page P is read from ** the database file. ** ** To start a read transaction, the reader records the index of the last ** valid frame in the WAL. The reader uses this recorded "mxFrame" value ** for all subsequent read operations. New transactions can be appended ** to the WAL, but as long as the reader uses its original mxFrame value ** and ignores the newly appended content, it will see a consistent snapshot ** of the database from a single point in time. This technique allows ** multiple concurrent readers to view different versions of the database ** content simultaneously. ** ** The reader algorithm in the previous paragraphs works correctly, but ** because frames for page P can appear anywhere within the WAL, the ** reader has to scan the entire WAL looking for page P frames. If the ** WAL is large (multiple megabytes is typical) that scan can be slow, ** and read performance suffers. To overcome this problem, a separate ** data structure called the wal-index is maintained to expedite the ** search for frames of a particular page. ** ** WAL-INDEX FORMAT ** ** Conceptually, the wal-index is shared memory, though VFS implementations ** might choose to implement the wal-index using a mmapped file. Because ** the wal-index is shared memory, SQLite does not support journal_mode=WAL ** on a network filesystem. All users of the database must be able to ** share memory. ** ** The wal-index is transient. After a crash, the wal-index can (and should ** be) reconstructed from the original WAL file. In fact, the VFS is required ** to either truncate or zero the header of the wal-index when the last ** connection to it closes. Because the wal-index is transient, it can ** use an architecture-specific format; it does not have to be cross-platform. ** Hence, unlike the database and WAL file formats which store all values ** as big endian, the wal-index can store multi-byte values in the native ** byte order of the host computer. ** ** The purpose of the wal-index is to answer this question quickly: Given ** a page number P, return the index of the last frame for page P in the WAL, ** or return NULL if there are no frames for page P in the WAL. ** ** The wal-index consists of a header region, followed by an one or ** more index blocks. ** ** The wal-index header contains the total number of frames within the WAL ** in the the mxFrame field. ** ** Each index block except for the first contains information on ** HASHTABLE_NPAGE frames. The first index block contains information on ** HASHTABLE_NPAGE_ONE frames. The values of HASHTABLE_NPAGE_ONE and ** HASHTABLE_NPAGE are selected so that together the wal-index header and ** first index block are the same size as all other index blocks in the ** wal-index. ** ** Each index block contains two sections, a page-mapping that contains the ** database page number associated with each wal frame, and a hash-table ** that allows readers to query an index block for a specific page number. ** The page-mapping is an array of HASHTABLE_NPAGE (or HASHTABLE_NPAGE_ONE ** for the first index block) 32-bit page numbers. The first entry in the ** first index-block contains the database page number corresponding to the ** first frame in the WAL file. The first entry in the second index block ** in the WAL file corresponds to the (HASHTABLE_NPAGE_ONE+1)th frame in ** the log, and so on. ** ** The last index block in a wal-index usually contains less than the full ** complement of HASHTABLE_NPAGE (or HASHTABLE_NPAGE_ONE) page-numbers, ** depending on the contents of the WAL file. This does not change the ** allocated size of the page-mapping array - the page-mapping array merely ** contains unused entries. ** ** Even without using the hash table, the last frame for page P ** can be found by scanning the page-mapping sections of each index block ** starting with the last index block and moving toward the first, and ** within each index block, starting at the end and moving toward the ** beginning. The first entry that equals P corresponds to the frame ** holding the content for that page. ** ** The hash table consists of HASHTABLE_NSLOT 16-bit unsigned integers. ** HASHTABLE_NSLOT = 2*HASHTABLE_NPAGE, and there is one entry in the ** hash table for each page number in the mapping section, so the hash ** table is never more than half full. The expected number of collisions ** prior to finding a match is 1. Each entry of the hash table is an ** 1-based index of an entry in the mapping section of the same ** index block. Let K be the 1-based index of the largest entry in ** the mapping section. (For index blocks other than the last, K will ** always be exactly HASHTABLE_NPAGE (4096) and for the last index block ** K will be (mxFrame%HASHTABLE_NPAGE).) Unused slots of the hash table ** contain a value of 0. ** ** To look for page P in the hash table, first compute a hash iKey on ** P as follows: ** ** iKey = (P * 383) % HASHTABLE_NSLOT ** ** Then start scanning entries of the hash table, starting with iKey ** (wrapping around to the beginning when the end of the hash table is ** reached) until an unused hash slot is found. Let the first unused slot ** be at index iUnused. (iUnused might be less than iKey if there was ** wrap-around.) Because the hash table is never more than half full, ** the search is guaranteed to eventually hit an unused entry. Let ** iMax be the value between iKey and iUnused, closest to iUnused, ** where aHash[iMax]==P. If there is no iMax entry (if there exists ** no hash slot such that aHash[i]==p) then page P is not in the ** current index block. Otherwise the iMax-th mapping entry of the ** current index block corresponds to the last entry that references ** page P. ** ** A hash search begins with the last index block and moves toward the ** first index block, looking for entries corresponding to page P. On ** average, only two or three slots in each index block need to be ** examined in order to either find the last entry for page P, or to ** establish that no such entry exists in the block. Each index block ** holds over 4000 entries. So two or three index blocks are sufficient ** to cover a typical 10 megabyte WAL file, assuming 1K pages. 8 or 10 ** comparisons (on average) suffice to either locate a frame in the ** WAL or to establish that the frame does not exist in the WAL. This ** is much faster than scanning the entire 10MB WAL. ** ** Note that entries are added in order of increasing K. Hence, one ** reader might be using some value K0 and a second reader that started ** at a later time (after additional transactions were added to the WAL ** and to the wal-index) might be using a different value K1, where K1>K0. ** Both readers can use the same hash table and mapping section to get ** the correct result. There may be entries in the hash table with ** K>K0 but to the first reader, those entries will appear to be unused ** slots in the hash table and so the first reader will get an answer as ** if no values greater than K0 had ever been inserted into the hash table ** in the first place - which is what reader one wants. Meanwhile, the ** second reader using K1 will see additional values that were inserted ** later, which is exactly what reader two wants. ** ** When a rollback occurs, the value of K is decreased. Hash table entries ** that correspond to frames greater than the new K value are removed ** from the hash table at this point. */ #ifndef SQLITE_OMIT_WAL #include "wal.h" /* ** Trace output macros */ #if defined(SQLITE_TEST) && defined(SQLITE_DEBUG) int sqlite3WalTrace = 0; # define WALTRACE(X) if(sqlite3WalTrace) sqlite3DebugPrintf X #else # define WALTRACE(X) #endif /* ** The maximum (and only) versions of the wal and wal-index formats ** that may be interpreted by this version of SQLite. ** ** If a client begins recovering a WAL file and finds that (a) the checksum ** values in the wal-header are correct and (b) the version field is not ** WAL_MAX_VERSION, recovery fails and SQLite returns SQLITE_CANTOPEN. ** ** Similarly, if a client successfully reads a wal-index header (i.e. the ** checksum test is successful) and finds that the version field is not ** WALINDEX_MAX_VERSION, then no read-transaction is opened and SQLite ** returns SQLITE_CANTOPEN. */ #define WAL_MAX_VERSION 3007000 #define WALINDEX_MAX_VERSION 3007000 /* ** Indices of various locking bytes. WAL_NREADER is the number ** of available reader locks and should be at least 3. */ #define WAL_WRITE_LOCK 0 #define WAL_ALL_BUT_WRITE 1 #define WAL_CKPT_LOCK 1 #define WAL_RECOVER_LOCK 2 #define WAL_READ_LOCK(I) (3+(I)) #define WAL_NREADER (SQLITE_SHM_NLOCK-3) /* Object declarations */ typedef struct WalIndexHdr WalIndexHdr; typedef struct WalIterator WalIterator; typedef struct WalCkptInfo WalCkptInfo; /* ** The following object holds a copy of the wal-index header content. ** ** The actual header in the wal-index consists of two copies of this ** object. */ struct WalIndexHdr { u32 iVersion; /* Wal-index version */ u32 unused; /* Unused (padding) field */ u32 iChange; /* Counter incremented each transaction */ u8 isInit; /* 1 when initialized */ u8 bigEndCksum; /* True if checksums in WAL are big-endian */ u16 szPage; /* Database page size in bytes */ u32 mxFrame; /* Index of last valid frame in the WAL */ u32 nPage; /* Size of database in pages */ u32 aFrameCksum[2]; /* Checksum of last frame in log */ u32 aSalt[2]; /* Two salt values copied from WAL header */ u32 aCksum[2]; /* Checksum over all prior fields */ }; /* ** A copy of the following object occurs in the wal-index immediately ** following the second copy of the WalIndexHdr. This object stores ** information used by checkpoint. ** ** nBackfill is the number of frames in the WAL that have been written ** back into the database. (We call the act of moving content from WAL to ** database "backfilling".) The nBackfill number is never greater than ** WalIndexHdr.mxFrame. nBackfill can only be increased by threads ** holding the WAL_CKPT_LOCK lock (which includes a recovery thread). ** However, a WAL_WRITE_LOCK thread can move the value of nBackfill from ** mxFrame back to zero when the WAL is reset. ** ** There is one entry in aReadMark[] for each reader lock. If a reader ** holds read-lock K, then the value in aReadMark[K] is no greater than ** the mxFrame for that reader. The value READMARK_NOT_USED (0xffffffff) ** for any aReadMark[] means that entry is unused. aReadMark[0] is ** a special case; its value is never used and it exists as a place-holder ** to avoid having to offset aReadMark[] indexs by one. Readers holding ** WAL_READ_LOCK(0) always ignore the entire WAL and read all content ** directly from the database. ** ** The value of aReadMark[K] may only be changed by a thread that ** is holding an exclusive lock on WAL_READ_LOCK(K). Thus, the value of ** aReadMark[K] cannot changed while there is a reader is using that mark ** since the reader will be holding a shared lock on WAL_READ_LOCK(K). ** ** The checkpointer may only transfer frames from WAL to database where ** the frame numbers are less than or equal to every aReadMark[] that is ** in use (that is, every aReadMark[j] for which there is a corresponding ** WAL_READ_LOCK(j)). New readers (usually) pick the aReadMark[] with the ** largest value and will increase an unused aReadMark[] to mxFrame if there ** is not already an aReadMark[] equal to mxFrame. The exception to the ** previous sentence is when nBackfill equals mxFrame (meaning that everything ** in the WAL has been backfilled into the database) then new readers ** will choose aReadMark[0] which has value 0 and hence such reader will ** get all their all content directly from the database file and ignore ** the WAL. ** ** Writers normally append new frames to the end of the WAL. However, ** if nBackfill equals mxFrame (meaning that all WAL content has been ** written back into the database) and if no readers are using the WAL ** (in other words, if there are no WAL_READ_LOCK(i) where i>0) then ** the writer will first "reset" the WAL back to the beginning and start ** writing new content beginning at frame 1. ** ** We assume that 32-bit loads are atomic and so no locks are needed in ** order to read from any aReadMark[] entries. */ struct WalCkptInfo { u32 nBackfill; /* Number of WAL frames backfilled into DB */ u32 aReadMark[WAL_NREADER]; /* Reader marks */ }; #define READMARK_NOT_USED 0xffffffff /* A block of WALINDEX_LOCK_RESERVED bytes beginning at ** WALINDEX_LOCK_OFFSET is reserved for locks. Since some systems ** only support mandatory file-locks, we do not read or write data ** from the region of the file on which locks are applied. */ #define WALINDEX_LOCK_OFFSET (sizeof(WalIndexHdr)*2 + sizeof(WalCkptInfo)) #define WALINDEX_LOCK_RESERVED 16 #define WALINDEX_HDR_SIZE (WALINDEX_LOCK_OFFSET+WALINDEX_LOCK_RESERVED) /* Size of header before each frame in wal */ #define WAL_FRAME_HDRSIZE 24 /* Size of write ahead log header, including checksum. */ /* #define WAL_HDRSIZE 24 */ #define WAL_HDRSIZE 32 /* WAL magic value. Either this value, or the same value with the least ** significant bit also set (WAL_MAGIC | 0x00000001) is stored in 32-bit ** big-endian format in the first 4 bytes of a WAL file. ** ** If the LSB is set, then the checksums for each frame within the WAL ** file are calculated by treating all data as an array of 32-bit ** big-endian words. Otherwise, they are calculated by interpreting ** all data as 32-bit little-endian words. */ #define WAL_MAGIC 0x377f0682 /* ** Return the offset of frame iFrame in the write-ahead log file, ** assuming a database page size of szPage bytes. The offset returned ** is to the start of the write-ahead log frame-header. */ #define walFrameOffset(iFrame, szPage) ( \ WAL_HDRSIZE + ((iFrame)-1)*(i64)((szPage)+WAL_FRAME_HDRSIZE) \ ) /* ** An open write-ahead log file is represented by an instance of the ** following object. */ struct Wal { sqlite3_vfs *pVfs; /* The VFS used to create pDbFd */ sqlite3_file *pDbFd; /* File handle for the database file */ sqlite3_file *pWalFd; /* File handle for WAL file */ u32 iCallback; /* Value to pass to log callback (or 0) */ int nWiData; /* Size of array apWiData */ volatile u32 **apWiData; /* Pointer to wal-index content in memory */ u16 szPage; /* Database page size */ i16 readLock; /* Which read lock is being held. -1 for none */ u8 exclusiveMode; /* Non-zero if connection is in exclusive mode */ u8 writeLock; /* True if in a write transaction */ u8 ckptLock; /* True if holding a checkpoint lock */ u8 readOnly; /* True if the WAL file is open read-only */ WalIndexHdr hdr; /* Wal-index header for current transaction */ const char *zWalName; /* Name of WAL file */ u32 nCkpt; /* Checkpoint sequence counter in the wal-header */ #ifdef SQLITE_DEBUG u8 lockError; /* True if a locking error has occurred */ #endif }; /* ** Each page of the wal-index mapping contains a hash-table made up of ** an array of HASHTABLE_NSLOT elements of the following type. */ typedef u16 ht_slot; /* ** This structure is used to implement an iterator that loops through ** all frames in the WAL in database page order. Where two or more frames ** correspond to the same database page, the iterator visits only the ** frame most recently written to the WAL (in other words, the frame with ** the largest index). ** ** The internals of this structure are only accessed by: ** ** walIteratorInit() - Create a new iterator, ** walIteratorNext() - Step an iterator, ** walIteratorFree() - Free an iterator. ** ** This functionality is used by the checkpoint code (see walCheckpoint()). */ struct WalIterator { int iPrior; /* Last result returned from the iterator */ int nSegment; /* Size of the aSegment[] array */ struct WalSegment { int iNext; /* Next slot in aIndex[] not yet returned */ ht_slot *aIndex; /* i0, i1, i2... such that aPgno[iN] ascend */ u32 *aPgno; /* Array of page numbers. */ int nEntry; /* Max size of aPgno[] and aIndex[] arrays */ int iZero; /* Frame number associated with aPgno[0] */ } aSegment[1]; /* One for every 32KB page in the WAL */ }; /* ** Define the parameters of the hash tables in the wal-index file. There ** is a hash-table following every HASHTABLE_NPAGE page numbers in the ** wal-index. ** ** Changing any of these constants will alter the wal-index format and ** create incompatibilities. */ #define HASHTABLE_NPAGE 4096 /* Must be power of 2 */ #define HASHTABLE_HASH_1 383 /* Should be prime */ #define HASHTABLE_NSLOT (HASHTABLE_NPAGE*2) /* Must be a power of 2 */ /* ** The block of page numbers associated with the first hash-table in a ** wal-index is smaller than usual. This is so that there is a complete ** hash-table on each aligned 32KB page of the wal-index. */ #define HASHTABLE_NPAGE_ONE (HASHTABLE_NPAGE - (WALINDEX_HDR_SIZE/sizeof(u32))) /* The wal-index is divided into pages of WALINDEX_PGSZ bytes each. */ #define WALINDEX_PGSZ ( \ sizeof(ht_slot)*HASHTABLE_NSLOT + HASHTABLE_NPAGE*sizeof(u32) \ ) /* ** Obtain a pointer to the iPage'th page of the wal-index. The wal-index ** is broken into pages of WALINDEX_PGSZ bytes. Wal-index pages are ** numbered from zero. ** ** If this call is successful, *ppPage is set to point to the wal-index ** page and SQLITE_OK is returned. If an error (an OOM or VFS error) occurs, ** then an SQLite error code is returned and *ppPage is set to 0. */ static int walIndexPage(Wal *pWal, int iPage, volatile u32 **ppPage){ int rc = SQLITE_OK; /* Enlarge the pWal->apWiData[] array if required */ if( pWal->nWiData<=iPage ){ int nByte = sizeof(u32*)*(iPage+1); volatile u32 **apNew; apNew = (volatile u32 **)sqlite3_realloc((void *)pWal->apWiData, nByte); if( !apNew ){ *ppPage = 0; return SQLITE_NOMEM; } memset((void*)&apNew[pWal->nWiData], 0, sizeof(u32*)*(iPage+1-pWal->nWiData)); pWal->apWiData = apNew; pWal->nWiData = iPage+1; } /* Request a pointer to the required page from the VFS */ if( pWal->apWiData[iPage]==0 ){ rc = sqlite3OsShmMap(pWal->pDbFd, iPage, WALINDEX_PGSZ, pWal->writeLock, (void volatile **)&pWal->apWiData[iPage] ); } *ppPage = pWal->apWiData[iPage]; assert( iPage==0 || *ppPage || rc!=SQLITE_OK ); return rc; } /* ** Return a pointer to the WalCkptInfo structure in the wal-index. */ static volatile WalCkptInfo *walCkptInfo(Wal *pWal){ assert( pWal->nWiData>0 && pWal->apWiData[0] ); return (volatile WalCkptInfo*)&(pWal->apWiData[0][sizeof(WalIndexHdr)/2]); } /* ** Return a pointer to the WalIndexHdr structure in the wal-index. */ static volatile WalIndexHdr *walIndexHdr(Wal *pWal){ assert( pWal->nWiData>0 && pWal->apWiData[0] ); return (volatile WalIndexHdr*)pWal->apWiData[0]; } /* ** The argument to this macro must be of type u32. On a little-endian ** architecture, it returns the u32 value that results from interpreting ** the 4 bytes as a big-endian value. On a big-endian architecture, it ** returns the value that would be produced by intepreting the 4 bytes ** of the input value as a little-endian integer. */ #define BYTESWAP32(x) ( \ (((x)&0x000000FF)<<24) + (((x)&0x0000FF00)<<8) \ + (((x)&0x00FF0000)>>8) + (((x)&0xFF000000)>>24) \ ) /* ** Generate or extend an 8 byte checksum based on the data in ** array aByte[] and the initial values of aIn[0] and aIn[1] (or ** initial values of 0 and 0 if aIn==NULL). ** ** The checksum is written back into aOut[] before returning. ** ** nByte must be a positive multiple of 8. */ static void walChecksumBytes( int nativeCksum, /* True for native byte-order, false for non-native */ u8 *a, /* Content to be checksummed */ int nByte, /* Bytes of content in a[]. Must be a multiple of 8. */ const u32 *aIn, /* Initial checksum value input */ u32 *aOut /* OUT: Final checksum value output */ ){ u32 s1, s2; u32 *aData = (u32 *)a; u32 *aEnd = (u32 *)&a[nByte]; if( aIn ){ s1 = aIn[0]; s2 = aIn[1]; }else{ s1 = s2 = 0; } assert( nByte>=8 ); assert( (nByte&0x00000007)==0 ); if( nativeCksum ){ do { s1 += *aData++ + s2; s2 += *aData++ + s1; }while( aData<aEnd ); }else{ do { s1 += BYTESWAP32(aData[0]) + s2; s2 += BYTESWAP32(aData[1]) + s1; aData += 2; }while( aData<aEnd ); } aOut[0] = s1; aOut[1] = s2; } /* ** Write the header information in pWal->hdr into the wal-index. ** ** The checksum on pWal->hdr is updated before it is written. */ static void walIndexWriteHdr(Wal *pWal){ volatile WalIndexHdr *aHdr = walIndexHdr(pWal); const int nCksum = offsetof(WalIndexHdr, aCksum); assert( pWal->writeLock ); pWal->hdr.isInit = 1; pWal->hdr.iVersion = WALINDEX_MAX_VERSION; walChecksumBytes(1, (u8*)&pWal->hdr, nCksum, 0, pWal->hdr.aCksum); memcpy((void *)&aHdr[1], (void *)&pWal->hdr, sizeof(WalIndexHdr)); sqlite3OsShmBarrier(pWal->pDbFd); memcpy((void *)&aHdr[0], (void *)&pWal->hdr, sizeof(WalIndexHdr)); } /* ** This function encodes a single frame header and writes it to a buffer ** supplied by the caller. A frame-header is made up of a series of ** 4-byte big-endian integers, as follows: ** ** 0: Page number. ** 4: For commit records, the size of the database image in pages ** after the commit. For all other records, zero. ** 8: Salt-1 (copied from the wal-header) ** 12: Salt-2 (copied from the wal-header) ** 16: Checksum-1. ** 20: Checksum-2. */ static void walEncodeFrame( Wal *pWal, /* The write-ahead log */ u32 iPage, /* Database page number for frame */ u32 nTruncate, /* New db size (or 0 for non-commit frames) */ u8 *aData, /* Pointer to page data */ u8 *aFrame /* OUT: Write encoded frame here */ ){ int nativeCksum; /* True for native byte-order checksums */ u32 *aCksum = pWal->hdr.aFrameCksum; assert( WAL_FRAME_HDRSIZE==24 ); sqlite3Put4byte(&aFrame[0], iPage); sqlite3Put4byte(&aFrame[4], nTruncate); memcpy(&aFrame[8], pWal->hdr.aSalt, 8); nativeCksum = (pWal->hdr.bigEndCksum==SQLITE_BIGENDIAN); walChecksumBytes(nativeCksum, aFrame, 8, aCksum, aCksum); walChecksumBytes(nativeCksum, aData, pWal->szPage, aCksum, aCksum); sqlite3Put4byte(&aFrame[16], aCksum[0]); sqlite3Put4byte(&aFrame[20], aCksum[1]); } /* ** Check to see if the frame with header in aFrame[] and content ** in aData[] is valid. If it is a valid frame, fill *piPage and ** *pnTruncate and return true. Return if the frame is not valid. */ static int walDecodeFrame( Wal *pWal, /* The write-ahead log */ u32 *piPage, /* OUT: Database page number for frame */ u32 *pnTruncate, /* OUT: New db size (or 0 if not commit) */ u8 *aData, /* Pointer to page data (for checksum) */ u8 *aFrame /* Frame data */ ){ int nativeCksum; /* True for native byte-order checksums */ u32 *aCksum = pWal->hdr.aFrameCksum; u32 pgno; /* Page number of the frame */ assert( WAL_FRAME_HDRSIZE==24 ); /* A frame is only valid if the salt values in the frame-header ** match the salt values in the wal-header. */ if( memcmp(&pWal->hdr.aSalt, &aFrame[8], 8)!=0 ){ return 0; } /* A frame is only valid if the page number is creater than zero. */ pgno = sqlite3Get4byte(&aFrame[0]); if( pgno==0 ){ return 0; } /* A frame is only valid if a checksum of the WAL header, ** all prior frams, the first 16 bytes of this frame-header, ** and the frame-data matches the checksum in the last 8 ** bytes of this frame-header. */ nativeCksum = (pWal->hdr.bigEndCksum==SQLITE_BIGENDIAN); walChecksumBytes(nativeCksum, aFrame, 8, aCksum, aCksum); walChecksumBytes(nativeCksum, aData, pWal->szPage, aCksum, aCksum); if( aCksum[0]!=sqlite3Get4byte(&aFrame[16]) || aCksum[1]!=sqlite3Get4byte(&aFrame[20]) ){ /* Checksum failed. */ return 0; } /* If we reach this point, the frame is valid. Return the page number ** and the new database size. */ *piPage = pgno; *pnTruncate = sqlite3Get4byte(&aFrame[4]); return 1; } #if defined(SQLITE_TEST) && defined(SQLITE_DEBUG) /* ** Names of locks. This routine is used to provide debugging output and is not ** a part of an ordinary build. */ static const char *walLockName(int lockIdx){ if( lockIdx==WAL_WRITE_LOCK ){ return "WRITE-LOCK"; }else if( lockIdx==WAL_CKPT_LOCK ){ return "CKPT-LOCK"; }else if( lockIdx==WAL_RECOVER_LOCK ){ return "RECOVER-LOCK"; }else{ static char zName[15]; sqlite3_snprintf(sizeof(zName), zName, "READ-LOCK[%d]", lockIdx-WAL_READ_LOCK(0)); return zName; } } #endif /*defined(SQLITE_TEST) || defined(SQLITE_DEBUG) */ /* ** Set or release locks on the WAL. Locks are either shared or exclusive. ** A lock cannot be moved directly between shared and exclusive - it must go ** through the unlocked state first. ** ** In locking_mode=EXCLUSIVE, all of these routines become no-ops. */ static int walLockShared(Wal *pWal, int lockIdx){ int rc; if( pWal->exclusiveMode ) return SQLITE_OK; rc = sqlite3OsShmLock(pWal->pDbFd, lockIdx, 1, SQLITE_SHM_LOCK | SQLITE_SHM_SHARED); WALTRACE(("WAL%p: acquire SHARED-%s %s\n", pWal, walLockName(lockIdx), rc ? "failed" : "ok")); VVA_ONLY( pWal->lockError = (u8)(rc!=SQLITE_OK && rc!=SQLITE_BUSY); ) return rc; } static void walUnlockShared(Wal *pWal, int lockIdx){ if( pWal->exclusiveMode ) return; (void)sqlite3OsShmLock(pWal->pDbFd, lockIdx, 1, SQLITE_SHM_UNLOCK | SQLITE_SHM_SHARED); WALTRACE(("WAL%p: release SHARED-%s\n", pWal, walLockName(lockIdx))); } static int walLockExclusive(Wal *pWal, int lockIdx, int n){ int rc; if( pWal->exclusiveMode ) return SQLITE_OK; rc = sqlite3OsShmLock(pWal->pDbFd, lockIdx, n, SQLITE_SHM_LOCK | SQLITE_SHM_EXCLUSIVE); WALTRACE(("WAL%p: acquire EXCLUSIVE-%s cnt=%d %s\n", pWal, walLockName(lockIdx), n, rc ? "failed" : "ok")); VVA_ONLY( pWal->lockError = (u8)(rc!=SQLITE_OK && rc!=SQLITE_BUSY); ) return rc; } static void walUnlockExclusive(Wal *pWal, int lockIdx, int n){ if( pWal->exclusiveMode ) return; (void)sqlite3OsShmLock(pWal->pDbFd, lockIdx, n, SQLITE_SHM_UNLOCK | SQLITE_SHM_EXCLUSIVE); WALTRACE(("WAL%p: release EXCLUSIVE-%s cnt=%d\n", pWal, walLockName(lockIdx), n)); } /* ** Compute a hash on a page number. The resulting hash value must land ** between 0 and (HASHTABLE_NSLOT-1). The walHashNext() function advances ** the hash to the next value in the event of a collision. */ static int walHash(u32 iPage){ assert( iPage>0 ); assert( (HASHTABLE_NSLOT & (HASHTABLE_NSLOT-1))==0 ); return (iPage*HASHTABLE_HASH_1) & (HASHTABLE_NSLOT-1); } static int walNextHash(int iPriorHash){ return (iPriorHash+1)&(HASHTABLE_NSLOT-1); } /* ** Return pointers to the hash table and page number array stored on ** page iHash of the wal-index. The wal-index is broken into 32KB pages ** numbered starting from 0. ** ** Set output variable *paHash to point to the start of the hash table ** in the wal-index file. Set *piZero to one less than the frame ** number of the first frame indexed by this hash table. If a ** slot in the hash table is set to N, it refers to frame number ** (*piZero+N) in the log. ** ** Finally, set *paPgno so that *paPgno[1] is the page number of the ** first frame indexed by the hash table, frame (*piZero+1). */ static int walHashGet( Wal *pWal, /* WAL handle */ int iHash, /* Find the iHash'th table */ volatile ht_slot **paHash, /* OUT: Pointer to hash index */ volatile u32 **paPgno, /* OUT: Pointer to page number array */ u32 *piZero /* OUT: Frame associated with *paPgno[0] */ ){ int rc; /* Return code */ volatile u32 *aPgno; rc = walIndexPage(pWal, iHash, &aPgno); assert( rc==SQLITE_OK || iHash>0 ); if( rc==SQLITE_OK ){ u32 iZero; volatile ht_slot *aHash; aHash = (volatile ht_slot *)&aPgno[HASHTABLE_NPAGE]; if( iHash==0 ){ aPgno = &aPgno[WALINDEX_HDR_SIZE/sizeof(u32)]; iZero = 0; }else{ iZero = HASHTABLE_NPAGE_ONE + (iHash-1)*HASHTABLE_NPAGE; } *paPgno = &aPgno[-1]; *paHash = aHash; *piZero = iZero; } return rc; } /* ** Return the number of the wal-index page that contains the hash-table ** and page-number array that contain entries corresponding to WAL frame ** iFrame. The wal-index is broken up into 32KB pages. Wal-index pages ** are numbered starting from 0. */ static int walFramePage(u32 iFrame){ int iHash = (iFrame+HASHTABLE_NPAGE-HASHTABLE_NPAGE_ONE-1) / HASHTABLE_NPAGE; assert( (iHash==0 || iFrame>HASHTABLE_NPAGE_ONE) && (iHash>=1 || iFrame<=HASHTABLE_NPAGE_ONE) && (iHash<=1 || iFrame>(HASHTABLE_NPAGE_ONE+HASHTABLE_NPAGE)) && (iHash>=2 || iFrame<=HASHTABLE_NPAGE_ONE+HASHTABLE_NPAGE) && (iHash<=2 || iFrame>(HASHTABLE_NPAGE_ONE+2*HASHTABLE_NPAGE)) ); return iHash; } /* ** Return the page number associated with frame iFrame in this WAL. */ static u32 walFramePgno(Wal *pWal, u32 iFrame){ int iHash = walFramePage(iFrame); if( iHash==0 ){ return pWal->apWiData[0][WALINDEX_HDR_SIZE/sizeof(u32) + iFrame - 1]; } return pWal->apWiData[iHash][(iFrame-1-HASHTABLE_NPAGE_ONE)%HASHTABLE_NPAGE]; } /* ** Remove entries from the hash table that point to WAL slots greater ** than pWal->hdr.mxFrame. ** ** This function is called whenever pWal->hdr.mxFrame is decreased due ** to a rollback or savepoint. ** ** At most only the hash table containing pWal->hdr.mxFrame needs to be ** updated. Any later hash tables will be automatically cleared when ** pWal->hdr.mxFrame advances to the point where those hash tables are ** actually needed. */ static void walCleanupHash(Wal *pWal){ volatile ht_slot *aHash = 0; /* Pointer to hash table to clear */ volatile u32 *aPgno = 0; /* Page number array for hash table */ u32 iZero = 0; /* frame == (aHash[x]+iZero) */ int iLimit = 0; /* Zero values greater than this */ int nByte; /* Number of bytes to zero in aPgno[] */ int i; /* Used to iterate through aHash[] */ assert( pWal->writeLock ); testcase( pWal->hdr.mxFrame==HASHTABLE_NPAGE_ONE-1 ); testcase( pWal->hdr.mxFrame==HASHTABLE_NPAGE_ONE ); testcase( pWal->hdr.mxFrame==HASHTABLE_NPAGE_ONE+1 ); if( pWal->hdr.mxFrame==0 ) return; /* Obtain pointers to the hash-table and page-number array containing ** the entry that corresponds to frame pWal->hdr.mxFrame. It is guaranteed ** that the page said hash-table and array reside on is already mapped. */ assert( pWal->nWiData>walFramePage(pWal->hdr.mxFrame) ); assert( pWal->apWiData[walFramePage(pWal->hdr.mxFrame)] ); walHashGet(pWal, walFramePage(pWal->hdr.mxFrame), &aHash, &aPgno, &iZero); /* Zero all hash-table entries that correspond to frame numbers greater ** than pWal->hdr.mxFrame. */ iLimit = pWal->hdr.mxFrame - iZero; assert( iLimit>0 ); for(i=0; i<HASHTABLE_NSLOT; i++){ if( aHash[i]>iLimit ){ aHash[i] = 0; } } /* Zero the entries in the aPgno array that correspond to frames with ** frame numbers greater than pWal->hdr.mxFrame. */ nByte = (int)((char *)aHash - (char *)&aPgno[iLimit+1]); memset((void *)&aPgno[iLimit+1], 0, nByte); #ifdef SQLITE_ENABLE_EXPENSIVE_ASSERT /* Verify that the every entry in the mapping region is still reachable ** via the hash table even after the cleanup. */ if( iLimit ){ int i; /* Loop counter */ int iKey; /* Hash key */ for(i=1; i<=iLimit; i++){ for(iKey=walHash(aPgno[i]); aHash[iKey]; iKey=walNextHash(iKey)){ if( aHash[iKey]==i ) break; } assert( aHash[iKey]==i ); } } #endif /* SQLITE_ENABLE_EXPENSIVE_ASSERT */ } /* ** Set an entry in the wal-index that will map database page number ** pPage into WAL frame iFrame. */ static int walIndexAppend(Wal *pWal, u32 iFrame, u32 iPage){ int rc; /* Return code */ u32 iZero = 0; /* One less than frame number of aPgno[1] */ volatile u32 *aPgno = 0; /* Page number array */ volatile ht_slot *aHash = 0; /* Hash table */ rc = walHashGet(pWal, walFramePage(iFrame), &aHash, &aPgno, &iZero); /* Assuming the wal-index file was successfully mapped, populate the ** page number array and hash table entry. */ if( rc==SQLITE_OK ){ int iKey; /* Hash table key */ int idx; /* Value to write to hash-table slot */ int nCollide; /* Number of hash collisions */ idx = iFrame - iZero; assert( idx <= HASHTABLE_NSLOT/2 + 1 ); /* If this is the first entry to be added to this hash-table, zero the ** entire hash table and aPgno[] array before proceding. */ if( idx==1 ){ int nByte = (int)((u8 *)&aHash[HASHTABLE_NSLOT] - (u8 *)&aPgno[1]); memset((void*)&aPgno[1], 0, nByte); } /* If the entry in aPgno[] is already set, then the previous writer ** must have exited unexpectedly in the middle of a transaction (after ** writing one or more dirty pages to the WAL to free up memory). ** Remove the remnants of that writers uncommitted transaction from ** the hash-table before writing any new entries. */ if( aPgno[idx] ){ walCleanupHash(pWal); assert( !aPgno[idx] ); } /* Write the aPgno[] array entry and the hash-table slot. */ nCollide = idx; for(iKey=walHash(iPage); aHash[iKey]; iKey=walNextHash(iKey)){ if( (nCollide--)==0 ) return SQLITE_CORRUPT_BKPT; } aPgno[idx] = iPage; aHash[iKey] = (ht_slot)idx; #ifdef SQLITE_ENABLE_EXPENSIVE_ASSERT /* Verify that the number of entries in the hash table exactly equals ** the number of entries in the mapping region. */ { int i; /* Loop counter */ int nEntry = 0; /* Number of entries in the hash table */ for(i=0; i<HASHTABLE_NSLOT; i++){ if( aHash[i] ) nEntry++; } assert( nEntry==idx ); } /* Verify that the every entry in the mapping region is reachable ** via the hash table. This turns out to be a really, really expensive ** thing to check, so only do this occasionally - not on every ** iteration. */ if( (idx&0x3ff)==0 ){ int i; /* Loop counter */ for(i=1; i<=idx; i++){ for(iKey=walHash(aPgno[i]); aHash[iKey]; iKey=walNextHash(iKey)){ if( aHash[iKey]==i ) break; } assert( aHash[iKey]==i ); } } #endif /* SQLITE_ENABLE_EXPENSIVE_ASSERT */ } return rc; } /* ** Recover the wal-index by reading the write-ahead log file. ** ** This routine first tries to establish an exclusive lock on the ** wal-index to prevent other threads/processes from doing anything ** with the WAL or wal-index while recovery is running. The ** WAL_RECOVER_LOCK is also held so that other threads will know ** that this thread is running recovery. If unable to establish ** the necessary locks, this routine returns SQLITE_BUSY. */ static int walIndexRecover(Wal *pWal){ int rc; /* Return Code */ i64 nSize; /* Size of log file */ u32 aFrameCksum[2] = {0, 0}; int iLock; /* Lock offset to lock for checkpoint */ int nLock; /* Number of locks to hold */ /* Obtain an exclusive lock on all byte in the locking range not already ** locked by the caller. The caller is guaranteed to have locked the ** WAL_WRITE_LOCK byte, and may have also locked the WAL_CKPT_LOCK byte. ** If successful, the same bytes that are locked here are unlocked before ** this function returns. */ assert( pWal->ckptLock==1 || pWal->ckptLock==0 ); assert( WAL_ALL_BUT_WRITE==WAL_WRITE_LOCK+1 ); assert( WAL_CKPT_LOCK==WAL_ALL_BUT_WRITE ); assert( pWal->writeLock ); iLock = WAL_ALL_BUT_WRITE + pWal->ckptLock; nLock = SQLITE_SHM_NLOCK - iLock; rc = walLockExclusive(pWal, iLock, nLock); if( rc ){ return rc; } WALTRACE(("WAL%p: recovery begin...\n", pWal)); memset(&pWal->hdr, 0, sizeof(WalIndexHdr)); rc = sqlite3OsFileSize(pWal->pWalFd, &nSize); if( rc!=SQLITE_OK ){ goto recovery_error; } if( nSize>WAL_HDRSIZE ){ u8 aBuf[WAL_HDRSIZE]; /* Buffer to load WAL header into */ u8 *aFrame = 0; /* Malloc'd buffer to load entire frame */ int szFrame; /* Number of bytes in buffer aFrame[] */ u8 *aData; /* Pointer to data part of aFrame buffer */ int iFrame; /* Index of last frame read */ i64 iOffset; /* Next offset to read from log file */ int szPage; /* Page size according to the log */ u32 magic; /* Magic value read from WAL header */ u32 version; /* Magic value read from WAL header */ /* Read in the WAL header. */ rc = sqlite3OsRead(pWal->pWalFd, aBuf, WAL_HDRSIZE, 0); if( rc!=SQLITE_OK ){ goto recovery_error; } /* If the database page size is not a power of two, or is greater than ** SQLITE_MAX_PAGE_SIZE, conclude that the WAL file contains no valid ** data. Similarly, if the 'magic' value is invalid, ignore the whole ** WAL file. */ magic = sqlite3Get4byte(&aBuf[0]); szPage = sqlite3Get4byte(&aBuf[8]); if( (magic&0xFFFFFFFE)!=WAL_MAGIC || szPage&(szPage-1) || szPage>SQLITE_MAX_PAGE_SIZE || szPage<512 ){ goto finished; } pWal->hdr.bigEndCksum = (u8)(magic&0x00000001); pWal->szPage = (u16)szPage; pWal->nCkpt = sqlite3Get4byte(&aBuf[12]); memcpy(&pWal->hdr.aSalt, &aBuf[16], 8); /* Verify that the WAL header checksum is correct */ walChecksumBytes(pWal->hdr.bigEndCksum==SQLITE_BIGENDIAN, aBuf, WAL_HDRSIZE-2*4, 0, pWal->hdr.aFrameCksum ); if( pWal->hdr.aFrameCksum[0]!=sqlite3Get4byte(&aBuf[24]) || pWal->hdr.aFrameCksum[1]!=sqlite3Get4byte(&aBuf[28]) ){ goto finished; } /* Verify that the version number on the WAL format is one that ** are able to understand */ version = sqlite3Get4byte(&aBuf[4]); if( version!=WAL_MAX_VERSION ){ rc = SQLITE_CANTOPEN_BKPT; goto finished; } /* Malloc a buffer to read frames into. */ szFrame = szPage + WAL_FRAME_HDRSIZE; aFrame = (u8 *)sqlite3_malloc(szFrame); if( !aFrame ){ rc = SQLITE_NOMEM; goto recovery_error; } aData = &aFrame[WAL_FRAME_HDRSIZE]; /* Read all frames from the log file. */ iFrame = 0; for(iOffset=WAL_HDRSIZE; (iOffset+szFrame)<=nSize; iOffset+=szFrame){ u32 pgno; /* Database page number for frame */ u32 nTruncate; /* dbsize field from frame header */ int isValid; /* True if this frame is valid */ /* Read and decode the next log frame. */ rc = sqlite3OsRead(pWal->pWalFd, aFrame, szFrame, iOffset); if( rc!=SQLITE_OK ) break; isValid = walDecodeFrame(pWal, &pgno, &nTruncate, aData, aFrame); if( !isValid ) break; rc = walIndexAppend(pWal, ++iFrame, pgno); if( rc!=SQLITE_OK ) break; /* If nTruncate is non-zero, this is a commit record. */ if( nTruncate ){ pWal->hdr.mxFrame = iFrame; pWal->hdr.nPage = nTruncate; pWal->hdr.szPage = (u16)szPage; aFrameCksum[0] = pWal->hdr.aFrameCksum[0]; aFrameCksum[1] = pWal->hdr.aFrameCksum[1]; } } sqlite3_free(aFrame); } finished: if( rc==SQLITE_OK ){ volatile WalCkptInfo *pInfo; int i; pWal->hdr.aFrameCksum[0] = aFrameCksum[0]; pWal->hdr.aFrameCksum[1] = aFrameCksum[1]; walIndexWriteHdr(pWal); /* Reset the checkpoint-header. This is safe because this thread is ** currently holding locks that exclude all other readers, writers and ** checkpointers. */ pInfo = walCkptInfo(pWal); pInfo->nBackfill = 0; pInfo->aReadMark[0] = 0; for(i=1; i<WAL_NREADER; i++) pInfo->aReadMark[i] = READMARK_NOT_USED; } recovery_error: WALTRACE(("WAL%p: recovery %s\n", pWal, rc ? "failed" : "ok")); walUnlockExclusive(pWal, iLock, nLock); return rc; } /* ** Close an open wal-index. */ static void walIndexClose(Wal *pWal, int isDelete){ sqlite3OsShmUnmap(pWal->pDbFd, isDelete); } /* ** Open a connection to the WAL file zWalName. The database file must ** already be opened on connection pDbFd. The buffer that zWalName points ** to must remain valid for the lifetime of the returned Wal* handle. ** ** A SHARED lock should be held on the database file when this function ** is called. The purpose of this SHARED lock is to prevent any other ** client from unlinking the WAL or wal-index file. If another process ** were to do this just after this client opened one of these files, the ** system would be badly broken. ** ** If the log file is successfully opened, SQLITE_OK is returned and ** *ppWal is set to point to a new WAL handle. If an error occurs, ** an SQLite error code is returned and *ppWal is left unmodified. */ int sqlite3WalOpen( sqlite3_vfs *pVfs, /* vfs module to open wal and wal-index */ sqlite3_file *pDbFd, /* The open database file */ const char *zWalName, /* Name of the WAL file */ Wal **ppWal /* OUT: Allocated Wal handle */ ){ int rc; /* Return Code */ Wal *pRet; /* Object to allocate and return */ int flags; /* Flags passed to OsOpen() */ assert( zWalName && zWalName[0] ); assert( pDbFd ); /* In the amalgamation, the os_unix.c and os_win.c source files come before ** this source file. Verify that the #defines of the locking byte offsets ** in os_unix.c and os_win.c agree with the WALINDEX_LOCK_OFFSET value. */ #ifdef WIN_SHM_BASE assert( WIN_SHM_BASE==WALINDEX_LOCK_OFFSET ); #endif #ifdef UNIX_SHM_BASE assert( UNIX_SHM_BASE==WALINDEX_LOCK_OFFSET ); #endif /* Allocate an instance of struct Wal to return. */ *ppWal = 0; pRet = (Wal*)sqlite3MallocZero(sizeof(Wal) + pVfs->szOsFile); if( !pRet ){ return SQLITE_NOMEM; } pRet->pVfs = pVfs; pRet->pWalFd = (sqlite3_file *)&pRet[1]; pRet->pDbFd = pDbFd; pRet->readLock = -1; pRet->zWalName = zWalName; /* Open file handle on the write-ahead log file. */ flags = (SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE|SQLITE_OPEN_WAL); rc = sqlite3OsOpen(pVfs, zWalName, pRet->pWalFd, flags, &flags); if( rc==SQLITE_OK && flags&SQLITE_OPEN_READONLY ){ pRet->readOnly = 1; } if( rc!=SQLITE_OK ){ walIndexClose(pRet, 0); sqlite3OsClose(pRet->pWalFd); sqlite3_free(pRet); }else{ *ppWal = pRet; WALTRACE(("WAL%d: opened\n", pRet)); } return rc; } /* ** Find the smallest page number out of all pages held in the WAL that ** has not been returned by any prior invocation of this method on the ** same WalIterator object. Write into *piFrame the frame index where ** that page was last written into the WAL. Write into *piPage the page ** number. ** ** Return 0 on success. If there are no pages in the WAL with a page ** number larger than *piPage, then return 1. */ static int walIteratorNext( WalIterator *p, /* Iterator */ u32 *piPage, /* OUT: The page number of the next page */ u32 *piFrame /* OUT: Wal frame index of next page */ ){ u32 iMin; /* Result pgno must be greater than iMin */ u32 iRet = 0xFFFFFFFF; /* 0xffffffff is never a valid page number */ int i; /* For looping through segments */ iMin = p->iPrior; assert( iMin<0xffffffff ); for(i=p->nSegment-1; i>=0; i--){ struct WalSegment *pSegment = &p->aSegment[i]; while( pSegment->iNext<pSegment->nEntry ){ u32 iPg = pSegment->aPgno[pSegment->aIndex[pSegment->iNext]]; if( iPg>iMin ){ if( iPg<iRet ){ iRet = iPg; *piFrame = pSegment->iZero + pSegment->aIndex[pSegment->iNext]; } break; } pSegment->iNext++; } } *piPage = p->iPrior = iRet; return (iRet==0xFFFFFFFF); } /* ** This function merges two sorted lists into a single sorted list. */ static void walMerge( u32 *aContent, /* Pages in wal */ ht_slot *aLeft, /* IN: Left hand input list */ int nLeft, /* IN: Elements in array *paLeft */ ht_slot **paRight, /* IN/OUT: Right hand input list */ int *pnRight, /* IN/OUT: Elements in *paRight */ ht_slot *aTmp /* Temporary buffer */ ){ int iLeft = 0; /* Current index in aLeft */ int iRight = 0; /* Current index in aRight */ int iOut = 0; /* Current index in output buffer */ int nRight = *pnRight; ht_slot *aRight = *paRight; assert( nLeft>0 && nRight>0 ); while( iRight<nRight || iLeft<nLeft ){ ht_slot logpage; Pgno dbpage; if( (iLeft<nLeft) && (iRight>=nRight || aContent[aLeft[iLeft]]<aContent[aRight[iRight]]) ){ logpage = aLeft[iLeft++]; }else{ logpage = aRight[iRight++]; } dbpage = aContent[logpage]; aTmp[iOut++] = logpage; if( iLeft<nLeft && aContent[aLeft[iLeft]]==dbpage ) iLeft++; assert( iLeft>=nLeft || aContent[aLeft[iLeft]]>dbpage ); assert( iRight>=nRight || aContent[aRight[iRight]]>dbpage ); } *paRight = aLeft; *pnRight = iOut; memcpy(aLeft, aTmp, sizeof(aTmp[0])*iOut); } /* ** Sort the elements in list aList, removing any duplicates. */ static void walMergesort( u32 *aContent, /* Pages in wal */ ht_slot *aBuffer, /* Buffer of at least *pnList items to use */ ht_slot *aList, /* IN/OUT: List to sort */ int *pnList /* IN/OUT: Number of elements in aList[] */ ){ struct Sublist { int nList; /* Number of elements in aList */ ht_slot *aList; /* Pointer to sub-list content */ }; const int nList = *pnList; /* Size of input list */ int nMerge = 0; /* Number of elements in list aMerge */ ht_slot *aMerge = 0; /* List to be merged */ int iList; /* Index into input list */ int iSub = 0; /* Index into aSub array */ struct Sublist aSub[13]; /* Array of sub-lists */ memset(aSub, 0, sizeof(aSub)); assert( nList<=HASHTABLE_NPAGE && nList>0 ); assert( HASHTABLE_NPAGE==(1<<(ArraySize(aSub)-1)) ); for(iList=0; iList<nList; iList++){ nMerge = 1; aMerge = &aList[iList]; for(iSub=0; iList & (1<<iSub); iSub++){ struct Sublist *p = &aSub[iSub]; assert( p->aList && p->nList<=(1<<iSub) ); assert( p->aList==&aList[iList&~((2<<iSub)-1)] ); walMerge(aContent, p->aList, p->nList, &aMerge, &nMerge, aBuffer); } aSub[iSub].aList = aMerge; aSub[iSub].nList = nMerge; } for(iSub++; iSub<ArraySize(aSub); iSub++){ if( nList & (1<<iSub) ){ struct Sublist *p = &aSub[iSub]; assert( p->nList<=(1<<iSub) ); assert( p->aList==&aList[nList&~((2<<iSub)-1)] ); walMerge(aContent, p->aList, p->nList, &aMerge, &nMerge, aBuffer); } } assert( aMerge==aList ); *pnList = nMerge; #ifdef SQLITE_DEBUG { int i; for(i=1; i<*pnList; i++){ assert( aContent[aList[i]] > aContent[aList[i-1]] ); } } #endif } /* ** Free an iterator allocated by walIteratorInit(). */ static void walIteratorFree(WalIterator *p){ sqlite3ScratchFree(p); } /* ** Construct a WalInterator object that can be used to loop over all ** pages in the WAL in ascending order. The caller must hold the checkpoint ** ** On success, make *pp point to the newly allocated WalInterator object ** return SQLITE_OK. Otherwise, return an error code. If this routine ** returns an error, the value of *pp is undefined. ** ** The calling routine should invoke walIteratorFree() to destroy the ** WalIterator object when it has finished with it. */ static int walIteratorInit(Wal *pWal, WalIterator **pp){ WalIterator *p; /* Return value */ int nSegment; /* Number of segments to merge */ u32 iLast; /* Last frame in log */ int nByte; /* Number of bytes to allocate */ int i; /* Iterator variable */ ht_slot *aTmp; /* Temp space used by merge-sort */ int rc = SQLITE_OK; /* Return Code */ /* This routine only runs while holding the checkpoint lock. And ** it only runs if there is actually content in the log (mxFrame>0). */ assert( pWal->ckptLock && pWal->hdr.mxFrame>0 ); iLast = pWal->hdr.mxFrame; /* Allocate space for the WalIterator object. */ nSegment = walFramePage(iLast) + 1; nByte = sizeof(WalIterator) + (nSegment-1)*sizeof(struct WalSegment) + iLast*sizeof(ht_slot); p = (WalIterator *)sqlite3ScratchMalloc(nByte); if( !p ){ return SQLITE_NOMEM; } memset(p, 0, nByte); p->nSegment = nSegment; /* Allocate temporary space used by the merge-sort routine. This block ** of memory will be freed before this function returns. */ aTmp = (ht_slot *)sqlite3ScratchMalloc( sizeof(ht_slot) * (iLast>HASHTABLE_NPAGE?HASHTABLE_NPAGE:iLast) ); if( !aTmp ){ rc = SQLITE_NOMEM; } for(i=0; rc==SQLITE_OK && i<nSegment; i++){ volatile ht_slot *aHash; u32 iZero; volatile u32 *aPgno; rc = walHashGet(pWal, i, &aHash, &aPgno, &iZero); if( rc==SQLITE_OK ){ int j; /* Counter variable */ int nEntry; /* Number of entries in this segment */ ht_slot *aIndex; /* Sorted index for this segment */ aPgno++; if( (i+1)==nSegment ){ nEntry = (int)(iLast - iZero); }else{ nEntry = (int)((u32*)aHash - (u32*)aPgno); } aIndex = &((ht_slot *)&p->aSegment[p->nSegment])[iZero]; iZero++; for(j=0; j<nEntry; j++){ aIndex[j] = (ht_slot)j; } walMergesort((u32 *)aPgno, aTmp, aIndex, &nEntry); p->aSegment[i].iZero = iZero; p->aSegment[i].nEntry = nEntry; p->aSegment[i].aIndex = aIndex; p->aSegment[i].aPgno = (u32 *)aPgno; } } sqlite3ScratchFree(aTmp); if( rc!=SQLITE_OK ){ walIteratorFree(p); } *pp = p; return rc; } /* ** Copy as much content as we can from the WAL back into the database file ** in response to an sqlite3_wal_checkpoint() request or the equivalent. ** ** The amount of information copies from WAL to database might be limited ** by active readers. This routine will never overwrite a database page ** that a concurrent reader might be using. ** ** All I/O barrier operations (a.k.a fsyncs) occur in this routine when ** SQLite is in WAL-mode in synchronous=NORMAL. That means that if ** checkpoints are always run by a background thread or background ** process, foreground threads will never block on a lengthy fsync call. ** ** Fsync is called on the WAL before writing content out of the WAL and ** into the database. This ensures that if the new content is persistent ** in the WAL and can be recovered following a power-loss or hard reset. ** ** Fsync is also called on the database file if (and only if) the entire ** WAL content is copied into the database file. This second fsync makes ** it safe to delete the WAL since the new content will persist in the ** database file. ** ** This routine uses and updates the nBackfill field of the wal-index header. ** This is the only routine tha will increase the value of nBackfill. ** (A WAL reset or recovery will revert nBackfill to zero, but not increase ** its value.) ** ** The caller must be holding sufficient locks to ensure that no other ** checkpoint is running (in any other thread or process) at the same ** time. */ static int walCheckpoint( Wal *pWal, /* Wal connection */ int sync_flags, /* Flags for OsSync() (or 0) */ int nBuf, /* Size of zBuf in bytes */ u8 *zBuf /* Temporary buffer to use */ ){ int rc; /* Return code */ int szPage = pWal->hdr.szPage; /* Database page-size */ WalIterator *pIter = 0; /* Wal iterator context */ u32 iDbpage = 0; /* Next database page to write */ u32 iFrame = 0; /* Wal frame containing data for iDbpage */ u32 mxSafeFrame; /* Max frame that can be backfilled */ int i; /* Loop counter */ volatile WalCkptInfo *pInfo; /* The checkpoint status information */ if( pWal->hdr.mxFrame==0 ) return SQLITE_OK; /* Allocate the iterator */ rc = walIteratorInit(pWal, &pIter); if( rc!=SQLITE_OK ){ return rc; } assert( pIter ); /*** TODO: Move this test out to the caller. Make it an assert() here ***/ if( pWal->hdr.szPage!=nBuf ){ rc = SQLITE_CORRUPT_BKPT; goto walcheckpoint_out; } /* Compute in mxSafeFrame the index of the last frame of the WAL that is ** safe to write into the database. Frames beyond mxSafeFrame might ** overwrite database pages that are in use by active readers and thus ** cannot be backfilled from the WAL. */ mxSafeFrame = pWal->hdr.mxFrame; pInfo = walCkptInfo(pWal); for(i=1; i<WAL_NREADER; i++){ u32 y = pInfo->aReadMark[i]; if( mxSafeFrame>=y ){ assert( y<=pWal->hdr.mxFrame ); rc = walLockExclusive(pWal, WAL_READ_LOCK(i), 1); if( rc==SQLITE_OK ){ pInfo->aReadMark[i] = READMARK_NOT_USED; walUnlockExclusive(pWal, WAL_READ_LOCK(i), 1); }else if( rc==SQLITE_BUSY ){ mxSafeFrame = y; }else{ goto walcheckpoint_out; } } } if( pInfo->nBackfill<mxSafeFrame && (rc = walLockExclusive(pWal, WAL_READ_LOCK(0), 1))==SQLITE_OK ){ u32 nBackfill = pInfo->nBackfill; /* Sync the WAL to disk */ if( sync_flags ){ rc = sqlite3OsSync(pWal->pWalFd, sync_flags); } /* Iterate through the contents of the WAL, copying data to the db file. */ while( rc==SQLITE_OK && 0==walIteratorNext(pIter, &iDbpage, &iFrame) ){ i64 iOffset; assert( walFramePgno(pWal, iFrame)==iDbpage ); if( iFrame<=nBackfill || iFrame>mxSafeFrame ) continue; iOffset = walFrameOffset(iFrame, szPage) + WAL_FRAME_HDRSIZE; /* testcase( IS_BIG_INT(iOffset) ); // requires a 4GiB WAL file */ rc = sqlite3OsRead(pWal->pWalFd, zBuf, szPage, iOffset); if( rc!=SQLITE_OK ) break; iOffset = (iDbpage-1)*(i64)szPage; testcase( IS_BIG_INT(iOffset) ); rc = sqlite3OsWrite(pWal->pDbFd, zBuf, szPage, iOffset); if( rc!=SQLITE_OK ) break; } /* If work was actually accomplished... */ if( rc==SQLITE_OK ){ if( mxSafeFrame==walIndexHdr(pWal)->mxFrame ){ i64 szDb = pWal->hdr.nPage*(i64)szPage; testcase( IS_BIG_INT(szDb) ); rc = sqlite3OsTruncate(pWal->pDbFd, szDb); if( rc==SQLITE_OK && sync_flags ){ rc = sqlite3OsSync(pWal->pDbFd, sync_flags); } } if( rc==SQLITE_OK ){ pInfo->nBackfill = mxSafeFrame; } } /* Release the reader lock held while backfilling */ walUnlockExclusive(pWal, WAL_READ_LOCK(0), 1); }else if( rc==SQLITE_BUSY ){ /* Reset the return code so as not to report a checkpoint failure ** just because active readers prevent any backfill. */ rc = SQLITE_OK; } walcheckpoint_out: walIteratorFree(pIter); return rc; } /* ** Close a connection to a log file. */ int sqlite3WalClose( Wal *pWal, /* Wal to close */ int sync_flags, /* Flags to pass to OsSync() (or 0) */ int nBuf, u8 *zBuf /* Buffer of at least nBuf bytes */ ){ int rc = SQLITE_OK; if( pWal ){ int isDelete = 0; /* True to unlink wal and wal-index files */ /* If an EXCLUSIVE lock can be obtained on the database file (using the ** ordinary, rollback-mode locking methods, this guarantees that the ** connection associated with this log file is the only connection to ** the database. In this case checkpoint the database and unlink both ** the wal and wal-index files. ** ** The EXCLUSIVE lock is not released before returning. */ rc = sqlite3OsLock(pWal->pDbFd, SQLITE_LOCK_EXCLUSIVE); if( rc==SQLITE_OK ){ pWal->exclusiveMode = 1; rc = sqlite3WalCheckpoint(pWal, sync_flags, nBuf, zBuf); if( rc==SQLITE_OK ){ isDelete = 1; } } walIndexClose(pWal, isDelete); sqlite3OsClose(pWal->pWalFd); if( isDelete ){ sqlite3OsDelete(pWal->pVfs, pWal->zWalName, 0); } WALTRACE(("WAL%p: closed\n", pWal)); sqlite3_free((void *)pWal->apWiData); sqlite3_free(pWal); } return rc; } /* ** Try to read the wal-index header. Return 0 on success and 1 if ** there is a problem. ** ** The wal-index is in shared memory. Another thread or process might ** be writing the header at the same time this procedure is trying to ** read it, which might result in inconsistency. A dirty read is detected ** by verifying that both copies of the header are the same and also by ** a checksum on the header. ** ** If and only if the read is consistent and the header is different from ** pWal->hdr, then pWal->hdr is updated to the content of the new header ** and *pChanged is set to 1. ** ** If the checksum cannot be verified return non-zero. If the header ** is read successfully and the checksum verified, return zero. */ static int walIndexTryHdr(Wal *pWal, int *pChanged){ u32 aCksum[2]; /* Checksum on the header content */ WalIndexHdr h1, h2; /* Two copies of the header content */ WalIndexHdr volatile *aHdr; /* Header in shared memory */ /* The first page of the wal-index must be mapped at this point. */ assert( pWal->nWiData>0 && pWal->apWiData[0] ); /* Read the header. This might happen currently with a write to the ** same area of shared memory on a different CPU in a SMP, ** meaning it is possible that an inconsistent snapshot is read ** from the file. If this happens, return non-zero. ** ** There are two copies of the header at the beginning of the wal-index. ** When reading, read [0] first then [1]. Writes are in the reverse order. ** Memory barriers are used to prevent the compiler or the hardware from ** reordering the reads and writes. */ aHdr = walIndexHdr(pWal); memcpy(&h1, (void *)&aHdr[0], sizeof(h1)); sqlite3OsShmBarrier(pWal->pDbFd); memcpy(&h2, (void *)&aHdr[1], sizeof(h2)); if( memcmp(&h1, &h2, sizeof(h1))!=0 ){ return 1; /* Dirty read */ } if( h1.isInit==0 ){ return 1; /* Malformed header - probably all zeros */ } walChecksumBytes(1, (u8*)&h1, sizeof(h1)-sizeof(h1.aCksum), 0, aCksum); if( aCksum[0]!=h1.aCksum[0] || aCksum[1]!=h1.aCksum[1] ){ return 1; /* Checksum does not match */ } if( memcmp(&pWal->hdr, &h1, sizeof(WalIndexHdr)) ){ *pChanged = 1; memcpy(&pWal->hdr, &h1, sizeof(WalIndexHdr)); pWal->szPage = pWal->hdr.szPage; } /* The header was successfully read. Return zero. */ return 0; } /* ** Read the wal-index header from the wal-index and into pWal->hdr. ** If the wal-header appears to be corrupt, try to reconstruct the ** wal-index from the WAL before returning. ** ** Set *pChanged to 1 if the wal-index header value in pWal->hdr is ** changed by this opertion. If pWal->hdr is unchanged, set *pChanged ** to 0. ** ** If the wal-index header is successfully read, return SQLITE_OK. ** Otherwise an SQLite error code. */ static int walIndexReadHdr(Wal *pWal, int *pChanged){ int rc; /* Return code */ int badHdr; /* True if a header read failed */ volatile u32 *page0; /* Chunk of wal-index containing header */ /* Ensure that page 0 of the wal-index (the page that contains the ** wal-index header) is mapped. Return early if an error occurs here. */ assert( pChanged ); rc = walIndexPage(pWal, 0, &page0); if( rc!=SQLITE_OK ){ return rc; }; assert( page0 || pWal->writeLock==0 ); /* If the first page of the wal-index has been mapped, try to read the ** wal-index header immediately, without holding any lock. This usually ** works, but may fail if the wal-index header is corrupt or currently ** being modified by another thread or process. */ badHdr = (page0 ? walIndexTryHdr(pWal, pChanged) : 1); /* If the first attempt failed, it might have been due to a race ** with a writer. So get a WRITE lock and try again. */ assert( badHdr==0 || pWal->writeLock==0 ); if( badHdr && SQLITE_OK==(rc = walLockExclusive(pWal, WAL_WRITE_LOCK, 1)) ){ pWal->writeLock = 1; if( SQLITE_OK==(rc = walIndexPage(pWal, 0, &page0)) ){ badHdr = walIndexTryHdr(pWal, pChanged); if( badHdr ){ /* If the wal-index header is still malformed even while holding ** a WRITE lock, it can only mean that the header is corrupted and ** needs to be reconstructed. So run recovery to do exactly that. */ rc = walIndexRecover(pWal); *pChanged = 1; } } pWal->writeLock = 0; walUnlockExclusive(pWal, WAL_WRITE_LOCK, 1); } /* If the header is read successfully, check the version number to make ** sure the wal-index was not constructed with some future format that ** this version of SQLite cannot understand. */ if( badHdr==0 && pWal->hdr.iVersion!=WALINDEX_MAX_VERSION ){ rc = SQLITE_CANTOPEN_BKPT; } return rc; } /* ** This is the value that walTryBeginRead returns when it needs to ** be retried. */ #define WAL_RETRY (-1) /* ** Attempt to start a read transaction. This might fail due to a race or ** other transient condition. When that happens, it returns WAL_RETRY to ** indicate to the caller that it is safe to retry immediately. ** ** On success return SQLITE_OK. On a permanent failure (such an ** I/O error or an SQLITE_BUSY because another process is running ** recovery) return a positive error code. ** ** The useWal parameter is true to force the use of the WAL and disable ** the case where the WAL is bypassed because it has been completely ** checkpointed. If useWal==0 then this routine calls walIndexReadHdr() ** to make a copy of the wal-index header into pWal->hdr. If the ** wal-index header has changed, *pChanged is set to 1 (as an indication ** to the caller that the local paget cache is obsolete and needs to be ** flushed.) When useWal==1, the wal-index header is assumed to already ** be loaded and the pChanged parameter is unused. ** ** The caller must set the cnt parameter to the number of prior calls to ** this routine during the current read attempt that returned WAL_RETRY. ** This routine will start taking more aggressive measures to clear the ** race conditions after multiple WAL_RETRY returns, and after an excessive ** number of errors will ultimately return SQLITE_PROTOCOL. The ** SQLITE_PROTOCOL return indicates that some other process has gone rogue ** and is not honoring the locking protocol. There is a vanishingly small ** chance that SQLITE_PROTOCOL could be returned because of a run of really ** bad luck when there is lots of contention for the wal-index, but that ** possibility is so small that it can be safely neglected, we believe. ** ** On success, this routine obtains a read lock on ** WAL_READ_LOCK(pWal->readLock). The pWal->readLock integer is ** in the range 0 <= pWal->readLock < WAL_NREADER. If pWal->readLock==(-1) ** that means the Wal does not hold any read lock. The reader must not ** access any database page that is modified by a WAL frame up to and ** including frame number aReadMark[pWal->readLock]. The reader will ** use WAL frames up to and including pWal->hdr.mxFrame if pWal->readLock>0 ** Or if pWal->readLock==0, then the reader will ignore the WAL ** completely and get all content directly from the database file. ** If the useWal parameter is 1 then the WAL will never be ignored and ** this routine will always set pWal->readLock>0 on success. ** When the read transaction is completed, the caller must release the ** lock on WAL_READ_LOCK(pWal->readLock) and set pWal->readLock to -1. ** ** This routine uses the nBackfill and aReadMark[] fields of the header ** to select a particular WAL_READ_LOCK() that strives to let the ** checkpoint process do as much work as possible. This routine might ** update values of the aReadMark[] array in the header, but if it does ** so it takes care to hold an exclusive lock on the corresponding ** WAL_READ_LOCK() while changing values. */ static int walTryBeginRead(Wal *pWal, int *pChanged, int useWal, int cnt){ volatile WalCkptInfo *pInfo; /* Checkpoint information in wal-index */ u32 mxReadMark; /* Largest aReadMark[] value */ int mxI; /* Index of largest aReadMark[] value */ int i; /* Loop counter */ int rc = SQLITE_OK; /* Return code */ assert( pWal->readLock<0 ); /* Not currently locked */ /* Take steps to avoid spinning forever if there is a protocol error. */ if( cnt>5 ){ if( cnt>100 ) return SQLITE_PROTOCOL; sqlite3OsSleep(pWal->pVfs, 1); } if( !useWal ){ rc = walIndexReadHdr(pWal, pChanged); if( rc==SQLITE_BUSY ){ /* If there is not a recovery running in another thread or process ** then convert BUSY errors to WAL_RETRY. If recovery is known to ** be running, convert BUSY to BUSY_RECOVERY. There is a race here ** which might cause WAL_RETRY to be returned even if BUSY_RECOVERY ** would be technically correct. But the race is benign since with ** WAL_RETRY this routine will be called again and will probably be ** right on the second iteration. */ if( pWal->apWiData[0]==0 ){ /* This branch is taken when the xShmMap() method returns SQLITE_BUSY. ** We assume this is a transient condition, so return WAL_RETRY. The ** xShmMap() implementation used by the default unix and win32 VFS ** modules may return SQLITE_BUSY due to a race condition in the ** code that determines whether or not the shared-memory region ** must be zeroed before the requested page is returned. */ rc = WAL_RETRY; }else if( SQLITE_OK==(rc = walLockShared(pWal, WAL_RECOVER_LOCK)) ){ walUnlockShared(pWal, WAL_RECOVER_LOCK); rc = WAL_RETRY; }else if( rc==SQLITE_BUSY ){ rc = SQLITE_BUSY_RECOVERY; } } if( rc!=SQLITE_OK ){ return rc; } } pInfo = walCkptInfo(pWal); if( !useWal && pInfo->nBackfill==pWal->hdr.mxFrame ){ /* The WAL has been completely backfilled (or it is empty). ** and can be safely ignored. */ rc = walLockShared(pWal, WAL_READ_LOCK(0)); sqlite3OsShmBarrier(pWal->pDbFd); if( rc==SQLITE_OK ){ if( memcmp((void *)walIndexHdr(pWal), &pWal->hdr, sizeof(WalIndexHdr)) ){ /* It is not safe to allow the reader to continue here if frames ** may have been appended to the log before READ_LOCK(0) was obtained. ** When holding READ_LOCK(0), the reader ignores the entire log file, ** which implies that the database file contains a trustworthy ** snapshoT. Since holding READ_LOCK(0) prevents a checkpoint from ** happening, this is usually correct. ** ** However, if frames have been appended to the log (or if the log ** is wrapped and written for that matter) before the READ_LOCK(0) ** is obtained, that is not necessarily true. A checkpointer may ** have started to backfill the appended frames but crashed before ** it finished. Leaving a corrupt image in the database file. */ walUnlockShared(pWal, WAL_READ_LOCK(0)); return WAL_RETRY; } pWal->readLock = 0; return SQLITE_OK; }else if( rc!=SQLITE_BUSY ){ return rc; } } /* If we get this far, it means that the reader will want to use ** the WAL to get at content from recent commits. The job now is ** to select one of the aReadMark[] entries that is closest to ** but not exceeding pWal->hdr.mxFrame and lock that entry. */ mxReadMark = 0; mxI = 0; for(i=1; i<WAL_NREADER; i++){ u32 thisMark = pInfo->aReadMark[i]; if( mxReadMark<=thisMark && thisMark<=pWal->hdr.mxFrame ){ assert( thisMark!=READMARK_NOT_USED ); mxReadMark = thisMark; mxI = i; } } if( mxI==0 ){ /* If we get here, it means that all of the aReadMark[] entries between ** 1 and WAL_NREADER-1 are zero. Try to initialize aReadMark[1] to ** be mxFrame, then retry. */ rc = walLockExclusive(pWal, WAL_READ_LOCK(1), 1); if( rc==SQLITE_OK ){ pInfo->aReadMark[1] = pWal->hdr.mxFrame; walUnlockExclusive(pWal, WAL_READ_LOCK(1), 1); rc = WAL_RETRY; }else if( rc==SQLITE_BUSY ){ rc = WAL_RETRY; } return rc; }else{ if( mxReadMark < pWal->hdr.mxFrame ){ for(i=1; i<WAL_NREADER; i++){ rc = walLockExclusive(pWal, WAL_READ_LOCK(i), 1); if( rc==SQLITE_OK ){ mxReadMark = pInfo->aReadMark[i] = pWal->hdr.mxFrame; mxI = i; walUnlockExclusive(pWal, WAL_READ_LOCK(i), 1); break; }else if( rc!=SQLITE_BUSY ){ return rc; } } } rc = walLockShared(pWal, WAL_READ_LOCK(mxI)); if( rc ){ return rc==SQLITE_BUSY ? WAL_RETRY : rc; } /* Now that the read-lock has been obtained, check that neither the ** value in the aReadMark[] array or the contents of the wal-index ** header have changed. ** ** It is necessary to check that the wal-index header did not change ** between the time it was read and when the shared-lock was obtained ** on WAL_READ_LOCK(mxI) was obtained to account for the possibility ** that the log file may have been wrapped by a writer, or that frames ** that occur later in the log than pWal->hdr.mxFrame may have been ** copied into the database by a checkpointer. If either of these things ** happened, then reading the database with the current value of ** pWal->hdr.mxFrame risks reading a corrupted snapshot. So, retry ** instead. ** ** This does not guarantee that the copy of the wal-index header is up to ** date before proceeding. That would not be possible without somehow ** blocking writers. It only guarantees that a dangerous checkpoint or ** log-wrap (either of which would require an exclusive lock on ** WAL_READ_LOCK(mxI)) has not occurred since the snapshot was valid. */ sqlite3OsShmBarrier(pWal->pDbFd); if( pInfo->aReadMark[mxI]!=mxReadMark || memcmp((void *)walIndexHdr(pWal), &pWal->hdr, sizeof(WalIndexHdr)) ){ walUnlockShared(pWal, WAL_READ_LOCK(mxI)); return WAL_RETRY; }else{ assert( mxReadMark<=pWal->hdr.mxFrame ); pWal->readLock = (i16)mxI; } } return rc; } /* ** Begin a read transaction on the database. ** ** This routine used to be called sqlite3OpenSnapshot() and with good reason: ** it takes a snapshot of the state of the WAL and wal-index for the current ** instant in time. The current thread will continue to use this snapshot. ** Other threads might append new content to the WAL and wal-index but ** that extra content is ignored by the current thread. ** ** If the database contents have changes since the previous read ** transaction, then *pChanged is set to 1 before returning. The ** Pager layer will use this to know that is cache is stale and ** needs to be flushed. */ int sqlite3WalBeginReadTransaction(Wal *pWal, int *pChanged){ int rc; /* Return code */ int cnt = 0; /* Number of TryBeginRead attempts */ do{ rc = walTryBeginRead(pWal, pChanged, 0, ++cnt); }while( rc==WAL_RETRY ); return rc; } /* ** Finish with a read transaction. All this does is release the ** read-lock. */ void sqlite3WalEndReadTransaction(Wal *pWal){ if( pWal->readLock>=0 ){ walUnlockShared(pWal, WAL_READ_LOCK(pWal->readLock)); pWal->readLock = -1; } } /* ** Read a page from the WAL, if it is present in the WAL and if the ** current read transaction is configured to use the WAL. ** ** The *pInWal is set to 1 if the requested page is in the WAL and ** has been loaded. Or *pInWal is set to 0 if the page was not in ** the WAL and needs to be read out of the database. */ int sqlite3WalRead( Wal *pWal, /* WAL handle */ Pgno pgno, /* Database page number to read data for */ int *pInWal, /* OUT: True if data is read from WAL */ int nOut, /* Size of buffer pOut in bytes */ u8 *pOut /* Buffer to write page data to */ ){ u32 iRead = 0; /* If !=0, WAL frame to return data from */ u32 iLast = pWal->hdr.mxFrame; /* Last page in WAL for this reader */ int iHash; /* Used to loop through N hash tables */ /* This routine is only be called from within a read transaction. */ assert( pWal->readLock>=0 || pWal->lockError ); /* If the "last page" field of the wal-index header snapshot is 0, then ** no data will be read from the wal under any circumstances. Return early ** in this case as an optimization. Likewise, if pWal->readLock==0, ** then the WAL is ignored by the reader so return early, as if the ** WAL were empty. */ if( iLast==0 || pWal->readLock==0 ){ *pInWal = 0; return SQLITE_OK; } /* Search the hash table or tables for an entry matching page number ** pgno. Each iteration of the following for() loop searches one ** hash table (each hash table indexes up to HASHTABLE_NPAGE frames). ** ** This code might run concurrently to the code in walIndexAppend() ** that adds entries to the wal-index (and possibly to this hash ** table). This means the value just read from the hash ** slot (aHash[iKey]) may have been added before or after the ** current read transaction was opened. Values added after the ** read transaction was opened may have been written incorrectly - ** i.e. these slots may contain garbage data. However, we assume ** that any slots written before the current read transaction was ** opened remain unmodified. ** ** For the reasons above, the if(...) condition featured in the inner ** loop of the following block is more stringent that would be required ** if we had exclusive access to the hash-table: ** ** (aPgno[iFrame]==pgno): ** This condition filters out normal hash-table collisions. ** ** (iFrame<=iLast): ** This condition filters out entries that were added to the hash ** table after the current read-transaction had started. */ for(iHash=walFramePage(iLast); iHash>=0 && iRead==0; iHash--){ volatile ht_slot *aHash; /* Pointer to hash table */ volatile u32 *aPgno; /* Pointer to array of page numbers */ u32 iZero; /* Frame number corresponding to aPgno[0] */ int iKey; /* Hash slot index */ int nCollide; /* Number of hash collisions remaining */ int rc; /* Error code */ rc = walHashGet(pWal, iHash, &aHash, &aPgno, &iZero); if( rc!=SQLITE_OK ){ return rc; } nCollide = HASHTABLE_NSLOT; for(iKey=walHash(pgno); aHash[iKey]; iKey=walNextHash(iKey)){ u32 iFrame = aHash[iKey] + iZero; if( iFrame<=iLast && aPgno[aHash[iKey]]==pgno ){ assert( iFrame>iRead ); iRead = iFrame; } if( (nCollide--)==0 ){ return SQLITE_CORRUPT_BKPT; } } } #ifdef SQLITE_ENABLE_EXPENSIVE_ASSERT /* If expensive assert() statements are available, do a linear search ** of the wal-index file content. Make sure the results agree with the ** result obtained using the hash indexes above. */ { u32 iRead2 = 0; u32 iTest; for(iTest=iLast; iTest>0; iTest--){ if( walFramePgno(pWal, iTest)==pgno ){ iRead2 = iTest; break; } } assert( iRead==iRead2 ); } #endif /* If iRead is non-zero, then it is the log frame number that contains the ** required page. Read and return data from the log file. */ if( iRead ){ i64 iOffset = walFrameOffset(iRead, pWal->hdr.szPage) + WAL_FRAME_HDRSIZE; *pInWal = 1; /* testcase( IS_BIG_INT(iOffset) ); // requires a 4GiB WAL */ return sqlite3OsRead(pWal->pWalFd, pOut, nOut, iOffset); } *pInWal = 0; return SQLITE_OK; } /* ** Set *pPgno to the size of the database file (or zero, if unknown). */ void sqlite3WalDbsize(Wal *pWal, Pgno *pPgno){ assert( pWal->readLock>=0 || pWal->lockError ); *pPgno = pWal->hdr.nPage; } /* ** This function starts a write transaction on the WAL. ** ** A read transaction must have already been started by a prior call ** to sqlite3WalBeginReadTransaction(). ** ** If another thread or process has written into the database since ** the read transaction was started, then it is not possible for this ** thread to write as doing so would cause a fork. So this routine ** returns SQLITE_BUSY in that case and no write transaction is started. ** ** There can only be a single writer active at a time. */ int sqlite3WalBeginWriteTransaction(Wal *pWal){ int rc; /* Cannot start a write transaction without first holding a read ** transaction. */ assert( pWal->readLock>=0 ); if( pWal->readOnly ){ return SQLITE_READONLY; } /* Only one writer allowed at a time. Get the write lock. Return ** SQLITE_BUSY if unable. */ rc = walLockExclusive(pWal, WAL_WRITE_LOCK, 1); if( rc ){ return rc; } pWal->writeLock = 1; /* If another connection has written to the database file since the ** time the read transaction on this connection was started, then ** the write is disallowed. */ if( memcmp(&pWal->hdr, (void *)walIndexHdr(pWal), sizeof(WalIndexHdr))!=0 ){ walUnlockExclusive(pWal, WAL_WRITE_LOCK, 1); pWal->writeLock = 0; rc = SQLITE_BUSY; } return rc; } /* ** End a write transaction. The commit has already been done. This ** routine merely releases the lock. */ int sqlite3WalEndWriteTransaction(Wal *pWal){ if( pWal->writeLock ){ walUnlockExclusive(pWal, WAL_WRITE_LOCK, 1); pWal->writeLock = 0; } return SQLITE_OK; } /* ** If any data has been written (but not committed) to the log file, this ** function moves the write-pointer back to the start of the transaction. ** ** Additionally, the callback function is invoked for each frame written ** to the WAL since the start of the transaction. If the callback returns ** other than SQLITE_OK, it is not invoked again and the error code is ** returned to the caller. ** ** Otherwise, if the callback function does not return an error, this ** function returns SQLITE_OK. */ int sqlite3WalUndo(Wal *pWal, int (*xUndo)(void *, Pgno), void *pUndoCtx){ int rc = SQLITE_OK; if( pWal->writeLock ){ Pgno iMax = pWal->hdr.mxFrame; Pgno iFrame; /* Restore the clients cache of the wal-index header to the state it ** was in before the client began writing to the database. */ memcpy(&pWal->hdr, (void *)walIndexHdr(pWal), sizeof(WalIndexHdr)); for(iFrame=pWal->hdr.mxFrame+1; ALWAYS(rc==SQLITE_OK) && iFrame<=iMax; iFrame++ ){ /* This call cannot fail. Unless the page for which the page number ** is passed as the second argument is (a) in the cache and ** (b) has an outstanding reference, then xUndo is either a no-op ** (if (a) is false) or simply expels the page from the cache (if (b) ** is false). ** ** If the upper layer is doing a rollback, it is guaranteed that there ** are no outstanding references to any page other than page 1. And ** page 1 is never written to the log until the transaction is ** committed. As a result, the call to xUndo may not fail. */ assert( walFramePgno(pWal, iFrame)!=1 ); rc = xUndo(pUndoCtx, walFramePgno(pWal, iFrame)); } walCleanupHash(pWal); } assert( rc==SQLITE_OK ); return rc; } /* ** Argument aWalData must point to an array of WAL_SAVEPOINT_NDATA u32 ** values. This function populates the array with values required to ** "rollback" the write position of the WAL handle back to the current ** point in the event of a savepoint rollback (via WalSavepointUndo()). */ void sqlite3WalSavepoint(Wal *pWal, u32 *aWalData){ assert( pWal->writeLock ); aWalData[0] = pWal->hdr.mxFrame; aWalData[1] = pWal->hdr.aFrameCksum[0]; aWalData[2] = pWal->hdr.aFrameCksum[1]; aWalData[3] = pWal->nCkpt; } /* ** Move the write position of the WAL back to the point identified by ** the values in the aWalData[] array. aWalData must point to an array ** of WAL_SAVEPOINT_NDATA u32 values that has been previously populated ** by a call to WalSavepoint(). */ int sqlite3WalSavepointUndo(Wal *pWal, u32 *aWalData){ int rc = SQLITE_OK; assert( pWal->writeLock ); assert( aWalData[3]!=pWal->nCkpt || aWalData[0]<=pWal->hdr.mxFrame ); if( aWalData[3]!=pWal->nCkpt ){ /* This savepoint was opened immediately after the write-transaction ** was started. Right after that, the writer decided to wrap around ** to the start of the log. Update the savepoint values to match. */ aWalData[0] = 0; aWalData[3] = pWal->nCkpt; } if( aWalData[0]<pWal->hdr.mxFrame ){ pWal->hdr.mxFrame = aWalData[0]; pWal->hdr.aFrameCksum[0] = aWalData[1]; pWal->hdr.aFrameCksum[1] = aWalData[2]; walCleanupHash(pWal); } return rc; } /* ** This function is called just before writing a set of frames to the log ** file (see sqlite3WalFrames()). It checks to see if, instead of appending ** to the current log file, it is possible to overwrite the start of the ** existing log file with the new frames (i.e. "reset" the log). If so, ** it sets pWal->hdr.mxFrame to 0. Otherwise, pWal->hdr.mxFrame is left ** unchanged. ** ** SQLITE_OK is returned if no error is encountered (regardless of whether ** or not pWal->hdr.mxFrame is modified). An SQLite error code is returned ** if some error */ static int walRestartLog(Wal *pWal){ int rc = SQLITE_OK; int cnt; if( pWal->readLock==0 ){ volatile WalCkptInfo *pInfo = walCkptInfo(pWal); assert( pInfo->nBackfill==pWal->hdr.mxFrame ); if( pInfo->nBackfill>0 ){ rc = walLockExclusive(pWal, WAL_READ_LOCK(1), WAL_NREADER-1); if( rc==SQLITE_OK ){ /* If all readers are using WAL_READ_LOCK(0) (in other words if no ** readers are currently using the WAL), then the transactions ** frames will overwrite the start of the existing log. Update the ** wal-index header to reflect this. ** ** In theory it would be Ok to update the cache of the header only ** at this point. But updating the actual wal-index header is also ** safe and means there is no special case for sqlite3WalUndo() ** to handle if this transaction is rolled back. */ int i; /* Loop counter */ u32 *aSalt = pWal->hdr.aSalt; /* Big-endian salt values */ pWal->nCkpt++; pWal->hdr.mxFrame = 0; sqlite3Put4byte((u8*)&aSalt[0], 1 + sqlite3Get4byte((u8*)&aSalt[0])); sqlite3_randomness(4, &aSalt[1]); walIndexWriteHdr(pWal); pInfo->nBackfill = 0; for(i=1; i<WAL_NREADER; i++) pInfo->aReadMark[i] = READMARK_NOT_USED; assert( pInfo->aReadMark[0]==0 ); walUnlockExclusive(pWal, WAL_READ_LOCK(1), WAL_NREADER-1); } } walUnlockShared(pWal, WAL_READ_LOCK(0)); pWal->readLock = -1; cnt = 0; do{ int notUsed; rc = walTryBeginRead(pWal, ¬Used, 1, ++cnt); }while( rc==WAL_RETRY ); } return rc; } /* ** Write a set of frames to the log. The caller must hold the write-lock ** on the log file (obtained using sqlite3WalBeginWriteTransaction()). */ int sqlite3WalFrames( Wal *pWal, /* Wal handle to write to */ int szPage, /* Database page-size in bytes */ PgHdr *pList, /* List of dirty pages to write */ Pgno nTruncate, /* Database size after this commit */ int isCommit, /* True if this is a commit */ int sync_flags /* Flags to pass to OsSync() (or 0) */ ){ int rc; /* Used to catch return codes */ u32 iFrame; /* Next frame address */ u8 aFrame[WAL_FRAME_HDRSIZE]; /* Buffer to assemble frame-header in */ PgHdr *p; /* Iterator to run through pList with. */ PgHdr *pLast = 0; /* Last frame in list */ int nLast = 0; /* Number of extra copies of last page */ assert( pList ); assert( pWal->writeLock ); #if defined(SQLITE_TEST) && defined(SQLITE_DEBUG) { int cnt; for(cnt=0, p=pList; p; p=p->pDirty, cnt++){} WALTRACE(("WAL%p: frame write begin. %d frames. mxFrame=%d. %s\n", pWal, cnt, pWal->hdr.mxFrame, isCommit ? "Commit" : "Spill")); } #endif /* See if it is possible to write these frames into the start of the ** log file, instead of appending to it at pWal->hdr.mxFrame. */ if( SQLITE_OK!=(rc = walRestartLog(pWal)) ){ return rc; } /* If this is the first frame written into the log, write the WAL ** header to the start of the WAL file. See comments at the top of ** this source file for a description of the WAL header format. */ iFrame = pWal->hdr.mxFrame; if( iFrame==0 ){ u8 aWalHdr[WAL_HDRSIZE]; /* Buffer to assemble wal-header in */ u32 aCksum[2]; /* Checksum for wal-header */ sqlite3Put4byte(&aWalHdr[0], (WAL_MAGIC | SQLITE_BIGENDIAN)); sqlite3Put4byte(&aWalHdr[4], WAL_MAX_VERSION); sqlite3Put4byte(&aWalHdr[8], szPage); sqlite3Put4byte(&aWalHdr[12], pWal->nCkpt); sqlite3_randomness(8, pWal->hdr.aSalt); memcpy(&aWalHdr[16], pWal->hdr.aSalt, 8); walChecksumBytes(1, aWalHdr, WAL_HDRSIZE-2*4, 0, aCksum); sqlite3Put4byte(&aWalHdr[24], aCksum[0]); sqlite3Put4byte(&aWalHdr[28], aCksum[1]); pWal->szPage = (u16)szPage; pWal->hdr.bigEndCksum = SQLITE_BIGENDIAN; pWal->hdr.aFrameCksum[0] = aCksum[0]; pWal->hdr.aFrameCksum[1] = aCksum[1]; rc = sqlite3OsWrite(pWal->pWalFd, aWalHdr, sizeof(aWalHdr), 0); WALTRACE(("WAL%p: wal-header write %s\n", pWal, rc ? "failed" : "ok")); if( rc!=SQLITE_OK ){ return rc; } } assert( pWal->szPage==szPage ); /* Write the log file. */ for(p=pList; p; p=p->pDirty){ u32 nDbsize; /* Db-size field for frame header */ i64 iOffset; /* Write offset in log file */ void *pData; iOffset = walFrameOffset(++iFrame, szPage); /* testcase( IS_BIG_INT(iOffset) ); // requires a 4GiB WAL */ /* Populate and write the frame header */ nDbsize = (isCommit && p->pDirty==0) ? nTruncate : 0; #if defined(SQLITE_HAS_CODEC) if( (pData = sqlite3PagerCodec(p))==0 ) return SQLITE_NOMEM; #else pData = p->pData; #endif walEncodeFrame(pWal, p->pgno, nDbsize, pData, aFrame); rc = sqlite3OsWrite(pWal->pWalFd, aFrame, sizeof(aFrame), iOffset); if( rc!=SQLITE_OK ){ return rc; } /* Write the page data */ rc = sqlite3OsWrite(pWal->pWalFd, pData, szPage, iOffset+sizeof(aFrame)); if( rc!=SQLITE_OK ){ return rc; } pLast = p; } /* Sync the log file if the 'isSync' flag was specified. */ if( sync_flags ){ i64 iSegment = sqlite3OsSectorSize(pWal->pWalFd); i64 iOffset = walFrameOffset(iFrame+1, szPage); assert( isCommit ); assert( iSegment>0 ); iSegment = (((iOffset+iSegment-1)/iSegment) * iSegment); while( iOffset<iSegment ){ void *pData; #if defined(SQLITE_HAS_CODEC) if( (pData = sqlite3PagerCodec(pLast))==0 ) return SQLITE_NOMEM; #else pData = pLast->pData; #endif walEncodeFrame(pWal, pLast->pgno, nTruncate, pData, aFrame); /* testcase( IS_BIG_INT(iOffset) ); // requires a 4GiB WAL */ rc = sqlite3OsWrite(pWal->pWalFd, aFrame, sizeof(aFrame), iOffset); if( rc!=SQLITE_OK ){ return rc; } iOffset += WAL_FRAME_HDRSIZE; rc = sqlite3OsWrite(pWal->pWalFd, pData, szPage, iOffset); if( rc!=SQLITE_OK ){ return rc; } nLast++; iOffset += szPage; } rc = sqlite3OsSync(pWal->pWalFd, sync_flags); } /* Append data to the wal-index. It is not necessary to lock the ** wal-index to do this as the SQLITE_SHM_WRITE lock held on the wal-index ** guarantees that there are no other writers, and no data that may ** be in use by existing readers is being overwritten. */ iFrame = pWal->hdr.mxFrame; for(p=pList; p && rc==SQLITE_OK; p=p->pDirty){ iFrame++; rc = walIndexAppend(pWal, iFrame, p->pgno); } while( nLast>0 && rc==SQLITE_OK ){ iFrame++; nLast--; rc = walIndexAppend(pWal, iFrame, pLast->pgno); } if( rc==SQLITE_OK ){ /* Update the private copy of the header. */ pWal->hdr.szPage = (u16)szPage; pWal->hdr.mxFrame = iFrame; if( isCommit ){ pWal->hdr.iChange++; pWal->hdr.nPage = nTruncate; } /* If this is a commit, update the wal-index header too. */ if( isCommit ){ walIndexWriteHdr(pWal); pWal->iCallback = iFrame; } } WALTRACE(("WAL%p: frame write %s\n", pWal, rc ? "failed" : "ok")); return rc; } /* ** This routine is called to implement sqlite3_wal_checkpoint() and ** related interfaces. ** ** Obtain a CHECKPOINT lock and then backfill as much information as ** we can from WAL into the database. */ int sqlite3WalCheckpoint( Wal *pWal, /* Wal connection */ int sync_flags, /* Flags to sync db file with (or 0) */ int nBuf, /* Size of temporary buffer */ u8 *zBuf /* Temporary buffer to use */ ){ int rc; /* Return code */ int isChanged = 0; /* True if a new wal-index header is loaded */ assert( pWal->ckptLock==0 ); WALTRACE(("WAL%p: checkpoint begins\n", pWal)); rc = walLockExclusive(pWal, WAL_CKPT_LOCK, 1); if( rc ){ /* Usually this is SQLITE_BUSY meaning that another thread or process ** is already running a checkpoint, or maybe a recovery. But it might ** also be SQLITE_IOERR. */ return rc; } pWal->ckptLock = 1; /* Copy data from the log to the database file. */ rc = walIndexReadHdr(pWal, &isChanged); if( rc==SQLITE_OK ){ rc = walCheckpoint(pWal, sync_flags, nBuf, zBuf); } if( isChanged ){ /* If a new wal-index header was loaded before the checkpoint was ** performed, then the pager-cache associated with pWal is now ** out of date. So zero the cached wal-index header to ensure that ** next time the pager opens a snapshot on this database it knows that ** the cache needs to be reset. */ memset(&pWal->hdr, 0, sizeof(WalIndexHdr)); } /* Release the locks. */ walUnlockExclusive(pWal, WAL_CKPT_LOCK, 1); pWal->ckptLock = 0; WALTRACE(("WAL%p: checkpoint %s\n", pWal, rc ? "failed" : "ok")); return rc; } /* Return the value to pass to a sqlite3_wal_hook callback, the ** number of frames in the WAL at the point of the last commit since ** sqlite3WalCallback() was called. If no commits have occurred since ** the last call, then return 0. */ int sqlite3WalCallback(Wal *pWal){ u32 ret = 0; if( pWal ){ ret = pWal->iCallback; pWal->iCallback = 0; } return (int)ret; } /* ** This function is called to change the WAL subsystem into or out ** of locking_mode=EXCLUSIVE. ** ** If op is zero, then attempt to change from locking_mode=EXCLUSIVE ** into locking_mode=NORMAL. This means that we must acquire a lock ** on the pWal->readLock byte. If the WAL is already in locking_mode=NORMAL ** or if the acquisition of the lock fails, then return 0. If the ** transition out of exclusive-mode is successful, return 1. This ** operation must occur while the pager is still holding the exclusive ** lock on the main database file. ** ** If op is one, then change from locking_mode=NORMAL into ** locking_mode=EXCLUSIVE. This means that the pWal->readLock must ** be released. Return 1 if the transition is made and 0 if the ** WAL is already in exclusive-locking mode - meaning that this ** routine is a no-op. The pager must already hold the exclusive lock ** on the main database file before invoking this operation. ** ** If op is negative, then do a dry-run of the op==1 case but do ** not actually change anything. The pager uses this to see if it ** should acquire the database exclusive lock prior to invoking ** the op==1 case. */ int sqlite3WalExclusiveMode(Wal *pWal, int op){ int rc; assert( pWal->writeLock==0 ); /* pWal->readLock is usually set, but might be -1 if there was a ** prior error while attempting to acquire are read-lock. This cannot ** happen if the connection is actually in exclusive mode (as no xShmLock ** locks are taken in this case). Nor should the pager attempt to ** upgrade to exclusive-mode following such an error. */ assert( pWal->readLock>=0 || pWal->lockError ); assert( pWal->readLock>=0 || (op<=0 && pWal->exclusiveMode==0) ); if( op==0 ){ if( pWal->exclusiveMode ){ pWal->exclusiveMode = 0; if( walLockShared(pWal, WAL_READ_LOCK(pWal->readLock))!=SQLITE_OK ){ pWal->exclusiveMode = 1; } rc = pWal->exclusiveMode==0; }else{ /* Already in locking_mode=NORMAL */ rc = 0; } }else if( op>0 ){ assert( pWal->exclusiveMode==0 ); assert( pWal->readLock>=0 ); walUnlockShared(pWal, WAL_READ_LOCK(pWal->readLock)); pWal->exclusiveMode = 1; rc = 1; }else{ rc = pWal->exclusiveMode==0; } return rc; } #endif /* #ifndef SQLITE_OMIT_WAL */ |
Added src/wal.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 | /* ** 2010 February 1 ** ** The author disclaims copyright to this source code. In place of ** a legal notice, here is a blessing: ** ** May you do good and not evil. ** May you find forgiveness for yourself and forgive others. ** May you share freely, never taking more than you give. ** ************************************************************************* ** This header file defines the interface to the write-ahead logging ** system. Refer to the comments below and the header comment attached to ** the implementation of each function in log.c for further details. */ #ifndef _WAL_H_ #define _WAL_H_ #include "sqliteInt.h" #ifdef SQLITE_OMIT_WAL # define sqlite3WalOpen(x,y,z) 0 # define sqlite3WalClose(w,x,y,z) 0 # define sqlite3WalBeginReadTransaction(y,z) 0 # define sqlite3WalEndReadTransaction(z) # define sqlite3WalRead(v,w,x,y,z) 0 # define sqlite3WalDbsize(y,z) # define sqlite3WalBeginWriteTransaction(y) 0 # define sqlite3WalEndWriteTransaction(x) 0 # define sqlite3WalUndo(x,y,z) 0 # define sqlite3WalSavepoint(y,z) # define sqlite3WalSavepointUndo(y,z) 0 # define sqlite3WalFrames(u,v,w,x,y,z) 0 # define sqlite3WalCheckpoint(u,v,w,x) 0 # define sqlite3WalCallback(z) 0 # define sqlite3WalExclusiveMode(y,z) 0 #else #define WAL_SAVEPOINT_NDATA 4 /* Connection to a write-ahead log (WAL) file. ** There is one object of this type for each pager. */ typedef struct Wal Wal; /* Open and close a connection to a write-ahead log. */ int sqlite3WalOpen(sqlite3_vfs*, sqlite3_file*, const char *zName, Wal**); int sqlite3WalClose(Wal *pWal, int sync_flags, int, u8 *); /* Used by readers to open (lock) and close (unlock) a snapshot. A ** snapshot is like a read-transaction. It is the state of the database ** at an instant in time. sqlite3WalOpenSnapshot gets a read lock and ** preserves the current state even if the other threads or processes ** write to or checkpoint the WAL. sqlite3WalCloseSnapshot() closes the ** transaction and releases the lock. */ int sqlite3WalBeginReadTransaction(Wal *pWal, int *); void sqlite3WalEndReadTransaction(Wal *pWal); /* Read a page from the write-ahead log, if it is present. */ int sqlite3WalRead(Wal *pWal, Pgno pgno, int *pInWal, int nOut, u8 *pOut); /* Return the size of the database as it existed at the beginning ** of the snapshot */ void sqlite3WalDbsize(Wal *pWal, Pgno *pPgno); /* Obtain or release the WRITER lock. */ int sqlite3WalBeginWriteTransaction(Wal *pWal); int sqlite3WalEndWriteTransaction(Wal *pWal); /* Undo any frames written (but not committed) to the log */ int sqlite3WalUndo(Wal *pWal, int (*xUndo)(void *, Pgno), void *pUndoCtx); /* Return an integer that records the current (uncommitted) write ** position in the WAL */ void sqlite3WalSavepoint(Wal *pWal, u32 *aWalData); /* Move the write position of the WAL back to iFrame. Called in ** response to a ROLLBACK TO command. */ int sqlite3WalSavepointUndo(Wal *pWal, u32 *aWalData); /* Write a frame or frames to the log. */ int sqlite3WalFrames(Wal *pWal, int, PgHdr *, Pgno, int, int); /* Copy pages from the log to the database file */ int sqlite3WalCheckpoint( Wal *pWal, /* Write-ahead log connection */ int sync_flags, /* Flags to sync db file with (or 0) */ int nBuf, /* Size of buffer nBuf */ u8 *zBuf /* Temporary buffer to use */ ); /* Return the value to pass to a sqlite3_wal_hook callback, the ** number of frames in the WAL at the point of the last commit since ** sqlite3WalCallback() was called. If no commits have occurred since ** the last call, then return 0. */ int sqlite3WalCallback(Wal *pWal); /* Tell the wal layer that an EXCLUSIVE lock has been obtained (or released) ** by the pager layer on the database file. */ int sqlite3WalExclusiveMode(Wal *pWal, int op); #endif /* ifndef SQLITE_OMIT_WAL */ #endif /* _WAL_H_ */ |
Changes to src/where.c.
︙ | ︙ | |||
231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 | #define WHERE_ROWID_EQ 0x00001000 /* rowid=EXPR or rowid IN (...) */ #define WHERE_ROWID_RANGE 0x00002000 /* rowid<EXPR and/or rowid>EXPR */ #define WHERE_COLUMN_EQ 0x00010000 /* x=EXPR or x IN (...) or x IS NULL */ #define WHERE_COLUMN_RANGE 0x00020000 /* x<EXPR and/or x>EXPR */ #define WHERE_COLUMN_IN 0x00040000 /* x IN (...) */ #define WHERE_COLUMN_NULL 0x00080000 /* x IS NULL */ #define WHERE_INDEXED 0x000f0000 /* Anything that uses an index */ #define WHERE_IN_ABLE 0x000f1000 /* Able to support an IN operator */ #define WHERE_TOP_LIMIT 0x00100000 /* x<EXPR or x<=EXPR constraint */ #define WHERE_BTM_LIMIT 0x00200000 /* x>EXPR or x>=EXPR constraint */ #define WHERE_IDX_ONLY 0x00800000 /* Use index only - omit table */ #define WHERE_ORDERBY 0x01000000 /* Output will appear in correct order */ #define WHERE_REVERSE 0x02000000 /* Scan in reverse order */ #define WHERE_UNIQUE 0x04000000 /* Selects no more than one row */ #define WHERE_VIRTUALTABLE 0x08000000 /* Use virtual-table processing */ #define WHERE_MULTI_OR 0x10000000 /* OR using multiple indices */ /* ** Initialize a preallocated WhereClause structure. */ static void whereClauseInit( WhereClause *pWC, /* The WhereClause to be initialized */ Parse *pParse, /* The parsing context */ | > > | 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 | #define WHERE_ROWID_EQ 0x00001000 /* rowid=EXPR or rowid IN (...) */ #define WHERE_ROWID_RANGE 0x00002000 /* rowid<EXPR and/or rowid>EXPR */ #define WHERE_COLUMN_EQ 0x00010000 /* x=EXPR or x IN (...) or x IS NULL */ #define WHERE_COLUMN_RANGE 0x00020000 /* x<EXPR and/or x>EXPR */ #define WHERE_COLUMN_IN 0x00040000 /* x IN (...) */ #define WHERE_COLUMN_NULL 0x00080000 /* x IS NULL */ #define WHERE_INDEXED 0x000f0000 /* Anything that uses an index */ #define WHERE_NOT_FULLSCAN 0x000f3000 /* Does not do a full table scan */ #define WHERE_IN_ABLE 0x000f1000 /* Able to support an IN operator */ #define WHERE_TOP_LIMIT 0x00100000 /* x<EXPR or x<=EXPR constraint */ #define WHERE_BTM_LIMIT 0x00200000 /* x>EXPR or x>=EXPR constraint */ #define WHERE_IDX_ONLY 0x00800000 /* Use index only - omit table */ #define WHERE_ORDERBY 0x01000000 /* Output will appear in correct order */ #define WHERE_REVERSE 0x02000000 /* Scan in reverse order */ #define WHERE_UNIQUE 0x04000000 /* Selects no more than one row */ #define WHERE_VIRTUALTABLE 0x08000000 /* Use virtual-table processing */ #define WHERE_MULTI_OR 0x10000000 /* OR using multiple indices */ #define WHERE_TEMP_INDEX 0x20000000 /* Uses an ephemeral index */ /* ** Initialize a preallocated WhereClause structure. */ static void whereClauseInit( WhereClause *pWC, /* The WhereClause to be initialized */ Parse *pParse, /* The parsing context */ |
︙ | ︙ | |||
1568 1569 1570 1571 1572 1573 1574 1575 1576 1577 1578 1579 1580 1581 | WhereCost *pCost /* Lowest cost query plan */ ){ #ifndef SQLITE_OMIT_OR_OPTIMIZATION const int iCur = pSrc->iCursor; /* The cursor of the table to be accessed */ const Bitmask maskSrc = getMask(pWC->pMaskSet, iCur); /* Bitmask for pSrc */ WhereTerm * const pWCEnd = &pWC->a[pWC->nTerm]; /* End of pWC->a[] */ WhereTerm *pTerm; /* A single term of the WHERE clause */ /* Search the WHERE clause terms for a usable WO_OR term. */ for(pTerm=pWC->a; pTerm<pWCEnd; pTerm++){ if( pTerm->eOperator==WO_OR && ((pTerm->prereqAll & ~maskSrc) & notReady)==0 && (pTerm->u.pOrInfo->indexable & maskSrc)!=0 ){ | > > > > > | 1570 1571 1572 1573 1574 1575 1576 1577 1578 1579 1580 1581 1582 1583 1584 1585 1586 1587 1588 | WhereCost *pCost /* Lowest cost query plan */ ){ #ifndef SQLITE_OMIT_OR_OPTIMIZATION const int iCur = pSrc->iCursor; /* The cursor of the table to be accessed */ const Bitmask maskSrc = getMask(pWC->pMaskSet, iCur); /* Bitmask for pSrc */ WhereTerm * const pWCEnd = &pWC->a[pWC->nTerm]; /* End of pWC->a[] */ WhereTerm *pTerm; /* A single term of the WHERE clause */ /* No OR-clause optimization allowed if the NOT INDEXED clause is used */ if( pSrc->notIndexed ){ return; } /* Search the WHERE clause terms for a usable WO_OR term. */ for(pTerm=pWC->a; pTerm<pWCEnd; pTerm++){ if( pTerm->eOperator==WO_OR && ((pTerm->prereqAll & ~maskSrc) & notReady)==0 && (pTerm->u.pOrInfo->indexable & maskSrc)!=0 ){ |
︙ | ︙ | |||
1611 1612 1613 1614 1615 1616 1617 1618 | used |= sTermCost.used; if( rTotal>=pCost->rCost ) break; } /* If there is an ORDER BY clause, increase the scan cost to account ** for the cost of the sort. */ if( pOrderBy!=0 ){ rTotal += nRow*estLog(nRow); | > > < > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1618 1619 1620 1621 1622 1623 1624 1625 1626 1627 1628 1629 1630 1631 1632 1633 1634 1635 1636 1637 1638 1639 1640 1641 1642 1643 1644 1645 1646 1647 1648 1649 1650 1651 1652 1653 1654 1655 1656 1657 1658 1659 1660 1661 1662 1663 1664 1665 1666 1667 1668 1669 1670 1671 1672 1673 1674 1675 1676 1677 1678 1679 1680 1681 1682 1683 1684 1685 1686 1687 1688 1689 1690 1691 1692 1693 1694 1695 1696 1697 1698 1699 1700 1701 1702 1703 1704 1705 1706 1707 1708 1709 1710 1711 1712 1713 1714 1715 1716 1717 1718 1719 1720 1721 1722 1723 1724 1725 1726 1727 1728 1729 1730 1731 1732 1733 1734 1735 1736 1737 1738 1739 1740 1741 1742 1743 1744 1745 1746 1747 1748 1749 1750 1751 1752 1753 1754 1755 1756 1757 1758 1759 1760 1761 1762 1763 1764 1765 1766 1767 1768 1769 1770 1771 1772 1773 1774 1775 1776 1777 1778 1779 1780 1781 1782 1783 1784 1785 1786 1787 1788 1789 1790 1791 1792 1793 1794 1795 1796 1797 1798 1799 1800 1801 1802 1803 1804 1805 1806 1807 1808 1809 1810 1811 1812 1813 1814 1815 1816 1817 1818 1819 1820 1821 1822 1823 1824 1825 1826 1827 1828 1829 1830 1831 1832 1833 1834 1835 1836 1837 1838 1839 1840 1841 1842 1843 1844 1845 1846 1847 1848 1849 1850 1851 1852 1853 1854 1855 1856 1857 1858 1859 1860 1861 1862 1863 1864 1865 1866 1867 1868 1869 1870 1871 1872 1873 1874 1875 1876 1877 1878 1879 1880 1881 1882 1883 1884 1885 1886 1887 1888 1889 1890 1891 1892 1893 | used |= sTermCost.used; if( rTotal>=pCost->rCost ) break; } /* If there is an ORDER BY clause, increase the scan cost to account ** for the cost of the sort. */ if( pOrderBy!=0 ){ WHERETRACE(("... sorting increases OR cost %.9g to %.9g\n", rTotal, rTotal+nRow*estLog(nRow))); rTotal += nRow*estLog(nRow); } /* If the cost of scanning using this OR term for optimization is ** less than the current cost stored in pCost, replace the contents ** of pCost. */ WHERETRACE(("... multi-index OR cost=%.9g nrow=%.9g\n", rTotal, nRow)); if( rTotal<pCost->rCost ){ pCost->rCost = rTotal; pCost->nRow = nRow; pCost->used = used; pCost->plan.wsFlags = flags; pCost->plan.u.pTerm = pTerm; } } } #endif /* SQLITE_OMIT_OR_OPTIMIZATION */ } #ifndef SQLITE_OMIT_AUTOMATIC_INDEX /* ** Return TRUE if the WHERE clause term pTerm is of a form where it ** could be used with an index to access pSrc, assuming an appropriate ** index existed. */ static int termCanDriveIndex( WhereTerm *pTerm, /* WHERE clause term to check */ struct SrcList_item *pSrc, /* Table we are trying to access */ Bitmask notReady /* Tables in outer loops of the join */ ){ char aff; if( pTerm->leftCursor!=pSrc->iCursor ) return 0; if( pTerm->eOperator!=WO_EQ ) return 0; if( (pTerm->prereqRight & notReady)!=0 ) return 0; aff = pSrc->pTab->aCol[pTerm->u.leftColumn].affinity; if( !sqlite3IndexAffinityOk(pTerm->pExpr, aff) ) return 0; return 1; } #endif #ifndef SQLITE_OMIT_AUTOMATIC_INDEX /* ** If the query plan for pSrc specified in pCost is a full table scan ** and indexing is allows (if there is no NOT INDEXED clause) and it ** possible to construct a transient index that would perform better ** than a full table scan even when the cost of constructing the index ** is taken into account, then alter the query plan to use the ** transient index. */ static void bestAutomaticIndex( Parse *pParse, /* The parsing context */ WhereClause *pWC, /* The WHERE clause */ struct SrcList_item *pSrc, /* The FROM clause term to search */ Bitmask notReady, /* Mask of cursors that are not available */ WhereCost *pCost /* Lowest cost query plan */ ){ double nTableRow; /* Rows in the input table */ double logN; /* log(nTableRow) */ double costTempIdx; /* per-query cost of the transient index */ WhereTerm *pTerm; /* A single term of the WHERE clause */ WhereTerm *pWCEnd; /* End of pWC->a[] */ Table *pTable; /* Table tht might be indexed */ if( (pParse->db->flags & SQLITE_AutoIndex)==0 ){ /* Automatic indices are disabled at run-time */ return; } if( (pCost->plan.wsFlags & WHERE_NOT_FULLSCAN)!=0 ){ /* We already have some kind of index in use for this query. */ return; } if( pSrc->notIndexed ){ /* The NOT INDEXED clause appears in the SQL. */ return; } assert( pParse->nQueryLoop >= (double)1 ); pTable = pSrc->pTab; nTableRow = pTable->pIndex ? pTable->pIndex->aiRowEst[0] : 1000000; logN = estLog(nTableRow); costTempIdx = 2*logN*(nTableRow/pParse->nQueryLoop + 1); if( costTempIdx>=pCost->rCost ){ /* The cost of creating the transient table would be greater than ** doing the full table scan */ return; } /* Search for any equality comparison term */ pWCEnd = &pWC->a[pWC->nTerm]; for(pTerm=pWC->a; pTerm<pWCEnd; pTerm++){ if( termCanDriveIndex(pTerm, pSrc, notReady) ){ WHERETRACE(("auto-index reduces cost from %.2f to %.2f\n", pCost->rCost, costTempIdx)); pCost->rCost = costTempIdx; pCost->nRow = logN + 1; pCost->plan.wsFlags = WHERE_TEMP_INDEX; pCost->used = pTerm->prereqRight; break; } } } #else # define bestAutomaticIndex(A,B,C,D,E) /* no-op */ #endif /* SQLITE_OMIT_AUTOMATIC_INDEX */ #ifndef SQLITE_OMIT_AUTOMATIC_INDEX /* ** Generate code to construct the Index object for an automatic index ** and to set up the WhereLevel object pLevel so that the code generator ** makes use of the automatic index. */ static void constructAutomaticIndex( Parse *pParse, /* The parsing context */ WhereClause *pWC, /* The WHERE clause */ struct SrcList_item *pSrc, /* The FROM clause term to get the next index */ Bitmask notReady, /* Mask of cursors that are not available */ WhereLevel *pLevel /* Write new index here */ ){ int nColumn; /* Number of columns in the constructed index */ WhereTerm *pTerm; /* A single term of the WHERE clause */ WhereTerm *pWCEnd; /* End of pWC->a[] */ int nByte; /* Byte of memory needed for pIdx */ Index *pIdx; /* Object describing the transient index */ Vdbe *v; /* Prepared statement under construction */ int regIsInit; /* Register set by initialization */ int addrInit; /* Address of the initialization bypass jump */ Table *pTable; /* The table being indexed */ KeyInfo *pKeyinfo; /* Key information for the index */ int addrTop; /* Top of the index fill loop */ int regRecord; /* Register holding an index record */ int n; /* Column counter */ int i; /* Loop counter */ int mxBitCol; /* Maximum column in pSrc->colUsed */ CollSeq *pColl; /* Collating sequence to on a column */ Bitmask idxCols; /* Bitmap of columns used for indexing */ Bitmask extraCols; /* Bitmap of additional columns */ /* Generate code to skip over the creation and initialization of the ** transient index on 2nd and subsequent iterations of the loop. */ v = pParse->pVdbe; assert( v!=0 ); regIsInit = ++pParse->nMem; addrInit = sqlite3VdbeAddOp1(v, OP_If, regIsInit); sqlite3VdbeAddOp2(v, OP_Integer, 1, regIsInit); /* Count the number of columns that will be added to the index ** and used to match WHERE clause constraints */ nColumn = 0; pTable = pSrc->pTab; pWCEnd = &pWC->a[pWC->nTerm]; idxCols = 0; for(pTerm=pWC->a; pTerm<pWCEnd; pTerm++){ if( termCanDriveIndex(pTerm, pSrc, notReady) ){ int iCol = pTerm->u.leftColumn; Bitmask cMask = iCol>=BMS ? ((Bitmask)1)<<(BMS-1) : ((Bitmask)1)<<iCol; testcase( iCol==BMS ); testcase( iCol==BMS-1 ); if( (idxCols & cMask)==0 ){ nColumn++; idxCols |= cMask; } } } assert( nColumn>0 ); pLevel->plan.nEq = nColumn; /* Count the number of additional columns needed to create a ** covering index. A "covering index" is an index that contains all ** columns that are needed by the query. With a covering index, the ** original table never needs to be accessed. Automatic indices must ** be a covering index because the index will not be updated if the ** original table changes and the index and table cannot both be used ** if they go out of sync. */ extraCols = pSrc->colUsed & (~idxCols | (((Bitmask)1)<<(BMS-1))); mxBitCol = (pTable->nCol >= BMS-1) ? BMS-1 : pTable->nCol; testcase( pTable->nCol==BMS-1 ); testcase( pTable->nCol==BMS-2 ); for(i=0; i<mxBitCol; i++){ if( extraCols & (((Bitmask)1)<<i) ) nColumn++; } if( pSrc->colUsed & (((Bitmask)1)<<(BMS-1)) ){ nColumn += pTable->nCol - BMS + 1; } pLevel->plan.wsFlags |= WHERE_COLUMN_EQ | WHERE_IDX_ONLY | WO_EQ; /* Construct the Index object to describe this index */ nByte = sizeof(Index); nByte += nColumn*sizeof(int); /* Index.aiColumn */ nByte += nColumn*sizeof(char*); /* Index.azColl */ nByte += nColumn; /* Index.aSortOrder */ pIdx = sqlite3DbMallocZero(pParse->db, nByte); if( pIdx==0 ) return; pLevel->plan.u.pIdx = pIdx; pIdx->azColl = (char**)&pIdx[1]; pIdx->aiColumn = (int*)&pIdx->azColl[nColumn]; pIdx->aSortOrder = (u8*)&pIdx->aiColumn[nColumn]; pIdx->zName = "auto-index"; pIdx->nColumn = nColumn; pIdx->pTable = pTable; n = 0; idxCols = 0; for(pTerm=pWC->a; pTerm<pWCEnd; pTerm++){ if( termCanDriveIndex(pTerm, pSrc, notReady) ){ int iCol = pTerm->u.leftColumn; Bitmask cMask = iCol>=BMS ? ((Bitmask)1)<<(BMS-1) : ((Bitmask)1)<<iCol; if( (idxCols & cMask)==0 ){ Expr *pX = pTerm->pExpr; idxCols |= cMask; pIdx->aiColumn[n] = pTerm->u.leftColumn; pColl = sqlite3BinaryCompareCollSeq(pParse, pX->pLeft, pX->pRight); pIdx->azColl[n] = pColl->zName; n++; } } } assert( (u32)n==pLevel->plan.nEq ); /* Add additional columns needed to make the automatic index into ** a covering index */ for(i=0; i<mxBitCol; i++){ if( extraCols & (((Bitmask)1)<<i) ){ pIdx->aiColumn[n] = i; pIdx->azColl[n] = "BINARY"; n++; } } if( pSrc->colUsed & (((Bitmask)1)<<(BMS-1)) ){ for(i=BMS-1; i<pTable->nCol; i++){ pIdx->aiColumn[n] = i; pIdx->azColl[n] = "BINARY"; n++; } } assert( n==nColumn ); /* Create the automatic index */ pKeyinfo = sqlite3IndexKeyinfo(pParse, pIdx); assert( pLevel->iIdxCur>=0 ); sqlite3VdbeAddOp4(v, OP_OpenAutoindex, pLevel->iIdxCur, nColumn+1, 0, (char*)pKeyinfo, P4_KEYINFO_HANDOFF); VdbeComment((v, "for %s", pTable->zName)); /* Fill the automatic index with content */ addrTop = sqlite3VdbeAddOp1(v, OP_Rewind, pLevel->iTabCur); regRecord = sqlite3GetTempReg(pParse); sqlite3GenerateIndexKey(pParse, pIdx, pLevel->iTabCur, regRecord, 1); sqlite3VdbeAddOp2(v, OP_IdxInsert, pLevel->iIdxCur, regRecord); sqlite3VdbeChangeP5(v, OPFLAG_USESEEKRESULT); sqlite3VdbeAddOp2(v, OP_Next, pLevel->iTabCur, addrTop+1); sqlite3VdbeChangeP5(v, SQLITE_STMTSTATUS_AUTOINDEX); sqlite3VdbeJumpHere(v, addrTop); sqlite3ReleaseTempReg(pParse, regRecord); /* Jump here when skipping the initialization */ sqlite3VdbeJumpHere(v, addrInit); } #endif /* SQLITE_OMIT_AUTOMATIC_INDEX */ #ifndef SQLITE_OMIT_VIRTUALTABLE /* ** Allocate and populate an sqlite3_index_info structure. It is the ** responsibility of the caller to eventually release the structure ** by passing the pointer returned by this function to sqlite3_free(). */ static sqlite3_index_info *allocateIndexInfo( |
︙ | ︙ | |||
1815 1816 1817 1818 1819 1820 1821 1822 1823 1824 1825 1826 1827 1828 | Table *pTab = pSrc->pTab; sqlite3_index_info *pIdxInfo; struct sqlite3_index_constraint *pIdxCons; struct sqlite3_index_constraint_usage *pUsage; WhereTerm *pTerm; int i, j; int nOrderBy; /* Make sure wsFlags is initialized to some sane value. Otherwise, if the ** malloc in allocateIndexInfo() fails and this function returns leaving ** wsFlags in an uninitialized state, the caller may behave unpredictably. */ memset(pCost, 0, sizeof(*pCost)); pCost->plan.wsFlags = WHERE_VIRTUALTABLE; | > | 2064 2065 2066 2067 2068 2069 2070 2071 2072 2073 2074 2075 2076 2077 2078 | Table *pTab = pSrc->pTab; sqlite3_index_info *pIdxInfo; struct sqlite3_index_constraint *pIdxCons; struct sqlite3_index_constraint_usage *pUsage; WhereTerm *pTerm; int i, j; int nOrderBy; double rCost; /* Make sure wsFlags is initialized to some sane value. Otherwise, if the ** malloc in allocateIndexInfo() fails and this function returns leaving ** wsFlags in an uninitialized state, the caller may behave unpredictably. */ memset(pCost, 0, sizeof(*pCost)); pCost->plan.wsFlags = WHERE_VIRTUALTABLE; |
︙ | ︙ | |||
1900 1901 1902 1903 1904 1905 1906 1907 1908 1909 1910 1911 1912 1913 1914 | pIdxCons = *(struct sqlite3_index_constraint**)&pIdxInfo->aConstraint; for(i=0; i<pIdxInfo->nConstraint; i++){ if( pUsage[i].argvIndex>0 ){ pCost->used |= pWC->a[pIdxCons[i].iTermOffset].prereqRight; } } /* The cost is not allowed to be larger than SQLITE_BIG_DBL (the ** inital value of lowestCost in this loop. If it is, then the ** (cost<lowestCost) test below will never be true. ** ** Use "(double)2" instead of "2.0" in case OMIT_FLOATING_POINT ** is defined. */ | > > > > > > > > > | | | 2150 2151 2152 2153 2154 2155 2156 2157 2158 2159 2160 2161 2162 2163 2164 2165 2166 2167 2168 2169 2170 2171 2172 2173 2174 2175 2176 2177 2178 2179 2180 2181 2182 2183 2184 | pIdxCons = *(struct sqlite3_index_constraint**)&pIdxInfo->aConstraint; for(i=0; i<pIdxInfo->nConstraint; i++){ if( pUsage[i].argvIndex>0 ){ pCost->used |= pWC->a[pIdxCons[i].iTermOffset].prereqRight; } } /* If there is an ORDER BY clause, and the selected virtual table index ** does not satisfy it, increase the cost of the scan accordingly. This ** matches the processing for non-virtual tables in bestBtreeIndex(). */ rCost = pIdxInfo->estimatedCost; if( pOrderBy && pIdxInfo->orderByConsumed==0 ){ rCost += estLog(rCost)*rCost; } /* The cost is not allowed to be larger than SQLITE_BIG_DBL (the ** inital value of lowestCost in this loop. If it is, then the ** (cost<lowestCost) test below will never be true. ** ** Use "(double)2" instead of "2.0" in case OMIT_FLOATING_POINT ** is defined. */ if( (SQLITE_BIG_DBL/((double)2))<rCost ){ pCost->rCost = (SQLITE_BIG_DBL/((double)2)); }else{ pCost->rCost = rCost; } pCost->plan.u.pVtabIdx = pIdxInfo; if( pIdxInfo->orderByConsumed ){ pCost->plan.wsFlags |= WHERE_ORDERBY; } pCost->plan.nEq = 0; pIdxInfo->nOrderBy = nOrderBy; |
︙ | ︙ | |||
2312 2313 2314 2315 2316 2317 2318 | ** the sub-select is assumed to return 25 rows for the purposes of ** determining nInMul. ** ** bInEst: ** Set to true if there was at least one "x IN (SELECT ...)" term used ** in determining the value of nInMul. ** | | | | 2571 2572 2573 2574 2575 2576 2577 2578 2579 2580 2581 2582 2583 2584 2585 2586 2587 2588 2589 2590 2591 2592 | ** the sub-select is assumed to return 25 rows for the purposes of ** determining nInMul. ** ** bInEst: ** Set to true if there was at least one "x IN (SELECT ...)" term used ** in determining the value of nInMul. ** ** estBound: ** An estimate on the amount of the table that must be searched. A ** value of 100 means the entire table is searched. Range constraints ** might reduce this to a value less than 100 to indicate that only ** a fraction of the table needs searching. In the absence of ** sqlite_stat2 ANALYZE data, a single inequality reduces the search ** space to 1/3rd its original size. So an x>? constraint reduces ** estBound to 33. Two constraints (x>? AND x<?) reduce estBound to 11. ** ** bSort: ** Boolean. True if there is an ORDER BY clause that will require an ** external sort (i.e. scanning the index being evaluated will not ** correctly order records). ** ** bLookup: |
︙ | ︙ | |||
2341 2342 2343 2344 2345 2346 2347 | ** ** SELECT a, b FROM tbl WHERE a = 1; ** SELECT a, b, c FROM tbl WHERE a = 1; */ int nEq; int bInEst = 0; int nInMul = 1; | | > > < | | | > > | 2600 2601 2602 2603 2604 2605 2606 2607 2608 2609 2610 2611 2612 2613 2614 2615 2616 2617 2618 2619 2620 2621 2622 2623 2624 2625 2626 2627 2628 2629 2630 2631 2632 2633 2634 2635 2636 2637 2638 2639 2640 2641 2642 2643 2644 2645 2646 2647 2648 2649 2650 2651 2652 2653 2654 | ** ** SELECT a, b FROM tbl WHERE a = 1; ** SELECT a, b, c FROM tbl WHERE a = 1; */ int nEq; int bInEst = 0; int nInMul = 1; int estBound = 100; int nBound = 0; /* Number of range constraints seen */ int bSort = 0; int bLookup = 0; WhereTerm *pTerm; /* A single term of the WHERE clause */ /* Determine the values of nEq and nInMul */ for(nEq=0; nEq<pProbe->nColumn; nEq++){ int j = pProbe->aiColumn[nEq]; pTerm = findTerm(pWC, iCur, j, notReady, eqTermMask, pIdx); if( pTerm==0 ) break; wsFlags |= (WHERE_COLUMN_EQ|WHERE_ROWID_EQ); if( pTerm->eOperator & WO_IN ){ Expr *pExpr = pTerm->pExpr; wsFlags |= WHERE_COLUMN_IN; if( ExprHasProperty(pExpr, EP_xIsSelect) ){ nInMul *= 25; bInEst = 1; }else if( ALWAYS(pExpr->x.pList) ){ nInMul *= pExpr->x.pList->nExpr + 1; } }else if( pTerm->eOperator & WO_ISNULL ){ wsFlags |= WHERE_COLUMN_NULL; } used |= pTerm->prereqRight; } /* Determine the value of estBound. */ if( nEq<pProbe->nColumn ){ int j = pProbe->aiColumn[nEq]; if( findTerm(pWC, iCur, j, notReady, WO_LT|WO_LE|WO_GT|WO_GE, pIdx) ){ WhereTerm *pTop = findTerm(pWC, iCur, j, notReady, WO_LT|WO_LE, pIdx); WhereTerm *pBtm = findTerm(pWC, iCur, j, notReady, WO_GT|WO_GE, pIdx); whereRangeScanEst(pParse, pProbe, nEq, pBtm, pTop, &estBound); if( pTop ){ nBound = 1; wsFlags |= WHERE_TOP_LIMIT; used |= pTop->prereqRight; } if( pBtm ){ nBound++; wsFlags |= WHERE_BTM_LIMIT; used |= pBtm->prereqRight; } wsFlags |= (WHERE_COLUMN_RANGE|WHERE_ROWID_RANGE); } }else if( pProbe->onError!=OE_None ){ testcase( wsFlags & WHERE_COLUMN_IN ); |
︙ | ︙ | |||
2409 2410 2411 2412 2413 2414 2415 | }else{ bSort = 1; } } /* If currently calculating the cost of using an index (not the IPK ** index), determine if all required column data may be obtained without | | | < | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | | > | > > | 2671 2672 2673 2674 2675 2676 2677 2678 2679 2680 2681 2682 2683 2684 2685 2686 2687 2688 2689 2690 2691 2692 2693 2694 2695 2696 2697 2698 2699 2700 2701 2702 2703 2704 2705 2706 2707 2708 2709 2710 2711 2712 2713 2714 2715 2716 2717 2718 2719 2720 2721 2722 2723 2724 2725 2726 2727 2728 2729 2730 2731 2732 2733 2734 2735 2736 2737 2738 2739 2740 2741 2742 2743 2744 2745 2746 2747 2748 2749 2750 2751 2752 2753 2754 2755 2756 2757 2758 2759 2760 2761 2762 2763 2764 2765 2766 2767 2768 2769 2770 2771 2772 2773 2774 2775 2776 2777 2778 2779 2780 2781 2782 2783 2784 2785 2786 2787 2788 2789 2790 2791 2792 2793 2794 2795 2796 2797 2798 2799 2800 2801 2802 2803 2804 2805 2806 2807 2808 2809 | }else{ bSort = 1; } } /* If currently calculating the cost of using an index (not the IPK ** index), determine if all required column data may be obtained without ** using the main table (i.e. if the index is a covering ** index for this query). If it is, set the WHERE_IDX_ONLY flag in ** wsFlags. Otherwise, set the bLookup variable to true. */ if( pIdx && wsFlags ){ Bitmask m = pSrc->colUsed; int j; for(j=0; j<pIdx->nColumn; j++){ int x = pIdx->aiColumn[j]; if( x<BMS-1 ){ m &= ~(((Bitmask)1)<<x); } } if( m==0 ){ wsFlags |= WHERE_IDX_ONLY; }else{ bLookup = 1; } } /* ** Estimate the number of rows of output. For an IN operator, ** do not let the estimate exceed half the rows in the table. */ nRow = (double)(aiRowEst[nEq] * nInMul); if( bInEst && nRow*2>aiRowEst[0] ){ nRow = aiRowEst[0]/2; nInMul = (int)(nRow / aiRowEst[nEq]); } /* Assume constant cost to access a row and logarithmic cost to ** do a binary search. Hence, the initial cost is the number of output ** rows plus log2(table-size) times the number of binary searches. */ cost = nRow + nInMul*estLog(aiRowEst[0]); /* Adjust the number of rows and the cost downward to reflect rows ** that are excluded by range constraints. */ nRow = (nRow * (double)estBound) / (double)100; cost = (cost * (double)estBound) / (double)100; /* Add in the estimated cost of sorting the result */ if( bSort ){ cost += cost*estLog(cost); } /* If all information can be taken directly from the index, we avoid ** doing table lookups. This reduces the cost by half. (Not really - ** this needs to be fixed.) */ if( pIdx && bLookup==0 ){ cost /= (double)2; } /**** Cost of using this index has now been computed ****/ /* If there are additional constraints on this table that cannot ** be used with the current index, but which might lower the number ** of output rows, adjust the nRow value accordingly. This only ** matters if the current index is the least costly, so do not bother ** with this step if we already know this index will not be chosen. ** Also, never reduce the output row count below 2 using this step. ** ** Do not reduce the output row count if pSrc is the only table that ** is notReady; if notReady is a power of two. This will be the case ** when the main sqlite3WhereBegin() loop is scanning for a table with ** and "optimal" index, and on such a scan the output row count ** reduction is not valid because it does not update the "pCost->used" ** bitmap. The notReady bitmap will also be a power of two when we ** are scanning for the last table in a 64-way join. We are willing ** to bypass this optimization in that corner case. */ if( nRow>2 && cost<=pCost->rCost && (notReady & (notReady-1))!=0 ){ int k; /* Loop counter */ int nSkipEq = nEq; /* Number of == constraints to skip */ int nSkipRange = nBound; /* Number of < constraints to skip */ Bitmask thisTab; /* Bitmap for pSrc */ thisTab = getMask(pWC->pMaskSet, iCur); for(pTerm=pWC->a, k=pWC->nTerm; nRow>2 && k; k--, pTerm++){ if( pTerm->wtFlags & TERM_VIRTUAL ) continue; if( (pTerm->prereqAll & notReady)!=thisTab ) continue; if( pTerm->eOperator & (WO_EQ|WO_IN|WO_ISNULL) ){ if( nSkipEq ){ /* Ignore the first nEq equality matches since the index ** has already accounted for these */ nSkipEq--; }else{ /* Assume each additional equality match reduces the result ** set size by a factor of 10 */ nRow /= 10; } }else if( pTerm->eOperator & (WO_LT|WO_LE|WO_GT|WO_GE) ){ if( nSkipRange ){ /* Ignore the first nBound range constraints since the index ** has already accounted for these */ nSkipRange--; }else{ /* Assume each additional range constraint reduces the result ** set size by a factor of 3 */ nRow /= 3; } }else{ /* Any other expression lowers the output row count by half */ nRow /= 2; } } if( nRow<2 ) nRow = 2; } WHERETRACE(( "%s(%s): nEq=%d nInMul=%d estBound=%d bSort=%d bLookup=%d wsFlags=0x%x\n" " notReady=0x%llx nRow=%.2f cost=%.2f used=0x%llx\n", pSrc->pTab->zName, (pIdx ? pIdx->zName : "ipk"), nEq, nInMul, estBound, bSort, bLookup, wsFlags, notReady, nRow, cost, used )); /* If this index is the best we have seen so far, then record this ** index and its cost in the pCost structure. */ if( (!pIdx || wsFlags) && (cost<pCost->rCost || (cost<=pCost->rCost && nRow<pCost->nRow)) ){ pCost->rCost = cost; pCost->nRow = nRow; pCost->used = used; pCost->plan.wsFlags = (wsFlags&wsFlagMask); pCost->plan.nEq = nEq; pCost->plan.u.pIdx = pIdx; } |
︙ | ︙ | |||
2511 2512 2513 2514 2515 2516 2517 | assert( pCost->plan.u.pIdx==0 || (pCost->plan.wsFlags&WHERE_ROWID_EQ)==0 ); assert( pSrc->pIndex==0 || pCost->plan.u.pIdx==0 || pCost->plan.u.pIdx==pSrc->pIndex ); WHERETRACE(("best index is: %s\n", | > | > | 2830 2831 2832 2833 2834 2835 2836 2837 2838 2839 2840 2841 2842 2843 2844 2845 2846 2847 2848 2849 | assert( pCost->plan.u.pIdx==0 || (pCost->plan.wsFlags&WHERE_ROWID_EQ)==0 ); assert( pSrc->pIndex==0 || pCost->plan.u.pIdx==0 || pCost->plan.u.pIdx==pSrc->pIndex ); WHERETRACE(("best index is: %s\n", ((pCost->plan.wsFlags & WHERE_NOT_FULLSCAN)==0 ? "none" : pCost->plan.u.pIdx ? pCost->plan.u.pIdx->zName : "ipk") )); bestOrClauseIndex(pParse, pWC, pSrc, notReady, pOrderBy, pCost); bestAutomaticIndex(pParse, pWC, pSrc, notReady, pCost); pCost->plan.wsFlags |= eqTermMask; } /* ** Find the query plan for accessing table pSrc->pTab. Write the ** best query plan and its cost into the WhereCost object supplied ** as the last parameter. This function may calculate the cost of |
︙ | ︙ | |||
2572 2573 2574 2575 2576 2577 2578 | ** loop. We would get the correct results if nothing were ever disabled, ** but joins might run a little slower. The trick is to disable as much ** as we can without disabling too much. If we disabled in (1), we'd get ** the wrong answer. See ticket #813. */ static void disableTerm(WhereLevel *pLevel, WhereTerm *pTerm){ if( pTerm | | | 2893 2894 2895 2896 2897 2898 2899 2900 2901 2902 2903 2904 2905 2906 2907 | ** loop. We would get the correct results if nothing were ever disabled, ** but joins might run a little slower. The trick is to disable as much ** as we can without disabling too much. If we disabled in (1), we'd get ** the wrong answer. See ticket #813. */ static void disableTerm(WhereLevel *pLevel, WhereTerm *pTerm){ if( pTerm && (pTerm->wtFlags & TERM_CODED)==0 && (pLevel->iLeftJoin==0 || ExprHasProperty(pTerm->pExpr, EP_FromJoin)) ){ pTerm->wtFlags |= TERM_CODED; if( pTerm->iParent>=0 ){ WhereTerm *pOther = &pTerm->pWC->a[pTerm->iParent]; if( (--pOther->nChild)==0 ){ disableTerm(pLevel, pOther); |
︙ | ︙ | |||
2770 2771 2772 2773 2774 2775 2776 | */ assert( pIdx->nColumn>=nEq ); for(j=0; j<nEq; j++){ int r1; int k = pIdx->aiColumn[j]; pTerm = findTerm(pWC, iCur, k, notReady, pLevel->plan.wsFlags, pIdx); if( NEVER(pTerm==0) ) break; | > > | | 3091 3092 3093 3094 3095 3096 3097 3098 3099 3100 3101 3102 3103 3104 3105 3106 3107 | */ assert( pIdx->nColumn>=nEq ); for(j=0; j<nEq; j++){ int r1; int k = pIdx->aiColumn[j]; pTerm = findTerm(pWC, iCur, k, notReady, pLevel->plan.wsFlags, pIdx); if( NEVER(pTerm==0) ) break; /* The following true for indices with redundant columns. ** Ex: CREATE INDEX i1 ON t1(a,b,a); SELECT * FROM t1 WHERE a=0 AND b=0; */ testcase( (pTerm->wtFlags & TERM_CODED)!=0 ); r1 = codeEqualityTerm(pParse, pTerm, pLevel, regBase+j); if( r1!=regBase+j ){ if( nReg==1 ){ sqlite3ReleaseTempReg(pParse, regBase); regBase = r1; }else{ sqlite3VdbeAddOp2(v, OP_SCopy, r1, regBase+j); |
︙ | ︙ | |||
2984 2985 2986 2987 2988 2989 2990 | } disableTerm(pLevel, pEnd); } start = sqlite3VdbeCurrentAddr(v); pLevel->op = bRev ? OP_Prev : OP_Next; pLevel->p1 = iCur; pLevel->p2 = start; | | > > > > | 3307 3308 3309 3310 3311 3312 3313 3314 3315 3316 3317 3318 3319 3320 3321 3322 3323 3324 3325 | } disableTerm(pLevel, pEnd); } start = sqlite3VdbeCurrentAddr(v); pLevel->op = bRev ? OP_Prev : OP_Next; pLevel->p1 = iCur; pLevel->p2 = start; if( pStart==0 && pEnd==0 ){ pLevel->p5 = SQLITE_STMTSTATUS_FULLSCAN_STEP; }else{ assert( pLevel->p5==0 ); } if( testOp!=OP_Noop ){ iRowidReg = iReleaseReg = sqlite3GetTempReg(pParse); sqlite3VdbeAddOp2(v, OP_Rowid, iCur, iRowidReg); sqlite3ExprCacheStore(pParse, iCur, -1, iRowidReg); sqlite3VdbeAddOp3(v, testOp, memEndValue, addrBrk, iRowidReg); sqlite3VdbeChangeP5(v, SQLITE_AFF_NUMERIC | SQLITE_JUMPIFNULL); } |
︙ | ︙ | |||
3053 3054 3055 3056 3057 3058 3059 | int endEq; /* True if range end uses ==, >= or <= */ int start_constraints; /* Start of range is constrained */ int nConstraint; /* Number of constraint terms */ Index *pIdx; /* The index we will be using */ int iIdxCur; /* The VDBE cursor for the index */ int nExtraReg = 0; /* Number of extra registers needed */ int op; /* Instruction opcode */ | > | | 3380 3381 3382 3383 3384 3385 3386 3387 3388 3389 3390 3391 3392 3393 3394 3395 | int endEq; /* True if range end uses ==, >= or <= */ int start_constraints; /* Start of range is constrained */ int nConstraint; /* Number of constraint terms */ Index *pIdx; /* The index we will be using */ int iIdxCur; /* The VDBE cursor for the index */ int nExtraReg = 0; /* Number of extra registers needed */ int op; /* Instruction opcode */ char *zStartAff; /* Affinity for start of range constraint */ char *zEndAff; /* Affinity for end of range constraint */ pIdx = pLevel->plan.u.pIdx; iIdxCur = pLevel->iIdxCur; k = pIdx->aiColumn[nEq]; /* Column for inequality constraints */ /* If this loop satisfies a sort order (pOrderBy) request that ** was passed to this function to implement a "SELECT min(x) ..." |
︙ | ︙ | |||
3094 3095 3096 3097 3098 3099 3100 | } /* Generate code to evaluate all constraint terms using == or IN ** and store the values of those terms in an array of registers ** starting at regBase. */ regBase = codeAllEqualityTerms( | | > | | | | | | | | | | | | | | > | 3422 3423 3424 3425 3426 3427 3428 3429 3430 3431 3432 3433 3434 3435 3436 3437 3438 3439 3440 3441 3442 3443 3444 3445 3446 3447 3448 3449 3450 3451 3452 3453 3454 3455 3456 3457 3458 3459 3460 3461 3462 3463 3464 3465 3466 3467 3468 3469 3470 3471 3472 3473 3474 3475 3476 3477 3478 3479 3480 3481 3482 3483 3484 3485 3486 3487 3488 3489 3490 3491 3492 3493 3494 3495 3496 3497 3498 3499 3500 3501 3502 3503 3504 3505 3506 3507 3508 3509 3510 3511 3512 3513 3514 3515 3516 | } /* Generate code to evaluate all constraint terms using == or IN ** and store the values of those terms in an array of registers ** starting at regBase. */ regBase = codeAllEqualityTerms( pParse, pLevel, pWC, notReady, nExtraReg, &zStartAff ); zEndAff = sqlite3DbStrDup(pParse->db, zStartAff); addrNxt = pLevel->addrNxt; /* If we are doing a reverse order scan on an ascending index, or ** a forward order scan on a descending index, interchange the ** start and end terms (pRangeStart and pRangeEnd). */ if( nEq<pIdx->nColumn && bRev==(pIdx->aSortOrder[nEq]==SQLITE_SO_ASC) ){ SWAP(WhereTerm *, pRangeEnd, pRangeStart); } testcase( pRangeStart && pRangeStart->eOperator & WO_LE ); testcase( pRangeStart && pRangeStart->eOperator & WO_GE ); testcase( pRangeEnd && pRangeEnd->eOperator & WO_LE ); testcase( pRangeEnd && pRangeEnd->eOperator & WO_GE ); startEq = !pRangeStart || pRangeStart->eOperator & (WO_LE|WO_GE); endEq = !pRangeEnd || pRangeEnd->eOperator & (WO_LE|WO_GE); start_constraints = pRangeStart || nEq>0; /* Seek the index cursor to the start of the range. */ nConstraint = nEq; if( pRangeStart ){ Expr *pRight = pRangeStart->pExpr->pRight; sqlite3ExprCode(pParse, pRight, regBase+nEq); sqlite3ExprCodeIsNullJump(v, pRight, regBase+nEq, addrNxt); if( zStartAff ){ if( sqlite3CompareAffinity(pRight, zStartAff[nEq])==SQLITE_AFF_NONE){ /* Since the comparison is to be performed with no conversions ** applied to the operands, set the affinity to apply to pRight to ** SQLITE_AFF_NONE. */ zStartAff[nEq] = SQLITE_AFF_NONE; } if( sqlite3ExprNeedsNoAffinityChange(pRight, zStartAff[nEq]) ){ zStartAff[nEq] = SQLITE_AFF_NONE; } } nConstraint++; }else if( isMinQuery ){ sqlite3VdbeAddOp2(v, OP_Null, 0, regBase+nEq); nConstraint++; startEq = 0; start_constraints = 1; } codeApplyAffinity(pParse, regBase, nConstraint, zStartAff); op = aStartOp[(start_constraints<<2) + (startEq<<1) + bRev]; assert( op!=0 ); testcase( op==OP_Rewind ); testcase( op==OP_Last ); testcase( op==OP_SeekGt ); testcase( op==OP_SeekGe ); testcase( op==OP_SeekLe ); testcase( op==OP_SeekLt ); sqlite3VdbeAddOp4Int(v, op, iIdxCur, addrNxt, regBase, nConstraint); /* Load the value for the inequality constraint at the end of the ** range (if any). */ nConstraint = nEq; if( pRangeEnd ){ Expr *pRight = pRangeEnd->pExpr->pRight; sqlite3ExprCacheRemove(pParse, regBase+nEq, 1); sqlite3ExprCode(pParse, pRight, regBase+nEq); sqlite3ExprCodeIsNullJump(v, pRight, regBase+nEq, addrNxt); if( zEndAff ){ if( sqlite3CompareAffinity(pRight, zEndAff[nEq])==SQLITE_AFF_NONE){ /* Since the comparison is to be performed with no conversions ** applied to the operands, set the affinity to apply to pRight to ** SQLITE_AFF_NONE. */ zEndAff[nEq] = SQLITE_AFF_NONE; } if( sqlite3ExprNeedsNoAffinityChange(pRight, zEndAff[nEq]) ){ zEndAff[nEq] = SQLITE_AFF_NONE; } } codeApplyAffinity(pParse, regBase, nEq+1, zEndAff); nConstraint++; } sqlite3DbFree(pParse->db, zStartAff); sqlite3DbFree(pParse->db, zEndAff); /* Top of the loop body */ pLevel->p2 = sqlite3VdbeCurrentAddr(v); /* Check if the index cursor is past the end of the range. */ op = aEndOp[(pRangeEnd || nEq) * (1 + bRev)]; testcase( op==OP_Noop ); |
︙ | ︙ | |||
3442 3443 3444 3445 3446 3447 3448 | #endif /* SQLITE_TEST */ /* ** Free a WhereInfo structure */ static void whereInfoFree(sqlite3 *db, WhereInfo *pWInfo){ | | > > > > > > > | 3772 3773 3774 3775 3776 3777 3778 3779 3780 3781 3782 3783 3784 3785 3786 3787 3788 3789 3790 3791 3792 3793 3794 3795 3796 3797 3798 3799 3800 3801 3802 | #endif /* SQLITE_TEST */ /* ** Free a WhereInfo structure */ static void whereInfoFree(sqlite3 *db, WhereInfo *pWInfo){ if( ALWAYS(pWInfo) ){ int i; for(i=0; i<pWInfo->nLevel; i++){ sqlite3_index_info *pInfo = pWInfo->a[i].pIdxInfo; if( pInfo ){ /* assert( pInfo->needToFreeIdxStr==0 || db->mallocFailed ); */ if( pInfo->needToFreeIdxStr ){ sqlite3_free(pInfo->idxStr); } sqlite3DbFree(db, pInfo); } if( pWInfo->a[i].plan.wsFlags & WHERE_TEMP_INDEX ){ Index *pIdx = pWInfo->a[i].plan.u.pIdx; if( pIdx ){ sqlite3DbFree(db, pIdx->zColAff); sqlite3DbFree(db, pIdx); } } } whereClauseClear(pWInfo->pWC); sqlite3DbFree(db, pWInfo); } } |
︙ | ︙ | |||
3572 3573 3574 3575 3576 3577 3578 3579 3580 3581 3582 3583 3584 3585 | int iFrom; /* First unused FROM clause element */ int andFlags; /* AND-ed combination of all pWC->a[].wtFlags */ sqlite3 *db; /* Database connection */ /* The number of tables in the FROM clause is limited by the number of ** bits in a Bitmask */ if( pTabList->nSrc>BMS ){ sqlite3ErrorMsg(pParse, "at most %d tables in a join", BMS); return 0; } /* This function normally generates a nested loop for all tables in ** pTabList. But if the WHERE_ONETABLE_ONLY flag is set, then we should | > | 3909 3910 3911 3912 3913 3914 3915 3916 3917 3918 3919 3920 3921 3922 3923 | int iFrom; /* First unused FROM clause element */ int andFlags; /* AND-ed combination of all pWC->a[].wtFlags */ sqlite3 *db; /* Database connection */ /* The number of tables in the FROM clause is limited by the number of ** bits in a Bitmask */ testcase( pTabList->nSrc==BMS ); if( pTabList->nSrc>BMS ){ sqlite3ErrorMsg(pParse, "at most %d tables in a join", BMS); return 0; } /* This function normally generates a nested loop for all tables in ** pTabList. But if the WHERE_ONETABLE_ONLY flag is set, then we should |
︙ | ︙ | |||
3599 3600 3601 3602 3603 3604 3605 3606 3607 3608 3609 3610 3611 3612 3613 3614 3615 3616 3617 3618 3619 3620 | nByteWInfo = ROUND8(sizeof(WhereInfo)+(nTabList-1)*sizeof(WhereLevel)); pWInfo = sqlite3DbMallocZero(db, nByteWInfo + sizeof(WhereClause) + sizeof(WhereMaskSet) ); if( db->mallocFailed ){ goto whereBeginError; } pWInfo->nLevel = nTabList; pWInfo->pParse = pParse; pWInfo->pTabList = pTabList; pWInfo->iBreak = sqlite3VdbeMakeLabel(v); pWInfo->pWC = pWC = (WhereClause *)&((u8 *)pWInfo)[nByteWInfo]; pWInfo->wctrlFlags = wctrlFlags; pMaskSet = (WhereMaskSet*)&pWC[1]; /* Split the WHERE clause into separate subexpressions where each ** subexpression is separated by an AND operator. */ initMaskSet(pMaskSet); whereClauseInit(pWC, pParse, pMaskSet); | > > > | 3937 3938 3939 3940 3941 3942 3943 3944 3945 3946 3947 3948 3949 3950 3951 3952 3953 3954 3955 3956 3957 3958 3959 3960 3961 | nByteWInfo = ROUND8(sizeof(WhereInfo)+(nTabList-1)*sizeof(WhereLevel)); pWInfo = sqlite3DbMallocZero(db, nByteWInfo + sizeof(WhereClause) + sizeof(WhereMaskSet) ); if( db->mallocFailed ){ sqlite3DbFree(db, pWInfo); pWInfo = 0; goto whereBeginError; } pWInfo->nLevel = nTabList; pWInfo->pParse = pParse; pWInfo->pTabList = pTabList; pWInfo->iBreak = sqlite3VdbeMakeLabel(v); pWInfo->pWC = pWC = (WhereClause *)&((u8 *)pWInfo)[nByteWInfo]; pWInfo->wctrlFlags = wctrlFlags; pWInfo->savedNQueryLoop = pParse->nQueryLoop; pMaskSet = (WhereMaskSet*)&pWC[1]; /* Split the WHERE clause into separate subexpressions where each ** subexpression is separated by an AND operator. */ initMaskSet(pMaskSet); whereClauseInit(pWC, pParse, pMaskSet); |
︙ | ︙ | |||
3708 3709 3710 3711 3712 3713 3714 | Bitmask m; /* Bitmask value for j or bestJ */ int isOptimal; /* Iterator for optimal/non-optimal search */ memset(&bestPlan, 0, sizeof(bestPlan)); bestPlan.rCost = SQLITE_BIG_DBL; /* Loop through the remaining entries in the FROM clause to find the | | | | > | > > > > | | | | | < > | > > > | > > > | 4049 4050 4051 4052 4053 4054 4055 4056 4057 4058 4059 4060 4061 4062 4063 4064 4065 4066 4067 4068 4069 4070 4071 4072 4073 4074 4075 4076 4077 4078 4079 4080 4081 4082 4083 4084 4085 4086 4087 4088 4089 4090 4091 4092 4093 4094 4095 4096 4097 4098 4099 4100 4101 4102 4103 4104 4105 4106 4107 4108 4109 4110 4111 4112 4113 4114 4115 4116 4117 4118 4119 4120 4121 4122 4123 4124 4125 4126 4127 4128 4129 4130 4131 4132 4133 4134 4135 4136 4137 4138 4139 4140 4141 4142 4143 4144 4145 4146 4147 4148 4149 4150 4151 4152 4153 4154 4155 4156 4157 4158 | Bitmask m; /* Bitmask value for j or bestJ */ int isOptimal; /* Iterator for optimal/non-optimal search */ memset(&bestPlan, 0, sizeof(bestPlan)); bestPlan.rCost = SQLITE_BIG_DBL; /* Loop through the remaining entries in the FROM clause to find the ** next nested loop. The loop tests all FROM clause entries ** either once or twice. ** ** The first test is always performed if there are two or more entries ** remaining and never performed if there is only one FROM clause entry ** to choose from. The first test looks for an "optimal" scan. In ** this context an optimal scan is one that uses the same strategy ** for the given FROM clause entry as would be selected if the entry ** were used as the innermost nested loop. In other words, a table ** is chosen such that the cost of running that table cannot be reduced ** by waiting for other tables to run first. This "optimal" test works ** by first assuming that the FROM clause is on the inner loop and finding ** its query plan, then checking to see if that query plan uses any ** other FROM clause terms that are notReady. If no notReady terms are ** used then the "optimal" query plan works. ** ** The second loop iteration is only performed if no optimal scan ** strategies were found by the first loop. This 2nd iteration is used to ** search for the lowest cost scan overall. ** ** Previous versions of SQLite performed only the second iteration - ** the next outermost loop was always that with the lowest overall ** cost. However, this meant that SQLite could select the wrong plan ** for scripts such as the following: ** ** CREATE TABLE t1(a, b); ** CREATE TABLE t2(c, d); ** SELECT * FROM t2, t1 WHERE t2.rowid = t1.a; ** ** The best strategy is to iterate through table t1 first. However it ** is not possible to determine this with a simple greedy algorithm. ** However, since the cost of a linear scan through table t2 is the same ** as the cost of a linear scan through table t1, a simple greedy ** algorithm may choose to use t2 for the outer loop, which is a much ** costlier approach. */ for(isOptimal=(iFrom<nTabList-1); isOptimal>=0; isOptimal--){ Bitmask mask; /* Mask of tables not yet ready */ for(j=iFrom, pTabItem=&pTabList->a[j]; j<nTabList; j++, pTabItem++){ int doNotReorder; /* True if this table should not be reordered */ WhereCost sCost; /* Cost information from best[Virtual]Index() */ ExprList *pOrderBy; /* ORDER BY clause for index to optimize */ doNotReorder = (pTabItem->jointype & (JT_LEFT|JT_CROSS))!=0; if( j!=iFrom && doNotReorder ) break; m = getMask(pMaskSet, pTabItem->iCursor); if( (m & notReady)==0 ){ if( j==iFrom ) iFrom++; continue; } mask = (isOptimal ? m : notReady); pOrderBy = ((i==0 && ppOrderBy )?*ppOrderBy:0); assert( pTabItem->pTab ); #ifndef SQLITE_OMIT_VIRTUALTABLE if( IsVirtual(pTabItem->pTab) ){ sqlite3_index_info **pp = &pWInfo->a[j].pIdxInfo; bestVirtualIndex(pParse, pWC, pTabItem, mask, pOrderBy, &sCost, pp); }else #endif { bestBtreeIndex(pParse, pWC, pTabItem, mask, pOrderBy, &sCost); } assert( isOptimal || (sCost.used¬Ready)==0 ); if( (sCost.used¬Ready)==0 && (bestJ<0 || sCost.rCost<bestPlan.rCost || (sCost.rCost<=bestPlan.rCost && sCost.nRow<bestPlan.nRow)) ){ WHERETRACE(("... best so far with cost=%g and nRow=%g\n", sCost.rCost, sCost.nRow)); bestPlan = sCost; bestJ = j; } if( doNotReorder ) break; } } assert( bestJ>=0 ); assert( notReady & getMask(pMaskSet, pTabList->a[bestJ].iCursor) ); WHERETRACE(("*** Optimizer selects table %d for loop %d\n", bestJ, pLevel-pWInfo->a)); if( (bestPlan.plan.wsFlags & WHERE_ORDERBY)!=0 ){ *ppOrderBy = 0; } andFlags &= bestPlan.plan.wsFlags; pLevel->plan = bestPlan.plan; testcase( bestPlan.plan.wsFlags & WHERE_INDEXED ); testcase( bestPlan.plan.wsFlags & WHERE_TEMP_INDEX ); if( bestPlan.plan.wsFlags & (WHERE_INDEXED|WHERE_TEMP_INDEX) ){ pLevel->iIdxCur = pParse->nTab++; }else{ pLevel->iIdxCur = -1; } notReady &= ~getMask(pMaskSet, pTabList->a[bestJ].iCursor); pLevel->iFrom = (u8)bestJ; if( bestPlan.nRow>=(double)1 ) pParse->nQueryLoop *= bestPlan.nRow; /* Check that if the table scanned by this loop iteration had an ** INDEXED BY clause attached to it, that the named index is being ** used for the scan. If not, then query compilation has failed. ** Return an error. */ pIdx = pTabList->a[bestJ].pIndex; |
︙ | ︙ | |||
3839 3840 3841 3842 3843 3844 3845 3846 3847 3848 3849 3850 3851 3852 3853 3854 3855 3856 3857 | pWInfo->a[0].plan.wsFlags &= ~WHERE_IDX_ONLY; } /* Open all tables in the pTabList and any indices selected for ** searching those tables. */ sqlite3CodeVerifySchema(pParse, -1); /* Insert the cookie verifier Goto */ for(i=0, pLevel=pWInfo->a; i<nTabList; i++, pLevel++){ Table *pTab; /* Table to open */ int iDb; /* Index of database containing table/index */ #ifndef SQLITE_OMIT_EXPLAIN if( pParse->explain==2 ){ char *zMsg; struct SrcList_item *pItem = &pTabList->a[pLevel->iFrom]; zMsg = sqlite3MPrintf(db, "TABLE %s", pItem->zName); if( pItem->zAlias ){ zMsg = sqlite3MAppendf(db, zMsg, "%s AS %s", zMsg, pItem->zAlias); } | > | > > | 4191 4192 4193 4194 4195 4196 4197 4198 4199 4200 4201 4202 4203 4204 4205 4206 4207 4208 4209 4210 4211 4212 4213 4214 4215 4216 4217 4218 4219 4220 | pWInfo->a[0].plan.wsFlags &= ~WHERE_IDX_ONLY; } /* Open all tables in the pTabList and any indices selected for ** searching those tables. */ sqlite3CodeVerifySchema(pParse, -1); /* Insert the cookie verifier Goto */ notReady = ~(Bitmask)0; for(i=0, pLevel=pWInfo->a; i<nTabList; i++, pLevel++){ Table *pTab; /* Table to open */ int iDb; /* Index of database containing table/index */ #ifndef SQLITE_OMIT_EXPLAIN if( pParse->explain==2 ){ char *zMsg; struct SrcList_item *pItem = &pTabList->a[pLevel->iFrom]; zMsg = sqlite3MPrintf(db, "TABLE %s", pItem->zName); if( pItem->zAlias ){ zMsg = sqlite3MAppendf(db, zMsg, "%s AS %s", zMsg, pItem->zAlias); } if( (pLevel->plan.wsFlags & WHERE_TEMP_INDEX)!=0 ){ zMsg = sqlite3MAppendf(db, zMsg, "%s WITH AUTOMATIC INDEX", zMsg); }else if( (pLevel->plan.wsFlags & WHERE_INDEXED)!=0 ){ zMsg = sqlite3MAppendf(db, zMsg, "%s WITH INDEX %s", zMsg, pLevel->plan.u.pIdx->zName); }else if( pLevel->plan.wsFlags & WHERE_MULTI_OR ){ zMsg = sqlite3MAppendf(db, zMsg, "%s VIA MULTI-INDEX UNION", zMsg); }else if( pLevel->plan.wsFlags & (WHERE_ROWID_EQ|WHERE_ROWID_RANGE) ){ zMsg = sqlite3MAppendf(db, zMsg, "%s USING PRIMARY KEY", zMsg); } |
︙ | ︙ | |||
3874 3875 3876 3877 3878 3879 3880 3881 | zMsg = sqlite3MAppendf(db, zMsg, "%s ORDER BY", zMsg); } sqlite3VdbeAddOp4(v, OP_Explain, i, pLevel->iFrom, 0, zMsg, P4_DYNAMIC); } #endif /* SQLITE_OMIT_EXPLAIN */ pTabItem = &pTabList->a[pLevel->iFrom]; pTab = pTabItem->pTab; iDb = sqlite3SchemaToIndex(db, pTab->pSchema); | > | > > > > > | > > > > > | 4229 4230 4231 4232 4233 4234 4235 4236 4237 4238 4239 4240 4241 4242 4243 4244 4245 4246 4247 4248 4249 4250 4251 4252 4253 4254 4255 4256 4257 4258 4259 4260 4261 4262 4263 4264 4265 4266 4267 4268 4269 4270 4271 4272 4273 4274 4275 4276 4277 4278 4279 4280 4281 4282 4283 4284 4285 4286 4287 4288 4289 4290 4291 | zMsg = sqlite3MAppendf(db, zMsg, "%s ORDER BY", zMsg); } sqlite3VdbeAddOp4(v, OP_Explain, i, pLevel->iFrom, 0, zMsg, P4_DYNAMIC); } #endif /* SQLITE_OMIT_EXPLAIN */ pTabItem = &pTabList->a[pLevel->iFrom]; pTab = pTabItem->pTab; pLevel->iTabCur = pTabItem->iCursor; iDb = sqlite3SchemaToIndex(db, pTab->pSchema); if( (pTab->tabFlags & TF_Ephemeral)!=0 || pTab->pSelect ){ /* Do nothing */ }else #ifndef SQLITE_OMIT_VIRTUALTABLE if( (pLevel->plan.wsFlags & WHERE_VIRTUALTABLE)!=0 ){ const char *pVTab = (const char *)sqlite3GetVTable(db, pTab); int iCur = pTabItem->iCursor; sqlite3VdbeAddOp4(v, OP_VOpen, iCur, 0, 0, pVTab, P4_VTAB); }else #endif if( (pLevel->plan.wsFlags & WHERE_IDX_ONLY)==0 && (wctrlFlags & WHERE_OMIT_OPEN)==0 ){ int op = pWInfo->okOnePass ? OP_OpenWrite : OP_OpenRead; sqlite3OpenTable(pParse, pTabItem->iCursor, iDb, pTab, op); testcase( pTab->nCol==BMS-1 ); testcase( pTab->nCol==BMS ); if( !pWInfo->okOnePass && pTab->nCol<BMS ){ Bitmask b = pTabItem->colUsed; int n = 0; for(; b; b=b>>1, n++){} sqlite3VdbeChangeP4(v, sqlite3VdbeCurrentAddr(v)-1, SQLITE_INT_TO_PTR(n), P4_INT32); assert( n<=pTab->nCol ); } }else{ sqlite3TableLock(pParse, iDb, pTab->tnum, 0, pTab->zName); } #ifndef SQLITE_OMIT_AUTOMATIC_INDEX if( (pLevel->plan.wsFlags & WHERE_TEMP_INDEX)!=0 ){ constructAutomaticIndex(pParse, pWC, pTabItem, notReady, pLevel); }else #endif if( (pLevel->plan.wsFlags & WHERE_INDEXED)!=0 ){ Index *pIx = pLevel->plan.u.pIdx; KeyInfo *pKey = sqlite3IndexKeyinfo(pParse, pIx); int iIdxCur = pLevel->iIdxCur; assert( pIx->pSchema==pTab->pSchema ); assert( iIdxCur>=0 ); sqlite3VdbeAddOp4(v, OP_OpenRead, iIdxCur, pIx->tnum, iDb, (char*)pKey, P4_KEYINFO_HANDOFF); VdbeComment((v, "%s", pIx->zName)); } sqlite3CodeVerifySchema(pParse, iDb); notReady &= ~getMask(pWC->pMaskSet, pTabItem->iCursor); } pWInfo->iTop = sqlite3VdbeCurrentAddr(v); if( db->mallocFailed ) goto whereBeginError; /* Generate the code to do the search. Each iteration of the for ** loop below generates code for a single nested loop of the VM ** program. */ notReady = ~(Bitmask)0; for(i=0; i<nTabList; i++){ |
︙ | ︙ | |||
3979 3980 3981 3982 3983 3984 3985 | /* Record the continuation address in the WhereInfo structure. Then ** clean up and return. */ return pWInfo; /* Jump here if malloc fails */ whereBeginError: | > > | > | 4345 4346 4347 4348 4349 4350 4351 4352 4353 4354 4355 4356 4357 4358 4359 4360 4361 4362 | /* Record the continuation address in the WhereInfo structure. Then ** clean up and return. */ return pWInfo; /* Jump here if malloc fails */ whereBeginError: if( pWInfo ){ pParse->nQueryLoop = pWInfo->savedNQueryLoop; whereInfoFree(db, pWInfo); } return 0; } /* ** Generate the end of the WHERE loop. See comments on ** sqlite3WhereBegin() for additional information. */ |
︙ | ︙ | |||
4049 4050 4051 4052 4053 4054 4055 | /* Close all of the cursors that were opened by sqlite3WhereBegin. */ assert( pWInfo->nLevel==1 || pWInfo->nLevel==pTabList->nSrc ); for(i=0, pLevel=pWInfo->a; i<pWInfo->nLevel; i++, pLevel++){ struct SrcList_item *pTabItem = &pTabList->a[pLevel->iFrom]; Table *pTab = pTabItem->pTab; assert( pTab!=0 ); | | > | > > | | | 4418 4419 4420 4421 4422 4423 4424 4425 4426 4427 4428 4429 4430 4431 4432 4433 4434 4435 4436 4437 4438 4439 4440 | /* Close all of the cursors that were opened by sqlite3WhereBegin. */ assert( pWInfo->nLevel==1 || pWInfo->nLevel==pTabList->nSrc ); for(i=0, pLevel=pWInfo->a; i<pWInfo->nLevel; i++, pLevel++){ struct SrcList_item *pTabItem = &pTabList->a[pLevel->iFrom]; Table *pTab = pTabItem->pTab; assert( pTab!=0 ); if( (pTab->tabFlags & TF_Ephemeral)==0 && pTab->pSelect==0 && (pWInfo->wctrlFlags & WHERE_OMIT_CLOSE)==0 ){ int ws = pLevel->plan.wsFlags; if( !pWInfo->okOnePass && (ws & WHERE_IDX_ONLY)==0 ){ sqlite3VdbeAddOp1(v, OP_Close, pTabItem->iCursor); } if( (ws & WHERE_INDEXED)!=0 && (ws & WHERE_TEMP_INDEX)==0 ){ sqlite3VdbeAddOp1(v, OP_Close, pLevel->iIdxCur); } } /* If this scan uses an index, make code substitutions to read data ** from the index in preference to the table. Sometimes, this means ** the table need never be read from. This is a performance boost, |
︙ | ︙ | |||
4102 4103 4104 4105 4106 4107 4108 4109 4110 4111 | } } } } /* Final cleanup */ whereInfoFree(db, pWInfo); return; } | > | 4474 4475 4476 4477 4478 4479 4480 4481 4482 4483 4484 | } } } } /* Final cleanup */ pParse->nQueryLoop = pWInfo->savedNQueryLoop; whereInfoFree(db, pWInfo); return; } |
Changes to test/all.test.
1 2 3 4 5 6 7 8 9 10 11 12 | # 2001 September 15 # # The author disclaims copyright to this source code. In place of # a legal notice, here is a blessing: # # May you do good and not evil. # May you find forgiveness for yourself and forgive others. # May you share freely, never taking more than you give. # #*********************************************************************** # This file runs all tests. # | < | < < < < | | < < < < | | < < | | < < | < < < < < | < | < < < < < | < < < < | | < < | < < < < < < < < < < < | < < < < < | < < | < < | | < | < < < < < < < < < < < | < | < < < < | | | < < < < < < < | < < < < < < < < < | < < < < < < | | < < < < < < | 1 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 | # 2001 September 15 # # The author disclaims copyright to this source code. In place of # a legal notice, here is a blessing: # # May you do good and not evil. # May you find forgiveness for yourself and forgive others. # May you share freely, never taking more than you give. # #*********************************************************************** # This file runs all tests. # set testdir [file dirname $argv0] source $testdir/permutations.test run_test_suite full run_test_suite memsubsys1 run_test_suite memsubsys2 run_test_suite singlethread run_test_suite multithread run_test_suite onefile run_test_suite utf16 run_test_suite exclusive run_test_suite persistent_journal run_test_suite persistent_journal_error run_test_suite no_journal run_test_suite no_journal_error run_test_suite autovacuum_ioerr run_test_suite no_mutex_try run_test_suite fullmutex run_test_suite journaltest run_test_suite inmemory_journal run_test_suite pcache0 run_test_suite pcache10 run_test_suite pcache50 run_test_suite pcache90 run_test_suite pcache100 if {$::tcl_platform(platform)=="unix"} { ifcapable !default_autovacuum { run_test_suite autovacuum_crash } } finish_test |
Changes to test/alter.test.
︙ | ︙ | |||
168 169 170 171 172 173 174 175 176 177 178 179 180 181 | index {sqlite_autoindex_T2_2} T2 \ ] } else { execsql { DROP TABLE TempTab; } } # Make sure the ALTER TABLE statements work with the # non-callback API # do_test alter-1.7 { stepsql $DB { ALTER TABLE [-t1-] RENAME to [*t1*]; | > > > > > > > > > > > > > > | 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 | index {sqlite_autoindex_T2_2} T2 \ ] } else { execsql { DROP TABLE TempTab; } } # Create bogus application-defined functions for functions used # internally by ALTER TABLE, to ensure that ALTER TABLE falls back # to the built-in functions. # proc failing_app_func {args} {error "bad function"} do_test alter-1.7-prep { db func substr failing_app_func db func like failing_app_func db func sqlite_rename_table failing_app_func db func sqlite_rename_trigger failing_app_func db func sqlite_rename_parent failing_app_func catchsql {SELECT substr(name,1,3) FROM sqlite_master} } {1 {bad function}} # Make sure the ALTER TABLE statements work with the # non-callback API # do_test alter-1.7 { stepsql $DB { ALTER TABLE [-t1-] RENAME to [*t1*]; |
︙ | ︙ | |||
563 564 565 566 567 568 569 | } db2 } {x y z} do_test alter-5.3 { db2 close } {} foreach tblname [execsql { | | > | 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 | } db2 } {x y z} do_test alter-5.3 { db2 close } {} foreach tblname [execsql { SELECT name FROM sqlite_master WHERE type='table' AND name NOT GLOB 'sqlite*' }] { execsql "DROP TABLE \"$tblname\"" } set ::tbl_name "abc\uABCDdef" do_test alter-6.1 { string length $::tbl_name |
︙ | ︙ | |||
684 685 686 687 688 689 690 | #------------------------------------------------------------------------ # alter-10.X - Make sure ALTER TABLE works with multi-byte UTF-8 characters # in the names. # do_test alter-10.1 { execsql "CREATE TABLE xyz(x UNIQUE)" execsql "ALTER TABLE xyz RENAME TO xyz\u1234abc" | | | | | | 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 | #------------------------------------------------------------------------ # alter-10.X - Make sure ALTER TABLE works with multi-byte UTF-8 characters # in the names. # do_test alter-10.1 { execsql "CREATE TABLE xyz(x UNIQUE)" execsql "ALTER TABLE xyz RENAME TO xyz\u1234abc" execsql {SELECT name FROM sqlite_master WHERE name GLOB 'xyz*'} } [list xyz\u1234abc] do_test alter-10.2 { execsql {SELECT name FROM sqlite_master WHERE name GLOB 'sqlite_autoindex*'} } [list sqlite_autoindex_xyz\u1234abc_1] do_test alter-10.3 { execsql "ALTER TABLE xyz\u1234abc RENAME TO xyzabc" execsql {SELECT name FROM sqlite_master WHERE name GLOB 'xyz*'} } [list xyzabc] do_test alter-10.4 { execsql {SELECT name FROM sqlite_master WHERE name GLOB 'sqlite_autoindex*'} } [list sqlite_autoindex_xyzabc_1] do_test alter-11.1 { sqlite3_exec db {CREATE TABLE t11(%c6%c6)} execsql { ALTER TABLE t11 ADD COLUMN abc; } |
︙ | ︙ | |||
791 792 793 794 795 796 797 | # do_test alter-13.1 { execsql { CREATE TABLE /* hi */ t3102a(x); CREATE TABLE t3102b -- comment (y); CREATE INDEX t3102c ON t3102a(x); | | | | | 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 | # do_test alter-13.1 { execsql { CREATE TABLE /* hi */ t3102a(x); CREATE TABLE t3102b -- comment (y); CREATE INDEX t3102c ON t3102a(x); SELECT name FROM sqlite_master WHERE name GLOB 't3102*' ORDER BY 1; } } {t3102a t3102b t3102c} do_test alter-13.2 { execsql { ALTER TABLE t3102a RENAME TO t3102a_rename; SELECT name FROM sqlite_master WHERE name GLOB 't3102*' ORDER BY 1; } } {t3102a_rename t3102b t3102c} do_test alter-13.3 { execsql { ALTER TABLE t3102b RENAME TO t3102b_rename; SELECT name FROM sqlite_master WHERE name GLOB 't3102*' ORDER BY 1; } } {t3102a_rename t3102b_rename t3102c} # Ticket #3651 do_test alter-14.1 { catchsql { CREATE TABLE t3651(a UNIQUE); |
︙ | ︙ |
Changes to test/alter2.test.
︙ | ︙ | |||
17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 | # set testdir [file dirname $argv0] source $testdir/tester.tcl # We have to have pragmas in order to do this test ifcapable {!pragma} return # These tests do not work if there is a codec. # #if {[catch {sqlite3 -has_codec} r] || $r} return # The file format change affects the way row-records stored in tables (but # not indices) are interpreted. Before version 3.1.3, a row-record for a # table with N columns was guaranteed to contain exactly N fields. As # of version 3.1.3, the record may contain up to N fields. In this case # the M fields that are present are the values for the left-most M # columns. The (N-M) rightmost columns contain NULL. | > > > > > > | 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 | # set testdir [file dirname $argv0] source $testdir/tester.tcl # We have to have pragmas in order to do this test ifcapable {!pragma} return # Do not use a codec for tests in this file, as the database file is # manipulated directly using tcl scripts. See proc [set_file_format]. # do_not_use_codec # These tests do not work if there is a codec. # #if {[catch {sqlite3 -has_codec} r] || $r} return # # The file format change affects the way row-records stored in tables (but # not indices) are interpreted. Before version 3.1.3, a row-record for a # table with N columns was guaranteed to contain exactly N fields. As # of version 3.1.3, the record may contain up to N fields. In this case # the M fields that are present are the values for the left-most M # columns. The (N-M) rightmost columns contain NULL. |
︙ | ︙ | |||
67 68 69 70 71 72 73 74 75 76 77 78 79 80 | PRAGMA writable_schema = 1; UPDATE sqlite_master SET sql = '$s' WHERE name = '$t' AND type = 'table'; PRAGMA writable_schema = 0; }] dbat close set_file_format 2 } #----------------------------------------------------------------------- # Some basic tests to make sure short rows are handled. # do_test alter2-1.1 { execsql { CREATE TABLE abc(a, b); | > > > > > > > > > > > > > > > | 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 | PRAGMA writable_schema = 1; UPDATE sqlite_master SET sql = '$s' WHERE name = '$t' AND type = 'table'; PRAGMA writable_schema = 0; }] dbat close set_file_format 2 } # Create bogus application-defined functions for functions used # internally by ALTER TABLE, to ensure that ALTER TABLE falls back # to the built-in functions. # proc failing_app_func {args} {error "bad function"} do_test alter2-1.0 { db func substr failing_app_func db func like failing_app_func db func sqlite_rename_table failing_app_func db func sqlite_rename_trigger failing_app_func db func sqlite_rename_parent failing_app_func catchsql {SELECT substr('abcdefg',1,3)} } {1 {bad function}} #----------------------------------------------------------------------- # Some basic tests to make sure short rows are handled. # do_test alter2-1.1 { execsql { CREATE TABLE abc(a, b); |
︙ | ︙ | |||
229 230 231 232 233 234 235 | # format that SQLite does not support (in this case 5). Note: The # file format is checked each time the schema is read, so changing the # file format requires incrementing the schema cookie. # do_test alter2-4.1 { db close set_file_format 5 | | > | 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 | # format that SQLite does not support (in this case 5). Note: The # file format is checked each time the schema is read, so changing the # file format requires incrementing the schema cookie. # do_test alter2-4.1 { db close set_file_format 5 catch { sqlite3 db test.db } set {} {} } {} do_test alter2-4.2 { # We have to run two queries here because the Tcl interface uses # sqlite3_prepare_v2(). In this case, the first query encounters an # SQLITE_SCHEMA error. Then, when trying to recompile the statement, the # "unsupported file format" error is encountered. So the error code # returned is SQLITE_SCHEMA, not SQLITE_ERROR as required by the following |
︙ | ︙ |
Changes to test/analyze.test.
︙ | ︙ | |||
292 293 294 295 296 297 298 | # do_test analyze-99.1 { execsql { PRAGMA writable_schema=on; UPDATE sqlite_master SET sql='nonsense' WHERE name='sqlite_stat1'; } db close | | | 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 | # do_test analyze-99.1 { execsql { PRAGMA writable_schema=on; UPDATE sqlite_master SET sql='nonsense' WHERE name='sqlite_stat1'; } db close catch { sqlite3 db test.db } catchsql { ANALYZE } } {1 {malformed database schema (sqlite_stat1) - near "nonsense": syntax error}} finish_test |
Changes to test/analyze2.test.
︙ | ︙ | |||
17 18 19 20 21 22 23 24 25 26 27 28 29 30 | set testdir [file dirname $argv0] source $testdir/tester.tcl ifcapable !stat2 { finish_test return } #-------------------------------------------------------------------- # Test organization: # # analyze2-1.*: Tests to verify that ANALYZE creates and populates the # sqlite_stat2 table as expected. # | > > > > > | 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 | set testdir [file dirname $argv0] source $testdir/tester.tcl ifcapable !stat2 { finish_test return } # Do not use a codec for tests in this file, as the database file is # manipulated directly using tcl scripts (using the [hexio_write] command). # do_not_use_codec #-------------------------------------------------------------------- # Test organization: # # analyze2-1.*: Tests to verify that ANALYZE creates and populates the # sqlite_stat2 table as expected. # |
︙ | ︙ |
Changes to test/async.test.
︙ | ︙ | |||
19 20 21 22 23 24 25 | rename finish_test async_really_finish_test proc finish_test {} { catch {db close} catch {db2 close} catch {db3 close} } | | | > > > > > > > > > > > > | < < < < | < < < | < < | | 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 | rename finish_test async_really_finish_test proc finish_test {} { catch {db close} catch {db2 close} catch {db3 close} } if {[info exists G(isquick)]} { set ASYNC_SAVE_ISQUICK $G(isquick) } set G(isquick) 1 set ASYNC_INCLUDE { insert.test insert2.test insert3.test lock.test lock2.test lock3.test select1.test select2.test select3.test select4.test trans.test } # Enable asynchronous IO. sqlite3async_initialize "" 1 # This proc flushes the contents of the async-IO queue through to the # underlying VFS. A couple of the test scripts identified in $ASYNC_INCLUDE # above contain lines like "catch flush_async_queue" in places where # this is required for the tests to work in async mode. # proc flush_async_queue {} { sqlite3async_control halt idle sqlite3async_start sqlite3async_wait sqlite3async_control halt never } rename do_test async_really_do_test proc do_test {name args} { uplevel async_really_do_test async_io-$name $args flush_async_queue } foreach testfile [lsort -dictionary [glob $testdir/*.test]] { set tail [file tail $testfile] if {[lsearch -exact $ASYNC_INCLUDE $tail]<0} continue source $testfile # Make sure everything is flushed through. This is because [source]ing # the next test file will delete the database file on disk (using # [file delete]). If the asynchronous backend still has the file # open, it will become confused. # flush_async_queue } # Flush the write-queue and disable asynchronous IO. This should ensure # all allocated memory is cleaned up. set sqlite3async_trace 1 flush_async_queue sqlite3async_shutdown set sqlite3async_trace 0 rename do_test {} rename async_really_do_test do_test rename finish_test {} rename async_really_finish_test finish_test if {[info exists ASYNC_SAVE_ISQUICK]} { set G(isquick) $ASYNC_SAVE_ISQUICK } finish_test |
Changes to test/async4.test.
︙ | ︙ | |||
9 10 11 12 13 14 15 16 17 18 19 20 21 22 | # #*********************************************************************** # # $Id: async4.test,v 1.4 2009/06/05 17:09:12 drh Exp $ set testdir [file dirname $argv0] source $testdir/tester.tcl # These tests only work for Tcl version 8.5 and later on Windows (for now) # if {$tcl_platform(platform)=="windows"} { scan $::tcl_version %f vx if {$vx<8.5} { finish_test | > > > > > | 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | # #*********************************************************************** # # $Id: async4.test,v 1.4 2009/06/05 17:09:12 drh Exp $ set testdir [file dirname $argv0] source $testdir/tester.tcl # Do not use a codec for tests in this file, as the database file is # manipulated directly using tcl scripts (using the [hexio_write] command). # do_not_use_codec # These tests only work for Tcl version 8.5 and later on Windows (for now) # if {$tcl_platform(platform)=="windows"} { scan $::tcl_version %f vx if {$vx<8.5} { finish_test |
︙ | ︙ | |||
90 91 92 93 94 95 96 97 98 99 100 101 102 103 | CREATE INDEX i1 ON t2(a); CREATE INDEX i2 ON t1(a); } db2 } {} do_test async4.1.15 { sqlite3async_start sqlite3async_wait execsql { pragma integrity_check } db2 } {{*** in database main *** Page 5 is never used}} do_test async4.1.16 { db close db2 close sqlite3async_start | > | 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 | CREATE INDEX i1 ON t2(a); CREATE INDEX i2 ON t1(a); } db2 } {} do_test async4.1.15 { sqlite3async_start sqlite3async_wait hexio_write test.db 28 00000000 execsql { pragma integrity_check } db2 } {{*** in database main *** Page 5 is never used}} do_test async4.1.16 { db close db2 close sqlite3async_start |
︙ | ︙ |
Added test/autoindex1.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 | # 2010 April 07 # # The author disclaims copyright to this source code. In place of # a legal notice, here is a blessing: # # May you do good and not evil. # May you find forgiveness for yourself and forgive others. # May you share freely, never taking more than you give. # #************************************************************************* # This file implements regression tests for SQLite library. The # focus of this script is testing automatic index creation logic. # set testdir [file dirname $argv0] source $testdir/tester.tcl # If the library is not compiled with automatic index support then # skip all tests in this file. # ifcapable {!autoindex} { finish_test return } # With automatic index turned off, we do a full scan of the T2 table do_test autoindex1-100 { db eval { CREATE TABLE t1(a,b); INSERT INTO t1 VALUES(1,11); INSERT INTO t1 VALUES(2,22); INSERT INTO t1 SELECT a+2, b+22 FROM t1; INSERT INTO t1 SELECT a+4, b+44 FROM t1; CREATE TABLE t2(c,d); INSERT INTO t2 SELECT a, 900+b FROM t1; } db eval { PRAGMA automatic_index=OFF; SELECT b, d FROM t1 JOIN t2 ON a=c ORDER BY b; } } {11 911 22 922 33 933 44 944 55 955 66 966 77 977 88 988} do_test autoindex1-101 { db status step } {63} do_test autoindex1-102 { db status autoindex } {0} # With autoindex turned on, we build an index once and then use that index # to find T2 values. do_test autoindex1-110 { db eval { PRAGMA automatic_index=ON; SELECT b, d FROM t1 JOIN t2 ON a=c ORDER BY b; } } {11 911 22 922 33 933 44 944 55 955 66 966 77 977 88 988} do_test autoindex1-111 { db status step } {7} do_test autoindex1-112 { db status autoindex } {7} # The same test as above, but this time the T2 query is a subquery rather # than a join. do_test autoindex1-200 { db eval { PRAGMA automatic_index=OFF; SELECT b, (SELECT d FROM t2 WHERE c=a) FROM t1; } } {11 911 22 922 33 933 44 944 55 955 66 966 77 977 88 988} do_test autoindex1-201 { db status step } {35} do_test autoindex1-202 { db status autoindex } {0} do_test autoindex1-210 { db eval { PRAGMA automatic_index=ON; SELECT b, (SELECT d FROM t2 WHERE c=a) FROM t1; } } {11 911 22 922 33 933 44 944 55 955 66 966 77 977 88 988} do_test autoindex1-211 { db status step } {7} do_test autoindex1-212 { db status autoindex } {7} # Modify the second table of the join while the join is in progress # do_test autoindex1-300 { set r {} db eval {SELECT b, d FROM t1 JOIN t2 ON (c=a)} { lappend r $b $d db eval {UPDATE t2 SET d=d+1} } set r } {11 911 22 922 33 933 44 944 55 955 66 966 77 977 88 988} do_test autoindex1-310 { db eval {SELECT d FROM t2 ORDER BY d} } {919 930 941 952 963 974 985 996} # The next test does a 10-way join on unindexed tables. Without # automatic indices, the join will take a long time to complete. # With automatic indices, it should only take about a second. # do_test autoindex1-400 { db eval { CREATE TABLE t4(a, b); INSERT INTO t4 VALUES(1,2); INSERT INTO t4 VALUES(2,3); } for {set n 2} {$n<4096} {set n [expr {$n+$n}]} { db eval {INSERT INTO t4 SELECT a+$n, b+$n FROM t4} } db eval { SELECT count(*) FROM t4; } } {4096} do_test autoindex1-401 { db eval { SELECT count(*) FROM t4 AS x1 JOIN t4 AS x2 ON x2.a=x1.b JOIN t4 AS x3 ON x3.a=x2.b JOIN t4 AS x4 ON x4.a=x3.b JOIN t4 AS x5 ON x5.a=x4.b JOIN t4 AS x6 ON x6.a=x5.b JOIN t4 AS x7 ON x7.a=x6.b JOIN t4 AS x8 ON x8.a=x7.b JOIN t4 AS x9 ON x9.a=x8.b JOIN t4 AS x10 ON x10.a=x9.b; } } {4087} # Ticket [8011086c85c6c404014c947fcf3eb9f42b184a0d] from 2010-07-08 # Make sure automatic indices are not created for the RHS of an IN expression # that is not a correlated subquery. # do_test autoindex1-500 { db eval { CREATE TABLE t501(a INTEGER PRIMARY KEY, b); CREATE TABLE t502(x INTEGER PRIMARY KEY, y); EXPLAIN QUERY PLAN SELECT b FROM t501 WHERE t501.a IN (SELECT x FROM t502 WHERE y=?); } } {0 0 {TABLE t501 USING PRIMARY KEY} 0 0 {TABLE t502}} do_test autoindex1-501 { db eval { EXPLAIN QUERY PLAN SELECT b FROM t501 WHERE t501.a IN (SELECT x FROM t502 WHERE y=t501.b); } } {0 0 {TABLE t501} 0 0 {TABLE t502 WITH AUTOMATIC INDEX}} do_test autoindex1-502 { db eval { EXPLAIN QUERY PLAN SELECT b FROM t501 WHERE t501.a=123 AND t501.a IN (SELECT x FROM t502 WHERE y=t501.b); } } {0 0 {TABLE t501 USING PRIMARY KEY} 0 0 {TABLE t502}} finish_test |
Changes to test/autovacuum.test.
︙ | ︙ | |||
650 651 652 653 654 655 656 | # do_test autovacuum-8.1 { db close sqlite3 db test.db sqlite3 db2 test.db db eval {PRAGMA auto_vacuum} } {1} | > | | | | | | > | 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 | # do_test autovacuum-8.1 { db close sqlite3 db test.db sqlite3 db2 test.db db eval {PRAGMA auto_vacuum} } {1} if {[permutation] == ""} { do_test autovacuum-8.2 { db eval {BEGIN EXCLUSIVE} catchsql {PRAGMA auto_vacuum} db2 } {1 {database is locked}} catch {db2 close} catch {db eval {COMMIT}} } do_test autovacuum-9.1 { execsql { DROP TABLE t1; DROP TABLE t2; DROP TABLE t3; DROP TABLE t4; |
︙ | ︙ |
Changes to test/avtrans.test.
︙ | ︙ | |||
18 19 20 21 22 23 24 | set testdir [file dirname $argv0] source $testdir/tester.tcl # Create several tables to work with. # do_test avtrans-1.0 { | > > | < | 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 | set testdir [file dirname $argv0] source $testdir/tester.tcl # Create several tables to work with. # do_test avtrans-1.0 { execsql { PRAGMA auto_vacuum=ON } wal_set_journal_mode execsql { CREATE TABLE one(a int PRIMARY KEY, b text); INSERT INTO one VALUES(1,'one'); INSERT INTO one VALUES(2,'two'); INSERT INTO one VALUES(3,'three'); SELECT b FROM one ORDER BY a; } } {one two three} |
︙ | ︙ | |||
44 45 46 47 48 49 50 51 52 53 54 55 56 57 | sqlite3 altdb test.db execsql {SELECT b FROM one ORDER BY a} altdb } {one two three} do_test avtrans-1.10 { execsql {SELECT b FROM two ORDER BY a} altdb } {I V X} integrity_check avtrans-1.11 # Basic transactions # do_test avtrans-2.1 { set v [catch {execsql {BEGIN}} msg] lappend v $msg } {0 {}} | > | 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 | sqlite3 altdb test.db execsql {SELECT b FROM one ORDER BY a} altdb } {one two three} do_test avtrans-1.10 { execsql {SELECT b FROM two ORDER BY a} altdb } {I V X} integrity_check avtrans-1.11 wal_check_journal_mode avtrans-1.12 # Basic transactions # do_test avtrans-2.1 { set v [catch {execsql {BEGIN}} msg] lappend v $msg } {0 {}} |
︙ | ︙ | |||
80 81 82 83 84 85 86 87 88 89 90 91 92 93 | BEGIN; SELECT a FROM one ORDER BY a; SELECT a FROM two ORDER BY a; END; } } {1 2 3 1 5 10} integrity_check avtrans-2.11 # Check the locking behavior # sqlite3_soft_heap_limit 0 do_test avtrans-3.1 { execsql { BEGIN; | > | 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 | BEGIN; SELECT a FROM one ORDER BY a; SELECT a FROM two ORDER BY a; END; } } {1 2 3 1 5 10} integrity_check avtrans-2.11 wal_check_journal_mode avtrans-2.12 # Check the locking behavior # sqlite3_soft_heap_limit 0 do_test avtrans-3.1 { execsql { BEGIN; |
︙ | ︙ | |||
158 159 160 161 162 163 164 | } {0 {1 4 5 10}} do_test avtrans-3.14 { set v [catch {execsql { SELECT a FROM one ORDER BY a; } db} msg] lappend v $msg } {0 {1 2 3 4}} | | | 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 | } {0 {1 4 5 10}} do_test avtrans-3.14 { set v [catch {execsql { SELECT a FROM one ORDER BY a; } db} msg] lappend v $msg } {0 {1 2 3 4}} sqlite3_soft_heap_limit $cmdlinearg(soft-heap-limit) integrity_check avtrans-3.15 do_test avtrans-4.1 { set v [catch {execsql { COMMIT; } db} msg] lappend v $msg |
︙ | ︙ | |||
850 851 852 853 854 855 856 | } # Repeat the following group of tests 20 times for quick testing and # 40 times for full testing. Each iteration of the test makes table # t3 a little larger, and thus takes a little longer, so doing 40 tests # is more than 2.0 times slower than doing 20 tests. Considerably more. # | | | 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 | } # Repeat the following group of tests 20 times for quick testing and # 40 times for full testing. Each iteration of the test makes table # t3 a little larger, and thus takes a little longer, so doing 40 tests # is more than 2.0 times slower than doing 20 tests. Considerably more. # if {[info exists G(isquick)]} { set limit 20 } else { set limit 40 } # Do rollbacks. Make sure the signature does not change. # |
︙ | ︙ | |||
909 910 911 912 913 914 915 916 917 918 919 920 921 | } [expr {$i%2==0}] } else { do_test avtrans-9.$i.5-$cnt { expr {$sqlite_fullsync_count==0} } {1} } } } set ::pager_old_format 0 } integrity_check avtrans-10.1 finish_test | > > | 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 | } [expr {$i%2==0}] } else { do_test avtrans-9.$i.5-$cnt { expr {$sqlite_fullsync_count==0} } {1} } } wal_check_journal_mode avtrans-9.$i-6.$cnt } set ::pager_old_format 0 } integrity_check avtrans-10.1 wal_check_journal_mode avtrans-10.2 finish_test |
Changes to test/backup.test.
︙ | ︙ | |||
11 12 13 14 15 16 17 18 19 20 21 22 23 24 | # This file implements regression tests for SQLite library. The # focus of this file is testing the sqlite3_backup_XXX API. # # $Id: backup.test,v 1.11 2009/06/05 17:09:12 drh Exp $ set testdir [file dirname $argv0] source $testdir/tester.tcl #--------------------------------------------------------------------- # Test organization: # # backup-1.*: Warm-body tests. # # backup-2.*: Test backup under various conditions. To and from in-memory | > > | 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | # This file implements regression tests for SQLite library. The # focus of this file is testing the sqlite3_backup_XXX API. # # $Id: backup.test,v 1.11 2009/06/05 17:09:12 drh Exp $ set testdir [file dirname $argv0] source $testdir/tester.tcl do_not_use_codec #--------------------------------------------------------------------- # Test organization: # # backup-1.*: Warm-body tests. # # backup-2.*: Test backup under various conditions. To and from in-memory |
︙ | ︙ | |||
34 35 36 37 38 39 40 41 42 43 44 45 46 47 | # # backup-7.*: Test SQLITE_BUSY and SQLITE_LOCKED errors. # # backup-8.*: Test multiple simultaneous backup operations. # # backup-9.*: Test that passing a negative argument to backup_step() is # interpreted as "copy the whole file". # proc data_checksum {db file} { $db one "SELECT md5sum(a, b) FROM ${file}.t1" } proc test_contents {name db1 file1 db2 file2} { $db2 eval {select * from sqlite_master} $db1 eval {select * from sqlite_master} set checksum [data_checksum $db2 $file2] | > > | 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 | # # backup-7.*: Test SQLITE_BUSY and SQLITE_LOCKED errors. # # backup-8.*: Test multiple simultaneous backup operations. # # backup-9.*: Test that passing a negative argument to backup_step() is # interpreted as "copy the whole file". # # backup-10.*: Test writing the source database mid backup. # proc data_checksum {db file} { $db one "SELECT md5sum(a, b) FROM ${file}.t1" } proc test_contents {name db1 file1 db2 file2} { $db2 eval {select * from sqlite_master} $db1 eval {select * from sqlite_master} set checksum [data_checksum $db2 $file2] |
︙ | ︙ | |||
483 484 485 486 487 488 489 490 491 492 493 494 495 496 | # # 1) Backing up file-to-file. The writer writes via an external pager. # 2) Backing up file-to-file. The writer writes via the same pager as # is used by the backup operation. # 3) Backing up memory-to-file. # set iTest 0 foreach {writer file} {db test.db db3 test.db db :memory:} { incr iTest catch { file delete bak.db } sqlite3 db2 bak.db catch { file delete $file } sqlite3 db $file sqlite3 db3 $file | > | 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 | # # 1) Backing up file-to-file. The writer writes via an external pager. # 2) Backing up file-to-file. The writer writes via the same pager as # is used by the backup operation. # 3) Backing up memory-to-file. # set iTest 0 file delete -force bak.db-wal foreach {writer file} {db test.db db3 test.db db :memory:} { incr iTest catch { file delete bak.db } sqlite3 db2 bak.db catch { file delete $file } sqlite3 db $file sqlite3 db3 $file |
︙ | ︙ | |||
901 902 903 904 905 906 907 908 909 910 911 912 913 914 | } {ok} db2 close db3 close } # Test that if the database is written to via the same database handle being # used as the source by a backup operation: # # 10.1.*: If the db is in-memory, the backup is restarted. # 10.2.*: If the db is a file, the backup is not restarted. # db close | > | 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 | } {ok} db2 close db3 close } #----------------------------------------------------------------------- # Test that if the database is written to via the same database handle being # used as the source by a backup operation: # # 10.1.*: If the db is in-memory, the backup is restarted. # 10.2.*: If the db is a file, the backup is not restarted. # db close |
︙ | ︙ |
Changes to test/backup2.test.
︙ | ︙ | |||
13 14 15 16 17 18 19 20 21 22 23 24 25 26 | # of the TCL interface - methods which are based on the # sqlite3_backup_XXX API. # # $Id: backup2.test,v 1.4 2009/04/07 14:14:23 danielk1977 Exp $ set testdir [file dirname $argv0] source $testdir/tester.tcl ifcapable !trigger||!view { finish_test ; return } # Fill a database with test data. # do_test backup2-1 { db eval { | > > | 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | # of the TCL interface - methods which are based on the # sqlite3_backup_XXX API. # # $Id: backup2.test,v 1.4 2009/04/07 14:14:23 danielk1977 Exp $ set testdir [file dirname $argv0] source $testdir/tester.tcl do_not_use_codec ifcapable !trigger||!view { finish_test ; return } # Fill a database with test data. # do_test backup2-1 { db eval { |
︙ | ︙ |
Changes to test/backup_malloc.test.
︙ | ︙ | |||
64 65 66 67 68 69 70 71 72 | error "out of memory" } # Finalize the backup. B finish } -cleanup { catch { B finish } } | > | | 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 | error "out of memory" } # Finalize the backup. B finish } -cleanup { catch { B finish } catch { db2 close } } do_malloc_test backup_malloc-2 -tclprep { sqlite3 db2 test2.db } -tclbody { set rc [catch {sqlite3_backup B db2 temp db main}] set errcode [sqlite3_errcode db2] if {$rc && ($errcode == "SQLITE_NOMEM" || $errcode == "SQLITE_IOERR_NOMEM")} { error "out of memory" } |
︙ | ︙ |
Changes to test/bigfile.test.
︙ | ︙ | |||
13 14 15 16 17 18 19 20 21 22 23 24 25 26 | # files larger than 4GB. # # $Id: bigfile.test,v 1.12 2009/03/05 04:27:08 shane Exp $ # set testdir [file dirname $argv0] source $testdir/tester.tcl # If SQLITE_DISABLE_LFS is defined, omit this file. ifcapable !lfs { finish_test return } | > > > > > | 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 | # files larger than 4GB. # # $Id: bigfile.test,v 1.12 2009/03/05 04:27:08 shane Exp $ # set testdir [file dirname $argv0] source $testdir/tester.tcl # Do not use a codec for this file, as the database is manipulated using # external methods (the [fake_big_file] and [hexio_write] commands). # do_not_use_codec # If SQLITE_DISABLE_LFS is defined, omit this file. ifcapable !lfs { finish_test return } |
︙ | ︙ | |||
65 66 67 68 69 70 71 72 73 74 75 76 77 78 | # db close if {[catch {fake_big_file 4096 [pwd]/test.db} msg]} { puts "**** Unable to create a file larger than 4096 MB. *****" finish_test return } do_test bigfile-1.2 { sqlite3 db test.db execsql { SELECT md5sum(x) FROM t1; } } $::MAGIC_SUM | > | 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 | # db close if {[catch {fake_big_file 4096 [pwd]/test.db} msg]} { puts "**** Unable to create a file larger than 4096 MB. *****" finish_test return } hexio_write test.db 28 00000000 do_test bigfile-1.2 { sqlite3 db test.db execsql { SELECT md5sum(x) FROM t1; } } $::MAGIC_SUM |
︙ | ︙ | |||
104 105 106 107 108 109 110 111 112 113 114 115 116 117 | db close if {[catch {fake_big_file 8192 [pwd]/test.db}]} { puts "**** Unable to create a file larger than 8192 MB. *****" finish_test return } do_test bigfile-1.5 { sqlite3 db test.db execsql { SELECT md5sum(x) FROM t1; } } $::MAGIC_SUM | > | 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 | db close if {[catch {fake_big_file 8192 [pwd]/test.db}]} { puts "**** Unable to create a file larger than 8192 MB. *****" finish_test return } hexio_write test.db 28 00000000 do_test bigfile-1.5 { sqlite3 db test.db execsql { SELECT md5sum(x) FROM t1; } } $::MAGIC_SUM |
︙ | ︙ | |||
142 143 144 145 146 147 148 149 150 151 152 153 154 155 | db close if {[catch {fake_big_file 16384 [pwd]/test.db}]} { puts "**** Unable to create a file larger than 16384 MB. *****" finish_test return } do_test bigfile-1.10 { sqlite3 db test.db execsql { SELECT md5sum(x) FROM t1; } } $::MAGIC_SUM | > | 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 | db close if {[catch {fake_big_file 16384 [pwd]/test.db}]} { puts "**** Unable to create a file larger than 16384 MB. *****" finish_test return } hexio_write test.db 28 00000000 do_test bigfile-1.10 { sqlite3 db test.db execsql { SELECT md5sum(x) FROM t1; } } $::MAGIC_SUM |
︙ | ︙ |
Changes to test/cache.test.
︙ | ︙ | |||
24 25 26 27 28 29 30 | set bt [btree_from_db $db] db_enter $db array set stats [btree_pager_stats $bt] db_leave $db return $stats(page) } | > | < < > | 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 | set bt [btree_from_db $db] db_enter $db array set stats [btree_pager_stats $bt] db_leave $db return $stats(page) } if {[permutation] == ""} { do_test cache-1.1 { pager_cache_size db } {0} } do_test cache-1.2 { execsql { PRAGMA auto_vacuum=OFF; CREATE TABLE abc(a, b, c); INSERT INTO abc VALUES(1, 2, 3); } |
︙ | ︙ | |||
54 55 56 57 58 59 60 | do_test cache-1.3.$ii { execsql {SELECT * FROM abc} pager_cache_size db } $::cache_size } | | | 54 55 56 57 58 59 60 61 62 63 | do_test cache-1.3.$ii { execsql {SELECT * FROM abc} pager_cache_size db } $::cache_size } sqlite3_soft_heap_limit $cmdlinearg(soft-heap-limit) finish_test |
Changes to test/capi2.test.
︙ | ︙ | |||
67 68 69 70 71 72 73 | } {name rowid text INTEGER} do_test capi2-1.6 { sqlite3_step $VM } {SQLITE_DONE} do_test capi2-1.7 { list [sqlite3_column_count $VM] [get_row_values $VM] [get_column_names $VM] } {2 {} {name rowid text INTEGER}} | > > > | | > | 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 | } {name rowid text INTEGER} do_test capi2-1.6 { sqlite3_step $VM } {SQLITE_DONE} do_test capi2-1.7 { list [sqlite3_column_count $VM] [get_row_values $VM] [get_column_names $VM] } {2 {} {name rowid text INTEGER}} # This used to be SQLITE_MISUSE. But now we automatically reset prepared # statements. do_test capi2-1.8 { sqlite3_step $VM } {SQLITE_ROW} # Update: In v2, once SQLITE_MISUSE is returned the statement handle cannot # be interrogated for more information. However in v3, since the column # count, names and types are determined at compile time, these are still # accessible after an SQLITE_MISUSE error. do_test capi2-1.9 { sqlite3_reset $VM list [sqlite3_column_count $VM] [get_row_values $VM] [get_column_names $VM] } {2 {} {name rowid text INTEGER}} do_test capi2-1.10 { sqlite3_data_count $VM } {0} do_test capi2-1.11 { |
︙ | ︙ |
Changes to test/capi3.test.
︙ | ︙ | |||
12 13 14 15 16 17 18 19 20 21 22 23 24 25 | # focus of this script testing the callback-free C/C++ API. # # $Id: capi3.test,v 1.70 2009/01/09 02:49:32 drh Exp $ # set testdir [file dirname $argv0] source $testdir/tester.tcl # Return the UTF-16 representation of the supplied UTF-8 string $str. # If $nt is true, append two 0x00 bytes as a nul terminator. proc utf16 {str {nt 1}} { set r [encoding convertto unicode $str] if {$nt} { append r "\x00\x00" | > > > > > | 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | # focus of this script testing the callback-free C/C++ API. # # $Id: capi3.test,v 1.70 2009/01/09 02:49:32 drh Exp $ # set testdir [file dirname $argv0] source $testdir/tester.tcl # Do not use a codec for tests in this file, as the database file is # manipulated directly using tcl scripts (using the [hexio_write] command). # do_not_use_codec # Return the UTF-16 representation of the supplied UTF-8 string $str. # If $nt is true, append two 0x00 bytes as a nul terminator. proc utf16 {str {nt 1}} { set r [encoding convertto unicode $str] if {$nt} { append r "\x00\x00" |
︙ | ︙ | |||
631 632 633 634 635 636 637 | set ::ENC [execsql {pragma encoding}] db close do_test capi3-6.0 { sqlite3 db test.db set DB [sqlite3_connection_pointer db] | | | 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 | set ::ENC [execsql {pragma encoding}] db close do_test capi3-6.0 { sqlite3 db test.db set DB [sqlite3_connection_pointer db] if {[sqlite3 -has-codec]==0} { sqlite3_key $DB xyzzy } set sql {SELECT a FROM t1 order by rowid} set STMT [sqlite3_prepare $DB $sql -1 TAIL] expr 0 } {0} do_test capi3-6.1 { db cache flush sqlite3_close $DB |
︙ | ︙ | |||
676 677 678 679 680 681 682 | if {![sqlite3 -has-codec]} { # Test what happens when the library encounters a newer file format. do_test capi3-7.1 { set_file_format 5 } {} do_test capi3-7.2 { | | | 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 | if {![sqlite3 -has-codec]} { # Test what happens when the library encounters a newer file format. do_test capi3-7.1 { set_file_format 5 } {} do_test capi3-7.2 { catch { sqlite3 db test.db } catchsql { SELECT * FROM sqlite_master; } } {1 {unsupported file format}} db close } |
︙ | ︙ | |||
704 705 706 707 708 709 710 | execsql { PRAGMA writable_schema=ON; INSERT INTO sqlite_master VALUES(NULL,NULL,NULL,NULL,NULL); } db close } {} do_test capi3-8.3 { | | | | 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 | execsql { PRAGMA writable_schema=ON; INSERT INTO sqlite_master VALUES(NULL,NULL,NULL,NULL,NULL); } db close } {} do_test capi3-8.3 { catch { sqlite3 db test.db } catchsql { SELECT * FROM sqlite_master; } } {1 {malformed database schema (?)}} do_test capi3-8.4 { # Build a 5-field row record. The first field is a string 'table', and # subsequent fields are all NULL. db close file delete -force test.db test.db-journal sqlite3 db test.db execsql { CREATE TABLE t1(a); PRAGMA writable_schema=ON; INSERT INTO sqlite_master VALUES('table',NULL,NULL,NULL,NULL); } db close } {}; do_test capi3-8.5 { catch { sqlite3 db test.db } catchsql { SELECT * FROM sqlite_master; } } {1 {malformed database schema (?)}} db close } file delete -force test.db |
︙ | ︙ |
Changes to test/capi3b.test.
︙ | ︙ | |||
137 138 139 140 141 142 143 | sqlite3_finalize $VM1 sqlite3_finalize $VM2 execsql {SELECT * FROM t1} } {1 2 3 4} catch {db2 close} | | | 137 138 139 140 141 142 143 144 145 | sqlite3_finalize $VM1 sqlite3_finalize $VM2 execsql {SELECT * FROM t1} } {1 2 3 4} catch {db2 close} sqlite3_soft_heap_limit $cmdlinearg(soft-heap-limit) finish_test |
Changes to test/capi3c.test.
︙ | ︙ | |||
14 15 16 17 18 19 20 21 22 23 24 25 26 27 | # test the new sqlite3_prepare_v2 interface. # # $Id: capi3c.test,v 1.23 2009/07/22 07:27:57 danielk1977 Exp $ # set testdir [file dirname $argv0] source $testdir/tester.tcl # Return the UTF-16 representation of the supplied UTF-8 string $str. # If $nt is true, append two 0x00 bytes as a nul terminator. proc utf16 {str {nt 1}} { set r [encoding convertto unicode $str] if {$nt} { append r "\x00\x00" | > > > > > | 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 | # test the new sqlite3_prepare_v2 interface. # # $Id: capi3c.test,v 1.23 2009/07/22 07:27:57 danielk1977 Exp $ # set testdir [file dirname $argv0] source $testdir/tester.tcl # Do not use a codec for tests in this file, as the database file is # manipulated directly using tcl scripts (using the [hexio_write] command). # do_not_use_codec # Return the UTF-16 representation of the supplied UTF-8 string $str. # If $nt is true, append two 0x00 bytes as a nul terminator. proc utf16 {str {nt 1}} { set r [encoding convertto unicode $str] if {$nt} { append r "\x00\x00" |
︙ | ︙ | |||
602 603 604 605 606 607 608 | set ::ENC [execsql {pragma encoding}] db close do_test capi3c-6.0 { sqlite3 db test.db set DB [sqlite3_connection_pointer db] | | | 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 | set ::ENC [execsql {pragma encoding}] db close do_test capi3c-6.0 { sqlite3 db test.db set DB [sqlite3_connection_pointer db] if {[sqlite3 -has-codec]==0} { sqlite3_key $DB xyzzy } set sql {SELECT a FROM t1 order by rowid} set STMT [sqlite3_prepare_v2 $DB $sql -1 TAIL] expr 0 } {0} do_test capi3c-6.1 { db cache flush sqlite3_close $DB |
︙ | ︙ | |||
649 650 651 652 653 654 655 | if {![sqlite3 -has-codec]} { # Test what happens when the library encounters a newer file format. do_test capi3c-7.1 { set_file_format 5 } {} do_test capi3c-7.2 { | | | 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 | if {![sqlite3 -has-codec]} { # Test what happens when the library encounters a newer file format. do_test capi3c-7.1 { set_file_format 5 } {} do_test capi3c-7.2 { catch { sqlite3 db test.db } catchsql { SELECT * FROM sqlite_master; } } {1 {unsupported file format}} db close } |
︙ | ︙ | |||
677 678 679 680 681 682 683 | execsql { PRAGMA writable_schema=ON; INSERT INTO sqlite_master VALUES(NULL,NULL,NULL,NULL,NULL); } db close } {} do_test capi3c-8.3 { | | | | 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 | execsql { PRAGMA writable_schema=ON; INSERT INTO sqlite_master VALUES(NULL,NULL,NULL,NULL,NULL); } db close } {} do_test capi3c-8.3 { catch { sqlite3 db test.db } catchsql { SELECT * FROM sqlite_master; } } {1 {malformed database schema (?)}} do_test capi3c-8.4 { # Build a 5-field row record. The first field is a string 'table', and # subsequent fields are all NULL. db close file delete -force test.db test.db-journal sqlite3 db test.db execsql { CREATE TABLE t1(a); PRAGMA writable_schema=ON; INSERT INTO sqlite_master VALUES('table',NULL,NULL,NULL,NULL); } db close } {}; do_test capi3c-8.5 { catch { sqlite3 db test.db } catchsql { SELECT * FROM sqlite_master; } } {1 {malformed database schema (?)}} db close } file delete -force test.db |
︙ | ︙ |
Changes to test/collate4.test.
︙ | ︙ | |||
317 318 319 320 321 322 323 324 325 326 327 328 329 330 | # defined collation sequences are involved. # # Indices may optimise WHERE clauses using <, >, <=, >=, = or IN # operators. # do_test collate4-2.1.0 { execsql { CREATE TABLE collate4t1(a COLLATE NOCASE); CREATE TABLE collate4t2(b COLLATE TEXT); INSERT INTO collate4t1 VALUES('a'); INSERT INTO collate4t1 VALUES('A'); INSERT INTO collate4t1 VALUES('b'); INSERT INTO collate4t1 VALUES('B'); | > | 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 | # defined collation sequences are involved. # # Indices may optimise WHERE clauses using <, >, <=, >=, = or IN # operators. # do_test collate4-2.1.0 { execsql { PRAGMA automatic_index=OFF; CREATE TABLE collate4t1(a COLLATE NOCASE); CREATE TABLE collate4t2(b COLLATE TEXT); INSERT INTO collate4t1 VALUES('a'); INSERT INTO collate4t1 VALUES('A'); INSERT INTO collate4t1 VALUES('b'); INSERT INTO collate4t1 VALUES('B'); |
︙ | ︙ | |||
422 423 424 425 426 427 428 | INSERT INTO collate4t1 VALUES('1', '1', '0'); INSERT INTO collate4t1 VALUES('1', '1', '1'); insert into collate4t2 SELECT * FROM collate4t1; } } {} do_test collate4-2.2.1 { count { | | | 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 | INSERT INTO collate4t1 VALUES('1', '1', '0'); INSERT INTO collate4t1 VALUES('1', '1', '1'); insert into collate4t2 SELECT * FROM collate4t1; } } {} do_test collate4-2.2.1 { count { SELECT * FROM collate4t2 NOT INDEXED NATURAL JOIN collate4t1 NOT INDEXED; } } {0 0 0 0 0 1 0 1 0 0 1 1 1 0 0 1 0 1 1 1 0 1 1 1 63} do_test collate4-2.2.1b { execsql { CREATE INDEX collate4i1 ON collate4t1(a, b, c); } count { |
︙ | ︙ |
Changes to test/corrupt.test.
︙ | ︙ | |||
15 16 17 18 19 20 21 22 23 24 25 26 27 28 | # # $Id: corrupt.test,v 1.12 2009/07/13 09:41:45 danielk1977 Exp $ catch {file delete -force test.db test.db-journal test.bu} set testdir [file dirname $argv0] source $testdir/tester.tcl # Construct a large database for testing. # do_test corrupt-1.1 { execsql { BEGIN; CREATE TABLE t1(x); | > > > > > | 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 | # # $Id: corrupt.test,v 1.12 2009/07/13 09:41:45 danielk1977 Exp $ catch {file delete -force test.db test.db-journal test.bu} set testdir [file dirname $argv0] source $testdir/tester.tcl # Do not use a codec for tests in this file, as the database file is # manipulated directly using tcl scripts (using the [hexio_write] command). # do_not_use_codec # Construct a large database for testing. # do_test corrupt-1.1 { execsql { BEGIN; CREATE TABLE t1(x); |
︙ | ︙ |
Changes to test/corrupt2.test.
︙ | ︙ | |||
13 14 15 16 17 18 19 20 21 22 23 24 25 26 | # This file implements tests to make sure SQLite does not crash or # segfault if it sees a corrupt database file. # # $Id: corrupt2.test,v 1.20 2009/04/06 17:50:03 danielk1977 Exp $ set testdir [file dirname $argv0] source $testdir/tester.tcl # The following tests - corrupt2-1.* - create some databases corrupted in # specific ways and ensure that SQLite detects them as corrupt. # do_test corrupt2-1.1 { execsql { PRAGMA auto_vacuum=0; | > > > > > > > > > | 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 | # This file implements tests to make sure SQLite does not crash or # segfault if it sees a corrupt database file. # # $Id: corrupt2.test,v 1.20 2009/04/06 17:50:03 danielk1977 Exp $ set testdir [file dirname $argv0] source $testdir/tester.tcl # Do not use a codec for tests in this file, as the database file is # manipulated directly using tcl scripts (using the [hexio_write] command). # do_not_use_codec set presql "" catch { set presql "$::G(perm:presql);" } unset -nocomplain ::G(perm:presql) # The following tests - corrupt2-1.* - create some databases corrupted in # specific ways and ensure that SQLite detects them as corrupt. # do_test corrupt2-1.1 { execsql { PRAGMA auto_vacuum=0; |
︙ | ︙ | |||
37 38 39 40 41 42 43 | copy_file test.db corrupt.db set f [open corrupt.db RDWR] seek $f 8 start puts $f blah close $f sqlite3 db2 corrupt.db | | > | | > | | > | | > | | > | | > | | > | | > | | > | > | 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 | copy_file test.db corrupt.db set f [open corrupt.db RDWR] seek $f 8 start puts $f blah close $f sqlite3 db2 corrupt.db catchsql " $::presql SELECT * FROM sqlite_master; " db2 } {1 {file is encrypted or is not a database}} do_test corrupt2-1.3 { db2 close # Corrupt the page-size (bytes 16 and 17 of page 1). file delete -force corrupt.db file delete -force corrupt.db-journal copy_file test.db corrupt.db set f [open corrupt.db RDWR] fconfigure $f -encoding binary seek $f 16 start puts -nonewline $f "\x00\xFF" close $f sqlite3 db2 corrupt.db catchsql " $::presql SELECT * FROM sqlite_master; " db2 } {1 {file is encrypted or is not a database}} do_test corrupt2-1.4 { db2 close # Corrupt the free-block list on page 1. file delete -force corrupt.db file delete -force corrupt.db-journal copy_file test.db corrupt.db set f [open corrupt.db RDWR] fconfigure $f -encoding binary seek $f 101 start puts -nonewline $f "\xFF\xFF" close $f sqlite3 db2 corrupt.db catchsql " $::presql SELECT * FROM sqlite_master; " db2 } {1 {database disk image is malformed}} do_test corrupt2-1.5 { db2 close # Corrupt the free-block list on page 1. file delete -force corrupt.db file delete -force corrupt.db-journal copy_file test.db corrupt.db set f [open corrupt.db RDWR] fconfigure $f -encoding binary seek $f 101 start puts -nonewline $f "\x00\xC8" seek $f 200 start puts -nonewline $f "\x00\x00" puts -nonewline $f "\x10\x00" close $f sqlite3 db2 corrupt.db catchsql " $::presql SELECT * FROM sqlite_master; " db2 } {1 {database disk image is malformed}} db2 close # Corrupt a database by having 2 indices of the same name: do_test corrupt2-2.1 { file delete -force corrupt.db file delete -force corrupt.db-journal copy_file test.db corrupt.db sqlite3 db2 corrupt.db execsql " $::presql CREATE INDEX a1 ON abc(a); CREATE INDEX a2 ON abc(b); PRAGMA writable_schema = 1; UPDATE sqlite_master SET name = 'a3', sql = 'CREATE INDEX a3' || substr(sql, 16, 10000) WHERE type = 'index'; PRAGMA writable_schema = 0; " db2 db2 close sqlite3 db2 corrupt.db catchsql " $::presql SELECT * FROM sqlite_master; " db2 } {1 {malformed database schema (a3) - index a3 already exists}} db2 close do_test corrupt2-3.1 { file delete -force corrupt.db file delete -force corrupt.db-journal sqlite3 db2 corrupt.db execsql " $::presql PRAGMA auto_vacuum = 1; PRAGMA page_size = 1024; CREATE TABLE t1(a, b, c); CREATE TABLE t2(a, b, c); INSERT INTO t2 VALUES(randomblob(100), randomblob(100), randomblob(100)); INSERT INTO t2 SELECT * FROM t2; INSERT INTO t2 SELECT * FROM t2; INSERT INTO t2 SELECT * FROM t2; INSERT INTO t2 SELECT * FROM t2; " db2 db2 close # On the root page of table t2 (page 4), set one of the child page-numbers # to 0. This corruption will be detected when SQLite attempts to update # the pointer-map after moving the content of page 4 to page 3 as part # of the DROP TABLE operation below. # set fd [open corrupt.db r+] fconfigure $fd -encoding binary -translation binary seek $fd [expr 1024*3 + 12] set zCelloffset [read $fd 2] binary scan $zCelloffset S iCelloffset seek $fd [expr 1024*3 + $iCelloffset] puts -nonewline $fd "\00\00\00\00" close $fd sqlite3 db2 corrupt.db catchsql " $::presql DROP TABLE t1; " db2 } {1 {database disk image is malformed}} do_test corrupt2-4.1 { catchsql { SELECT * FROM t2; } db2 } {1 {database disk image is malformed}} db2 close unset -nocomplain result do_test corrupt2-5.1 { file delete -force corrupt.db file delete -force corrupt.db-journal sqlite3 db2 corrupt.db execsql " $::presql PRAGMA auto_vacuum = 0; PRAGMA page_size = 1024; CREATE TABLE t1(a, b, c); CREATE TABLE t2(a, b, c); INSERT INTO t2 VALUES(randomblob(100), randomblob(100), randomblob(100)); INSERT INTO t2 SELECT * FROM t2; INSERT INTO t2 SELECT * FROM t2; INSERT INTO t2 SELECT * FROM t2; INSERT INTO t2 SELECT * FROM t2; INSERT INTO t1 SELECT * FROM t2; " db2 db2 close # This block links a page from table t2 into the t1 table structure. # set fd [open corrupt.db r+] fconfigure $fd -encoding binary -translation binary seek $fd [expr 1024 + 12] set zCelloffset [read $fd 2] binary scan $zCelloffset S iCelloffset seek $fd [expr 1024 + $iCelloffset] set zChildPage [read $fd 4] seek $fd [expr 2*1024 + 12] set zCelloffset [read $fd 2] binary scan $zCelloffset S iCelloffset seek $fd [expr 2*1024 + $iCelloffset] puts -nonewline $fd $zChildPage close $fd sqlite3 db2 corrupt.db db2 eval $::presql db2 eval {SELECT rowid FROM t1} { set result [db2 eval {pragma integrity_check}] break } set result } {{*** in database main *** On tree page 2 cell 0: 2nd reference to page 10 |
︙ | ︙ | |||
238 239 240 241 242 243 244 245 246 247 248 249 250 251 | array set A $args catch {db close} file delete -force corrupt.db file delete -force corrupt.db-journal sqlite3 db corrupt.db eval $A(-tclprep) db eval $A(-sqlprep) db close eval $A(-corrupt) sqlite3 db corrupt.db | > | 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 | array set A $args catch {db close} file delete -force corrupt.db file delete -force corrupt.db-journal sqlite3 db corrupt.db db eval $::presql eval $A(-tclprep) db eval $A(-sqlprep) db close eval $A(-corrupt) sqlite3 db corrupt.db |
︙ | ︙ | |||
272 273 274 275 276 277 278 | INSERT INTO t1 VALUES(1, randomblob(2500)); INSERT INTO t1 VALUES(2, randomblob(2500)); DELETE FROM t1 WHERE a = 1; } -corrupt { hexio_write corrupt.db [expr 1024*5] 00000008 } -test { do_test corrupt2-6.1 { | | | | > | > < > | 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 | INSERT INTO t1 VALUES(1, randomblob(2500)); INSERT INTO t1 VALUES(2, randomblob(2500)); DELETE FROM t1 WHERE a = 1; } -corrupt { hexio_write corrupt.db [expr 1024*5] 00000008 } -test { do_test corrupt2-6.1 { catchsql " $::presql pragma incremental_vacuum = 1 " } {1 {database disk image is malformed}} } # The last page in the database page is a non-root b-tree page. # corruption_test -sqlprep { PRAGMA auto_vacuum = incremental; PRAGMA page_size = 1024; CREATE TABLE t1(a INTEGER PRIMARY KEY, b); INSERT INTO t1 VALUES(1, randomblob(2500)); INSERT INTO t1 VALUES(2, randomblob(50)); INSERT INTO t1 SELECT NULL, randomblob(50) FROM t1; INSERT INTO t1 SELECT NULL, randomblob(50) FROM t1; INSERT INTO t1 SELECT NULL, randomblob(50) FROM t1; INSERT INTO t1 SELECT NULL, randomblob(50) FROM t1; DELETE FROM t1 WHERE a = 1; } -corrupt { hexio_write corrupt.db [expr 1024*2 + 8] 00000009 } -test { do_test corrupt2-6.2 { catchsql " $::presql pragma incremental_vacuum = 1 " } {1 {database disk image is malformed}} } # Set up a pointer-map entry so that the last page of the database # file appears to be a b-tree root page. This should be detected # as corruption. # corruption_test -sqlprep { PRAGMA auto_vacuum = incremental; PRAGMA page_size = 1024; CREATE TABLE t1(a INTEGER PRIMARY KEY, b); INSERT INTO t1 VALUES(1, randomblob(2500)); INSERT INTO t1 VALUES(2, randomblob(2500)); INSERT INTO t1 VALUES(3, randomblob(2500)); DELETE FROM t1 WHERE a = 1; } -corrupt { set nPage [expr [file size corrupt.db] / 1024] hexio_write corrupt.db [expr 1024 + ($nPage-3)*5] 010000000 } -test { do_test corrupt2-6.3 { catchsql " $::presql pragma incremental_vacuum = 1 " } {1 {database disk image is malformed}} } corruption_test -sqlprep { PRAGMA auto_vacuum = 1; PRAGMA page_size = 1024; CREATE TABLE t1(a INTEGER PRIMARY KEY, b); INSERT INTO t1 VALUES(1, randomblob(2500)); DELETE FROM t1 WHERE a = 1; } -corrupt { set nAppend [expr 1024*207 - [file size corrupt.db]] set fd [open corrupt.db r+] seek $fd 0 end puts -nonewline $fd [string repeat x $nAppend] close $fd hexio_write corrupt.db 28 00000000 } -test { do_test corrupt2-6.4 { catchsql " $::presql BEGIN EXCLUSIVE; COMMIT; " } {1 {database disk image is malformed}} } } set sqlprep { PRAGMA auto_vacuum = 0; |
︙ | ︙ | |||
369 370 371 372 373 374 375 | set zRightChild [read $fd 4] binary scan $zRightChild I iRightChild seek $fd [expr 1024*($iRightChild-1)] puts -nonewline $fd "\x0D" close $fd } -test { do_test corrupt2-7.1 { | | | | | 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 | set zRightChild [read $fd 4] binary scan $zRightChild I iRightChild seek $fd [expr 1024*($iRightChild-1)] puts -nonewline $fd "\x0D" close $fd } -test { do_test corrupt2-7.1 { catchsql " $::presql SELECT b FROM t1 ORDER BY b ASC " } {1 {database disk image is malformed}} } corruption_test -sqlprep $sqlprep -corrupt { # Mess up the page-header of one of the leaf pages of the index B-Tree. # The corruption is detected as part of an OP_Prev opcode. # set fd [open corrupt.db r+] fconfigure $fd -translation binary -encoding binary seek $fd [expr 1024*2 + 12] set zCellOffset [read $fd 2] binary scan $zCellOffset S iCellOffset seek $fd [expr 1024*2 + $iCellOffset] set zChild [read $fd 4] binary scan $zChild I iChild seek $fd [expr 1024*($iChild-1)+3] puts -nonewline $fd "\xFFFF" close $fd } -test { do_test corrupt2-7.1 { catchsql " $::presql SELECT b FROM t1 ORDER BY b DESC " } {1 {database disk image is malformed}} } corruption_test -sqlprep $sqlprep -corrupt { # Set the page-flags of one of the leaf pages of the table B-Tree to # 0x0A (interpreted by SQLite as "leaf page of an index B-Tree"). # set fd [open corrupt.db r+] fconfigure $fd -translation binary -encoding binary seek $fd [expr 1024*1 + 8] set zRightChild [read $fd 4] binary scan $zRightChild I iRightChild seek $fd [expr 1024*($iRightChild-1)] puts -nonewline $fd "\x0A" close $fd } -test { do_test corrupt2-8.1 { catchsql " $::presql SELECT * FROM t1 WHERE rowid=1000 " } {1 {database disk image is malformed}} } corruption_test -sqlprep { CREATE TABLE t1(a, b, c); CREATE TABLE t8(a, b, c); CREATE TABLE tE(a, b, c); CREATE TABLE t2(a, b, c); CREATE TABLE t9(a, b, c); CREATE TABLE tF(a, b, c); CREATE TABLE t3(a, b, c); CREATE TABLE tA(a, b, c); CREATE TABLE tG(a, b, c); |
︙ | ︙ | |||
436 437 438 439 440 441 442 | set zRightChild [read $fd 4] binary scan $zRightChild I iRightChild seek $fd [expr 1024*($iRightChild-1)+3] puts -nonewline $fd "\x00\x00" close $fd } -test { do_test corrupt2-9.1 { | | | | 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 | set zRightChild [read $fd 4] binary scan $zRightChild I iRightChild seek $fd [expr 1024*($iRightChild-1)+3] puts -nonewline $fd "\x00\x00" close $fd } -test { do_test corrupt2-9.1 { catchsql " $::presql SELECT sql FROM sqlite_master " } {1 {database disk image is malformed}} } corruption_test -sqlprep { CREATE TABLE t1(a, b, c); CREATE TABLE t2(a, b, c); PRAGMA writable_schema = 1; UPDATE sqlite_master SET rootpage = NULL WHERE name = 't2'; } -test { do_test corrupt2-10.1 { catchsql " $::presql SELECT * FROM t2 " } {1 {malformed database schema (t2)}} do_test corrupt2-10.2 { sqlite3_errcode db } {SQLITE_CORRUPT} } corruption_test -sqlprep { |
︙ | ︙ | |||
472 473 474 475 476 477 478 | DELETE FROM t1; } -corrupt { set offset [expr [file size corrupt.db] - 1024] hexio_write corrupt.db $offset FF hexio_write corrupt.db 24 12345678 } -test { do_test corrupt2-11.1 { | | | | > | 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 | DELETE FROM t1; } -corrupt { set offset [expr [file size corrupt.db] - 1024] hexio_write corrupt.db $offset FF hexio_write corrupt.db 24 12345678 } -test { do_test corrupt2-11.1 { catchsql " $::presql PRAGMA incremental_vacuum " } {1 {database disk image is malformed}} } corruption_test -sqlprep { PRAGMA auto_vacuum = incremental; CREATE TABLE t1(a INTEGER PRIMARY KEY, b); CREATE TABLE t2(a INTEGER PRIMARY KEY, b); INSERT INTO t1 VALUES(1, randstr(100,100)); INSERT INTO t1 SELECT NULL, randstr(100,100) FROM t1; INSERT INTO t1 SELECT NULL, randstr(100,100) FROM t1; INSERT INTO t1 SELECT NULL, randstr(100,100) FROM t1; INSERT INTO t1 SELECT NULL, randstr(100,100) FROM t1; INSERT INTO t1 SELECT NULL, randstr(100,100) FROM t1; INSERT INTO t2 SELECT * FROM t1; DELETE FROM t1; } -corrupt { set pgno [expr [file size corrupt.db] / 1024] hexio_write corrupt.db [expr 1024+5*($pgno-3)] 03 hexio_write corrupt.db 24 12345678 } -test { do_test corrupt2-12.1 { catchsql " $::presql PRAGMA incremental_vacuum " } {1 {database disk image is malformed}} } ifcapable autovacuum { # It is not possible for the last page in a database file to be the # pending-byte page (AKA the locking page). This test verifies that if # an attempt is made to commit a transaction to such an auto-vacuum # database SQLITE_CORRUPT is returned. # corruption_test -tclprep { db eval { PRAGMA auto_vacuum = full; PRAGMA page_size = 1024; CREATE TABLE t1(a INTEGER PRIMARY KEY, b); INSERT INTO t1 VALUES(NULL, randstr(50,50)); } for {set ii 0} {$ii < 10} {incr ii} { db eval " $::presql INSERT INTO t1 SELECT NULL, randstr(50,50) FROM t1 " } } -corrupt { do_test corrupt2-13.1 { file size corrupt.db } $::sqlite_pending_byte hexio_write corrupt.db [expr $::sqlite_pending_byte+1023] 00 hexio_write corrupt.db 28 00000000 } -test { do_test corrupt2-13.2 { file size corrupt.db } [expr $::sqlite_pending_byte + 1024] do_test corrupt2-13.3 { catchsql { DELETE FROM t1 WHERE rowid < 30; } } {1 {database disk image is malformed}} } } finish_test |
Changes to test/corrupt3.test.
︙ | ︙ | |||
13 14 15 16 17 18 19 20 21 22 23 24 25 26 | # This file implements tests to make sure SQLite does not crash or # segfault if it sees a corrupt database file. # # $Id: corrupt3.test,v 1.2 2007/04/06 21:42:22 drh Exp $ set testdir [file dirname $argv0] source $testdir/tester.tcl # We must have the page_size pragma for these tests to work. # ifcapable !pager_pragmas { finish_test return } | > > | 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | # This file implements tests to make sure SQLite does not crash or # segfault if it sees a corrupt database file. # # $Id: corrupt3.test,v 1.2 2007/04/06 21:42:22 drh Exp $ set testdir [file dirname $argv0] source $testdir/tester.tcl if {[sqlite3 -has-codec]} { finish_test ; return } # We must have the page_size pragma for these tests to work. # ifcapable !pager_pragmas { finish_test return } |
︙ | ︙ |
Changes to test/corrupt4.test.
︙ | ︙ | |||
13 14 15 16 17 18 19 20 21 22 23 24 25 26 | # This file implements tests to make sure SQLite does not crash or # segfault if it sees a corrupt database file. # # $Id: corrupt4.test,v 1.1 2007/09/07 14:32:07 drh Exp $ set testdir [file dirname $argv0] source $testdir/tester.tcl # We must have the page_size pragma for these tests to work. # ifcapable !pager_pragmas { finish_test return } | > > > > > | 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 | # This file implements tests to make sure SQLite does not crash or # segfault if it sees a corrupt database file. # # $Id: corrupt4.test,v 1.1 2007/09/07 14:32:07 drh Exp $ set testdir [file dirname $argv0] source $testdir/tester.tcl # Do not use a codec for tests in this file, as the database file is # manipulated directly using tcl scripts (using the [hexio_write] command). # do_not_use_codec # We must have the page_size pragma for these tests to work. # ifcapable !pager_pragmas { finish_test return } |
︙ | ︙ |
Changes to test/corrupt6.test.
︙ | ︙ | |||
14 15 16 17 18 19 20 21 22 23 24 25 26 27 | # segfault if it sees a corrupt database file. It specifically focuses # on corrupt SerialTypeLen values. # # $Id: corrupt6.test,v 1.2 2008/05/19 15:37:10 shane Exp $ set testdir [file dirname $argv0] source $testdir/tester.tcl # We must have the page_size pragma for these tests to work. # ifcapable !pager_pragmas { finish_test return } | > > > > > | 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 | # segfault if it sees a corrupt database file. It specifically focuses # on corrupt SerialTypeLen values. # # $Id: corrupt6.test,v 1.2 2008/05/19 15:37:10 shane Exp $ set testdir [file dirname $argv0] source $testdir/tester.tcl # Do not use a codec for tests in this file, as the database file is # manipulated directly using tcl scripts (using the [hexio_write] command). # do_not_use_codec # We must have the page_size pragma for these tests to work. # ifcapable !pager_pragmas { finish_test return } |
︙ | ︙ |
Changes to test/corrupt7.test.
︙ | ︙ | |||
14 15 16 17 18 19 20 21 22 23 24 25 26 27 | # segfault if it sees a corrupt database file. It specifically focuses # on corrupt cell offsets in a btree page. # # $Id: corrupt7.test,v 1.8 2009/08/10 10:18:08 danielk1977 Exp $ set testdir [file dirname $argv0] source $testdir/tester.tcl # We must have the page_size pragma for these tests to work. # ifcapable !pager_pragmas { finish_test return } | > > > > > | 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 | # segfault if it sees a corrupt database file. It specifically focuses # on corrupt cell offsets in a btree page. # # $Id: corrupt7.test,v 1.8 2009/08/10 10:18:08 danielk1977 Exp $ set testdir [file dirname $argv0] source $testdir/tester.tcl # Do not use a codec for tests in this file, as the database file is # manipulated directly using tcl scripts (using the [hexio_write] command). # do_not_use_codec # We must have the page_size pragma for these tests to work. # ifcapable !pager_pragmas { finish_test return } |
︙ | ︙ |
Changes to test/corrupt8.test.
︙ | ︙ | |||
14 15 16 17 18 19 20 21 22 23 24 25 26 27 | # segfault if it sees a corrupt database file. It specifically focuses # on corrupt pointer map pages. # # $Id: corrupt8.test,v 1.2 2008/07/11 03:34:10 drh Exp $ set testdir [file dirname $argv0] source $testdir/tester.tcl # We must have the page_size pragma for these tests to work. # ifcapable !pager_pragmas||!autovacuum { finish_test return } | > > > > > | 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 | # segfault if it sees a corrupt database file. It specifically focuses # on corrupt pointer map pages. # # $Id: corrupt8.test,v 1.2 2008/07/11 03:34:10 drh Exp $ set testdir [file dirname $argv0] source $testdir/tester.tcl # Do not use a codec for tests in this file, as the database file is # manipulated directly using tcl scripts (using the [hexio_write] command). # do_not_use_codec # We must have the page_size pragma for these tests to work. # ifcapable !pager_pragmas||!autovacuum { finish_test return } |
︙ | ︙ |
Changes to test/corrupt9.test.
︙ | ︙ | |||
14 15 16 17 18 19 20 21 22 23 24 25 26 27 | # segfault if it sees a corrupt database file. It specifically focuses # on corruption in the form of duplicate entries on the freelist. # # $Id: corrupt9.test,v 1.3 2009/06/04 02:47:04 shane Exp $ set testdir [file dirname $argv0] source $testdir/tester.tcl # We must have the page_size pragma for these tests to work. # ifcapable !pager_pragmas { finish_test return } | > > > > > | 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 | # segfault if it sees a corrupt database file. It specifically focuses # on corruption in the form of duplicate entries on the freelist. # # $Id: corrupt9.test,v 1.3 2009/06/04 02:47:04 shane Exp $ set testdir [file dirname $argv0] source $testdir/tester.tcl # Do not use a codec for tests in this file, as the database file is # manipulated directly using tcl scripts (using the [hexio_write] command). # do_not_use_codec # We must have the page_size pragma for these tests to work. # ifcapable !pager_pragmas { finish_test return } |
︙ | ︙ |
Changes to test/corruptA.test.
︙ | ︙ | |||
33 34 35 36 37 38 39 40 41 | # Corrupt the file header in various ways and make sure the corruption # is detected when opening the database file. # db close file copy -force test.db test.db-template do_test corruptA-2.1 { file copy -force test.db-template test.db | > > | | 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 | # Corrupt the file header in various ways and make sure the corruption # is detected when opening the database file. # db close file copy -force test.db test.db-template set unreadable_version 02 ifcapable wal { set unreadable_version 03 } do_test corruptA-2.1 { file copy -force test.db-template test.db hexio_write test.db 19 $unreadable_version ;# the read format number sqlite3 db test.db catchsql {SELECT * FROM t1} } {1 {file is encrypted or is not a database}} do_test corruptA-2.2 { db close file copy -force test.db-template test.db |
︙ | ︙ |
Changes to test/corruptB.test.
︙ | ︙ | |||
21 22 23 24 25 26 27 28 29 30 31 32 33 34 | # maximum page number. # # $Id: corruptB.test,v 1.4 2009/07/21 19:25:24 danielk1977 Exp $ set testdir [file dirname $argv0] source $testdir/tester.tcl do_test corruptB-1.1 { execsql { PRAGMA auto_vacuum = 1; CREATE TABLE t1(x); INSERT INTO t1 VALUES(randomblob(200)); INSERT INTO t1 SELECT randomblob(200) FROM t1; | > > > > > | 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 | # maximum page number. # # $Id: corruptB.test,v 1.4 2009/07/21 19:25:24 danielk1977 Exp $ set testdir [file dirname $argv0] source $testdir/tester.tcl # Do not use a codec for tests in this file, as the database file is # manipulated directly using tcl scripts (using the [hexio_write] command). # do_not_use_codec do_test corruptB-1.1 { execsql { PRAGMA auto_vacuum = 1; CREATE TABLE t1(x); INSERT INTO t1 VALUES(randomblob(200)); INSERT INTO t1 SELECT randomblob(200) FROM t1; |
︙ | ︙ | |||
150 151 152 153 154 155 156 | db close file copy -force bak.db test.db hexio_write test.db [expr $offset+8] [hexio_render_int32 0x6FFFFFFF] } {4} do_test corruptB-2.1.2 { sqlite3 db test.db catchsql { SELECT * FROM t1 } | | | 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 | db close file copy -force bak.db test.db hexio_write test.db [expr $offset+8] [hexio_render_int32 0x6FFFFFFF] } {4} do_test corruptB-2.1.2 { sqlite3 db test.db catchsql { SELECT * FROM t1 } } {1 {database disk image is malformed}} #--------------------------------------------------------------------------- # Corrupt the header-size field of a database record. # do_test corruptB-3.1.1 { db close |
︙ | ︙ |
Changes to test/corruptC.test.
︙ | ︙ | |||
17 18 19 20 21 22 23 24 25 26 27 28 29 30 | # # $Id: corruptC.test,v 1.14 2009/07/11 06:55:34 danielk1977 Exp $ catch {file delete -force test.db test.db-journal test.bu} set testdir [file dirname $argv0] source $testdir/tester.tcl # Construct a compact, dense database for testing. # do_test corruptC-1.1 { execsql { PRAGMA auto_vacuum = 0; PRAGMA legacy_file_format=1; | > > > > > | 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 | # # $Id: corruptC.test,v 1.14 2009/07/11 06:55:34 danielk1977 Exp $ catch {file delete -force test.db test.db-journal test.bu} set testdir [file dirname $argv0] source $testdir/tester.tcl # Do not use a codec for tests in this file, as the database file is # manipulated directly using tcl scripts (using the [hexio_write] command). # do_not_use_codec # Construct a compact, dense database for testing. # do_test corruptC-1.1 { execsql { PRAGMA auto_vacuum = 0; PRAGMA legacy_file_format=1; |
︙ | ︙ | |||
63 64 65 66 67 68 69 | # db close copy_file test.db test.bu sqlite3 db test.db set fsize [file size test.db] # Set a quasi-random random seed. | | | 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 | # db close copy_file test.db test.bu sqlite3 db test.db set fsize [file size test.db] # Set a quasi-random random seed. if {[info exists ::G(issoak)]} { # If we are doing SOAK tests, we want a different # random seed for each run. Ideally we would like # to use [clock clicks] or something like that here. set qseed [file mtime test.db] } else { # If we are not doing soak tests, # make it repeatable. |
︙ | ︙ | |||
271 272 273 274 275 276 277 278 279 280 281 282 283 284 | execsql { INSERT INTO t1 VALUES (1, $blob) } sqlite3 db test.db set filesize [file size test.db] hexio_write test.db [expr $filesize-2048] 00000001 catchsql {DELETE FROM t1 WHERE rowid = (SELECT max(rowid) FROM t1)} } {1 {database disk image is malformed}} # # Now test for a series of quasi-random seeds. # We loop over the entire file size and touch # each byte at least once. for {set tn 0} {$tn<$fsize} {incr tn 1} { | > > > > > > > > > > > | 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 | execsql { INSERT INTO t1 VALUES (1, $blob) } sqlite3 db test.db set filesize [file size test.db] hexio_write test.db [expr $filesize-2048] 00000001 catchsql {DELETE FROM t1 WHERE rowid = (SELECT max(rowid) FROM t1)} } {1 {database disk image is malformed}} # At one point this particular corrupt database was causing a buffer # overread. Which caused a crash in a run of all.test once. # do_test corruptC-2.15 { db close copy_file test.bu test.db hexio_write test.db 986 b9 sqlite3 db test.db catchsql {SELECT count(*) FROM sqlite_master;} } {1 {malformed database schema (t1i1) - no such table: main.t1}} # # Now test for a series of quasi-random seeds. # We loop over the entire file size and touch # each byte at least once. for {set tn 0} {$tn<$fsize} {incr tn 1} { |
︙ | ︙ |
Changes to test/corruptE.test.
︙ | ︙ | |||
16 17 18 19 20 21 22 23 24 25 26 27 28 29 | # # $Id: corruptE.test,v 1.14 2009/07/11 06:55:34 danielk1977 Exp $ catch {file delete -force test.db test.db-journal test.bu} set testdir [file dirname $argv0] source $testdir/tester.tcl # Do not run the tests in this file if ENABLE_OVERSIZE_CELL_CHECK is on. # ifcapable oversize_cell_check { finish_test return } | > > > > > | 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 | # # $Id: corruptE.test,v 1.14 2009/07/11 06:55:34 danielk1977 Exp $ catch {file delete -force test.db test.db-journal test.bu} set testdir [file dirname $argv0] source $testdir/tester.tcl # Do not use a codec for tests in this file, as the database file is # manipulated directly using tcl scripts (using the [hexio_write] command). # do_not_use_codec # Do not run the tests in this file if ENABLE_OVERSIZE_CELL_CHECK is on. # ifcapable oversize_cell_check { finish_test return } |
︙ | ︙ |
Changes to test/crash8.test.
︙ | ︙ | |||
254 255 256 257 258 259 260 261 262 263 264 265 266 267 | INSERT INTO ab VALUES(2, NULL); INSERT INTO ab VALUES(3, NULL); INSERT INTO ab VALUES(4, NULL); INSERT INTO ab VALUES(5, NULL); INSERT INTO ab VALUES(6, NULL); UPDATE ab SET b = randstr(1000,1000); ATTACH 'test2.db' AS aux; CREATE TABLE aux.ab(a, b); INSERT INTO aux.ab SELECT * FROM main.ab; UPDATE aux.ab SET b = randstr(1000,1000) WHERE a>=1; UPDATE ab SET b = randstr(1000,1000) WHERE a>=1; } list [file exists test.db-journal] [file exists test2.db-journal] | > | 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 | INSERT INTO ab VALUES(2, NULL); INSERT INTO ab VALUES(3, NULL); INSERT INTO ab VALUES(4, NULL); INSERT INTO ab VALUES(5, NULL); INSERT INTO ab VALUES(6, NULL); UPDATE ab SET b = randstr(1000,1000); ATTACH 'test2.db' AS aux; PRAGMA aux.journal_mode = persist; CREATE TABLE aux.ab(a, b); INSERT INTO aux.ab SELECT * FROM main.ab; UPDATE aux.ab SET b = randstr(1000,1000) WHERE a>=1; UPDATE ab SET b = randstr(1000,1000) WHERE a>=1; } list [file exists test.db-journal] [file exists test2.db-journal] |
︙ | ︙ | |||
378 379 380 381 382 383 384 | INSERT INTO t1 SELECT randomblob(900) FROM t1; INSERT INTO t1 SELECT randomblob(900) FROM t1; INSERT INTO t1 SELECT randomblob(900) FROM t1; INSERT INTO t1 SELECT randomblob(900) FROM t1; /* 64 rows */ BEGIN; UPDATE t1 SET x = randomblob(900); } | | | 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 | INSERT INTO t1 SELECT randomblob(900) FROM t1; INSERT INTO t1 SELECT randomblob(900) FROM t1; INSERT INTO t1 SELECT randomblob(900) FROM t1; INSERT INTO t1 SELECT randomblob(900) FROM t1; /* 64 rows */ BEGIN; UPDATE t1 SET x = randomblob(900); } file delete -force testX.db testX.db-journal testX.db-wal copy_file test.db testX.db copy_file test.db-journal testX.db-journal db close crashsql -file test.db -delay [expr ($::i%2) + 1] { SELECT * FROM sqlite_master; INSERT INTO t1 VALUES(randomblob(900)); |
︙ | ︙ |
Changes to test/ctime.test.
︙ | ︙ | |||
82 83 84 85 86 87 88 | do_test ctime-1.5 { set ans1 [ catchsql { SELECT sqlite_compileoption_used('THREADSAFE=0'); } ] set ans2 [ catchsql { SELECT sqlite_compileoption_used('THREADSAFE=1'); } ] | > > > | | | 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 | do_test ctime-1.5 { set ans1 [ catchsql { SELECT sqlite_compileoption_used('THREADSAFE=0'); } ] set ans2 [ catchsql { SELECT sqlite_compileoption_used('THREADSAFE=1'); } ] set ans3 [ catchsql { SELECT sqlite_compileoption_used('THREADSAFE=2'); } ] lsort [ list $ans1 $ans2 $ans3 ] } {{0 0} {0 0} {0 1}} do_test ctime-1.6 { execsql { SELECT sqlite_compileoption_used('THREADSAFE='); } } {0} |
︙ | ︙ |
Changes to test/date.test.
︙ | ︙ | |||
11 12 13 14 15 16 17 18 19 20 21 22 23 24 | # This file implements regression tests for SQLite library. The # focus of this file is testing date and time functions. # # $Id: date.test,v 1.34 2009/04/16 12:58:03 drh Exp $ set testdir [file dirname $argv0] source $testdir/tester.tcl # Skip this whole file if date and time functions are omitted # at compile-time # ifcapable {!datetime} { finish_test return | > > > > > | 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | # This file implements regression tests for SQLite library. The # focus of this file is testing date and time functions. # # $Id: date.test,v 1.34 2009/04/16 12:58:03 drh Exp $ set testdir [file dirname $argv0] source $testdir/tester.tcl # Do not use a codec for tests in this file, as the database file is # manipulated directly using tcl scripts (using the [hexio_write] command). # do_not_use_codec # Skip this whole file if date and time functions are omitted # at compile-time # ifcapable {!datetime} { finish_test return |
︙ | ︙ | |||
491 492 493 494 495 496 497 | # Test for issues reported by BareFeet (list.sql at tandb.com.au) # on mailing list on 2008-06-12. # # Put a floating point number in the database so that we can manipulate # raw bits using the hexio interface. # | > | | | | | | | | | | | | | | | | | | | | | | | | | | > | 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 | # Test for issues reported by BareFeet (list.sql at tandb.com.au) # on mailing list on 2008-06-12. # # Put a floating point number in the database so that we can manipulate # raw bits using the hexio interface. # if {0==[sqlite3 -has-codec]} { do_test date-14.1 { execsql { PRAGMA auto_vacuum=OFF; PRAGMA page_size = 1024; CREATE TABLE t1(x); INSERT INTO t1 VALUES(1.1); } db close hexio_write test.db 2040 4142ba32bffffff9 sqlite3 db test.db db eval {SELECT * FROM t1} } {2454629.5} # Changing the least significant byte of the floating point value between # 00 and FF should always generate a time of either 23:59:59 or 00:00:00, # never 24:00:00 # for {set i 0} {$i<=255} {incr i} { db close hexio_write test.db 2047 [format %02x $i] sqlite3 db test.db do_test date-14.2.$i { set date [db one {SELECT datetime(x) FROM t1}] expr {$date eq "2008-06-12 00:00:00" || $date eq "2008-06-11 23:59:59"} } {1} } } finish_test |
Added test/dbstatus.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 | # 2010 March 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. # #*********************************************************************** # # Tests for the sqlite3_db_status() function # set testdir [file dirname $argv0] source $testdir/tester.tcl # Make sure sqlite3_db_config() and sqlite3_db_status are working. # unset -nocomplain PAGESZ unset -nocomplain BASESZ do_test dbstatus-1.1 { db close sqlite3 db :memory: db eval { CREATE TABLE t1(x); } set sz1 [lindex [sqlite3_db_status db SQLITE_DBSTATUS_CACHE_USED 0] 1] db eval { CREATE TABLE t2(y); } set sz2 [lindex [sqlite3_db_status db SQLITE_DBSTATUS_CACHE_USED 0] 1] set ::PAGESZ [expr {$sz2-$sz1}] set ::BASESZ [expr {$sz1-$::PAGESZ}] expr {$::PAGESZ>1024 && $::PAGESZ<1200} } {1} do_test dbstatus-1.2 { db eval { INSERT INTO t1 VALUES(zeroblob(9000)); } lindex [sqlite3_db_status db SQLITE_DBSTATUS_CACHE_USED 0] 1 } [expr {$BASESZ + 10*$PAGESZ}] finish_test |
Changes to test/descidx1.test.
︙ | ︙ | |||
12 13 14 15 16 17 18 19 20 21 22 23 24 25 | # focus of this script is descending indices. # # $Id: descidx1.test,v 1.10 2008/03/19 00:21:31 drh Exp $ # set testdir [file dirname $argv0] source $testdir/tester.tcl db eval {PRAGMA legacy_file_format=OFF} # This procedure sets the value of the file-format in file 'test.db' # to $newval. Also, the schema cookie is incremented. # proc set_file_format {newval} { | > > > > > | 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | # focus of this script is descending indices. # # $Id: descidx1.test,v 1.10 2008/03/19 00:21:31 drh Exp $ # set testdir [file dirname $argv0] source $testdir/tester.tcl # Do not use a codec for tests in this file, as the database file is # manipulated directly using tcl scripts (using the [hexio_write] command). # do_not_use_codec db eval {PRAGMA legacy_file_format=OFF} # This procedure sets the value of the file-format in file 'test.db' # to $newval. Also, the schema cookie is incremented. # proc set_file_format {newval} { |
︙ | ︙ |
Changes to test/descidx2.test.
︙ | ︙ | |||
12 13 14 15 16 17 18 19 20 21 22 23 24 25 | # focus of this script is descending indices. # # $Id: descidx2.test,v 1.5 2008/03/19 00:21:31 drh Exp $ # set testdir [file dirname $argv0] source $testdir/tester.tcl db eval {PRAGMA legacy_file_format=OFF} # This procedure sets the value of the file-format in file 'test.db' # to $newval. Also, the schema cookie is incremented. # proc set_file_format {newval} { | > > > > > > | 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 | # focus of this script is descending indices. # # $Id: descidx2.test,v 1.5 2008/03/19 00:21:31 drh Exp $ # set testdir [file dirname $argv0] source $testdir/tester.tcl # Do not use a codec for tests in this file, as the database file is # manipulated directly using tcl scripts (using the [hexio_write] command). # do_not_use_codec db eval {PRAGMA legacy_file_format=OFF} # This procedure sets the value of the file-format in file 'test.db' # to $newval. Also, the schema cookie is incremented. # proc set_file_format {newval} { |
︙ | ︙ |
Changes to test/descidx3.test.
︙ | ︙ | |||
12 13 14 15 16 17 18 19 20 21 22 23 24 25 | # focus of this script is descending indices. # # $Id: descidx3.test,v 1.6 2008/03/19 00:21:31 drh Exp $ # set testdir [file dirname $argv0] source $testdir/tester.tcl ifcapable !bloblit { finish_test return } db eval {PRAGMA legacy_file_format=OFF} | > > > > > | 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | # focus of this script is descending indices. # # $Id: descidx3.test,v 1.6 2008/03/19 00:21:31 drh Exp $ # set testdir [file dirname $argv0] source $testdir/tester.tcl # Do not use a codec for tests in this file, as the database file is # manipulated directly using tcl scripts (using the [hexio_write] command). # do_not_use_codec ifcapable !bloblit { finish_test return } db eval {PRAGMA legacy_file_format=OFF} |
︙ | ︙ |
Added test/e_expr.test.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 | # 2010 July 16 # # The author disclaims copyright to this source code. In place of # a legal notice, here is a blessing: # # May you do good and not evil. # May you find forgiveness for yourself and forgive others. # May you share freely, never taking more than you give. # #*********************************************************************** # # This file implements tests to verify that the "testable statements" in # the lang_expr.html document are correct. # set testdir [file dirname $argv0] source $testdir/tester.tcl source $testdir/malloc_common.tcl # Set up three global variables: # # ::opname An array mapping from SQL operator to an easy to parse # name. The names are used as part of test case names. # # ::opprec An array mapping from SQL operator to a numeric # precedence value. Operators that group more tightly # have lower numeric precedences. # # ::oplist A list of all SQL operators supported by SQLite. # foreach {op opn} { || cat * mul / div % mod + add - sub << lshift >> rshift & bitand | bitor < less <= lesseq > more >= moreeq = eq1 == eq2 <> ne1 != ne2 IS is LIKE like GLOB glob AND and OR or MATCH match REGEXP regexp {IS NOT} isnt } { set ::opname($op) $opn } set oplist [list] foreach {prec opl} { 1 || 2 {* / %} 3 {+ -} 4 {<< >> & |} 5 {< <= > >=} 6 {= == != <> IS {IS NOT} LIKE GLOB MATCH REGEXP} 7 AND 8 OR } { foreach op $opl { set ::opprec($op) $prec lappend oplist $op } } # Hook in definitions of MATCH and REGEX. The following implementations # cause MATCH and REGEX to behave similarly to the == operator. # proc matchfunc {a b} { return [expr {$a==$b}] } proc regexfunc {a b} { return [expr {$a==$b}] } db func match -argcount 2 matchfunc db func regexp -argcount 2 regexfunc #------------------------------------------------------------------------- # Test cases e_expr-1.* attempt to verify that all binary operators listed # in the documentation exist and that the relative precedences of the # operators are also as the documentation suggests. # # EVIDENCE-OF: R-15514-65163 SQLite understands the following binary # operators, in order from highest to lowest precedence: || * / % + - # << >> & | < <= > >= = == != <> IS IS # NOT IN LIKE GLOB MATCH REGEXP AND OR # # EVIDENCE-OF: R-38759-38789 Operators IS and IS NOT have the same # precedence as =. # unset -nocomplain untested foreach op1 $oplist { foreach op2 $oplist { set untested($op1,$op2) 1 foreach {tn A B C} { 1 22 45 66 2 0 0 0 3 0 0 1 4 0 1 0 5 0 1 1 6 1 0 0 7 1 0 1 8 1 1 0 9 1 1 1 10 5 6 1 11 1 5 6 12 1 5 5 13 5 5 1 14 5 2 1 15 1 4 1 16 -1 0 1 17 0 1 -1 } { set testname "e_expr-1.$opname($op1).$opname($op2).$tn" # If $op2 groups more tightly than $op1, then the result # of executing $sql1 whould be the same as executing $sql3. # If $op1 groups more tightly, or if $op1 and $op2 have # the same precedence, then executing $sql1 should return # the same value as $sql2. # set sql1 "SELECT $A $op1 $B $op2 $C" set sql2 "SELECT ($A $op1 $B) $op2 $C" set sql3 "SELECT $A $op1 ($B $op2 $C)" set a2 [db one $sql2] set a3 [db one $sql3] do_execsql_test $testname $sql1 [list [ if {$opprec($op2) < $opprec($op1)} {set a3} {set a2} ]] if {$a2 != $a3} { unset -nocomplain untested($op1,$op2) } } } } foreach op {* AND OR + || & |} { unset untested($op,$op) } unset untested(+,-) ;# Since (a+b)-c == a+(b-c) unset untested(*,<<) ;# Since (a*b)<<c == a*(b<<c) do_test e_expr-1.1 { array names untested } {} # At one point, test 1.2.2 was failing. Instead of the correct result, it # was returning {1 1 0}. This would seem to indicate that LIKE has the # same precedence as '<'. Which is incorrect. It has lower precedence. # do_execsql_test e_expr-1.2.1 { SELECT 0 < 2 LIKE 1, (0 < 2) LIKE 1, 0 < (2 LIKE 1) } {1 1 0} do_execsql_test e_expr-1.2.2 { SELECT 0 LIKE 0 < 2, (0 LIKE 0) < 2, 0 LIKE (0 < 2) } {0 1 0} # Showing that LIKE and == have the same precedence # do_execsql_test e_expr-1.2.3 { SELECT 2 LIKE 2 == 1, (2 LIKE 2) == 1, 2 LIKE (2 == 1) } {1 1 0} do_execsql_test e_expr-1.2.4 { SELECT 2 == 2 LIKE 1, (2 == 2) LIKE 1, 2 == (2 LIKE 1) } {1 1 0} # Showing that < groups more tightly than == (< has higher precedence). # do_execsql_test e_expr-1.2.5 { SELECT 0 < 2 == 1, (0 < 2) == 1, 0 < (2 == 1) } {1 1 0} do_execsql_test e_expr-1.6 { SELECT 0 == 0 < 2, (0 == 0) < 2, 0 == (0 < 2) } {0 1 0} #------------------------------------------------------------------------- # Check that the four unary prefix operators mentioned in the # documentation exist. # # EVIDENCE-OF: R-13958-53419 Supported unary prefix operators are these: # - + ~ NOT # do_execsql_test e_expr-2.1 { SELECT - 10 } {-10} do_execsql_test e_expr-2.2 { SELECT + 10 } {10} do_execsql_test e_expr-2.3 { SELECT ~ 10 } {-11} do_execsql_test e_expr-2.4 { SELECT NOT 10 } {0} #------------------------------------------------------------------------- # Tests for the two statements made regarding the unary + operator. # # EVIDENCE-OF: R-53670-03373 The unary operator + is a no-op. # # EVIDENCE-OF: R-19480-30968 It can be applied to strings, numbers, # blobs or NULL and it always returns a result with the same value as # the operand. # foreach {tn literal type} { 1 'helloworld' text 2 45 integer 3 45.2 real 4 45.0 real 5 X'ABCDEF' blob 6 NULL null } { set sql " SELECT quote( + $literal ), typeof( + $literal) " do_execsql_test e_expr-3.$tn $sql [list $literal $type] } #------------------------------------------------------------------------- # Check that both = and == are both acceptable as the "equals" operator. # Similarly, either != or <> work as the not-equals operator. # # EVIDENCE-OF: R-03679-60639 Equals can be either = or ==. # # EVIDENCE-OF: R-30082-38996 The non-equals operator can be either != or # <>. # foreach {tn literal different} { 1 'helloworld' '12345' 2 22 23 3 'xyz' X'78797A' 4 X'78797A00' 'xyz' } { do_execsql_test e_expr-4.$tn " SELECT $literal = $literal, $literal == $literal, $literal = $different, $literal == $different, $literal = NULL, $literal == NULL, $literal != $literal, $literal <> $literal, $literal != $different, $literal <> $different, $literal != NULL, $literal != NULL " {1 1 0 0 {} {} 0 0 1 1 {} {}} } #------------------------------------------------------------------------- # Test the || operator. # # EVIDENCE-OF: R-44409-62641 The || operator is "concatenate" - it joins # together the two strings of its operands. # foreach {tn a b} { 1 'helloworld' '12345' 2 22 23 } { set as [db one "SELECT $a"] set bs [db one "SELECT $b"] do_execsql_test e_expr-5.$tn "SELECT $a || $b" [list "${as}${bs}"] } #------------------------------------------------------------------------- # Test the % operator. # # EVIDENCE-OF: R-08914-63790 The operator % outputs the value of its # left operand modulo its right operand. # do_execsql_test e_expr-6.1 {SELECT 72%5} {2} do_execsql_test e_expr-6.2 {SELECT 72%-5} {2} do_execsql_test e_expr-6.3 {SELECT -72%-5} {-2} do_execsql_test e_expr-6.4 {SELECT -72%5} {-2} #------------------------------------------------------------------------- # Test that the results of all binary operators are either numeric or # NULL, except for the || operator, which may evaluate to either a text # value or NULL. # # EVIDENCE-OF: R-20665-17792 The result of any binary operator is either # a numeric value or NULL, except for the || concatenation operator # which always evaluates to either NULL or a text value. # set literals { 1 'abc' 2 'hexadecimal' 3 '' 4 123 5 -123 6 0 7 123.4 8 0.0 9 -123.4 10 X'ABCDEF' 11 X'' 12 X'0000' 13 NULL } foreach op $oplist { foreach {n1 rhs} $literals { foreach {n2 lhs} $literals { set t [db one " SELECT typeof($lhs $op $rhs) "] do_test e_expr-7.$opname($op).$n1.$n2 { expr { ($op=="||" && ($t == "text" || $t == "null")) || ($op!="||" && ($t == "integer" || $t == "real" || $t == "null")) } } 1 }} } #------------------------------------------------------------------------- # Test the IS and IS NOT operators. # # EVIDENCE-OF: R-24731-45773 The IS and IS NOT operators work like = and # != except when one or both of the operands are NULL. # # EVIDENCE-OF: R-06325-15315 In this case, if both operands are NULL, # then the IS operator evaluates to 1 (true) and the IS NOT operator # evaluates to 0 (false). # # EVIDENCE-OF: R-19812-36779 If one operand is NULL and the other is # not, then the IS operator evaluates to 0 (false) and the IS NOT # operator is 1 (true). # # EVIDENCE-OF: R-61975-13410 It is not possible for an IS or IS NOT # expression to evaluate to NULL. # do_execsql_test e_expr-8.1.1 { SELECT NULL IS NULL } {1} do_execsql_test e_expr-8.1.2 { SELECT 'ab' IS NULL } {0} do_execsql_test e_expr-8.1.3 { SELECT NULL IS 'ab' } {0} do_execsql_test e_expr-8.1.4 { SELECT 'ab' IS 'ab' } {1} do_execsql_test e_expr-8.1.5 { SELECT NULL == NULL } {{}} do_execsql_test e_expr-8.1.6 { SELECT 'ab' == NULL } {{}} do_execsql_test e_expr-8.1.7 { SELECT NULL == 'ab' } {{}} do_execsql_test e_expr-8.1.8 { SELECT 'ab' == 'ab' } {1} do_execsql_test e_expr-8.1.9 { SELECT NULL IS NOT NULL } {0} do_execsql_test e_expr-8.1.10 { SELECT 'ab' IS NOT NULL } {1} do_execsql_test e_expr-8.1.11 { SELECT NULL IS NOT 'ab' } {1} do_execsql_test e_expr-8.1.12 { SELECT 'ab' IS NOT 'ab' } {0} do_execsql_test e_expr-8.1.13 { SELECT NULL != NULL } {{}} do_execsql_test e_expr-8.1.14 { SELECT 'ab' != NULL } {{}} do_execsql_test e_expr-8.1.15 { SELECT NULL != 'ab' } {{}} do_execsql_test e_expr-8.1.16 { SELECT 'ab' != 'ab' } {0} foreach {n1 rhs} $literals { foreach {n2 lhs} $literals { if {$rhs!="NULL" && $lhs!="NULL"} { set eq [execsql "SELECT $lhs = $rhs, $lhs != $rhs"] } else { set eq [list [expr {$lhs=="NULL" && $rhs=="NULL"}] \ [expr {$lhs!="NULL" || $rhs!="NULL"}] ] } set test e_expr-8.2.$n1.$n2 do_execsql_test $test.1 "SELECT $lhs IS $rhs, $lhs IS NOT $rhs" $eq do_execsql_test $test.2 " SELECT ($lhs IS $rhs) IS NULL, ($lhs IS NOT $rhs) IS NULL " {0 0} } } finish_test |
Changes to test/e_fts3.test.
︙ | ︙ | |||
248 249 250 251 252 253 254 255 256 257 258 259 260 261 | } {no such column: main.docs} ddl_test 1.3.2.8 { DROP TABLE docs } ########################################################################## # Test the examples in section 3 (full-text index queries). # ddl_test 1.4.1.1 { CREATE VIRTUAL TABLE docs USING fts3(title, body) } foreach {tn title body} { 2 "linux driver" "a device" 3 "driver" "linguistic trick" 4 "problems" "linux problems" 5 "linux" "big problems" 6 "linux driver" "a device driver problem" 7 "good times" "applications for linux" | > | 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 | } {no such column: main.docs} ddl_test 1.3.2.8 { DROP TABLE docs } ########################################################################## # Test the examples in section 3 (full-text index queries). # ddl_test 1.4.1.1 { CREATE VIRTUAL TABLE docs USING fts3(title, body) } unset -nocomplain R foreach {tn title body} { 2 "linux driver" "a device" 3 "driver" "linguistic trick" 4 "problems" "linux problems" 5 "linux" "big problems" 6 "linux driver" "a device driver problem" 7 "good times" "applications for linux" |
︙ | ︙ |
Changes to test/exclusive.test.
︙ | ︙ | |||
208 209 210 211 212 213 214 | } db2 } {1 {database is locked}} do_test exclusive-2.8 { execsql { ROLLBACK; } db2 } {} | | | 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 | } db2 } {1 {database is locked}} do_test exclusive-2.8 { execsql { ROLLBACK; } db2 } {} sqlite3_soft_heap_limit $cmdlinearg(soft-heap-limit) do_test exclusive-2.9 { # Write the database to establish the exclusive lock with connection 'db. execsql { INSERT INTO abc VALUES(7, 8, 9); } db catchsql { |
︙ | ︙ |
Changes to test/exclusive2.test.
︙ | ︙ | |||
295 296 297 298 299 300 301 | } {4} do_test exclusive2-3.6 { execsql { INSERT INTO t1 VALUES(randstr(10, 400)); } readPagerChangeCounter test.db } {5} | | | 295 296 297 298 299 300 301 302 303 304 | } {4} do_test exclusive2-3.6 { execsql { INSERT INTO t1 VALUES(randstr(10, 400)); } readPagerChangeCounter test.db } {5} sqlite3_soft_heap_limit $cmdlinearg(soft-heap-limit) finish_test |
Changes to test/filectrl.test.
︙ | ︙ | |||
30 31 32 33 34 35 36 | do_test filectrl-1.4 { sqlite3 db test.db file_control_lasterrno_test db } {} do_test filectrl-1.5 { db close sqlite3 db test_control_lockproxy.db | | | 30 31 32 33 34 35 36 37 38 39 40 41 | do_test filectrl-1.4 { sqlite3 db test.db file_control_lasterrno_test db } {} do_test filectrl-1.5 { db close sqlite3 db test_control_lockproxy.db file_control_lockproxy_test db [pwd] } {} db close file delete -force .test_control_lockproxy.db-conch test.proxy finish_test |
Changes to test/filefmt.test.
︙ | ︙ | |||
12 13 14 15 16 17 18 19 20 21 22 23 24 25 | # # This file implements tests to verify database file format. # # $Id: filefmt.test,v 1.3 2009/06/18 11:34:43 drh Exp $ set testdir [file dirname $argv0] source $testdir/tester.tcl db close file delete -force test.db test.db-journal # Database begins with valid 16-byte header string. # do_test filefmt-1.1 { sqlite3 db test.db | > > > > > > | 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 | # # This file implements tests to verify database file format. # # $Id: filefmt.test,v 1.3 2009/06/18 11:34:43 drh Exp $ set testdir [file dirname $argv0] source $testdir/tester.tcl # Do not use a codec for tests in this file, as the database file is # manipulated directly using tcl scripts (using the [hexio_write] command). # do_not_use_codec db close file delete -force test.db test.db-journal # Database begins with valid 16-byte header string. # do_test filefmt-1.1 { sqlite3 db test.db |
︙ | ︙ | |||
107 108 109 110 111 112 113 | sqlite3 db test.db catchsql { SELECT count(*) FROM sqlite_master } } {1 {file is encrypted or is not a database}} } | > > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | sqlite3 db test.db catchsql { SELECT count(*) FROM sqlite_master } } {1 {file is encrypted or is not a database}} } #------------------------------------------------------------------------- # The following block of tests - filefmt-2.* - test that versions 3.7.0 # and later can read and write databases that have been modified or created # by 3.6.23.1 and earlier. The difference difference is that 3.7.0 stores # the size of the database in the database file header, whereas 3.6.23.1 # always derives this from the size of the file. # db close file delete -force test.db set a_string_counter 1 proc a_string {n} { incr ::a_string_counter string range [string repeat "${::a_string_counter}." $n] 1 $n } sqlite3 db test.db db func a_string a_string do_execsql_test filefmt-2.1.1 { PRAGMA page_size = 1024; PRAGMA auto_vacuum = 0; CREATE TABLE t1(a); CREATE INDEX i1 ON t1(a); INSERT INTO t1 VALUES(a_string(3000)); CREATE TABLE t2(a); INSERT INTO t2 VALUES(1); } {} do_test filefmt-2.1.2 { hexio_read test.db 28 4 } {00000009} do_test filefmt-2.1.3 { sql36231 { INSERT INTO t1 VALUES(a_string(3000)) } } {} do_execsql_test filefmt-2.1.4 { INSERT INTO t2 VALUES(2) } {} integrity_check filefmt-2.1.5 do_test filefmt-2.1.6 { hexio_read test.db 28 4 } {00000010} db close file delete -force test.db sqlite3 db test.db db func a_string a_string do_execsql_test filefmt-2.2.1 { PRAGMA page_size = 1024; PRAGMA auto_vacuum = 0; CREATE TABLE t1(a); CREATE INDEX i1 ON t1(a); INSERT INTO t1 VALUES(a_string(3000)); CREATE TABLE t2(a); INSERT INTO t2 VALUES(1); } {} do_test filefmt-2.2.2 { hexio_read test.db 28 4 } {00000009} do_test filefmt-2.2.3 { sql36231 { INSERT INTO t1 VALUES(a_string(3000)) } } {} do_execsql_test filefmt-2.2.4 { PRAGMA integrity_check; BEGIN; INSERT INTO t2 VALUES(2); SAVEPOINT a; INSERT INTO t2 VALUES(3); ROLLBACK TO a; } {ok} integrity_check filefmt-2.2.5 do_execsql_test filefmt-2.2.6 { COMMIT } {} db close sqlite3 db test.db integrity_check filefmt-2.2.7 finish_test |
Changes to test/fkey2.test.
︙ | ︙ | |||
69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 | # updated and deleted. # # fkey2-17.*: Test that the "count_changes" pragma does not interfere with # FK constraint processing. # # fkey2-18.*: Test that the authorization callback is invoked when processing # FK constraints. # # fkey2-genfkey.*: Tests that were used with the shell tool .genfkey # command. Recycled to test the built-in implementation. # execsql { PRAGMA foreign_keys = on } set FkeySimpleSchema { PRAGMA foreign_keys = on; | > > > > > > | 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 | # updated and deleted. # # fkey2-17.*: Test that the "count_changes" pragma does not interfere with # FK constraint processing. # # fkey2-18.*: Test that the authorization callback is invoked when processing # FK constraints. # # fkey2-20.*: Test that ON CONFLICT clauses specified as part of statements # do not affect the operation of FK constraints. # # fkey2-genfkey.*: Tests that were used with the shell tool .genfkey # command. Recycled to test the built-in implementation. # # fkey2-dd08e5.*: Tests to verify that ticket dd08e5a988d00decc4a543daa8d # has been fixed. # execsql { PRAGMA foreign_keys = on } set FkeySimpleSchema { PRAGMA foreign_keys = on; |
︙ | ︙ | |||
1406 1407 1408 1409 1410 1411 1412 | } {1} do_test fkey2-17.1.2 { set STMT [sqlite3_prepare_v2 db "INSERT INTO two VALUES(4, 5, 6)" -1 dummy] sqlite3_step $STMT } {SQLITE_CONSTRAINT} do_test fkey2-17.1.3 { sqlite3_step $STMT | | | 1412 1413 1414 1415 1416 1417 1418 1419 1420 1421 1422 1423 1424 1425 1426 | } {1} do_test fkey2-17.1.2 { set STMT [sqlite3_prepare_v2 db "INSERT INTO two VALUES(4, 5, 6)" -1 dummy] sqlite3_step $STMT } {SQLITE_CONSTRAINT} do_test fkey2-17.1.3 { sqlite3_step $STMT } {SQLITE_CONSTRAINT} do_test fkey2-17.1.4 { sqlite3_finalize $STMT } {SQLITE_CONSTRAINT} do_test fkey2-17.1.5 { execsql { INSERT INTO one VALUES(2, 3, 4); INSERT INTO one VALUES(3, 4, 5); |
︙ | ︙ | |||
1594 1595 1596 1597 1598 1599 1600 1601 1602 1603 1604 1605 1606 1607 | catchsql { UPDATE short SET f = 2 WHERE f IS NULL } } {1 {foreign key constraint failed}} db auth {} unset authargs } #------------------------------------------------------------------------- # The following block of tests, those prefixed with "fkey2-genfkey.", are # the same tests that were used to test the ".genfkey" command provided # by the shell tool. So these tests show that the built-in foreign key # implementation is more or less compatible with the triggers generated # by genfkey. # | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1600 1601 1602 1603 1604 1605 1606 1607 1608 1609 1610 1611 1612 1613 1614 1615 1616 1617 1618 1619 1620 1621 1622 1623 1624 1625 1626 1627 1628 1629 1630 1631 1632 1633 1634 1635 1636 1637 1638 1639 1640 1641 1642 1643 1644 1645 1646 1647 1648 1649 1650 1651 1652 1653 1654 1655 1656 1657 1658 1659 1660 1661 1662 1663 1664 1665 1666 1667 1668 1669 1670 1671 1672 1673 1674 1675 1676 1677 1678 1679 1680 1681 1682 1683 1684 1685 1686 1687 1688 1689 1690 1691 1692 1693 1694 1695 1696 1697 1698 1699 1700 1701 1702 1703 1704 1705 1706 1707 1708 1709 1710 1711 1712 1713 1714 1715 1716 1717 1718 1719 1720 1721 1722 1723 1724 1725 1726 1727 1728 | catchsql { UPDATE short SET f = 2 WHERE f IS NULL } } {1 {foreign key constraint failed}} db auth {} unset authargs } do_test fkey2-19.1 { execsql { CREATE TABLE main(id INTEGER PRIMARY KEY); CREATE TABLE sub(id INT REFERENCES main(id)); INSERT INTO main VALUES(1); INSERT INTO main VALUES(2); INSERT INTO sub VALUES(2); } } {} do_test fkey2-19.2 { set S [sqlite3_prepare_v2 db "DELETE FROM main WHERE id = ?" -1 dummy] sqlite3_bind_int $S 1 2 sqlite3_step $S } {SQLITE_CONSTRAINT} do_test fkey2-19.3 { sqlite3_reset $S } {SQLITE_CONSTRAINT} do_test fkey2-19.4 { sqlite3_bind_int $S 1 1 sqlite3_step $S } {SQLITE_DONE} do_test fkey2-19.4 { sqlite3_finalize $S } {SQLITE_OK} drop_all_tables do_test fkey2-20.1 { execsql { CREATE TABLE pp(a PRIMARY KEY, b); CREATE TABLE cc(c PRIMARY KEY, d REFERENCES pp); } } {} foreach {tn insert} { 1 "INSERT" 2 "INSERT OR IGNORE" 3 "INSERT OR ABORT" 4 "INSERT OR ROLLBACK" 5 "INSERT OR REPLACE" 6 "INSERT OR FAIL" } { do_test fkey2-20.2.$tn.1 { catchsql "$insert INTO cc VALUES(1, 2)" } {1 {foreign key constraint failed}} do_test fkey2-20.2.$tn.2 { execsql { SELECT * FROM cc } } {} do_test fkey2-20.2.$tn.3 { execsql { BEGIN; INSERT INTO pp VALUES(2, 'two'); INSERT INTO cc VALUES(1, 2); } catchsql "$insert INTO cc VALUES(3, 4)" } {1 {foreign key constraint failed}} do_test fkey2-20.2.$tn.4 { execsql { COMMIT ; SELECT * FROM cc } } {1 2} do_test fkey2-20.2.$tn.5 { execsql { DELETE FROM cc ; DELETE FROM pp } } {} } foreach {tn update} { 1 "UPDATE" 2 "UPDATE OR IGNORE" 3 "UPDATE OR ABORT" 4 "UPDATE OR ROLLBACK" 5 "UPDATE OR REPLACE" 6 "UPDATE OR FAIL" } { do_test fkey2-20.3.$tn.1 { execsql { INSERT INTO pp VALUES(2, 'two'); INSERT INTO cc VALUES(1, 2); } } {} do_test fkey2-20.3.$tn.2 { catchsql "$update pp SET a = 1" } {1 {foreign key constraint failed}} do_test fkey2-20.3.$tn.3 { execsql { SELECT * FROM pp } } {2 two} do_test fkey2-20.3.$tn.4 { catchsql "$update cc SET d = 1" } {1 {foreign key constraint failed}} do_test fkey2-20.3.$tn.5 { execsql { SELECT * FROM cc } } {1 2} do_test fkey2-20.3.$tn.6 { execsql { BEGIN; INSERT INTO pp VALUES(3, 'three'); } catchsql "$update pp SET a = 1 WHERE a = 2" } {1 {foreign key constraint failed}} do_test fkey2-20.3.$tn.7 { execsql { COMMIT ; SELECT * FROM pp } } {2 two 3 three} do_test fkey2-20.3.$tn.8 { execsql { BEGIN; INSERT INTO cc VALUES(2, 2); } catchsql "$update cc SET d = 1 WHERE c = 1" } {1 {foreign key constraint failed}} do_test fkey2-20.3.$tn.9 { execsql { COMMIT ; SELECT * FROM cc } } {1 2 2 2} do_test fkey2-20.3.$tn.10 { execsql { DELETE FROM cc ; DELETE FROM pp } } {} } #------------------------------------------------------------------------- # The following block of tests, those prefixed with "fkey2-genfkey.", are # the same tests that were used to test the ".genfkey" command provided # by the shell tool. So these tests show that the built-in foreign key # implementation is more or less compatible with the triggers generated # by genfkey. # |
︙ | ︙ | |||
1769 1770 1771 1772 1773 1774 1775 1776 1777 | do_test fkey2-genfkey.3.6 { execsql { UPDATE t3 SET h = 2, i = 2; DELETE FROM t1; SELECT * FROM t3; } } {hello {} {}} finish_test | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1890 1891 1892 1893 1894 1895 1896 1897 1898 1899 1900 1901 1902 1903 1904 1905 1906 1907 1908 1909 1910 1911 1912 1913 1914 1915 1916 1917 1918 1919 1920 1921 1922 1923 1924 1925 1926 1927 1928 1929 1930 1931 1932 1933 1934 1935 1936 1937 1938 1939 1940 | do_test fkey2-genfkey.3.6 { execsql { UPDATE t3 SET h = 2, i = 2; DELETE FROM t1; SELECT * FROM t3; } } {hello {} {}} #------------------------------------------------------------------------- # Verify that ticket dd08e5a988d00decc4a543daa8dbbfab9c577ad8 has been # fixed. # do_test fkey2-dd08e5.1.1 { execsql { PRAGMA foreign_keys=ON; CREATE TABLE tdd08(a INTEGER PRIMARY KEY, b); CREATE UNIQUE INDEX idd08 ON tdd08(a,b); INSERT INTO tdd08 VALUES(200,300); CREATE TABLE tdd08_b(w,x,y, FOREIGN KEY(x,y) REFERENCES tdd08(a,b)); INSERT INTO tdd08_b VALUES(100,200,300); } } {} do_test fkey2-dd08e5.1.2 { catchsql { DELETE FROM tdd08; } } {1 {foreign key constraint failed}} do_test fkey2-dd08e5.1.3 { execsql { SELECT * FROM tdd08; } } {200 300} do_test fkey2-dd08e5.1.4 { catchsql { INSERT INTO tdd08_b VALUES(400,500,300); } } {1 {foreign key constraint failed}} do_test fkey2-dd08e5.1.5 { catchsql { UPDATE tdd08_b SET x=x+1; } } {1 {foreign key constraint failed}} do_test fkey2-dd08e5.1.6 { catchsql { UPDATE tdd08 SET a=a+1; } } {1 {foreign key constraint failed}} finish_test |
Changes to test/fts2.test.
︙ | ︙ | |||
17 18 19 20 21 22 23 | } while {[set arg [lshift argv]] != ""} { switch -- $arg { -sharedpagercache { sqlite3_enable_shared_cache 1 } -soak { | | | < | | 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 | } while {[set arg [lshift argv]] != ""} { switch -- $arg { -sharedpagercache { sqlite3_enable_shared_cache 1 } -soak { set G(issoak) 1 } default { set argv [linsert $argv 0 $arg] break } } } set testdir [file dirname $argv0] source $testdir/tester.tcl # If SQLITE_ENABLE_FTS2 is defined, omit this file. ifcapable !fts2 { return } rename finish_test really_finish_test proc finish_test {} {} set G(isquick) 1 set EXCLUDE { fts2.test } # Files to include in the test. If this list is empty then everything # that is not in the EXCLUDE list is run. # set INCLUDE { } foreach testfile [lsort -dictionary [glob $testdir/fts2*.test]] { set tail [file tail $testfile] if {[lsearch -exact $EXCLUDE $tail]>=0} continue if {[llength $INCLUDE]>0 && [lsearch -exact $INCLUDE $tail]<0} continue source $testfile catch {db close} if {$sqlite_open_file_count>0} { puts "$tail did not close all files: $sqlite_open_file_count" fail_test $tail set sqlite_open_file_count 0 } } set sqlite_open_file_count 0 really_finish_test |
Changes to test/fts3.test.
1 2 3 4 5 6 7 8 9 10 11 | # 2007 November 23 # # May you do good and not evil. # May you find forgiveness for yourself and forgive others. # May you share freely, never taking more than you give. # #*********************************************************************** # This file runs all tests. # # $Id: fts3.test,v 1.2 2008/07/23 18:17:32 drh Exp $ | < < < < < < < < < < < < < < < < < < < < < | < < < | < < < | < < < < < | < < < < < < < < < < < < < < < < < < < | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | # 2007 November 23 # # May you do good and not evil. # May you find forgiveness for yourself and forgive others. # May you share freely, never taking more than you give. # #*********************************************************************** # This file runs all tests. # # $Id: fts3.test,v 1.2 2008/07/23 18:17:32 drh Exp $ set testdir [file dirname $argv0] source $testdir/permutations.test ifcapable fts3 { run_test_suite fts3 } finish_test |
Changes to test/fts3_common.tcl.
︙ | ︙ | |||
284 285 286 287 288 289 290 | } } } } ########################################################################## | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | 284 285 286 287 288 289 290 | } } } } ########################################################################## |
Changes to test/fts3an.test.
︙ | ︙ | |||
181 182 183 184 185 186 187 | db eval {SELECT offsets(t3) as o FROM t3 WHERE t3 MATCH 'l*'} { set l [llength $o] lappend t [expr {$l/4}] } set t } $ret | | < | > | | | | > > > > > > > > > > > > > > > > > > > > > > | 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 | db eval {SELECT offsets(t3) as o FROM t3 WHERE t3 MATCH 'l*'} { set l [llength $o] lappend t [expr {$l/4}] } set t } $ret # Test a boundary condition: More than 2^16 terms that match a searched for # prefix in a single segment. # puts "This next test can take a little while (~ 30 seconds)..." do_test fts3an-4.1 { execsql { CREATE VIRTUAL TABLE ft USING fts3(x) } execsql BEGIN execsql { INSERT INTO ft VALUES(NULL) } execsql { INSERT INTO ft SELECT * FROM ft } ;# 2 execsql { INSERT INTO ft SELECT * FROM ft } ;# 4 execsql { INSERT INTO ft SELECT * FROM ft } ;# 8 execsql { INSERT INTO ft SELECT * FROM ft } ;# 16 execsql { INSERT INTO ft SELECT * FROM ft } ;# 32 execsql { INSERT INTO ft SELECT * FROM ft } ;# 64 execsql { INSERT INTO ft SELECT * FROM ft } ;# 128 execsql { INSERT INTO ft SELECT * FROM ft } ;# 256 execsql { INSERT INTO ft SELECT * FROM ft } ;# 512 execsql { INSERT INTO ft SELECT * FROM ft } ;# 1024 execsql { INSERT INTO ft SELECT * FROM ft } ;# 2048 execsql { INSERT INTO ft SELECT * FROM ft } ;# 4096 execsql { INSERT INTO ft SELECT * FROM ft } ;# 8192 execsql { INSERT INTO ft SELECT * FROM ft } ;# 16384 execsql { INSERT INTO ft SELECT * FROM ft } ;# 32768 execsql { INSERT INTO ft SELECT * FROM ft } ;# 65536 execsql { INSERT INTO ft SELECT * FROM ft } ;# 131072 execsql COMMIT execsql { UPDATE ft SET x = 'abc' || rowid } execsql { SELECT count(*) FROM ft WHERE x MATCH 'abc*' } } {131072} finish_test |
Changes to test/fts3query.test.
︙ | ︙ | |||
16 17 18 19 20 21 22 23 24 25 26 27 28 29 | set testdir [file dirname $argv0] source $testdir/tester.tcl # If this build does not include FTS3, skip the tests in this file. # ifcapable !fts3 { finish_test ; return } source $testdir/fts3_common.tcl set DO_MALLOC_TEST 0 do_test fts3query-1.1 { execsql { CREATE VIRTUAL TABLE t1 USING fts3(x); BEGIN; | > | 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | set testdir [file dirname $argv0] source $testdir/tester.tcl # If this build does not include FTS3, skip the tests in this file. # ifcapable !fts3 { finish_test ; return } source $testdir/malloc_common.tcl source $testdir/fts3_common.tcl set DO_MALLOC_TEST 0 do_test fts3query-1.1 { execsql { CREATE VIRTUAL TABLE t1 USING fts3(x); BEGIN; |
︙ | ︙ | |||
96 97 98 99 100 101 102 103 104 105 | return $r } db func mit mit do_test fts3query-3.3 { execsql { SELECT mit(matchinfo(foobar)) FROM foobar WHERE foobar MATCH 'the' } } {{1 1 3 3 1}} finish_test | > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 | return $r } db func mit mit do_test fts3query-3.3 { execsql { SELECT mit(matchinfo(foobar)) FROM foobar WHERE foobar MATCH 'the' } } {{1 1 3 3 1}} # The following tests check that ticket 775b39dd3c has been fixed. # proc eqp {sql} { uplevel [list execsql "EXPLAIN QUERY PLAN $sql"] } do_test fts3query-4.1 { execsql { DROP TABLE IF EXISTS t1; CREATE TABLE t1(number INTEGER PRIMARY KEY, date); CREATE INDEX i1 ON t1(date); CREATE VIRTUAL TABLE ft USING fts3(title); CREATE TABLE bt(title); } } {} do_test fts3query-4.2 { eqp "SELECT t1.number FROM t1, ft WHERE t1.number=ft.rowid ORDER BY t1.date" } {0 0 {TABLE t1 WITH INDEX i1 ORDER BY} 1 1 {TABLE ft VIRTUAL TABLE INDEX 1:}} do_test fts3query-4.3 { eqp "SELECT t1.number FROM ft, t1 WHERE t1.number=ft.rowid ORDER BY t1.date" } {0 1 {TABLE t1 WITH INDEX i1 ORDER BY} 1 0 {TABLE ft VIRTUAL TABLE INDEX 1:}} do_test fts3query-4.4 { eqp "SELECT t1.number FROM t1, bt WHERE t1.number=bt.rowid ORDER BY t1.date" } {0 0 {TABLE t1 WITH INDEX i1 ORDER BY} 1 1 {TABLE bt USING PRIMARY KEY}} do_test fts3query-4.5 { eqp "SELECT t1.number FROM bt, t1 WHERE t1.number=bt.rowid ORDER BY t1.date" } {0 1 {TABLE t1 WITH INDEX i1 ORDER BY} 1 0 {TABLE bt USING PRIMARY KEY}} finish_test |
Changes to test/fts3rnd.test.
︙ | ︙ | |||
78 79 80 81 82 83 84 85 86 87 88 89 90 91 | set testdir [file dirname $argv0] source $testdir/tester.tcl # If this build does not include FTS3, skip the tests in this file. # ifcapable !fts3 { finish_test ; return } source $testdir/fts3_common.tcl set G(nVocab) 100 set nVocab 100 set lVocab [list] expr srand(0) | > | 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 | set testdir [file dirname $argv0] source $testdir/tester.tcl # If this build does not include FTS3, skip the tests in this file. # ifcapable !fts3 { finish_test ; return } source $testdir/fts3_common.tcl source $testdir/malloc_common.tcl set G(nVocab) 100 set nVocab 100 set lVocab [list] expr srand(0) |
︙ | ︙ |
Changes to test/fuzz.test.
︙ | ︙ | |||
23 24 25 26 27 28 29 | set testdir [file dirname $argv0] source $testdir/tester.tcl set ::REPEATS 5000 # If running quick.test, don't do so many iterations. | | | | 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 | set testdir [file dirname $argv0] source $testdir/tester.tcl set ::REPEATS 5000 # If running quick.test, don't do so many iterations. if {[info exists ::G(isquick)]} { if {$::G(isquick)} { set ::REPEATS 20 } } source $testdir/fuzz_common.tcl expr srand(0) #---------------------------------------------------------------- # These tests caused errors that were first caught by the tests |
︙ | ︙ |
Changes to test/fuzz_malloc.test.
︙ | ︙ | |||
21 22 23 24 25 26 27 | finish_test return } source $testdir/malloc_common.tcl source $testdir/fuzz_common.tcl | | | | 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 | finish_test return } source $testdir/malloc_common.tcl source $testdir/fuzz_common.tcl if {[info exists G(isquick)]} { set ::REPEATS 20 } elseif {[info exists G(issoak)]} { set ::REPEATS 100 } else { set ::REPEATS 40 } # # Usage: do_fuzzy_malloc_test <testname> ?<options>? |
︙ | ︙ |
Changes to test/hook.test.
︙ | ︙ | |||
329 330 331 332 333 334 335 336 337 | SELECT count(*) FROM t1; } } {1} # # End rollback-hook testing. #---------------------------------------------------------------------------- finish_test | > > > > > > > > > > > > > > > > > > > > > > > > > > > | 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 | SELECT count(*) FROM t1; } } {1} # # End rollback-hook testing. #---------------------------------------------------------------------------- #---------------------------------------------------------------------------- # Test that if a commit-hook returns non-zero (causing a rollback), the # rollback-hook is invoked. # proc commit_hook {} { lappend ::hooks COMMIT return 1 } proc rollback_hook {} { lappend ::hooks ROLLBACK } do_test hook-6.1 { set ::hooks [list] db commit_hook commit_hook db rollback_hook rollback_hook catchsql { BEGIN; INSERT INTO t1 VALUES('two', 'II'); COMMIT; } execsql { SELECT * FROM t1 } } {one I} do_test hook-6.2 { set ::hooks } {COMMIT ROLLBACK} unset ::hooks finish_test |
Changes to test/incrblob.test.
︙ | ︙ | |||
204 205 206 207 208 209 210 | execsql { SELECT i FROM blobs } } {45} do_test incrblob-2.$AutoVacuumMode.9 { nRead db } [expr $AutoVacuumMode ? 4 : 30] } | | | 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 | execsql { SELECT i FROM blobs } } {45} do_test incrblob-2.$AutoVacuumMode.9 { nRead db } [expr $AutoVacuumMode ? 4 : 30] } sqlite3_soft_heap_limit $cmdlinearg(soft-heap-limit) #------------------------------------------------------------------------ # incrblob-3.*: # # Test the outcome of trying to write to a read-only blob handle. # do_test incrblob-3.1 { |
︙ | ︙ | |||
496 497 498 499 500 501 502 | } {0 {}} do_test incrblob-6.15 { execsql { SELECT * FROM blobs WHERE rowid = 4; } } {a different invocation} db2 close | | | 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 | } {0 {}} do_test incrblob-6.15 { execsql { SELECT * FROM blobs WHERE rowid = 4; } } {a different invocation} db2 close sqlite3_soft_heap_limit $cmdlinearg(soft-heap-limit) #----------------------------------------------------------------------- # The following tests verify the behaviour of the incremental IO # APIs in the following cases: # # 7.1 A row that containing an open blob is modified. # |
︙ | ︙ |
Changes to test/incrvacuum.test.
︙ | ︙ | |||
723 724 725 726 727 728 729 | } } {0} # Verify that the incremental_vacuum pragma fails gracefully if it # is used against an invalid database file. # | > | | | | | | | | | > > | 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 | } } {0} # Verify that the incremental_vacuum pragma fails gracefully if it # is used against an invalid database file. # if {[permutation] == ""} { do_test incrvacuum-14.1 { set out [open invalid.db w] puts $out "This is not an SQLite database file" close $out sqlite3 db3 invalid.db catchsql { PRAGMA incremental_vacuum(10); } db3 } {1 {file is encrypted or is not a database}} db3 close } do_test incrvacuum-15.1 { db close db2 close file delete -force test.db sqlite3 db test.db |
︙ | ︙ | |||
776 777 778 779 780 781 782 | execsql { COMMIT; PRAGMA integrity_check; } } {ok} | < | 779 780 781 782 783 784 785 786 | execsql { COMMIT; PRAGMA integrity_check; } } {ok} finish_test |
Changes to test/index3.test.
︙ | ︙ | |||
45 46 47 48 49 50 51 | # do_test index3-99.1 { execsql { PRAGMA writable_schema=on; UPDATE sqlite_master SET sql='nonsense'; } db close | | | < < | 45 46 47 48 49 50 51 52 53 54 55 56 | # do_test index3-99.1 { execsql { PRAGMA writable_schema=on; UPDATE sqlite_master SET sql='nonsense'; } db close catch { sqlite3 db test.db } catchsql { DROP INDEX i1 } } {1 {malformed database schema (t1) - near "nonsense": syntax error}} finish_test |
Changes to test/init.test.
︙ | ︙ | |||
13 14 15 16 17 18 19 20 21 22 23 24 25 26 | # focus of this file is testing the effects of a failure in # sqlite3_initialize(). # # set testdir [file dirname $argv0] source $testdir/tester.tcl db close foreach {t failed rc started} { 1.1 {} SQLITE_OK {mutex mem pcache} 1.2 {mutex} SQLITE_ERROR {} 1.3 {mem} SQLITE_ERROR {mutex} | > > > > | 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | # focus of this file is testing the effects of a failure in # sqlite3_initialize(). # # set testdir [file dirname $argv0] source $testdir/tester.tcl if {[db eval {SELECT sqlite_compileoption_used('THREADSAFE=0')}]} { finish_test return } db close foreach {t failed rc started} { 1.1 {} SQLITE_OK {mutex mem pcache} 1.2 {mutex} SQLITE_ERROR {} 1.3 {mem} SQLITE_ERROR {mutex} |
︙ | ︙ | |||
72 73 74 75 76 77 78 | } SQLITE_OK init_wrapper_uninstall } } autoinstall_test_functions finish_test | < | 76 77 78 79 80 81 82 | } SQLITE_OK init_wrapper_uninstall } } autoinstall_test_functions finish_test |
Changes to test/io.test.
︙ | ︙ | |||
196 197 198 199 200 201 202 203 204 | # Test that the journal file is created and sync()d if the transaction # modifies a single database page and also appends a page to the file. # Internally, this case is handled differently to the one above. The # journal file is not actually created until the 'COMMIT' statement # is executed. # do_test io-2.6.1 { execsql { | > > > > > > | | | > > > | | 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 | # Test that the journal file is created and sync()d if the transaction # modifies a single database page and also appends a page to the file. # Internally, this case is handled differently to the one above. The # journal file is not actually created until the 'COMMIT' statement # is executed. # # Changed 2010-03-27: The size of the database is now stored in # bytes 28..31 and so when a page is added to the database, page 1 # is immediately modified and the journal file immediately comes into # existance. To fix this test, the BEGIN is changed into a a # BEGIN IMMEDIATE and the INSERT is omitted. # do_test io-2.6.1 { execsql { BEGIN IMMEDIATE; -- INSERT INTO abc VALUES(9, randstr(1000,1000)); } file exists test.db-journal } {0} do_test io-2.6.2 { # Create a file at "test.db-journal". This will prevent SQLite from # opening the journal for exclusive access. As a result, the COMMIT # should fail with SQLITE_CANTOPEN and the transaction rolled back. # file mkdir test.db-journal catchsql { INSERT INTO abc VALUES(9, randstr(1000,1000)); COMMIT } } {1 {unable to open database file}} do_test io-2.6.3 { file delete -force test.db-journal catchsql { COMMIT } } {0 {}} do_test io-2.6.4 { execsql { SELECT * FROM abc } } {1 2 3 4 5 6 7 8} # Test that if the database modification is part of multi-file commit, # the journal file is always created. In this case, the journal file # is created during execution of the COMMIT statement, so we have to |
︙ | ︙ |
Added test/journal2.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 | # 2010 June 16 # # The author disclaims copyright to this source code. In place of # a legal notice, here is a blessing: # # May you do good and not evil. # May you find forgiveness for yourself and forgive others. # May you share freely, never taking more than you give. # #*********************************************************************** # This file implements regression tests for SQLite library. Specifically, # it tests SQLite when using a VFS that claims the SAFE_DELETE property. # set testdir [file dirname $argv0] source $testdir/tester.tcl source $testdir/lock_common.tcl source $testdir/malloc_common.tcl db close if {[permutation] == "inmemory_journal"} { finish_test return } set a_string_counter 1 proc a_string {n} { global a_string_counter incr a_string_counter string range [string repeat "${a_string_counter}." $n] 1 $n } # Create a [testvfs] and install it as the default VFS. Set the device # characteristics flags to "SAFE_DELETE". # testvfs tvfs -default 1 tvfs devchar undeletable_when_open # Set up a hook so that each time a journal file is opened, closed or # deleted, the method name ("xOpen", "xClose" or "xDelete") and the final # segment of the journal file-name (i.e. "test.db-journal") are appended to # global list variable $::oplog. # tvfs filter {xOpen xClose xDelete} tvfs script journal_op_catcher proc journal_op_catcher {method filename args} { # If global variable ::tvfs_error_on_write is defined, then return an # IO error to every attempt to modify the file-system. Otherwise, return # SQLITE_OK. # if {[info exists ::tvfs_error_on_write]} { if {[lsearch {xDelete xWrite xTruncate} $method]>=0} { return SQLITE_IOERR } } # The rest of this command only deals with xOpen(), xClose() and xDelete() # operations on journal files. If this invocation does not represent such # an operation, return with no further ado. # set f [file tail $filename] if {[string match *journal $f]==0} return if {[lsearch {xOpen xDelete xClose} $method]<0} return # Append a record of this operation to global list variable $::oplog. # lappend ::oplog $method $f # If this is an attempt to delete a journal file for which there exists # one ore more open handles, return an error. The code in test_vfs.c # will not invoke the xDelete method of the "real" VFS in this case. # if {[info exists ::open_journals($f)]==0} { set ::open_journals($f) 0 } switch -- $method { xOpen { incr ::open_journals($f) +1 } xClose { incr ::open_journals($f) -1 } xDelete { if {$::open_journals($f)>0} { return SQLITE_IOERR } } } return "" } do_test journal2-1.1 { set ::oplog [list] sqlite3 db test.db execsql { CREATE TABLE t1(a, b) } set ::oplog } {xOpen test.db-journal xClose test.db-journal xDelete test.db-journal} do_test journal2-1.2 { set ::oplog [list] execsql { PRAGMA journal_mode = truncate; INSERT INTO t1 VALUES(1, 2); } set ::oplog } {xOpen test.db-journal} do_test journal2-1.3 { set ::oplog [list] execsql { INSERT INTO t1 VALUES(3, 4) } set ::oplog } {} do_test journal2-1.4 { execsql { SELECT * FROM t1 } } {1 2 3 4} # Add a second connection. This connection attempts to commit data in # journal_mode=DELETE mode. When it tries to delete the journal file, # the VFS layer returns an IO error. # do_test journal2-1.5 { set ::oplog [list] sqlite3 db2 test.db execsql { PRAGMA journal_mode = delete } db2 catchsql { INSERT INTO t1 VALUES(5, 6) } db2 } {1 {disk I/O error}} do_test journal2-1.6 { file exists test.db-journal } 1 do_test journal2-1.7 { execsql { SELECT * FROM t1 } } {1 2 3 4} do_test journal2-1.8 { execsql { PRAGMA journal_mode = truncate } db2 execsql { INSERT INTO t1 VALUES(5, 6) } db2 } {} do_test journal2-1.9 { execsql { SELECT * FROM t1 } } {1 2 3 4 5 6} # Grow the database until it is reasonably large. # do_test journal2-1.10 { db2 close db func a_string a_string execsql { CREATE TABLE t2(a UNIQUE, b UNIQUE); INSERT INTO t2 VALUES(a_string(200), a_string(300)); INSERT INTO t2 SELECT a_string(200), a_string(300) FROM t2; -- 2 INSERT INTO t2 SELECT a_string(200), a_string(300) FROM t2; -- 4 INSERT INTO t2 SELECT a_string(200), a_string(300) FROM t2; -- 8 INSERT INTO t2 SELECT a_string(200), a_string(300) FROM t2; -- 16 INSERT INTO t2 SELECT a_string(200), a_string(300) FROM t2; -- 32 INSERT INTO t2 SELECT a_string(200), a_string(300) FROM t2; -- 64 } file size test.db-journal } {0} do_test journal2-1.11 { set sz [expr [file size test.db] / 1024] expr {$sz>120 && $sz<200} } 1 # Using new connection [db2] (with journal_mode=DELETE), write a lot of # data to the database. So that many pages within the database file are # modified before the transaction is committed. # # Then, enable simulated IO errors in all calls to xDelete, xWrite # and xTruncate before committing the transaction and closing the # database file. From the point of view of other file-system users, it # appears as if the process hosting [db2] unexpectedly exited. # do_test journal2-1.12 { sqlite3 db2 test.db execsql { PRAGMA cache_size = 10; BEGIN; INSERT INTO t2 SELECT randomblob(200), randomblob(300) FROM t2; -- 128 } db2 } {} do_test journal2-1.13 { tvfs filter {xOpen xClose xDelete xWrite xTruncate} set ::tvfs_error_on_write 1 catchsql { COMMIT } db2 } {1 {disk I/O error}} db2 close unset ::tvfs_error_on_write file copy -force test.db testX.db do_test journal2-1.14 { file exists test.db-journal } 1 do_test journal2-1.15 { execsql { SELECT count(*) FROM t2; PRAGMA integrity_check; } } {64 ok} # This block checks that in the test case above, connection [db2] really # did begin writing to the database file before it hit IO errors. If # this is true, then the copy of the database file made before [db] # rolled back the hot journal should fail the integrity-check. # do_test journal2-1.16 { set sz [expr [file size testX.db] / 1024] expr {$sz>240 && $sz<400} } 1 do_test journal2-1.17 { expr {[catchsql { PRAGMA integrity_check } db] == "0 ok"} } {1} do_test journal2-1.20 { sqlite3 db2 testX.db expr {[catchsql { PRAGMA integrity_check } db2] == "0 ok"} } {0} do_test journal2-1.21 { db2 close } {} db close #------------------------------------------------------------------------- # Test that it is possible to switch from journal_mode=truncate to # journal_mode=WAL on a SAFE_DELETE file-system. SQLite should close and # delete the journal file when committing the transaction that switches # the system to WAL mode. # ifcapable wal { do_test journal2-2.1 { faultsim_delete_and_reopen set ::oplog [list] execsql { PRAGMA journal_mode = persist } set ::oplog } {} do_test journal2-2.2 { execsql { CREATE TABLE t1(x); INSERT INTO t1 VALUES(3.14159); } set ::oplog } {xOpen test.db-journal} do_test journal2-2.3 { expr {[file size test.db-journal] > 512} } {1} do_test journal2-2.4 { set ::oplog [list] execsql { PRAGMA journal_mode = WAL } set ::oplog } {xClose test.db-journal xDelete test.db-journal} db close } tvfs delete finish_test |
Added test/journal3.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 | # 2010 July 15 # # The author disclaims copyright to this source code. In place of # a legal notice, here is a blessing: # # May you do good and not evil. # May you find forgiveness for yourself and forgive others. # May you share freely, never taking more than you give. # #*********************************************************************** # This file implements regression tests for SQLite library. # set testdir [file dirname $argv0] source $testdir/tester.tcl source $testdir/lock_common.tcl source $testdir/malloc_common.tcl #------------------------------------------------------------------------- # If a connection is required to create a journal file, it creates it with # the same file-system permissions as the database file itself. Test this. # if {$::tcl_platform(platform) == "unix"} { set umask [exec /bin/sh -c umask] faultsim_delete_and_reopen do_test journal3-1.1 { execsql { CREATE TABLE tx(y, z) } } {} foreach {tn permissions} { 1 00644 2 00666 3 00600 4 00755 } { db close set effective [format %.5o [expr $permissions & ~$umask]] do_test journal3-1.2.$tn.1 { catch { file delete -force test.db-journal } file attributes test.db -permissions $permissions file attributes test.db -permissions } $permissions do_test journal3-1.2.$tn.2 { file exists test.db-journal } {0} do_test journal3-1.2.$tn.3 { sqlite3 db test.db execsql { BEGIN; INSERT INTO tx DEFAULT VALUES; } file exists test.db-journal } {1} do_test journal3-1.2.$tn.4 { file attr test.db-journal -perm } $effective do_execsql_test journal3-1.2.$tn.5 { ROLLBACK } {} } } finish_test |
Changes to test/jrnlmode.test.
︙ | ︙ | |||
128 129 130 131 132 133 134 | PRAGMA aux1.journal_mode; } } [list off [temp_journal_mode persist] memory] do_test jrnlmode-1.11 { execsql { PRAGMA journal_mode; } | | | 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 | PRAGMA aux1.journal_mode; } } [list off [temp_journal_mode persist] memory] do_test jrnlmode-1.11 { execsql { PRAGMA journal_mode; } } {off} do_test jrnlmode-1.12 { execsql { ATTACH ':memory:' as aux2; } execsql { PRAGMA main.journal_mode; PRAGMA aux1.journal_mode; |
︙ | ︙ | |||
313 314 315 316 317 318 319 320 321 | do_test jrnlmode-5.2 { execsql { PRAGMA journal_size_limit } } {-1} do_test jrnlmode-5.3 { execsql { ATTACH 'test2.db' AS aux; PRAGMA aux.journal_size_limit; } | > | > | > > | | 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 | do_test jrnlmode-5.2 { execsql { PRAGMA journal_size_limit } } {-1} do_test jrnlmode-5.3 { execsql { ATTACH 'test2.db' AS aux; PRAGMA aux.journal_mode=persist; PRAGMA aux.journal_size_limit; } } {persist -1} do_test jrnlmode-5.4.1 { execsql { PRAGMA aux.journal_size_limit = 999999999999 } } {999999999999} do_test jrnlmode-5.4.2 { execsql { PRAGMA aux.journal_size_limit = 10240 } } {10240} do_test jrnlmode-5.5 { execsql { PRAGMA main.journal_size_limit = 20480 } } {20480} do_test jrnlmode-5.6 { execsql { PRAGMA journal_size_limit } } {20480} do_test jrnlmode-5.7 { execsql { PRAGMA aux.journal_size_limit } } {10240} do_test jrnlmode-5.8 { execsql { ATTACH 'test3.db' AS aux2; PRAGMA aux2.journal_mode=persist; } } {persist} do_test jrnlmode-5.9 { execsql { CREATE TABLE main.t1(a, b, c); CREATE TABLE aux.t2(a, b, c); CREATE TABLE aux2.t3(a, b, c); } |
︙ | ︙ | |||
450 451 452 453 454 455 456 | list [file exists test.db-journal] [file size test.db-journal] } {1 0} } ifcapable pragma { # These tests are not run as part of the "journaltest" permutation, # as the test_journal.c layer is incompatible with in-memory journaling. | | | 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 | list [file exists test.db-journal] [file size test.db-journal] } {1 0} } ifcapable pragma { # These tests are not run as part of the "journaltest" permutation, # as the test_journal.c layer is incompatible with in-memory journaling. if {[permutation] ne "journaltest"} { do_test jrnlmode-6.1 { execsql { PRAGMA journal_mode = truncate; CREATE TABLE t4(a, b); BEGIN; INSERT INTO t4 VALUES(1, 2); |
︙ | ︙ | |||
480 481 482 483 484 485 486 | do_test jrnlmode-6.5 { execsql { PRAGMA journal_mode = MEMORY; BEGIN; INSERT INTO t4 VALUES(3, 4); } file exists test.db-journal | | | | > > > > > > > > > > > > > > > > | 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 | do_test jrnlmode-6.5 { execsql { PRAGMA journal_mode = MEMORY; BEGIN; INSERT INTO t4 VALUES(3, 4); } file exists test.db-journal } {0} do_test jrnlmode-6.7 { execsql { COMMIT; SELECT * FROM t4; } } {1 2 3 4} do_test jrnlmode-6.8 { file exists test.db-journal } {0} do_test jrnlmode-6.9 { execsql { PRAGMA journal_mode = DELETE; BEGIN IMMEDIATE; INSERT INTO t4 VALUES(1,2); COMMIT; } file exists test.db-journal } {0} } } ifcapable pragma { catch { db close } do_test jrnlmode-7.1 { foreach f [glob -nocomplain test.db*] { file delete -force $f } sqlite3 db test.db execsql { PRAGMA journal_mode = memory; PRAGMA auto_vacuum = 0; PRAGMA page_size = 1024; PRAGMA user_version = 5; PRAGMA user_version; } } {memory 5} do_test jrnlmode-7.2 { file size test.db } {1024} } finish_test |
Changes to test/jrnlmode2.test.
1 2 3 4 5 6 7 8 9 10 11 | # 2009 March 24 # # The author disclaims copyright to this source code. In place of # a legal notice, here is a blessing: # # May you do good and not evil. # May you find forgiveness for yourself and forgive others. # May you share freely, never taking more than you give. # #*********************************************************************** # | < > | | > > > > > > > > > > > > | > > > > > > > > > > | 1 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 | # 2009 March 24 # # The author disclaims copyright to this source code. In place of # a legal notice, here is a blessing: # # May you do good and not evil. # May you find forgiveness for yourself and forgive others. # May you share freely, never taking more than you give. # #*********************************************************************** # set testdir [file dirname $argv0] source $testdir/tester.tcl ifcapable {!pager_pragmas} { finish_test return } #------------------------------------------------------------------------- # The tests in this file check that the following two bugs (both now fixed) # do not reappear. # # jrnlmode2-1.*: Demonstrate bug #3745: # # In persistent journal mode, if: # # * There is a persistent journal in the file-system, AND # * there exists a connection with a shared lock on the db file, # # then a second connection cannot open a read-transaction on the database. # The reason is because while determining that the persistent-journal is # not a hot-journal, SQLite currently grabs an exclusive lock on the # database file. If this fails because another connection has a shared # lock, then SQLITE_BUSY is returned to the user. # # jrnlmode2-2.*: Demonstrate bug #3751: # # If a connection is opened in SQLITE_OPEN_READONLY mode, the underlying # unix file descriptor on the database file is opened in O_RDONLY mode. # # When SQLite queries the database file for the schema in order to compile # the SELECT statement, it sees the empty journal in the file system, it # attempts to obtain an exclusive lock on the database file (this is a # bug). The attempt to obtain an exclusive (write) lock on a read-only file # fails at the OS level. Under unix, fcntl() reports an EBADF - "Bad file # descriptor" - error. # do_test jrnlmode2-1.1 { execsql { PRAGMA journal_mode = persist; CREATE TABLE t1(a, b); INSERT INTO t1 VALUES(1, 2); |
︙ | ︙ | |||
42 43 44 45 46 47 48 49 50 51 52 53 54 55 | sqlite3 db2 test.db execsql { SELECT * FROM t1 } db2 } {1 2} do_test jrnlmode2-1.4 { execsql { INSERT INTO t1 VALUES(3, 4); BEGIN; SELECT * FROM t1; } execsql { PRAGMA lock_status } } {main shared temp closed} do_test jrnlmode2-1.5 { | > > | 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 | sqlite3 db2 test.db execsql { SELECT * FROM t1 } db2 } {1 2} do_test jrnlmode2-1.4 { execsql { INSERT INTO t1 VALUES(3, 4); } execsql { BEGIN; SELECT * FROM t1; } execsql { PRAGMA lock_status } } {main shared temp closed} do_test jrnlmode2-1.5 { |
︙ | ︙ | |||
83 84 85 86 87 88 89 90 91 | do_test jrnlmode2-2.4 { sqlite3 db2 test.db -readonly 1 catchsql { SELECT * FROM t1 } db2 } {0 {1 2 3 4 5 6}} do_test jrnlmode2-2.5 { file delete test.db-journal } {} | > < | 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 | do_test jrnlmode2-2.4 { sqlite3 db2 test.db -readonly 1 catchsql { SELECT * FROM t1 } db2 } {0 {1 2 3 4 5 6}} do_test jrnlmode2-2.5 { db close file delete test.db-journal } {} do_test jrnlmode2-2.6 { sqlite3 db2 test.db -readonly 1 catchsql { SELECT * FROM t1 } db2 } {0 {1 2 3 4 5 6}} catch { db2 close } finish_test |
Changes to test/lock2.test.
︙ | ︙ | |||
12 13 14 15 16 17 18 | # focus of this script is database locks between competing processes. # # $Id: lock2.test,v 1.11 2009/05/01 10:55:34 danielk1977 Exp $ set testdir [file dirname $argv0] source $testdir/tester.tcl | | < < < < < < < < | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | 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 | # focus of this script is database locks between competing processes. # # $Id: lock2.test,v 1.11 2009/05/01 10:55:34 danielk1977 Exp $ set testdir [file dirname $argv0] source $testdir/tester.tcl source $testdir/lock_common.tcl # Simple locking test case: # # lock2-1.1: Connect a second process to the database. # lock2-1.2: Establish a RESERVED lock with this process. # lock2-1.3: Get a SHARED lock with the second process. # lock2-1.4: Try for a RESERVED lock with process 2. This fails. # lock2-1.5: Try to upgrade the first process to EXCLUSIVE, this fails so # it gets PENDING. # lock2-1.6: Release the SHARED lock held by the second process. # lock2-1.7: Attempt to reaquire a SHARED lock with the second process. # this fails due to the PENDING lock. # lock2-1.8: Ensure the first process can now upgrade to EXCLUSIVE. # do_test lock2-1.1 { set ::tf1 [launch_testfixture] testfixture $::tf1 { sqlite3 db test.db -key xyzzy db eval {select * from sqlite_master} } } {} do_test lock2-1.1.1 { execsql {pragma lock_status} |
︙ | ︙ | |||
117 118 119 120 121 122 123 | BEGIN; SELECT * FROM sqlite_master; } } } {} do_test lock2-1.4 { testfixture $::tf1 { | < | < > | | > > | | 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 | BEGIN; SELECT * FROM sqlite_master; } } } {} do_test lock2-1.4 { testfixture $::tf1 { catch { db eval { CREATE TABLE def(d, e, f) } } msg set msg } } {database is locked} do_test lock2-1.5 { catchsql { COMMIT; } } {1 {database is locked}} do_test lock2-1.6 { testfixture $::tf1 { db eval { SELECT * FROM sqlite_master; COMMIT; } } } {} do_test lock2-1.7 { testfixture $::tf1 { catch { db eval { BEGIN; SELECT * FROM sqlite_master; } } msg set msg } } {database is locked} do_test lock2-1.8 { catchsql { COMMIT; } } {0 {}} do_test lock2-1.9 { execsql { SELECT * FROM sqlite_master; } } "table abc abc [expr $AUTOVACUUM?3:2] {CREATE TABLE abc(a, b, c)}" catch flush_async_queue do_test lock2-1.10 { testfixture $::tf1 { db eval { SELECT * FROM sqlite_master; } } } "table abc abc [expr $AUTOVACUUM?3:2] {CREATE TABLE abc(a, b, c)}" catch {testfixture $::tf1 {db close}} catch {close $::tf1} sqlite3_soft_heap_limit $cmdlinearg(soft-heap-limit) finish_test |
Changes to test/lock4.test.
︙ | ︙ | |||
12 13 14 15 16 17 18 19 20 21 22 23 24 25 | # focus of this script is database locks. # # $Id: lock4.test,v 1.10 2009/05/06 00:52:41 drh Exp $ set testdir [file dirname $argv0] source $testdir/tester.tcl # Initialize the test.db database so that it is non-empty # do_test lock4-1.1 { db eval { PRAGMA auto_vacuum=OFF; CREATE TABLE t1(x); | > > | 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | # focus of this script is database locks. # # $Id: lock4.test,v 1.10 2009/05/06 00:52:41 drh Exp $ set testdir [file dirname $argv0] source $testdir/tester.tcl do_not_use_codec # Initialize the test.db database so that it is non-empty # do_test lock4-1.1 { db eval { PRAGMA auto_vacuum=OFF; CREATE TABLE t1(x); |
︙ | ︙ |
Changes to test/lock5.test.
︙ | ︙ | |||
117 118 119 120 121 122 123 | # Make sure we are not accidentally using the dotfile locking scheme. do_test lock5-flock.2 { file exists test.db.lock } {0} do_test lock5-flock.3 { | | | 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 | # Make sure we are not accidentally using the dotfile locking scheme. do_test lock5-flock.2 { file exists test.db.lock } {0} do_test lock5-flock.3 { catch { sqlite3 db2 test.db -vfs unix-flock } catchsql { SELECT * FROM t1 } db2 } {1 {database is locked}} do_test lock5-flock.4 { execsql COMMIT catchsql { SELECT * FROM t1 } db2 } {0 {1 2}} |
︙ | ︙ |
Changes to test/lock6.test.
︙ | ︙ | |||
157 158 159 160 161 162 163 | } db } {} catch {close $::tf1} set env(SQLITE_FORCE_PROXY_LOCKING) $using_proxy set sqlite_hostid_num 0 | | < | 157 158 159 160 161 162 163 164 165 166 167 | } db } {} catch {close $::tf1} set env(SQLITE_FORCE_PROXY_LOCKING) $using_proxy set sqlite_hostid_num 0 sqlite3_soft_heap_limit $cmdlinearg(soft-heap-limit) } finish_test |
Added test/lock_common.tcl.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 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 | # 2010 April 14 # # The author disclaims copyright to this source code. In place of # a legal notice, here is a blessing: # # May you do good and not evil. # May you find forgiveness for yourself and forgive others. # May you share freely, never taking more than you give. # #*********************************************************************** # This file contains code used by several different test scripts. The # code in this file allows testfixture to control another process (or # processes) to test locking. # proc do_multiclient_test {varname script} { foreach code [list { set ::code2_chan [launch_testfixture] set ::code3_chan [launch_testfixture] proc code2 {tcl} { testfixture $::code2_chan $tcl } proc code3 {tcl} { testfixture $::code3_chan $tcl } set tn 1 } { proc code2 {tcl} { uplevel #0 $tcl } proc code3 {tcl} { uplevel #0 $tcl } set tn 2 }] { faultsim_delete_and_reopen # Open connections [db2] and [db3]. Depending on which iteration this # is, the connections may be created in this interpreter, or in # interpreters running in other OS processes. As such, the [db2] and [db3] # commands should only be accessed within [code2] and [code3] blocks, # respectively. # eval $code code2 { sqlite3 db2 test.db } code3 { sqlite3 db3 test.db } # Shorthand commands. Execute SQL using database connection [db2] or # [db3]. Return the results. # proc sql1 {sql} { db eval $sql } proc sql2 {sql} { code2 [list db2 eval $sql] } proc sql3 {sql} { code3 [list db3 eval $sql] } proc csql1 {sql} { list [catch { sql1 $sql } msg] $msg } proc csql2 {sql} { list [catch { sql2 $sql } msg] $msg } proc csql3 {sql} { list [catch { sql3 $sql } msg] $msg } uplevel set $varname $tn uplevel $script code2 { db2 close } code3 { db3 close } catch { close $::code2_chan } catch { close $::code3_chan } } } # Launch another testfixture process to be controlled by this one. A # channel name is returned that may be passed as the first argument to proc # 'testfixture' to execute a command. The child testfixture process is shut # down by closing the channel. proc launch_testfixture {} { write_main_loop set prg [info nameofexec] if {$prg eq ""} { set prg [file join . testfixture] } set chan [open "|$prg tf_main.tcl" r+] fconfigure $chan -buffering line testfixture $chan "sqlite3_test_control_pending_byte $::sqlite_pending_byte" return $chan } # Execute a command in a child testfixture process, connected by two-way # channel $chan. Return the result of the command, or an error message. # proc testfixture {chan cmd} { puts $chan $cmd puts $chan OVER set r "" while { 1 } { set line [gets $chan] if { $line == "OVER" } { set res [lindex $r 1] if { [lindex $r 0] } { error $res } return $res } if {[eof $chan]} { return "ERROR: Child process hung up" } append r $line } } proc testfixture_nb_cb {varname chan} { if {[eof $chan]} { append ::tfnb($chan) "ERROR: Child process hung up" set line "OVER" } else { set line [gets $chan] } if { $line == "OVER" } { set $varname [lindex $::tfnb($chan) 1] unset ::tfnb($chan) close $chan } else { append ::tfnb($chan) $line } } proc testfixture_nb {varname cmd} { set chan [launch_testfixture] set ::tfnb($chan) "" fconfigure $chan -blocking 0 -buffering none puts $chan $cmd puts $chan OVER fileevent $chan readable [list testfixture_nb_cb $varname $chan] return "" } # Write the main loop for the child testfixture processes into file # tf_main.tcl. The parent (this script) interacts with the child processes # via a two way pipe. The parent writes a script to the stdin of the child # process, followed by the word "OVER" on a line of its own. The child # process evaluates the script and writes the results to stdout, followed # by an "OVER" of its own. # set main_loop_written 0 proc write_main_loop {} { if {$::main_loop_written} return set wrapper "" if {[sqlite3 -has-codec] && [info exists ::do_not_use_codec]==0} { set wrapper " rename sqlite3 sqlite_orig proc sqlite3 {args} {[info body sqlite3]} " } set fd [open tf_main.tcl w] puts $fd [string map [list %WRAPPER% $wrapper] { %WRAPPER% set script "" while {![eof stdin]} { flush stdout set line [gets stdin] if { $line == "OVER" } { set rc [catch {eval $script} result] puts [list $rc $result] puts OVER flush stdout set script "" } else { append script $line append script "\n" } } }] close $fd set main_loop_written 1 } |
Changes to test/lookaside.test.
︙ | ︙ | |||
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 | set testdir [file dirname $argv0] source $testdir/tester.tcl ifcapable !lookaside { finish_test return } catch {db close} sqlite3_shutdown sqlite3_config_pagecache 0 0 sqlite3_config_scratch 0 0 sqlite3_initialize autoinstall_test_functions sqlite3 db test.db # Make sure sqlite3_db_config() and sqlite3_db_status are working. # do_test lookaside-1.1 { catch {sqlite3_config_error db} } {0} do_test lookaside-1.2 { sqlite3_db_config_lookaside db 1 18 18 } {0} do_test lookaside-1.3 { sqlite3_db_status db SQLITE_DBSTATUS_LOOKASIDE_USED 0 } {0 0 0} do_test lookaside-1.4 { db eval {CREATE TABLE t1(w,x,y,z);} foreach {x y z} [sqlite3_db_status db SQLITE_DBSTATUS_LOOKASIDE_USED 0] break expr {$x==0 && $y<$z && $z==18} } {0} do_test lookaside-1.5 { foreach {x y z} [sqlite3_db_status db SQLITE_DBSTATUS_LOOKASIDE_USED 1] break | > > > > > > > > > > | 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 | set testdir [file dirname $argv0] source $testdir/tester.tcl ifcapable !lookaside { finish_test return } # The tests in this file configure the lookaside allocator after a # connection is opened. This will not work if there is any "presql" # configured (SQL run within the [sqlite3] wrapper in tester.tcl). if {[info exists ::G(perm:presql)]} { finish_test return } catch {db close} sqlite3_shutdown sqlite3_config_pagecache 0 0 sqlite3_config_scratch 0 0 sqlite3_initialize autoinstall_test_functions sqlite3 db test.db # Make sure sqlite3_db_config() and sqlite3_db_status are working. # do_test lookaside-1.1 { catch {sqlite3_config_error db} } {0} do_test lookaside-1.2 { sqlite3_db_config_lookaside db 1 18 18 } {0} do_test lookaside-1.3 { sqlite3_db_status db SQLITE_DBSTATUS_LOOKASIDE_USED 0 } {0 0 0} do_test lookaside-1.4 { db eval {CREATE TABLE t1(w,x,y,z);} foreach {x y z} [sqlite3_db_status db SQLITE_DBSTATUS_LOOKASIDE_USED 0] break expr {$x==0 && $y<$z && $z==18} } {0} do_test lookaside-1.5 { foreach {x y z} [sqlite3_db_status db SQLITE_DBSTATUS_LOOKASIDE_USED 1] break |
︙ | ︙ |
Changes to test/main.test.
︙ | ︙ | |||
294 295 296 297 298 299 300 | db complete "create \200trigger;" } {1} } # Try to open a database with a corrupt database file. # | > | | | | | | | | | > | 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 | db complete "create \200trigger;" } {1} } # Try to open a database with a corrupt database file. # if {[permutation] == ""} { do_test main-2.0 { catch {db close} file delete -force test.db set fd [open test.db w] puts $fd hi! close $fd set v [catch {sqlite3 db test.db} msg] if {$v} {lappend v $msg} {lappend v {}} } {0 {}} } # Here are some tests for tokenize.c. # do_test main-3.1 { catch {db close} catch {foreach f [glob -nocomplain testdb/*] {file delete -force $f}} file delete -force testdb |
︙ | ︙ |
Changes to test/malloc.test.
︙ | ︙ | |||
330 331 332 333 334 335 336 337 338 339 340 341 342 343 | if {$tcl_platform(platform)!="windows"} { do_malloc_test 14 -tclprep { catch {db close} sqlite3 db2 test2.db sqlite3_extended_result_codes db2 1 db2 eval { PRAGMA synchronous = 0; CREATE TABLE t1(a, b); INSERT INTO t1 VALUES(1, 2); BEGIN; INSERT INTO t1 VALUES(3, 4); } copy_file test2.db test.db | > | 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 | if {$tcl_platform(platform)!="windows"} { do_malloc_test 14 -tclprep { catch {db close} sqlite3 db2 test2.db sqlite3_extended_result_codes db2 1 db2 eval { PRAGMA journal_mode = DELETE; /* For inmemory_journal permutation */ PRAGMA synchronous = 0; CREATE TABLE t1(a, b); INSERT INTO t1 VALUES(1, 2); BEGIN; INSERT INTO t1 VALUES(3, 4); } copy_file test2.db test.db |
︙ | ︙ |
Changes to test/mallocAll.test.
︙ | ︙ | |||
26 27 28 29 30 31 32 | } } set testdir [file dirname $argv0] source $testdir/tester.tcl rename finish_test really_finish_test proc finish_test {} {} | | | 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 | } } set testdir [file dirname $argv0] source $testdir/tester.tcl rename finish_test really_finish_test proc finish_test {} {} set G(isquick) 1 set EXCLUDE { mallocAll.test } if {[sqlite3 -has-codec]} { # lappend EXCLUDE \ |
︙ | ︙ | |||
52 53 54 55 56 57 58 | set tail [file tail $testfile] if {[lsearch -exact $EXCLUDE $tail]>=0} continue if {[llength $INCLUDE]>0 && [lsearch -exact $INCLUDE $tail]<0} continue source $testfile catch {db close} if {$sqlite_open_file_count>0} { puts "$tail did not close all files: $sqlite_open_file_count" | < | | 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 | set tail [file tail $testfile] if {[lsearch -exact $EXCLUDE $tail]>=0} continue if {[llength $INCLUDE]>0 && [lsearch -exact $INCLUDE $tail]<0} continue source $testfile catch {db close} if {$sqlite_open_file_count>0} { puts "$tail did not close all files: $sqlite_open_file_count" fail_test $tail set sqlite_open_file_count 0 } } source $testdir/misuse.test set sqlite_open_file_count 0 really_finish_test |
Changes to test/mallocC.test.
︙ | ︙ | |||
72 73 74 75 76 77 78 | # Checksum the database. #do_test mallocC-$tn.$::n.3 { # allcksum db #} $sum #integrity_check mallocC-$tn.$::n.4 | < | 72 73 74 75 76 77 78 79 80 81 82 83 84 85 | # Checksum the database. #do_test mallocC-$tn.$::n.3 { # allcksum db #} $sum #integrity_check mallocC-$tn.$::n.4 } unset ::mallocopts } sqlite3_extended_result_codes db 1 execsql { |
︙ | ︙ |
Changes to test/mallocI.test.
︙ | ︙ | |||
53 54 55 56 57 58 59 60 61 62 63 | SELECT * FROM t1 } -cleanup { do_test mallocI-4.$::n.2 { # If this INSERT is possible then [db] does not hold a shared lock # on the database file. catchsql { INSERT INTO t1 VALUES(1, 2, 3) } db2 } {0 {}} } catch { db2 close } finish_test | > | 53 54 55 56 57 58 59 60 61 62 63 64 | SELECT * FROM t1 } -cleanup { do_test mallocI-4.$::n.2 { # If this INSERT is possible then [db] does not hold a shared lock # on the database file. catchsql { INSERT INTO t1 VALUES(1, 2, 3) } db2 } {0 {}} catch {db2 close} } catch { db2 close } finish_test |
Changes to test/malloc_common.tcl.
︙ | ︙ | |||
18 19 20 21 22 23 24 25 26 27 28 29 30 31 | # ifcapable builtin_test { set MEMDEBUG 1 } else { set MEMDEBUG 0 return 0 } # Usage: do_malloc_test <test number> <options...> # # The first argument, <test number>, is an integer used to name the # tests executed by this proc. Options are as follows: # # -tclprep TCL script to run to prepare test. | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | # ifcapable builtin_test { set MEMDEBUG 1 } else { set MEMDEBUG 0 return 0 } # Transient and persistent OOM errors: # set FAULTSIM(oom-transient) [list \ -injectstart {oom_injectstart 0} \ -injectstop oom_injectstop \ -injecterrlist {{1 {out of memory}}} \ ] set FAULTSIM(oom-persistent) [list \ -injectstart {oom_injectstart 1000000} \ -injectstop oom_injectstop \ -injecterrlist {{1 {out of memory}}} \ ] # Transient and persistent IO errors: # set FAULTSIM(ioerr-transient) [list \ -injectstart {ioerr_injectstart 0} \ -injectstop ioerr_injectstop \ -injecterrlist {{1 {disk I/O error}}} \ ] set FAULTSIM(ioerr-persistent) [list \ -injectstart {ioerr_injectstart 1} \ -injectstop ioerr_injectstop \ -injecterrlist {{1 {disk I/O error}}} \ ] # SQLITE_FULL errors (always persistent): # set FAULTSIM(full) [list \ -injectinstall fullerr_injectinstall \ -injectstart fullerr_injectstart \ -injectstop fullerr_injectstop \ -injecterrlist {{1 {database or disk is full}}} \ -injectuninstall fullerr_injectuninstall \ ] # Transient and persistent SHM errors: # set FAULTSIM(shmerr-transient) [list \ -injectinstall shmerr_injectinstall \ -injectstart {shmerr_injectstart 0} \ -injectstop shmerr_injectstop \ -injecterrlist {{1 {disk I/O error}}} \ -injectuninstall shmerr_injectuninstall \ ] set FAULTSIM(shmerr-persistent) [list \ -injectinstall shmerr_injectinstall \ -injectstart {shmerr_injectstart 1} \ -injectstop shmerr_injectstop \ -injecterrlist {{1 {disk I/O error}}} \ -injectuninstall shmerr_injectuninstall \ ] # Transient and persistent CANTOPEN errors: # set FAULTSIM(cantopen-transient) [list \ -injectinstall cantopen_injectinstall \ -injectstart {cantopen_injectstart 0} \ -injectstop cantopen_injectstop \ -injecterrlist {{1 {unable to open database file}}} \ -injectuninstall cantopen_injectuninstall \ ] set FAULTSIM(cantopen-persistent) [list \ -injectinstall cantopen_injectinstall \ -injectstart {cantopen_injectstart 1} \ -injectstop cantopen_injectstop \ -injecterrlist {{1 {unable to open database file}}} \ -injectuninstall cantopen_injectuninstall \ ] #-------------------------------------------------------------------------- # Usage do_faultsim_test NAME ?OPTIONS...? # # -faults List of fault types to simulate. # # -prep Script to execute before -body. # # -body Script to execute (with fault injection). # # -test Script to execute after -body. # proc do_faultsim_test {name args} { global FAULTSIM set DEFAULT(-faults) [array names FAULTSIM] set DEFAULT(-prep) "" set DEFAULT(-body) "" set DEFAULT(-test) "" array set O [array get DEFAULT] array set O $args foreach o [array names O] { if {[info exists DEFAULT($o)]==0} { error "unknown option: $o" } } set faultlist [list] foreach f $O(-faults) { set flist [array names FAULTSIM $f] if {[llength $flist]==0} { error "unknown fault: $f" } set faultlist [concat $faultlist $flist] } set testspec [list -prep $O(-prep) -body $O(-body) -test $O(-test)] foreach f [lsort -unique $faultlist] { eval do_one_faultsim_test "$name-$f" $FAULTSIM($f) $testspec } } #------------------------------------------------------------------------- # Procedures to save and restore the current file-system state: # # faultsim_save # faultsim_restore # faultsim_save_and_close # faultsim_restore_and_reopen # faultsim_delete_and_reopen # proc faultsim_save {} { foreach f [glob -nocomplain sv_test.db*] { file delete -force $f } foreach f [glob -nocomplain test.db*] { set f2 "sv_$f" file copy -force $f $f2 } } proc faultsim_save_and_close {} { faultsim_save catch { db close } return "" } proc faultsim_restore {} { foreach f [glob -nocomplain test.db*] { file delete -force $f } foreach f2 [glob -nocomplain sv_test.db*] { set f [string range $f2 3 end] file copy -force $f2 $f } } proc faultsim_restore_and_reopen {{dbfile test.db}} { catch { db close } faultsim_restore sqlite3 db $dbfile sqlite3_extended_result_codes db 1 sqlite3_db_config_lookaside db 0 0 0 } proc faultsim_integrity_check {{db db}} { set ic [$db eval { PRAGMA integrity_check }] if {$ic != "ok"} { error "Integrity check: $ic" } } proc faultsim_delete_and_reopen {{file test.db}} { catch { db close } foreach f [glob -nocomplain test.db*] { file delete -force $f } sqlite3 db $file } # The following procs are used as [do_one_faultsim_test] callbacks when # injecting OOM faults into test cases. # proc oom_injectstart {nRepeat iFail} { sqlite3_memdebug_fail $iFail -repeat $nRepeat } proc oom_injectstop {} { sqlite3_memdebug_fail -1 } # The following procs are used as [do_one_faultsim_test] callbacks when # injecting IO error faults into test cases. # proc ioerr_injectstart {persist iFail} { set ::sqlite_io_error_persist $persist set ::sqlite_io_error_pending $iFail } proc ioerr_injectstop {} { set sv $::sqlite_io_error_hit set ::sqlite_io_error_persist 0 set ::sqlite_io_error_pending 0 set ::sqlite_io_error_hardhit 0 set ::sqlite_io_error_hit 0 set ::sqlite_io_error_pending 0 return $sv } # The following procs are used as [do_one_faultsim_test] callbacks when # injecting shared-memory related error faults into test cases. # proc shmerr_injectinstall {} { testvfs shmfault -default true shmfault filter {xShmOpen xShmMap xShmLock} } proc shmerr_injectuninstall {} { catch {db close} catch {db2 close} shmfault delete } proc shmerr_injectstart {persist iFail} { shmfault ioerr $iFail $persist } proc shmerr_injectstop {} { shmfault ioerr } # The following procs are used as [do_one_faultsim_test] callbacks when # injecting SQLITE_FULL error faults into test cases. # proc fullerr_injectinstall {} { testvfs shmfault -default true } proc fullerr_injectuninstall {} { catch {db close} catch {db2 close} shmfault delete } proc fullerr_injectstart {iFail} { shmfault full $iFail 1 } proc fullerr_injectstop {} { shmfault full } # The following procs are used as [do_one_faultsim_test] callbacks when # injecting SQLITE_CANTOPEN error faults into test cases. # proc cantopen_injectinstall {} { testvfs shmfault -default true } proc cantopen_injectuninstall {} { catch {db close} catch {db2 close} shmfault delete } proc cantopen_injectstart {persist iFail} { shmfault cantopen $iFail $persist } proc cantopen_injectstop {} { shmfault cantopen } # This command is not called directly. It is used by the # [faultsim_test_result] command created by [do_faultsim_test] and used # by -test scripts. # proc faultsim_test_result_int {args} { upvar testrc testrc testresult testresult testnfail testnfail set t [list $testrc $testresult] set r $args if { ($testnfail==0 && $t != [lindex $r 0]) || [lsearch $r $t]<0 } { error "nfail=$testnfail rc=$testrc result=$testresult" } } #-------------------------------------------------------------------------- # Usage do_one_faultsim_test NAME ?OPTIONS...? # # The first argument, <test number>, is used as a prefix of the test names # taken by tests executed by this command. Options are as follows. All # options take a single argument. # # -injectstart Script to enable fault-injection. # # -injectstop Script to disable fault-injection. # # -injecterrlist List of generally acceptable test results (i.e. error # messages). Example: [list {1 {out of memory}}] # # -injectinstall # # -injectuninstall # # -prep Script to execute before -body. # # -body Script to execute (with fault injection). # # -test Script to execute after -body. # proc do_one_faultsim_test {testname args} { set DEFAULT(-injectstart) "expr" set DEFAULT(-injectstop) "expr 0" set DEFAULT(-injecterrlist) [list] set DEFAULT(-injectinstall) "" set DEFAULT(-injectuninstall) "" set DEFAULT(-prep) "" set DEFAULT(-body) "" set DEFAULT(-test) "" array set O [array get DEFAULT] array set O $args foreach o [array names O] { if {[info exists DEFAULT($o)]==0} { error "unknown option: $o" } } proc faultsim_test_proc {testrc testresult testnfail} $O(-test) proc faultsim_test_result {args} " uplevel faultsim_test_result_int \$args [list $O(-injecterrlist)] " eval $O(-injectinstall) set stop 0 for {set iFail 1} {!$stop} {incr iFail} { # Evaluate the -prep script. # eval $O(-prep) # Start the fault-injection. Run the -body script. Stop the fault # injection. Local var $nfail is set to the total number of faults # injected into the system this trial. # eval $O(-injectstart) $iFail set rc [catch $O(-body) res] set nfail [eval $O(-injectstop)] # Run the -test script. If it throws no error, consider this trial # sucessful. If it does throw an error, cause a [do_test] test to # fail (and print out the unexpected exception thrown by the -test # script at the same time). # set rc [catch [list faultsim_test_proc $rc $res $nfail] res] if {$rc == 0} {set res ok} do_test $testname.$iFail [list list $rc $res] {0 ok} # If no faults where injected this trial, don't bother running # any more. This test is finished. # if {$nfail==0} { set stop 1 } } eval $O(-injectuninstall) } # Usage: do_malloc_test <test number> <options...> # # The first argument, <test number>, is an integer used to name the # tests executed by this proc. Options are as follows: # # -tclprep TCL script to run to prepare test. |
︙ | ︙ | |||
83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 | do_test ${tn}.${zRepeat}.${::n} { # Remove all traces of database files test.db and test2.db # from the file-system. Then open (empty database) "test.db" # with the handle [db]. # catch {db close} catch {file delete -force test.db} catch {file delete -force test.db-journal} catch {file delete -force test2.db} catch {file delete -force test2.db-journal} if {[info exists ::mallocopts(-testdb)]} { file copy $::mallocopts(-testdb) test.db } catch { sqlite3 db test.db } if {[info commands db] ne ""} { sqlite3_extended_result_codes db 1 } | > > > | 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 | do_test ${tn}.${zRepeat}.${::n} { # Remove all traces of database files test.db and test2.db # from the file-system. Then open (empty database) "test.db" # with the handle [db]. # catch {db close} catch {db2 close} catch {file delete -force test.db} catch {file delete -force test.db-journal} catch {file delete -force test.db-wal} catch {file delete -force test2.db} catch {file delete -force test2.db-journal} catch {file delete -force test2.db-wal} if {[info exists ::mallocopts(-testdb)]} { file copy $::mallocopts(-testdb) test.db } catch { sqlite3 db test.db } if {[info commands db] ne ""} { sqlite3_extended_result_codes db 1 } |
︙ | ︙ | |||
161 162 163 164 165 166 167 | catch [list uplevel #0 $::mallocopts(-cleanup)] msg } } } unset ::mallocopts sqlite3_memdebug_fail -1 } | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | catch [list uplevel #0 $::mallocopts(-cleanup)] msg } } } unset ::mallocopts sqlite3_memdebug_fail -1 } #------------------------------------------------------------------------- # This proc is used to test a single SELECT statement. Parameter $name is # passed a name for the test case (i.e. "fts3_malloc-1.4.1") and parameter # $sql is passed the text of the SELECT statement. Parameter $result is # set to the expected output if the SELECT statement is successfully # executed using [db eval]. # # Example: # # do_select_test testcase-1.1 "SELECT 1+1, 1+2" {1 2} # # If global variable DO_MALLOC_TEST is set to a non-zero value, or if # it is not defined at all, then OOM testing is performed on the SELECT # statement. Each OOM test case is said to pass if either (a) executing # the SELECT statement succeeds and the results match those specified # by parameter $result, or (b) TCL throws an "out of memory" error. # # If DO_MALLOC_TEST is defined and set to zero, then the SELECT statement # is executed just once. In this case the test case passes if the results # match the expected results passed via parameter $result. # proc do_select_test {name sql result} { uplevel [list doPassiveTest 0 $name $sql [list 0 $result]] } proc do_restart_select_test {name sql result} { uplevel [list doPassiveTest 1 $name $sql [list 0 $result]] } proc do_error_test {name sql error} { uplevel [list doPassiveTest 0 $name $sql [list 1 $error]] } proc doPassiveTest {isRestart name sql catchres} { if {![info exists ::DO_MALLOC_TEST]} { set ::DO_MALLOC_TEST 1 } switch $::DO_MALLOC_TEST { 0 { # No malloc failures. do_test $name [list set {} [uplevel [list catchsql $sql]]] $catchres return } 1 { # Simulate transient failures. set nRepeat 1 set zName "transient" set nStartLimit 100000 set nBackup 1 } 2 { # Simulate persistent failures. set nRepeat 1 set zName "persistent" set nStartLimit 100000 set nBackup 1 } 3 { # Simulate transient failures with extra brute force. set nRepeat 100000 set zName "ridiculous" set nStartLimit 1 set nBackup 10 } } # The set of acceptable results from running [catchsql $sql]. # set answers [list {1 {out of memory}} $catchres] set str [join $answers " OR "] set nFail 1 for {set iLimit $nStartLimit} {$nFail} {incr iLimit} { for {set iFail 1} {$nFail && $iFail<=$iLimit} {incr iFail} { for {set iTest 0} {$iTest<$nBackup && ($iFail-$iTest)>0} {incr iTest} { if {$isRestart} { sqlite3 db test.db } sqlite3_memdebug_fail [expr $iFail-$iTest] -repeat $nRepeat set res [uplevel [list catchsql $sql]] if {[lsearch -exact $answers $res]>=0} { set res $str } set testname "$name.$zName.$iFail" do_test "$name.$zName.$iLimit.$iFail" [list set {} $res] $str set nFail [sqlite3_memdebug_fail -1 -benigncnt nBenign] } } } } #------------------------------------------------------------------------- # Test a single write to the database. In this case a "write" is a # DELETE, UPDATE or INSERT statement. # # If OOM testing is performed, there are several acceptable outcomes: # # 1) The write succeeds. No error is returned. # # 2) An "out of memory" exception is thrown and: # # a) The statement has no effect, OR # b) The current transaction is rolled back, OR # c) The statement succeeds. This can only happen if the connection # is in auto-commit mode (after the statement is executed, so this # includes COMMIT statements). # # If the write operation eventually succeeds, zero is returned. If a # transaction is rolled back, non-zero is returned. # # Parameter $name is the name to use for the test case (or test cases). # The second parameter, $tbl, should be the name of the database table # being modified. Parameter $sql contains the SQL statement to test. # proc do_write_test {name tbl sql} { if {![info exists ::DO_MALLOC_TEST]} { set ::DO_MALLOC_TEST 1 } # Figure out an statement to get a checksum for table $tbl. db eval "SELECT * FROM $tbl" V break set cksumsql "SELECT md5sum([join [concat rowid $V(*)] ,]) FROM $tbl" # Calculate the initial table checksum. set cksum1 [db one $cksumsql] if {$::DO_MALLOC_TEST } { set answers [list {1 {out of memory}} {0 {}}] if {$::DO_MALLOC_TEST==1} { set modes {100000 persistent} } else { set modes {1 transient} } } else { set answers [list {0 {}}] set modes [list 0 nofail] } set str [join $answers " OR "] foreach {nRepeat zName} $modes { for {set iFail 1} 1 {incr iFail} { if {$::DO_MALLOC_TEST} {sqlite3_memdebug_fail $iFail -repeat $nRepeat} set res [uplevel [list catchsql $sql]] set nFail [sqlite3_memdebug_fail -1 -benigncnt nBenign] if {$nFail==0} { do_test $name.$zName.$iFail [list set {} $res] {0 {}} return } else { if {[lsearch $answers $res]>=0} { set res $str } do_test $name.$zName.$iFail [list set {} $res] $str set cksum2 [db one $cksumsql] if {$cksum1 != $cksum2} return } } } } |
Changes to test/memleak.test.
︙ | ︙ | |||
71 72 73 74 75 76 77 | source $testfile if {[info exists Leak]} { lappend LeakList $Leak } } if {$LeakList!=""} { puts -nonewline memory-leak-test-$tail... | | < | | 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 | source $testfile if {[info exists Leak]} { lappend LeakList $Leak } } if {$LeakList!=""} { puts -nonewline memory-leak-test-$tail... incr_ntest foreach x $LeakList { if {$x!=[lindex $LeakList 0]} { puts " failed! ($LeakList)" fail_test memory-leak-test-$tail break } } puts " Ok" } } memleak_finish_test |
︙ | ︙ |
Changes to test/memsubsys1.test.
︙ | ︙ | |||
12 13 14 15 16 17 18 19 20 21 22 23 24 25 | # This file contains tests of the memory allocation subsystem # # $Id: memsubsys1.test,v 1.17 2009/07/18 14:36:24 danielk1977 Exp $ set testdir [file dirname $argv0] source $testdir/tester.tcl sqlite3_reset_auto_extension # This procedure constructs a new database in test.db. It fills # this database with many small records (enough to force multiple # rebalance operations in the btree-layer and to require a large # page cache), verifies correct results, then returns. # proc build_test_db {testname pragmas} { | > > > > > > > > > | 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 | # This file contains tests of the memory allocation subsystem # # $Id: memsubsys1.test,v 1.17 2009/07/18 14:36:24 danielk1977 Exp $ set testdir [file dirname $argv0] source $testdir/tester.tcl sqlite3_reset_auto_extension # This test assumes that no page-cache or scratch buffers are installed # by default when a new database connection is opened. As a result, it # will not work with the "memsubsys1" permutation. # if {[permutation] == "memsubsys1"} { finish_test return } # This procedure constructs a new database in test.db. It fills # this database with many small records (enough to force multiple # rebalance operations in the btree-layer and to require a large # page cache), verifies correct results, then returns. # proc build_test_db {testname pragmas} { |
︙ | ︙ |
Changes to test/minmax3.test.
︙ | ︙ | |||
8 9 10 11 12 13 14 15 16 17 18 19 20 21 | # May you share freely, never taking more than you give. # #*********************************************************************** # $Id: minmax3.test,v 1.5 2008/07/12 14:52:20 drh Exp $ set testdir [file dirname $argv0] source $testdir/tester.tcl # Do an SQL statement. Append the search count to the end of the result. # proc count sql { set ::sqlite_search_count 0 return [concat [execsql $sql] $::sqlite_search_count] } | > > > > > | 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | # May you share freely, never taking more than you give. # #*********************************************************************** # $Id: minmax3.test,v 1.5 2008/07/12 14:52:20 drh Exp $ set testdir [file dirname $argv0] source $testdir/tester.tcl # Do not use a codec for tests in this file, as the database file is # manipulated directly using tcl scripts (using the [hexio_write] command). # do_not_use_codec # Do an SQL statement. Append the search count to the end of the result. # proc count sql { set ::sqlite_search_count 0 return [concat [execsql $sql] $::sqlite_search_count] } |
︙ | ︙ |
Changes to test/misc1.test.
︙ | ︙ | |||
477 478 479 480 481 482 483 | # do_test misc1-14.1 { file mkdir tempdir cd tempdir execsql {BEGIN} file exists ./test.db-journal } {0} | | | > > > > | 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 | # do_test misc1-14.1 { file mkdir tempdir cd tempdir execsql {BEGIN} file exists ./test.db-journal } {0} do_test misc1-14.2a { execsql {UPDATE t1 SET a=a||'x' WHERE 0} file exists ../test.db-journal } {0} do_test misc1-14.2b { execsql {UPDATE t1 SET a=a||'y' WHERE 1} file exists ../test.db-journal } {1} do_test misc1-14.3 { cd .. file delete -force tempdir execsql {COMMIT} file exists ./test.db-journal |
︙ | ︙ |
Changes to test/misc5.test.
︙ | ︙ | |||
507 508 509 510 511 512 513 | } } {one} } # Ticket #1370. Do not overwrite small files (less than 1024 bytes) # when trying to open them as a database. # | > | | | | | | | | | | | > | 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 | } } {one} } # Ticket #1370. Do not overwrite small files (less than 1024 bytes) # when trying to open them as a database. # if {[permutation] == ""} { do_test misc5-4.1 { db close file delete -force test.db set fd [open test.db w] puts $fd "This is not really a database" close $fd sqlite3 db test.db catchsql { CREATE TABLE t1(a,b,c); } } {1 {file is encrypted or is not a database}} } # Ticket #1371. Allow floating point numbers of the form .N or N. # do_test misc5-5.1 { execsql {SELECT .1 } } 0.1 do_test misc5-5.2 { |
︙ | ︙ |
Changes to test/nan.test.
︙ | ︙ | |||
19 20 21 22 23 24 25 26 27 28 29 30 31 32 | # out of range. # # $Id: nan.test,v 1.5 2008/09/18 11:30:13 danielk1977 Exp $ # set testdir [file dirname $argv0] source $testdir/tester.tcl do_test nan-1.1.1 { db eval { PRAGMA auto_vacuum=OFF; PRAGMA page_size=1024; CREATE TABLE t1(x FLOAT); } | > > > > > | 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 | # out of range. # # $Id: nan.test,v 1.5 2008/09/18 11:30:13 danielk1977 Exp $ # set testdir [file dirname $argv0] source $testdir/tester.tcl # Do not use a codec for tests in this file, as the database file is # manipulated directly using tcl scripts (using the [hexio_write] command). # do_not_use_codec do_test nan-1.1.1 { db eval { PRAGMA auto_vacuum=OFF; PRAGMA page_size=1024; CREATE TABLE t1(x FLOAT); } |
︙ | ︙ | |||
57 58 59 60 61 62 63 | } {{} null inf real -inf real {} null} do_test nan-1.1.5 { sqlite3_bind_double $::STMT 1 NaN0 sqlite3_step $::STMT sqlite3_reset $::STMT db eval {SELECT x, typeof(x) FROM t1} } {{} null inf real -inf real {} null {} null} | | | | 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 | } {{} null inf real -inf real {} null} do_test nan-1.1.5 { sqlite3_bind_double $::STMT 1 NaN0 sqlite3_step $::STMT sqlite3_reset $::STMT db eval {SELECT x, typeof(x) FROM t1} } {{} null inf real -inf real {} null {} null} do_test nan-1.1.6 { sqlite3_bind_double $::STMT 1 -NaN0 sqlite3_step $::STMT sqlite3_reset $::STMT db eval {SELECT x, typeof(x) FROM t1} } {{} null inf real -inf real {} null {} null {} null} do_test nan-1.1.7 { db eval { UPDATE t1 SET x=x-x; SELECT x, typeof(x) FROM t1; } } {{} null {} null {} null {} null {} null {} null} } |
︙ | ︙ | |||
111 112 113 114 115 116 117 | } {{} null Inf real -Inf real {} null} do_test nan-1.2.5 { sqlite3_bind_double $::STMT 1 NaN0 sqlite3_step $::STMT sqlite3_reset $::STMT db eval {SELECT CAST(x AS text), typeof(x) FROM t1} } {{} null Inf real -Inf real {} null {} null} | | | | 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 | } {{} null Inf real -Inf real {} null} do_test nan-1.2.5 { sqlite3_bind_double $::STMT 1 NaN0 sqlite3_step $::STMT sqlite3_reset $::STMT db eval {SELECT CAST(x AS text), typeof(x) FROM t1} } {{} null Inf real -Inf real {} null {} null} do_test nan-1.2.6 { sqlite3_bind_double $::STMT 1 -NaN0 sqlite3_step $::STMT sqlite3_reset $::STMT db eval {SELECT CAST(x AS text), typeof(x) FROM t1} } {{} null Inf real -Inf real {} null {} null {} null} do_test nan-1.2.7 { db eval { UPDATE t1 SET x=x-x; SELECT CAST(x AS text), typeof(x) FROM t1; } } {{} null {} null {} null {} null {} null {} null} do_test nan-2.1 { |
︙ | ︙ | |||
244 245 246 247 248 249 250 | } {Inf real} do_test nan-4.10 { db eval {DELETE FROM t1} db eval "INSERT INTO t1 VALUES(-[string repeat 9 309].0)" db eval {SELECT CAST(x AS text), typeof(x) FROM t1} } {-Inf real} | | | | | | | | | | 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 | } {Inf real} do_test nan-4.10 { db eval {DELETE FROM t1} db eval "INSERT INTO t1 VALUES(-[string repeat 9 309].0)" db eval {SELECT CAST(x AS text), typeof(x) FROM t1} } {-Inf real} do_test nan-4.11 { db eval {DELETE FROM t1} db eval "INSERT INTO t1 VALUES(1234.5[string repeat 0 10000]12345)" db eval {SELECT x, typeof(x) FROM t1} } {1234.5 real} do_test nan-4.12 { db eval {DELETE FROM t1} db eval "INSERT INTO t1 VALUES(-1234.5[string repeat 0 10000]12345)" db eval {SELECT x, typeof(x) FROM t1} } {-1234.5 real} do_test nan-4.13 { db eval {DELETE FROM t1} set small [string repeat 0 10000].[string repeat 0 324][string repeat 9 10000] db eval "INSERT INTO t1 VALUES($small)" db eval {SELECT x, typeof(x) FROM t1} } {0.0 real} do_test nan-4.14 { db eval {DELETE FROM t1} set small \ -[string repeat 0 10000].[string repeat 0 324][string repeat 9 10000] db eval "INSERT INTO t1 VALUES($small)" db eval {SELECT x, typeof(x) FROM t1} } {0.0 real} # These tests test some really, really small floating point numbers. # if {$tcl_platform(platform) != "symbian"} { # These two are not run on symbian because tcl has trouble converting # the very small numbers back to text form (probably due to a difference # in the sprintf() implementation). # do_test nan-4.15 { db eval {DELETE FROM t1} set small \ [string repeat 0 10000].[string repeat 0 323][string repeat 9 10000] db eval "INSERT INTO t1 VALUES($small)" db eval {SELECT x, typeof(x) FROM t1} } {9.88131291682493e-324 real} do_test nan-4.16 { db eval {DELETE FROM t1} set small \ -[string repeat 0 10000].[string repeat 0 323][string repeat 9 10000] db eval "INSERT INTO t1 VALUES($small)" db eval {SELECT x, typeof(x) FROM t1} } {-9.88131291682493e-324 real} } do_test nan-4.17 { db eval {DELETE FROM t1} set small [string repeat 0 10000].[string repeat 0 323][string repeat 9 10000] db eval "INSERT INTO t1 VALUES($small)" db eval {SELECT CAST(x AS text), typeof(x) FROM t1} } {9.88131291682493e-324 real} do_test nan-4.18 { db eval {DELETE FROM t1} set small \ -[string repeat 0 10000].[string repeat 0 323][string repeat 9 10000] db eval "INSERT INTO t1 VALUES($small)" db eval {SELECT CAST(x AS text), typeof(x) FROM t1} } {-9.88131291682493e-324 real} |
︙ | ︙ |
Added test/notify3.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 | # 2010 June 30 # # The author disclaims copyright to this source code. In place of # a legal notice, here is a blessing: # # May you do good and not evil. # May you find forgiveness for yourself and forgive others. # May you share freely, never taking more than you give. # #*********************************************************************** # This file implements regression tests for SQLite library. The # focus of this file is testing the sqlite3_unlock_notify() API. # set testdir [file dirname $argv0] source $testdir/tester.tcl # This script only runs if shared-cache and unlock-notify are available. # ifcapable !unlock_notify||!shared_cache { finish_test return } set esc [sqlite3_enable_shared_cache 1] sqlite3 db test.db file delete -force test.db2 test.db2-journal test.db2-wal sqlite3 db2 test.db2 do_test notify3-1.1 { execsql { CREATE TABLE t1(a, b); INSERT INTO t1 VALUES('t1 A', 't1 B'); } } {} do_test notify3-1.2 { execsql { CREATE TABLE t2(a, b); INSERT INTO t2 VALUES('t2 A', 't2 B'); } db2 } {} do_test notify3-1.3 { execsql { BEGIN EXCLUSIVE; INSERT INTO t2 VALUES('t2 C', 't2 D'); } db2 } {} do_test notify3-1.4 { catchsql { ATTACH 'test.db2' AS aux } } {0 {}} do_test notify3-1.5 { catchsql { SELECT * FROM t2 } } {1 {database schema is locked: aux}} do_test notify3-1.6 { list [sqlite3_errcode db] [sqlite3_extended_errcode db] } {SQLITE_LOCKED SQLITE_LOCKED_SHAREDCACHE} do_test notify3-1.7 { sqlite3_extended_result_codes db 1 catch { set ::stmt [sqlite3_prepare_v2 db "SELECT * FROM t2" -1 tail] } msg set msg } {(262) database schema is locked: aux} do_test notify3-1.8 { set ::when 1 db unlock_notify { set ::res $::when } set ::when 2 execsql { COMMIT } db2 set ::res } {2} do_test notify3-1.9 { catchsql { SELECT * FROM t2 } } {0 {{t2 A} {t2 B} {t2 C} {t2 D}}} db close set err {{1 {unable to open database: test.db2}}} set noerr {{0 {}}} # When a new database is attached, the connection doing the attaching # tries to load any unloaded schemas for both the new database and any # already attached databases (including the main database). If it is # unable to load any such schemas, then the ATTACH statement fails. # # This block tests that if the loading of schemas as a result of an # ATTACH fails due to locks on the schema table held by other shared-cache # connections the extended error code is SQLITE_LOCKED_SHAREDCACHE and # it is possible to use the unlock-notify mechanism to determine when # the ATTACH might succeed. # foreach { tn db1_loaded db2_loaded enable_extended_errors result error1 error2 } " 0 0 0 0 $err SQLITE_LOCKED SQLITE_LOCKED_SHAREDCACHE 1 0 0 1 $err SQLITE_LOCKED_SHAREDCACHE SQLITE_LOCKED_SHAREDCACHE 2 0 1 0 $err SQLITE_LOCKED SQLITE_LOCKED_SHAREDCACHE 3 0 1 1 $err SQLITE_LOCKED_SHAREDCACHE SQLITE_LOCKED_SHAREDCACHE 4 1 0 0 $err SQLITE_LOCKED SQLITE_LOCKED_SHAREDCACHE 5 1 0 1 $err SQLITE_LOCKED_SHAREDCACHE SQLITE_LOCKED_SHAREDCACHE 6 1 1 0 $noerr SQLITE_OK SQLITE_OK 7 1 1 1 $noerr SQLITE_OK SQLITE_OK " { do_test notify3-2.$tn.1 { catch { db1 close } catch { db2 close } sqlite3 db1 test.db sqlite3 db2 test.db2 sqlite3_extended_result_codes db1 $enable_extended_errors sqlite3_extended_result_codes db2 $enable_extended_errors if { $db1_loaded } { db1 eval "SELECT * FROM sqlite_master" } if { $db2_loaded } { db2 eval "SELECT * FROM sqlite_master" } db2 eval "BEGIN EXCLUSIVE" catchsql "ATTACH 'test.db2' AS two" db1 } $result do_test notify3-2.$tn.2 { list [sqlite3_errcode db1] [sqlite3_extended_errcode db1] } [list $error1 $error2] do_test notify3-2.$tn.3 { db1 unlock_notify {set invoked 1} set invoked 0 db2 eval commit set invoked } [lindex $result 0] } catch { db1 close } catch { db2 close } sqlite3_enable_shared_cache $esc finish_test |
Added test/pager1.test.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334 1335 1336 1337 1338 1339 1340 1341 1342 1343 1344 1345 1346 1347 1348 1349 1350 1351 1352 1353 1354 1355 1356 1357 1358 1359 1360 1361 1362 1363 1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 1375 1376 1377 1378 1379 1380 1381 1382 1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398 1399 1400 1401 1402 1403 1404 1405 1406 1407 1408 1409 1410 1411 1412 1413 1414 1415 1416 1417 1418 1419 1420 1421 1422 1423 1424 1425 1426 1427 1428 1429 1430 1431 1432 1433 1434 1435 1436 1437 1438 1439 1440 1441 1442 1443 1444 1445 1446 1447 1448 1449 1450 1451 1452 1453 1454 1455 1456 1457 1458 1459 1460 1461 1462 1463 1464 1465 1466 1467 1468 1469 1470 1471 1472 1473 1474 1475 1476 1477 1478 1479 1480 1481 1482 1483 1484 1485 1486 1487 1488 1489 1490 1491 1492 1493 1494 1495 1496 1497 1498 1499 1500 1501 1502 1503 1504 1505 1506 1507 1508 1509 1510 1511 1512 1513 1514 1515 1516 1517 1518 1519 1520 1521 1522 1523 1524 1525 1526 1527 1528 1529 1530 1531 1532 1533 1534 1535 1536 1537 1538 1539 1540 1541 1542 1543 1544 1545 1546 1547 1548 1549 1550 1551 1552 1553 1554 1555 1556 1557 1558 1559 1560 1561 1562 1563 1564 1565 1566 1567 1568 1569 1570 1571 1572 1573 1574 1575 1576 1577 1578 1579 1580 1581 1582 1583 1584 1585 1586 1587 1588 1589 1590 1591 1592 1593 1594 1595 1596 1597 1598 1599 1600 1601 1602 1603 1604 1605 1606 1607 1608 1609 1610 1611 1612 1613 1614 1615 1616 1617 1618 1619 1620 1621 1622 1623 1624 1625 1626 1627 1628 1629 1630 1631 1632 1633 1634 1635 1636 1637 1638 1639 1640 1641 1642 1643 1644 1645 1646 1647 1648 1649 1650 1651 1652 1653 1654 1655 1656 1657 1658 1659 1660 1661 1662 1663 1664 1665 1666 1667 1668 1669 1670 1671 1672 1673 1674 1675 1676 1677 1678 1679 1680 1681 1682 1683 1684 1685 1686 1687 1688 1689 1690 1691 1692 1693 1694 1695 1696 1697 1698 1699 1700 1701 1702 1703 1704 1705 1706 1707 1708 1709 1710 1711 1712 1713 1714 1715 1716 1717 1718 1719 1720 1721 1722 1723 1724 1725 1726 1727 1728 1729 1730 1731 1732 1733 1734 1735 1736 1737 1738 1739 1740 1741 1742 1743 1744 1745 1746 1747 1748 1749 1750 1751 1752 1753 1754 1755 1756 1757 1758 1759 1760 1761 1762 1763 1764 1765 1766 1767 1768 1769 1770 1771 1772 1773 1774 1775 1776 1777 1778 1779 1780 1781 1782 1783 1784 1785 1786 1787 1788 1789 1790 1791 1792 1793 1794 1795 1796 1797 1798 1799 1800 1801 1802 1803 1804 1805 1806 1807 1808 1809 1810 1811 1812 1813 1814 1815 1816 1817 1818 1819 1820 1821 1822 1823 1824 1825 1826 1827 1828 1829 1830 1831 1832 1833 1834 1835 1836 1837 1838 1839 1840 1841 1842 1843 1844 1845 1846 1847 1848 1849 1850 1851 1852 1853 1854 1855 1856 1857 1858 1859 1860 1861 1862 1863 1864 1865 1866 1867 1868 1869 1870 1871 1872 1873 1874 1875 1876 1877 1878 1879 1880 1881 1882 1883 1884 1885 1886 1887 1888 1889 1890 1891 1892 1893 1894 1895 1896 1897 1898 1899 1900 1901 1902 1903 1904 1905 1906 1907 1908 1909 1910 1911 1912 1913 1914 1915 1916 1917 1918 1919 1920 1921 1922 1923 1924 1925 1926 1927 1928 1929 1930 1931 1932 1933 1934 1935 1936 1937 1938 1939 1940 1941 1942 1943 1944 1945 1946 1947 1948 1949 1950 1951 1952 1953 1954 1955 1956 1957 1958 1959 1960 1961 1962 1963 1964 1965 1966 1967 1968 1969 1970 1971 1972 1973 1974 1975 1976 1977 1978 1979 1980 1981 1982 1983 1984 1985 1986 1987 1988 1989 1990 1991 1992 1993 1994 1995 1996 1997 1998 1999 2000 2001 2002 2003 2004 2005 2006 2007 2008 2009 2010 2011 2012 2013 2014 2015 2016 2017 2018 2019 2020 2021 2022 2023 2024 2025 2026 2027 2028 2029 2030 2031 2032 2033 2034 2035 2036 2037 2038 2039 2040 2041 2042 2043 2044 2045 2046 2047 2048 2049 2050 2051 2052 2053 2054 2055 2056 2057 2058 2059 2060 2061 2062 2063 2064 2065 2066 2067 2068 2069 2070 2071 2072 2073 2074 2075 2076 2077 2078 2079 2080 2081 2082 2083 2084 2085 2086 2087 2088 2089 2090 2091 2092 2093 2094 2095 2096 2097 2098 2099 2100 2101 2102 2103 2104 2105 2106 2107 2108 2109 2110 2111 2112 2113 2114 2115 2116 2117 2118 2119 2120 2121 2122 2123 2124 2125 2126 2127 2128 2129 2130 2131 2132 2133 2134 2135 2136 2137 2138 2139 2140 2141 2142 2143 2144 2145 2146 2147 2148 2149 2150 2151 2152 2153 2154 2155 2156 2157 2158 2159 2160 2161 2162 2163 2164 2165 2166 2167 2168 2169 2170 2171 2172 2173 2174 2175 2176 2177 2178 2179 2180 2181 2182 2183 2184 2185 2186 2187 2188 2189 2190 2191 2192 2193 2194 2195 2196 2197 2198 2199 2200 2201 2202 2203 2204 2205 2206 2207 2208 2209 2210 2211 2212 2213 2214 2215 2216 2217 2218 2219 2220 2221 2222 2223 2224 2225 2226 2227 2228 2229 2230 2231 2232 2233 2234 2235 2236 2237 2238 2239 2240 2241 2242 2243 2244 2245 2246 | # 2010 June 15 # # The author disclaims copyright to this source code. In place of # a legal notice, here is a blessing: # # May you do good and not evil. # May you find forgiveness for yourself and forgive others. # May you share freely, never taking more than you give. # #*********************************************************************** # set testdir [file dirname $argv0] source $testdir/tester.tcl source $testdir/lock_common.tcl source $testdir/malloc_common.tcl source $testdir/wal_common.tcl # Do not use a codec for tests in this file, as the database file is # manipulated directly using tcl scripts (using the [hexio_write] command). # do_not_use_codec # # pager1-1.*: Test inter-process locking (clients in multiple processes). # # pager1-2.*: Test intra-process locking (multiple clients in this process). # # pager1-3.*: Savepoint related tests. # # pager1-4.*: Hot-journal related tests. # # pager1-5.*: Cases related to multi-file commits. # # pager1-6.*: Cases related to "PRAGMA max_page_count" # # pager1-7.*: Cases specific to "PRAGMA journal_mode=TRUNCATE" # # pager1-8.*: Cases using temporary and in-memory databases. # # pager1-9.*: Tests related to the backup API. # # pager1-10.*: Test that the assumed file-system sector-size is limited to # 64KB. # # pager1-12.*: Tests involving "PRAGMA page_size" # # pager1-13.*: Cases specific to "PRAGMA journal_mode=PERSIST" # # pager1-14.*: Cases specific to "PRAGMA journal_mode=OFF" # # pager1-15.*: Varying sqlite3_vfs.szOsFile # # pager1-16.*: Varying sqlite3_vfs.mxPathname # # pager1-17.*: Tests related to "PRAGMA omit_readlock" # # pager1-18.*: Test that the pager layer responds correctly if the b-tree # requests an invalid page number (due to db corruption). # proc recursive_select {id table {script {}}} { set cnt 0 db eval "SELECT rowid, * FROM $table WHERE rowid = ($id-1)" { recursive_select $rowid $table $script incr cnt } if {$cnt==0} { eval $script } } set a_string_counter 1 proc a_string {n} { global a_string_counter incr a_string_counter string range [string repeat "${a_string_counter}." $n] 1 $n } db func a_string a_string do_multiclient_test tn { # Create and populate a database table using connection [db]. Check # that connections [db2] and [db3] can see the schema and content. # do_test pager1-$tn.1 { sql1 { CREATE TABLE t1(a PRIMARY KEY, b); CREATE INDEX i1 ON t1(b); INSERT INTO t1 VALUES(1, 'one'); INSERT INTO t1 VALUES(2, 'two'); } } {} do_test pager1-$tn.2 { sql2 { SELECT * FROM t1 } } {1 one 2 two} do_test pager1-$tn.3 { sql3 { SELECT * FROM t1 } } {1 one 2 two} # Open a transaction and add a row using [db]. This puts [db] in # RESERVED state. Check that connections [db2] and [db3] can still # read the database content as it was before the transaction was # opened. [db] should see the inserted row. # do_test pager1-$tn.4 { sql1 { BEGIN; INSERT INTO t1 VALUES(3, 'three'); } } {} do_test pager1-$tn.5 { sql2 { SELECT * FROM t1 } } {1 one 2 two} do_test pager1-$tn.7 { sql1 { SELECT * FROM t1 } } {1 one 2 two 3 three} # [db] still has an open write transaction. Check that this prevents # other connections (specifically [db2]) from writing to the database. # # Even if [db2] opens a transaction first, it may not write to the # database. After the attempt to write the db within a transaction, # [db2] is left with an open transaction, but not a read-lock on # the main database. So it does not prevent [db] from committing. # do_test pager1-$tn.8 { csql2 { UPDATE t1 SET a = a + 10 } } {1 {database is locked}} do_test pager1-$tn.9 { csql2 { BEGIN; UPDATE t1 SET a = a + 10; } } {1 {database is locked}} # Have [db] commit its transactions. Check the other connections can # now see the new database content. # do_test pager1-$tn.10 { sql1 { COMMIT } } {} do_test pager1-$tn.11 { sql1 { SELECT * FROM t1 } } {1 one 2 two 3 three} do_test pager1-$tn.12 { sql2 { SELECT * FROM t1 } } {1 one 2 two 3 three} do_test pager1-$tn.13 { sql3 { SELECT * FROM t1 } } {1 one 2 two 3 three} # Check that, as noted above, [db2] really did keep an open transaction # after the attempt to write the database failed. # do_test pager1-$tn.14 { csql2 { BEGIN } } {1 {cannot start a transaction within a transaction}} do_test pager1-$tn.15 { sql2 { ROLLBACK } } {} # Have [db2] open a transaction and take a read-lock on the database. # Check that this prevents [db] from writing to the database (outside # of any transaction). After this fails, check that [db3] can read # the db (showing that [db] did not take a PENDING lock etc.) # do_test pager1-$tn.15 { sql2 { BEGIN; SELECT * FROM t1; } } {1 one 2 two 3 three} do_test pager1-$tn.16 { csql1 { UPDATE t1 SET a = a + 10 } } {1 {database is locked}} do_test pager1-$tn.17 { sql3 { SELECT * FROM t1 } } {1 one 2 two 3 three} # This time, have [db] open a transaction before writing the database. # This works - [db] gets a RESERVED lock which does not conflict with # the SHARED lock [db2] is holding. # do_test pager1-$tn.18 { sql1 { BEGIN; UPDATE t1 SET a = a + 10; } } {} do_test pager1-$tn-19 { sql1 { PRAGMA lock_status } } {main reserved temp closed} do_test pager1-$tn-20 { sql2 { PRAGMA lock_status } } {main shared temp closed} # Check that all connections can still read the database. Only [db] sees # the updated content (as the transaction has not been committed yet). # do_test pager1-$tn.21 { sql1 { SELECT * FROM t1 } } {11 one 12 two 13 three} do_test pager1-$tn.22 { sql2 { SELECT * FROM t1 } } {1 one 2 two 3 three} do_test pager1-$tn.23 { sql3 { SELECT * FROM t1 } } {1 one 2 two 3 three} # Because [db2] still has the SHARED lock, [db] is unable to commit the # transaction. If it tries, an error is returned and the connection # upgrades to a PENDING lock. # # Once this happens, [db] can read the database and see the new content, # [db2] (still holding SHARED) can still read the old content, but [db3] # (not holding any lock) is prevented by [db]'s PENDING from reading # the database. # do_test pager1-$tn.24 { csql1 { COMMIT } } {1 {database is locked}} do_test pager1-$tn-25 { sql1 { PRAGMA lock_status } } {main pending temp closed} do_test pager1-$tn.26 { sql1 { SELECT * FROM t1 } } {11 one 12 two 13 three} do_test pager1-$tn.27 { sql2 { SELECT * FROM t1 } } {1 one 2 two 3 three} do_test pager1-$tn.28 { csql3 { SELECT * FROM t1 } } {1 {database is locked}} # Have [db2] commit its read transaction, releasing the SHARED lock it # is holding. Now, neither [db2] nor [db3] may read the database (as [db] # is still holding a PENDING). # do_test pager1-$tn.29 { sql2 { COMMIT } } {} do_test pager1-$tn.30 { csql2 { SELECT * FROM t1 } } {1 {database is locked}} do_test pager1-$tn.31 { csql3 { SELECT * FROM t1 } } {1 {database is locked}} # [db] is now able to commit the transaction. Once the transaction is # committed, all three connections can read the new content. # do_test pager1-$tn.25 { sql1 { UPDATE t1 SET a = a+10 } } {} do_test pager1-$tn.26 { sql1 { COMMIT } } {} do_test pager1-$tn.27 { sql1 { SELECT * FROM t1 } } {21 one 22 two 23 three} do_test pager1-$tn.27 { sql2 { SELECT * FROM t1 } } {21 one 22 two 23 three} do_test pager1-$tn.28 { sql3 { SELECT * FROM t1 } } {21 one 22 two 23 three} # Install a busy-handler for connection [db]. # set ::nbusy [list] proc busy {n} { lappend ::nbusy $n if {$n>5} { sql2 COMMIT } return 0 } db busy busy do_test pager1-$tn.29 { sql1 { BEGIN ; INSERT INTO t1 VALUES('x', 'y') } } {} do_test pager1-$tn.30 { sql2 { BEGIN ; SELECT * FROM t1 } } {21 one 22 two 23 three} do_test pager1-$tn.31 { sql1 COMMIT } {} do_test pager1-$tn.32 { set ::nbusy } {0 1 2 3 4 5 6} } #------------------------------------------------------------------------- # Savepoint related test cases. # # pager1-3.1.2.*: Force a savepoint rollback to cause the database file # to grow. # # pager1-3.1.3.*: Use a journal created in synchronous=off mode as part # of a savepoint rollback. # do_test pager1-3.1.1 { faultsim_delete_and_reopen execsql { CREATE TABLE t1(a PRIMARY KEY, b); CREATE TABLE counter( i CHECK (i<5), u CHECK (u<10) ); INSERT INTO counter VALUES(0, 0); CREATE TRIGGER tr1 AFTER INSERT ON t1 BEGIN UPDATE counter SET i = i+1; END; CREATE TRIGGER tr2 AFTER UPDATE ON t1 BEGIN UPDATE counter SET u = u+1; END; } execsql { SELECT * FROM counter } } {0 0} do_execsql_test pager1-3.1.2 { PRAGMA cache_size = 10; BEGIN; INSERT INTO t1 VALUES(1, randomblob(1500)); INSERT INTO t1 VALUES(2, randomblob(1500)); INSERT INTO t1 VALUES(3, randomblob(1500)); SELECT * FROM counter; } {3 0} do_catchsql_test pager1-3.1.3 { INSERT INTO t1 SELECT a+3, randomblob(1500) FROM t1 } {1 {constraint failed}} do_execsql_test pager1-3.4 { SELECT * FROM counter } {3 0} do_execsql_test pager1-3.5 { SELECT a FROM t1 } {1 2 3} do_execsql_test pager1-3.6 { COMMIT } {} foreach {tn sql tcl} { 7 { PRAGMA synchronous = NORMAL ; PRAGMA temp_store = 0 } { testvfs tv -default 1 tv devchar safe_append } 8 { PRAGMA synchronous = NORMAL ; PRAGMA temp_store = 2 } { testvfs tv -default 1 tv devchar sequential } 9 { PRAGMA synchronous = FULL } { } 10 { PRAGMA synchronous = NORMAL } { } 11 { PRAGMA synchronous = OFF } { } 12 { PRAGMA synchronous = FULL ; PRAGMA fullfsync = 1 } { } 13 { PRAGMA synchronous = FULL } { testvfs tv -default 1 tv devchar sequential } 14 { PRAGMA locking_mode = EXCLUSIVE } { } } { do_test pager1-3.$tn.1 { eval $tcl faultsim_delete_and_reopen db func a_string a_string execsql $sql execsql { PRAGMA auto_vacuum = 2; PRAGMA cache_size = 10; CREATE TABLE z(x INTEGER PRIMARY KEY, y); BEGIN; INSERT INTO z VALUES(NULL, a_string(800)); INSERT INTO z SELECT NULL, a_string(800) FROM z; -- 2 INSERT INTO z SELECT NULL, a_string(800) FROM z; -- 4 INSERT INTO z SELECT NULL, a_string(800) FROM z; -- 8 INSERT INTO z SELECT NULL, a_string(800) FROM z; -- 16 INSERT INTO z SELECT NULL, a_string(800) FROM z; -- 32 INSERT INTO z SELECT NULL, a_string(800) FROM z; -- 64 INSERT INTO z SELECT NULL, a_string(800) FROM z; -- 128 INSERT INTO z SELECT NULL, a_string(800) FROM z; -- 256 COMMIT; } execsql { PRAGMA auto_vacuum } } {2} do_execsql_test pager1-3.$tn.2 { BEGIN; INSERT INTO z VALUES(NULL, a_string(800)); INSERT INTO z VALUES(NULL, a_string(800)); SAVEPOINT one; UPDATE z SET y = NULL WHERE x>256; PRAGMA incremental_vacuum; SELECT count(*) FROM z WHERE x < 100; ROLLBACK TO one; COMMIT; } {99} do_execsql_test pager1-3.$tn.3 { BEGIN; SAVEPOINT one; UPDATE z SET y = y||x; ROLLBACK TO one; COMMIT; SELECT count(*) FROM z; } {258} do_execsql_test pager1-3.$tn.4 { SAVEPOINT one; UPDATE z SET y = y||x; ROLLBACK TO one; } {} do_execsql_test pager1-3.$tn.5 { SELECT count(*) FROM z; RELEASE one; PRAGMA integrity_check; } {258 ok} do_execsql_test pager1-3.$tn.6 { SAVEPOINT one; RELEASE one; } {} db close catch { tv delete } } #------------------------------------------------------------------------- # Hot journal rollback related test cases. # # pager1.4.1.*: Test that the pager module deletes very small invalid # journal files. # # pager1.4.2.*: Test that if the master journal pointer at the end of a # hot-journal file appears to be corrupt (checksum does not # compute) the associated journal is rolled back (and no # xAccess() call to check for the presence of any master # journal file is made). # # pager1.4.3.*: Test that the contents of a hot-journal are ignored if the # page-size or sector-size in the journal header appear to # be invalid (too large, too small or not a power of 2). # # pager1.4.4.*: Test hot-journal rollback of journal file with a master # journal pointer generated in various "PRAGMA synchronous" # modes. # # pager1.4.5.*: Test that hot-journal rollback stops if it encounters a # journal-record for which the checksum fails. # # pager1.4.6.*: Test that when rolling back a hot-journal that contains a # master journal pointer, the master journal file is deleted # after all the hot-journals that refer to it are deleted. # # pager1.4.7.*: Test that if a hot-journal file exists but a client can # open it for reading only, the database cannot be accessed and # SQLITE_CANTOPEN is returned. # do_test pager1.4.1.1 { faultsim_delete_and_reopen execsql { CREATE TABLE x(y, z); INSERT INTO x VALUES(1, 2); } set fd [open test.db-journal w] puts -nonewline $fd "helloworld" close $fd file exists test.db-journal } {1} do_test pager1.4.1.2 { execsql { SELECT * FROM x } } {1 2} do_test pager1.4.1.3 { file exists test.db-journal } {0} # Set up a [testvfs] to snapshot the file-system just before SQLite # deletes the master-journal to commit a multi-file transaction. # # In subsequent test cases, invoking [faultsim_restore_and_reopen] sets # up the file system to contain two databases, two hot-journal files and # a master-journal. # do_test pager1.4.2.1 { testvfs tstvfs -default 1 tstvfs filter xDelete tstvfs script xDeleteCallback proc xDeleteCallback {method file args} { set file [file tail $file] if { [string match *mj* $file] } { faultsim_save } } faultsim_delete_and_reopen db func a_string a_string execsql { ATTACH 'test.db2' AS aux; PRAGMA journal_mode = DELETE; PRAGMA main.cache_size = 10; PRAGMA aux.cache_size = 10; CREATE TABLE t1(a UNIQUE, b UNIQUE); CREATE TABLE aux.t2(a UNIQUE, b UNIQUE); INSERT INTO t1 VALUES(a_string(200), a_string(300)); INSERT INTO t1 SELECT a_string(200), a_string(300) FROM t1; INSERT INTO t1 SELECT a_string(200), a_string(300) FROM t1; INSERT INTO t2 SELECT * FROM t1; BEGIN; INSERT INTO t1 SELECT a_string(201), a_string(301) FROM t1; INSERT INTO t1 SELECT a_string(202), a_string(302) FROM t1; INSERT INTO t1 SELECT a_string(203), a_string(303) FROM t1; INSERT INTO t1 SELECT a_string(204), a_string(304) FROM t1; REPLACE INTO t2 SELECT * FROM t1; COMMIT; } db close tstvfs delete } {} do_test pager1.4.2.2 { faultsim_restore_and_reopen execsql { SELECT count(*) FROM t1; PRAGMA integrity_check; } } {4 ok} do_test pager1.4.2.3 { faultsim_restore_and_reopen foreach f [glob test.db-mj*] { file delete -force $f } execsql { SELECT count(*) FROM t1; PRAGMA integrity_check; } } {64 ok} do_test pager1.4.2.4 { faultsim_restore_and_reopen hexio_write test.db-journal [expr [file size test.db-journal]-20] 123456 execsql { SELECT count(*) FROM t1; PRAGMA integrity_check; } } {4 ok} do_test pager1.4.2.5 { faultsim_restore_and_reopen hexio_write test.db-journal [expr [file size test.db-journal]-20] 123456 foreach f [glob test.db-mj*] { file delete -force $f } execsql { SELECT count(*) FROM t1; PRAGMA integrity_check; } } {4 ok} do_test pager1.4.3.1 { testvfs tstvfs -default 1 tstvfs filter xSync tstvfs script xSyncCallback proc xSyncCallback {method file args} { set file [file tail $file] if { 0==[string match *journal $file] } { faultsim_save } } faultsim_delete_and_reopen execsql { PRAGMA journal_mode = DELETE; CREATE TABLE t1(a, b); INSERT INTO t1 VALUES(1, 2); INSERT INTO t1 VALUES(3, 4); } db close tstvfs delete } {} foreach {tn ofst value result} { 2 20 31 {1 2 3 4} 3 20 32 {1 2 3 4} 4 20 33 {1 2 3 4} 5 20 65536 {1 2 3 4} 6 20 131072 {1 2 3 4} 7 24 511 {1 2 3 4} 8 24 513 {1 2 3 4} 9 24 65536 {1 2 3 4} 10 32 65536 {1 2} } { do_test pager1.4.3.$tn { faultsim_restore_and_reopen hexio_write test.db-journal $ofst [format %.8x $value] execsql { SELECT * FROM t1 } } $result } db close # Set up a VFS that snapshots the file-system just before a master journal # file is deleted to commit a multi-file transaction. Specifically, the # file-system is saved just before the xDelete() call to remove the # master journal file from the file-system. # testvfs tv -default 1 tv script copy_on_mj_delete set ::mj_filename_length 0 proc copy_on_mj_delete {method filename args} { if {[string match *mj* [file tail $filename]]} { set ::mj_filename_length [string length $filename] faultsim_save } return SQLITE_OK } set pwd [pwd] foreach {tn1 tcl} { 1 { set prefix "test.db" } 2 { # This test depends on the underlying VFS being able to open paths # 512 bytes in length. The idea is to create a hot-journal file that # contains a master-journal pointer so large that it could contain # a valid page record (if the file page-size is 512 bytes). So as to # make sure SQLite doesn't get confused by this. # set nPadding [expr 511 - $::mj_filename_length] if {$tcl_platform(platform)=="windows"} { # TBD need to figure out how to do this correctly for Windows!!! set nPadding [expr 255 - $::mj_filename_length] } # We cannot just create a really long database file name to open, as # Linux limits a single component of a path to 255 bytes by default # (and presumably other systems have limits too). So create a directory # hierarchy to work in. # set dirname "d123456789012345678901234567890/" set nDir [expr $nPadding / 32] if { $nDir } { set p [string repeat $dirname $nDir] file mkdir $p cd $p } set padding [string repeat x [expr $nPadding %32]] set prefix "test.db${padding}" } } { eval $tcl foreach {tn2 sql} { o { PRAGMA main.synchronous=OFF; PRAGMA aux.synchronous=OFF; PRAGMA journal_mode = DELETE; } o512 { PRAGMA main.synchronous=OFF; PRAGMA aux.synchronous=OFF; PRAGMA main.page_size = 512; PRAGMA aux.page_size = 512; PRAGMA journal_mode = DELETE; } n { PRAGMA main.synchronous=NORMAL; PRAGMA aux.synchronous=NORMAL; PRAGMA journal_mode = DELETE; } f { PRAGMA main.synchronous=FULL; PRAGMA aux.synchronous=FULL; PRAGMA journal_mode = DELETE; } } { set tn "${tn1}.${tn2}" # Set up a connection to have two databases, test.db (main) and # test.db2 (aux). Then run a multi-file transaction on them. The # VFS will snapshot the file-system just before the master-journal # file is deleted to commit the transaction. # tv filter xDelete do_test pager1-4.4.$tn.1 { faultsim_delete_and_reopen $prefix execsql " ATTACH '${prefix}2' AS aux; $sql CREATE TABLE a(x); CREATE TABLE aux.b(x); INSERT INTO a VALUES('double-you'); INSERT INTO a VALUES('why'); INSERT INTO a VALUES('zed'); INSERT INTO b VALUES('won'); INSERT INTO b VALUES('too'); INSERT INTO b VALUES('free'); " execsql { BEGIN; INSERT INTO a SELECT * FROM b WHERE rowid<=3; INSERT INTO b SELECT * FROM a WHERE rowid<=3; COMMIT; } } {} tv filter {} # Check that the transaction was committed successfully. # do_execsql_test pager1-4.4.$tn.2 { SELECT * FROM a } {double-you why zed won too free} do_execsql_test pager1-4.4.$tn.3 { SELECT * FROM b } {won too free double-you why zed} # Restore the file-system and reopen the databases. Check that it now # appears that the transaction was not committed (because the file-system # was restored to the state where it had not been). # do_test pager1-4.4.$tn.4 { faultsim_restore_and_reopen $prefix execsql "ATTACH '${prefix}2' AS aux" } {} do_execsql_test pager1-4.4.$tn.5 {SELECT * FROM a} {double-you why zed} do_execsql_test pager1-4.4.$tn.6 {SELECT * FROM b} {won too free} # Restore the file-system again. This time, before reopening the databases, # delete the master-journal file from the file-system. It now appears that # the transaction was committed (no master-journal file == no rollback). # do_test pager1-4.4.$tn.7 { faultsim_restore_and_reopen $prefix foreach f [glob ${prefix}-mj*] { file delete -force $f } execsql "ATTACH '${prefix}2' AS aux" } {} do_execsql_test pager1-4.4.$tn.8 { SELECT * FROM a } {double-you why zed won too free} do_execsql_test pager1-4.4.$tn.9 { SELECT * FROM b } {won too free double-you why zed} } cd $pwd } db close tv delete file delete -force $dirname # Set up a VFS to make a copy of the file-system just before deleting a # journal file to commit a transaction. The transaction modifies exactly # two database pages (and page 1 - the change counter). # testvfs tv -default 1 tv sectorsize 512 tv script copy_on_journal_delete tv filter xDelete proc copy_on_journal_delete {method filename args} { if {[string match *journal $filename]} faultsim_save return SQLITE_OK } faultsim_delete_and_reopen do_execsql_test pager1.4.5.1 { PRAGMA journal_mode = DELETE; PRAGMA page_size = 1024; CREATE TABLE t1(a, b); CREATE TABLE t2(a, b); INSERT INTO t1 VALUES('I', 'II'); INSERT INTO t2 VALUES('III', 'IV'); BEGIN; INSERT INTO t1 VALUES(1, 2); INSERT INTO t2 VALUES(3, 4); COMMIT; } {delete} tv filter {} # Check the transaction was committed: # do_execsql_test pager1.4.5.2 { SELECT * FROM t1; SELECT * FROM t2; } {I II 1 2 III IV 3 4} # Now try four tests: # # pager1-4.5.3: Restore the file-system. Check that the whole transaction # is rolled back. # # pager1-4.5.4: Restore the file-system. Corrupt the first record in the # journal. Check the transaction is not rolled back. # # pager1-4.5.5: Restore the file-system. Corrupt the second record in the # journal. Check that the first record in the transaction is # played back, but not the second. # # pager1-4.5.6: Restore the file-system. Try to open the database with a # readonly connection. This should fail, as a read-only # connection cannot roll back the database file. # faultsim_restore_and_reopen do_execsql_test pager1.4.5.3 { SELECT * FROM t1; SELECT * FROM t2; } {I II III IV} faultsim_restore_and_reopen hexio_write test.db-journal [expr 512+4+1024 - 202] 0123456789ABCDEF do_execsql_test pager1.4.5.4 { SELECT * FROM t1; SELECT * FROM t2; } {I II 1 2 III IV 3 4} faultsim_restore_and_reopen hexio_write test.db-journal [expr 512+4+1024+4+4+1024 - 202] 0123456789ABCDEF do_execsql_test pager1.4.5.5 { SELECT * FROM t1; SELECT * FROM t2; } {I II III IV 3 4} faultsim_restore_and_reopen db close sqlite3 db test.db -readonly 1 do_catchsql_test pager1.4.5.6 { SELECT * FROM t1; SELECT * FROM t2; } {1 {disk I/O error}} db close # Snapshot the file-system just before multi-file commit. Save the name # of the master journal file in $::mj_filename. # tv script copy_on_mj_delete tv filter xDelete proc copy_on_mj_delete {method filename args} { if {[string match *mj* [file tail $filename]]} { set ::mj_filename $filename faultsim_save } return SQLITE_OK } do_test pager1.4.6.1 { faultsim_delete_and_reopen execsql { PRAGMA journal_mode = DELETE; ATTACH 'test.db2' AS two; CREATE TABLE t1(a, b); CREATE TABLE two.t2(a, b); INSERT INTO t1 VALUES(1, 't1.1'); INSERT INTO t2 VALUES(1, 't2.1'); BEGIN; UPDATE t1 SET b = 't1.2'; UPDATE t2 SET b = 't2.2'; COMMIT; } tv filter {} db close } {} faultsim_restore_and_reopen do_execsql_test pager1.4.6.2 { SELECT * FROM t1 } {1 t1.1} do_test pager1.4.6.3 { file exists $::mj_filename } {1} do_execsql_test pager1.4.6.4 { ATTACH 'test.db2' AS two; SELECT * FROM t2; } {1 t2.1} do_test pager1.4.6.5 { file exists $::mj_filename } {0} faultsim_restore_and_reopen db close do_test pager1.4.6.8 { set ::mj_filename1 $::mj_filename tv filter xDelete sqlite3 db test.db2 execsql { PRAGMA journal_mode = DELETE; ATTACH 'test.db3' AS three; CREATE TABLE three.t3(a, b); INSERT INTO t3 VALUES(1, 't3.1'); BEGIN; UPDATE t2 SET b = 't2.3'; UPDATE t3 SET b = 't3.3'; COMMIT; } expr {$::mj_filename1 != $::mj_filename} } {1} faultsim_restore_and_reopen tv filter {} # The file-system now contains: # # * three databases # * three hot-journal files # * two master-journal files. # # The hot-journals associated with test.db2 and test.db3 point to # master journal $::mj_filename. The hot-journal file associated with # test.db points to master journal $::mj_filename1. So reading from # test.db should delete $::mj_filename1. # do_test pager1.4.6.9 { lsort [glob test.db*] } [lsort [list \ test.db test.db2 test.db3 \ test.db-journal test.db2-journal test.db3-journal \ [file tail $::mj_filename] [file tail $::mj_filename1] ]] # The master-journal $::mj_filename1 contains pointers to test.db and # test.db2. However the hot-journal associated with test.db2 points to # a different master-journal. Therefore, reading from test.db only should # be enough to cause SQLite to delete $::mj_filename1. # do_test pager1.4.6.10 { file exists $::mj_filename } {1} do_test pager1.4.6.11 { file exists $::mj_filename1 } {1} do_execsql_test pager1.4.6.12 { SELECT * FROM t1 } {1 t1.1} do_test pager1.4.6.13 { file exists $::mj_filename } {1} do_test pager1.4.6.14 { file exists $::mj_filename1 } {0} do_execsql_test pager1.4.6.12 { ATTACH 'test.db2' AS two; SELECT * FROM t2; } {1 t2.1} do_test pager1.4.6.13 { file exists $::mj_filename } {1} do_execsql_test pager1.4.6.14 { ATTACH 'test.db3' AS three; SELECT * FROM t3; } {1 t3.1} do_test pager1.4.6.15 { file exists $::mj_filename } {0} db close tv delete testvfs tv -default 1 tv sectorsize 512 tv script copy_on_journal_delete tv filter xDelete proc copy_on_journal_delete {method filename args} { if {[string match *journal $filename]} faultsim_save return SQLITE_OK } faultsim_delete_and_reopen do_execsql_test pager1.4.7.1 { PRAGMA journal_mode = DELETE; CREATE TABLE t1(x PRIMARY KEY, y); CREATE INDEX i1 ON t1(y); INSERT INTO t1 VALUES('I', 'one'); INSERT INTO t1 VALUES('II', 'four'); INSERT INTO t1 VALUES('III', 'nine'); BEGIN; INSERT INTO t1 VALUES('IV', 'sixteen'); INSERT INTO t1 VALUES('V' , 'twentyfive'); COMMIT; } {delete} tv filter {} db close tv delete do_test pager1.4.7.2 { faultsim_restore_and_reopen catch {file attributes test.db-journal -permissions r--------} catch {file attributes test.db-journal -readonly 1} catchsql { SELECT * FROM t1 } } {1 {unable to open database file}} do_test pager1.4.7.3 { db close catch {file attributes test.db-journal -permissions rw-rw-rw-} catch {file attributes test.db-journal -readonly 0} file delete test.db-journal file exists test.db-journal } {0} #------------------------------------------------------------------------- # The following tests deal with multi-file commits. # # pager1-5.1.*: The case where a multi-file cannot be committed because # another connection is holding a SHARED lock on one of the # files. After the SHARED lock is removed, the COMMIT succeeds. # # pager1-5.2.*: Multi-file commits with journal_mode=memory. # # pager1-5.3.*: Multi-file commits with journal_mode=memory. # # pager1-5.4.*: Check that with synchronous=normal, the master-journal file # name is added to a journal file immediately after the last # journal record. But with synchronous=full, extra unused space # is allocated between the last journal record and the # master-journal file name so that the master-journal file # name does not lie on the same sector as the last journal file # record. # # pager1-5.5.*: Check that in journal_mode=PERSIST mode, a journal file is # truncated to zero bytes when a multi-file transaction is # committed (instead of the first couple of bytes being zeroed). # # do_test pager1-5.1.1 { faultsim_delete_and_reopen execsql { ATTACH 'test.db2' AS aux; CREATE TABLE t1(a, b); CREATE TABLE aux.t2(a, b); INSERT INTO t1 VALUES(17, 'Lenin'); INSERT INTO t1 VALUES(22, 'Stalin'); INSERT INTO t1 VALUES(53, 'Khrushchev'); } } {} do_test pager1-5.1.2 { execsql { BEGIN; INSERT INTO t1 VALUES(64, 'Brezhnev'); INSERT INTO t2 SELECT * FROM t1; } sqlite3 db2 test.db2 execsql { BEGIN; SELECT * FROM t2; } db2 } {} do_test pager1-5.1.3 { catchsql COMMIT } {1 {database is locked}} do_test pager1-5.1.4 { execsql COMMIT db2 execsql COMMIT execsql { SELECT * FROM t2 } db2 } {17 Lenin 22 Stalin 53 Khrushchev 64 Brezhnev} do_test pager1-5.1.5 { db2 close } {} do_test pager1-5.2.1 { execsql { PRAGMA journal_mode = memory; BEGIN; INSERT INTO t1 VALUES(84, 'Andropov'); INSERT INTO t2 VALUES(84, 'Andropov'); COMMIT; } } {memory} do_test pager1-5.3.1 { execsql { PRAGMA journal_mode = off; BEGIN; INSERT INTO t1 VALUES(85, 'Gorbachev'); INSERT INTO t2 VALUES(85, 'Gorbachev'); COMMIT; } } {off} do_test pager1-5.4.1 { db close testvfs tv sqlite3 db test.db -vfs tv execsql { ATTACH 'test.db2' AS aux } tv filter xDelete tv script max_journal_size tv sectorsize 512 set ::max_journal 0 proc max_journal_size {method args} { set sz 0 catch { set sz [file size test.db-journal] } if {$sz > $::max_journal} { set ::max_journal $sz } return SQLITE_OK } execsql { PRAGMA journal_mode = DELETE; PRAGMA synchronous = NORMAL; BEGIN; INSERT INTO t1 VALUES(85, 'Gorbachev'); INSERT INTO t2 VALUES(85, 'Gorbachev'); COMMIT; } set ::max_journal } [expr 2615+[string length [pwd]]] do_test pager1-5.4.2 { set ::max_journal 0 execsql { PRAGMA synchronous = full; BEGIN; DELETE FROM t1 WHERE b = 'Lenin'; DELETE FROM t2 WHERE b = 'Lenin'; COMMIT; } set ::max_journal } [expr 3111+[string length [pwd]]] db close tv delete do_test pager1-5.5.1 { sqlite3 db test.db execsql { ATTACH 'test.db2' AS aux; PRAGMA journal_mode = PERSIST; CREATE TABLE t3(a, b); INSERT INTO t3 SELECT randomblob(1500), randomblob(1500) FROM t1; UPDATE t3 SET b = randomblob(1500); } expr [file size test.db-journal] > 15000 } {1} do_test pager1-5.5.2 { execsql { PRAGMA synchronous = full; BEGIN; DELETE FROM t1 WHERE b = 'Stalin'; DELETE FROM t2 WHERE b = 'Stalin'; COMMIT; } file size test.db-journal } {0} #------------------------------------------------------------------------- # The following tests work with "PRAGMA max_page_count" # do_test pager1-6.1 { faultsim_delete_and_reopen execsql { PRAGMA auto_vacuum = none; PRAGMA max_page_count = 10; CREATE TABLE t2(a, b); CREATE TABLE t3(a, b); CREATE TABLE t4(a, b); CREATE TABLE t5(a, b); CREATE TABLE t6(a, b); CREATE TABLE t7(a, b); CREATE TABLE t8(a, b); CREATE TABLE t9(a, b); CREATE TABLE t10(a, b); } } {10} do_catchsql_test pager1-6.2 { CREATE TABLE t11(a, b) } {1 {database or disk is full}} do_execsql_test pager1-6.4 { PRAGMA max_page_count } {10} do_execsql_test pager1-6.5 { PRAGMA max_page_count = 15 } {15} do_execsql_test pager1-6.6 { CREATE TABLE t11(a, b) } {} do_execsql_test pager1-6.7 { BEGIN; INSERT INTO t11 VALUES(1, 2); PRAGMA max_page_count = 13; } {13} do_execsql_test pager1-6.8 { INSERT INTO t11 VALUES(3, 4); PRAGMA max_page_count = 10; } {11} do_execsql_test pager1-6.9 { COMMIT } {} #------------------------------------------------------------------------- # The following tests work with "PRAGMA journal_mode=TRUNCATE" and # "PRAGMA locking_mode=EXCLUSIVE". # # Each test is specified with 5 variables. As follows: # # $tn: Test Number. Used as part of the [do_test] test names. # $sql: SQL to execute. # $res: Expected result of executing $sql. # $js: The expected size of the journal file, in bytes, after executing # the SQL script. Or -1 if the journal is not expected to exist. # $ws: The expected size of the WAL file, in bytes, after executing # the SQL script. Or -1 if the WAL is not expected to exist. # ifcapable wal { faultsim_delete_and_reopen foreach {tn sql res js ws} [subst { 1 { CREATE TABLE t1(a, b); PRAGMA auto_vacuum=OFF; PRAGMA synchronous=NORMAL; PRAGMA page_size=1024; PRAGMA locking_mode=EXCLUSIVE; PRAGMA journal_mode=TRUNCATE; INSERT INTO t1 VALUES(1, 2); } {exclusive truncate} 0 -1 2 { BEGIN IMMEDIATE; SELECT * FROM t1; COMMIT; } {1 2} 0 -1 3 { BEGIN; SELECT * FROM t1; COMMIT; } {1 2} 0 -1 4 { PRAGMA journal_mode = WAL } wal -1 -1 5 { INSERT INTO t1 VALUES(3, 4) } {} -1 [wal_file_size 1 1024] 6 { PRAGMA locking_mode = NORMAL } normal -1 [wal_file_size 1 1024] 7 { INSERT INTO t1 VALUES(5, 6); } {} -1 [wal_file_size 2 1024] 8 { PRAGMA journal_mode = TRUNCATE } truncate 0 -1 9 { INSERT INTO t1 VALUES(7, 8) } {} 0 -1 10 { SELECT * FROM t1 } {1 2 3 4 5 6 7 8} 0 -1 }] { do_execsql_test pager1-7.1.$tn.1 $sql $res catch { set J -1 ; set J [file size test.db-journal] } catch { set W -1 ; set W [file size test.db-wal] } do_test pager1-7.1.$tn.2 { list $J $W } [list $js $ws] } } do_test pager1-7.2.1 { faultsim_delete_and_reopen execsql { PRAGMA locking_mode = EXCLUSIVE; CREATE TABLE t1(a, b); BEGIN; PRAGMA journal_mode = delete; PRAGMA journal_mode = truncate; } } {exclusive delete truncate} do_test pager1-7.2.2 { execsql { INSERT INTO t1 VALUES(1, 2) } execsql { PRAGMA journal_mode = persist } } {truncate} do_test pager1-7.2.3 { execsql { COMMIT } execsql { PRAGMA journal_mode = persist; PRAGMA journal_size_limit; } } {persist -1} #------------------------------------------------------------------------- # The following tests, pager1-8.*, test that the special filenames # ":memory:" and "" open temporary databases. # foreach {tn filename} { 1 :memory: 2 "" } { do_test pager1-8.$tn.1 { faultsim_delete_and_reopen db close sqlite3 db $filename execsql { PRAGMA auto_vacuum = 1; CREATE TABLE x1(x); INSERT INTO x1 VALUES('Charles'); INSERT INTO x1 VALUES('James'); INSERT INTO x1 VALUES('Mary'); SELECT * FROM x1; } } {Charles James Mary} do_test pager1-8.$tn.2 { sqlite3 db2 $filename catchsql { SELECT * FROM x1 } db2 } {1 {no such table: x1}} do_execsql_test pager1-8.$tn.3 { BEGIN; INSERT INTO x1 VALUES('William'); INSERT INTO x1 VALUES('Anne'); ROLLBACK; } {} } #------------------------------------------------------------------------- # The next block of tests - pager1-9.* - deal with interactions between # the pager and the backup API. Test cases: # # pager1-9.1.*: Test that a backup completes successfully even if the # source db is written to during the backup op. # # pager1-9.2.*: Test that a backup completes successfully even if the # source db is written to and then rolled back during a # backup operation. # do_test pager1-9.0.1 { faultsim_delete_and_reopen db func a_string a_string execsql { PRAGMA cache_size = 10; BEGIN; CREATE TABLE ab(a, b, UNIQUE(a, b)); INSERT INTO ab VALUES( a_string(200), a_string(300) ); INSERT INTO ab SELECT a_string(200), a_string(300) FROM ab; INSERT INTO ab SELECT a_string(200), a_string(300) FROM ab; INSERT INTO ab SELECT a_string(200), a_string(300) FROM ab; INSERT INTO ab SELECT a_string(200), a_string(300) FROM ab; INSERT INTO ab SELECT a_string(200), a_string(300) FROM ab; INSERT INTO ab SELECT a_string(200), a_string(300) FROM ab; INSERT INTO ab SELECT a_string(200), a_string(300) FROM ab; COMMIT; } } {} do_test pager1-9.0.2 { sqlite3 db2 test.db2 db2 eval { PRAGMA cache_size = 10 } sqlite3_backup B db2 main db main list [B step 10000] [B finish] } {SQLITE_DONE SQLITE_OK} do_test pager1-9.0.3 { db one {SELECT md5sum(a, b) FROM ab} } [db2 one {SELECT md5sum(a, b) FROM ab}] do_test pager1-9.1.1 { execsql { UPDATE ab SET a = a_string(201) } sqlite3_backup B db2 main db main B step 30 } {SQLITE_OK} do_test pager1-9.1.2 { execsql { UPDATE ab SET b = a_string(301) } list [B step 10000] [B finish] } {SQLITE_DONE SQLITE_OK} do_test pager1-9.1.3 { db one {SELECT md5sum(a, b) FROM ab} } [db2 one {SELECT md5sum(a, b) FROM ab}] do_test pager1-9.1.4 { execsql { SELECT count(*) FROM ab } } {128} do_test pager1-9.2.1 { execsql { UPDATE ab SET a = a_string(202) } sqlite3_backup B db2 main db main B step 30 } {SQLITE_OK} do_test pager1-9.2.2 { execsql { BEGIN; UPDATE ab SET b = a_string(301); ROLLBACK; } list [B step 10000] [B finish] } {SQLITE_DONE SQLITE_OK} do_test pager1-9.2.3 { db one {SELECT md5sum(a, b) FROM ab} } [db2 one {SELECT md5sum(a, b) FROM ab}] do_test pager1-9.2.4 { execsql { SELECT count(*) FROM ab } } {128} db close db2 close do_test pager1-9.3.1 { testvfs tv -default 1 tv sectorsize 4096 faultsim_delete_and_reopen execsql { PRAGMA page_size = 1024 } for {set ii 0} {$ii < 4} {incr ii} { execsql "CREATE TABLE t${ii}(a, b)" } } {} do_test pager1-9.3.2 { sqlite3 db2 test.db2 execsql { PRAGMA page_size = 4096; PRAGMA synchronous = OFF; CREATE TABLE t1(a, b); CREATE TABLE t2(a, b); } db2 sqlite3_backup B db2 main db main B step 30 list [B step 10000] [B finish] } {SQLITE_DONE SQLITE_OK} do_test pager1-9.3.3 { db2 close db close tv delete file size test.db2 } [file size test.db] do_test pager1-9.4.1 { faultsim_delete_and_reopen sqlite3 db2 test.db2 execsql { PRAGMA page_size = 4096; CREATE TABLE t1(a, b); CREATE TABLE t2(a, b); } db2 sqlite3_backup B db2 main db main list [B step 10000] [B finish] } {SQLITE_DONE SQLITE_OK} do_test pager1-9.4.2 { list [file size test.db2] [file size test.db] } {0 0} db2 close #------------------------------------------------------------------------- # Test that regardless of the value returned by xSectorSize(), the # minimum effective sector-size is 512 and the maximum 65536 bytes. # testvfs tv -default 1 foreach sectorsize { 32 64 128 256 512 1024 2048 4096 8192 16384 32768 65536 131072 262144 } { tv sectorsize $sectorsize set eff $sectorsize if {$sectorsize < 512} { set eff 512 } if {$sectorsize > 65536} { set eff 65536 } do_test pager1-10.$sectorsize.1 { faultsim_delete_and_reopen db func a_string a_string execsql { PRAGMA journal_mode = PERSIST; PRAGMA page_size = 1024; BEGIN; CREATE TABLE t1(a, b); CREATE TABLE t2(a, b); CREATE TABLE t3(a, b); COMMIT; } file size test.db-journal } [expr $sectorsize > 65536 ? 65536 : $sectorsize] do_test pager1-10.$sectorsize.2 { execsql { INSERT INTO t3 VALUES(a_string(300), a_string(300)); INSERT INTO t3 SELECT * FROM t3; /* 2 */ INSERT INTO t3 SELECT * FROM t3; /* 4 */ INSERT INTO t3 SELECT * FROM t3; /* 8 */ INSERT INTO t3 SELECT * FROM t3; /* 16 */ INSERT INTO t3 SELECT * FROM t3; /* 32 */ } } {} do_test pager1-10.$sectorsize.3 { db close sqlite3 db test.db execsql { PRAGMA cache_size = 10; BEGIN; } recursive_select 32 t3 {db eval "INSERT INTO t2 VALUES(1, 2)"} execsql { COMMIT; SELECT * FROM t2; } } {1 2} do_test pager1-10.$sectorsize.4 { execsql { CREATE TABLE t6(a, b); CREATE TABLE t7(a, b); CREATE TABLE t5(a, b); DROP TABLE t6; DROP TABLE t7; } execsql { BEGIN; CREATE TABLE t6(a, b); } recursive_select 32 t3 {db eval "INSERT INTO t5 VALUES(1, 2)"} execsql { COMMIT; SELECT * FROM t5; } } {1 2} } db close tv sectorsize 4096 do_test pager1.10.x.1 { faultsim_delete_and_reopen execsql { PRAGMA auto_vacuum = none; PRAGMA page_size = 1024; CREATE TABLE t1(x); } for {set i 0} {$i<30} {incr i} { execsql { INSERT INTO t1 VALUES(zeroblob(900)) } } file size test.db } {32768} do_test pager1.10.x.2 { execsql { CREATE TABLE t2(x); DROP TABLE t2; } file size test.db } {33792} do_test pager1.10.x.3 { execsql { BEGIN; CREATE TABLE t2(x); } recursive_select 30 t1 execsql { CREATE TABLE t3(x); COMMIT; } } {} db close tv delete testvfs tv -default 1 faultsim_delete_and_reopen db func a_string a_string do_execsql_test pager1-11.1 { PRAGMA journal_mode = DELETE; PRAGMA cache_size = 10; BEGIN; CREATE TABLE zz(top PRIMARY KEY); INSERT INTO zz VALUES(a_string(222)); INSERT INTO zz SELECT a_string((SELECT 222+max(rowid) FROM zz)) FROM zz; INSERT INTO zz SELECT a_string((SELECT 222+max(rowid) FROM zz)) FROM zz; INSERT INTO zz SELECT a_string((SELECT 222+max(rowid) FROM zz)) FROM zz; INSERT INTO zz SELECT a_string((SELECT 222+max(rowid) FROM zz)) FROM zz; INSERT INTO zz SELECT a_string((SELECT 222+max(rowid) FROM zz)) FROM zz; COMMIT; BEGIN; UPDATE zz SET top = a_string(345); } {delete} proc lockout {method args} { return SQLITE_IOERR } tv script lockout tv filter {xWrite xTruncate xSync} do_catchsql_test pager1-11.2 { COMMIT } {1 {disk I/O error}} tv script {} do_test pager1-11.3 { sqlite3 db2 test.db execsql { PRAGMA journal_mode = TRUNCATE; PRAGMA integrity_check; } db2 } {truncate ok} do_test pager1-11.4 { db2 close file exists test.db-journal } {0} do_execsql_test pager1-11.5 { SELECT count(*) FROM zz } {32} db close tv delete #------------------------------------------------------------------------- # Test "PRAGMA page_size" # testvfs tv -default 1 tv sectorsize 1024 foreach pagesize { 512 1024 2048 4096 8192 16384 32768 } { faultsim_delete_and_reopen # The sector-size (according to the VFS) is 1024 bytes. So if the # page-size requested using "PRAGMA page_size" is greater than the # compile time value of SQLITE_MAX_PAGE_SIZE, then the effective # page-size remains 1024 bytes. # set eff $pagesize if {$eff > $::SQLITE_MAX_PAGE_SIZE} { set eff 1024 } do_test pager1-12.$pagesize.1 { sqlite3 db2 test.db execsql " PRAGMA page_size = $pagesize; CREATE VIEW v AS SELECT * FROM sqlite_master; " db2 file size test.db } $eff do_test pager1-12.$pagesize.2 { sqlite3 db2 test.db execsql { SELECT count(*) FROM v; PRAGMA main.page_size; } db2 } [list 1 $eff] do_test pager1-12.$pagesize.3 { execsql { SELECT count(*) FROM v; PRAGMA main.page_size; } } [list 1 $eff] db2 close } db close tv delete #------------------------------------------------------------------------- # Test specal "PRAGMA journal_mode=PERSIST" test cases. # # pager1-13.1.*: This tests a special case encountered in persistent # journal mode: If the journal associated with a transaction # is smaller than the journal file (because a previous # transaction left a very large non-hot journal file in the # file-system), then SQLite has to be careful that there is # not a journal-header left over from a previous transaction # immediately following the journal content just written. # If there is, and the process crashes so that the journal # becomes a hot-journal and must be rolled back by another # process, there is a danger that the other process may roll # back the aborted transaction, then continue copying data # from an older transaction from the remainder of the journal. # See the syncJournal() function for details. # # pager1-13.2.*: Same test as the previous. This time, throw an index into # the mix to make the integrity-check more likely to catch # errors. # testvfs tv -default 1 tv script xSyncCb tv filter xSync proc xSyncCb {method filename args} { set t [file tail $filename] if {$t == "test.db"} faultsim_save return SQLITE_OK } faultsim_delete_and_reopen db func a_string a_string # The UPDATE statement at the end of this test case creates a really big # journal. Since the cache-size is only 10 pages, the journal contains # frequent journal headers. # do_execsql_test pager1-13.1.1 { PRAGMA page_size = 1024; PRAGMA journal_mode = PERSIST; PRAGMA cache_size = 10; BEGIN; CREATE TABLE t1(a INTEGER PRIMARY KEY, b BLOB); INSERT INTO t1 VALUES(NULL, a_string(400)); INSERT INTO t1 SELECT NULL, a_string(400) FROM t1; /* 2 */ INSERT INTO t1 SELECT NULL, a_string(400) FROM t1; /* 4 */ INSERT INTO t1 SELECT NULL, a_string(400) FROM t1; /* 8 */ INSERT INTO t1 SELECT NULL, a_string(400) FROM t1; /* 16 */ INSERT INTO t1 SELECT NULL, a_string(400) FROM t1; /* 32 */ INSERT INTO t1 SELECT NULL, a_string(400) FROM t1; /* 64 */ INSERT INTO t1 SELECT NULL, a_string(400) FROM t1; /* 128 */ COMMIT; UPDATE t1 SET b = a_string(400); } {persist} # Run transactions of increasing sizes. Eventually, one (or more than one) # of these will write just enough content that one of the old headers created # by the transaction in the block above lies immediately after the content # journalled by the current transaction. # for {set nUp 1} {$nUp<64} {incr nUp} { do_execsql_test pager1-13.1.2.$nUp.1 { UPDATE t1 SET b = a_string(399) WHERE a <= $nUp } {} do_execsql_test pager1-13.1.2.$nUp.2 { PRAGMA integrity_check } {ok} # Try to access the snapshot of the file-system. # sqlite3 db2 sv_test.db do_test pager1-13.1.2.$nUp.3 { execsql { SELECT sum(length(b)) FROM t1 } db2 } [expr {128*400 - ($nUp-1)}] do_test pager1-13.1.2.$nUp.4 { execsql { PRAGMA integrity_check } db2 } {ok} db2 close } # Same test as above. But this time with an index on the table. # do_execsql_test pager1-13.2.1 { CREATE INDEX i1 ON t1(b); UPDATE t1 SET b = a_string(400); } {} for {set nUp 1} {$nUp<64} {incr nUp} { do_execsql_test pager1-13.2.2.$nUp.1 { UPDATE t1 SET b = a_string(399) WHERE a <= $nUp } {} do_execsql_test pager1-13.2.2.$nUp.2 { PRAGMA integrity_check } {ok} sqlite3 db2 sv_test.db do_test pager1-13.2.2.$nUp.3 { execsql { SELECT sum(length(b)) FROM t1 } db2 } [expr {128*400 - ($nUp-1)}] do_test pager1-13.2.2.$nUp.4 { execsql { PRAGMA integrity_check } db2 } {ok} db2 close } db close tv delete #------------------------------------------------------------------------- # Test specal "PRAGMA journal_mode=OFF" test cases. # faultsim_delete_and_reopen do_execsql_test pager1-14.1.1 { PRAGMA journal_mode = OFF; CREATE TABLE t1(a, b); BEGIN; INSERT INTO t1 VALUES(1, 2); COMMIT; SELECT * FROM t1; } {off 1 2} do_catchsql_test pager1-14.1.2 { BEGIN; INSERT INTO t1 VALUES(3, 4); ROLLBACK; } {0 {}} do_execsql_test pager1-14.1.3 { SELECT * FROM t1; } {1 2 3 4} do_catchsql_test pager1-14.1.4 { BEGIN; INSERT INTO t1(rowid, a, b) SELECT a+3, b, b FROM t1; INSERT INTO t1(rowid, a, b) SELECT a+3, b, b FROM t1; } {1 {PRIMARY KEY must be unique}} do_execsql_test pager1-14.1.5 { COMMIT; SELECT * FROM t1; } {1 2 3 4 2 2 4 4} #------------------------------------------------------------------------- # Test opening and closing the pager sub-system with different values # for the sqlite3_vfs.szOsFile variable. # faultsim_delete_and_reopen do_execsql_test pager1-15.0 { CREATE TABLE tx(y, z); INSERT INTO tx VALUES('Ayutthaya', 'Beijing'); INSERT INTO tx VALUES('London', 'Tokyo'); } {} db close for {set i 0} {$i<513} {incr i 3} { testvfs tv -default 1 -szosfile $i sqlite3 db test.db do_execsql_test pager1-15.$i.1 { SELECT * FROM tx; } {Ayutthaya Beijing London Tokyo} db close tv delete } #------------------------------------------------------------------------- # Check that it is not possible to open a database file if the full path # to the associated journal file will be longer than sqlite3_vfs.mxPathname. # testvfs tv -default 1 tv script xOpenCb tv filter xOpen proc xOpenCb {method filename} { set ::file_len [string length $filename] } sqlite3 db test.db db close tv delete for {set ii [expr $::file_len-5]} {$ii < [expr $::file_len+20]} {incr ii} { testvfs tv -default 1 -mxpathname $ii # The length of the full path to file "test.db-journal" is ($::file_len+8). # If the configured sqlite3_vfs.mxPathname value greater than or equal to # this, then the file can be opened. Otherwise, it cannot. # if {$ii >= [expr $::file_len+8]} { set res {0 {}} } else { set res {1 {unable to open database file}} } do_test pager1-16.1.$ii { list [catch { sqlite3 db test.db } msg] $msg } $res catch {db close} tv delete } #------------------------------------------------------------------------- # Test "PRAGMA omit_readlock". # # pager1-17.$tn.1.*: Test that if a second connection has an open # read-transaction, it is not usually possible to write # the database. # # pager1-17.$tn.2.*: Test that if the second connection was opened with # the SQLITE_OPEN_READONLY flag, and # "PRAGMA omit_readlock = 1" is executed before attaching # the database and opening a read-transaction on it, it is # possible to write the db. # # pager1-17.$tn.3.*: Test that if the second connection was *not* opened with # the SQLITE_OPEN_READONLY flag, executing # "PRAGMA omit_readlock = 1" has no effect. # do_multiclient_test tn { do_test pager1-17.$tn.1.1 { sql1 { CREATE TABLE t1(a, b); INSERT INTO t1 VALUES(1, 2); } sql2 { BEGIN; SELECT * FROM t1; } } {1 2} do_test pager1-17.$tn.1.2 { csql1 { INSERT INTO t1 VALUES(3, 4) } } {1 {database is locked}} do_test pager1-17.$tn.1.3 { sql2 { COMMIT } sql1 { INSERT INTO t1 VALUES(3, 4) } } {} do_test pager1-17.$tn.2.1 { code2 { db2 close sqlite3 db2 :memory: -readonly 1 } sql2 { PRAGMA omit_readlock = 1; ATTACH 'test.db' AS two; BEGIN; SELECT * FROM t1; } } {1 2 3 4} do_test pager1-17.$tn.2.2 { sql1 "INSERT INTO t1 VALUES(5, 6)" } {} do_test pager1-17.$tn.2.3 { sql2 "SELECT * FROM t1" } {1 2 3 4} do_test pager1-17.$tn.2.4 { sql2 "COMMIT ; SELECT * FROM t1" } {1 2 3 4 5 6} do_test pager1-17.$tn.3.1 { code2 { db2 close sqlite3 db2 :memory: } sql2 { PRAGMA omit_readlock = 1; ATTACH 'test.db' AS two; BEGIN; SELECT * FROM t1; } } {1 2 3 4 5 6} do_test pager1-17.$tn.3.2 { csql1 { INSERT INTO t1 VALUES(3, 4) } } {1 {database is locked}} do_test pager1-17.$tn.3.3 { sql2 COMMIT } {} } #------------------------------------------------------------------------- # Test the pagers response to the b-tree layer requesting illegal page # numbers: # # + The locking page, # + Page 0, # + A page with a page number greater than (2^31-1). # do_test pager1-18.1 { faultsim_delete_and_reopen db func a_string a_string execsql { PRAGMA page_size = 1024; CREATE TABLE t1(a, b); INSERT INTO t1 VALUES(a_string(500), a_string(200)); INSERT INTO t1 SELECT a_string(500), a_string(200) FROM t1; INSERT INTO t1 SELECT a_string(500), a_string(200) FROM t1; INSERT INTO t1 SELECT a_string(500), a_string(200) FROM t1; INSERT INTO t1 SELECT a_string(500), a_string(200) FROM t1; INSERT INTO t1 SELECT a_string(500), a_string(200) FROM t1; INSERT INTO t1 SELECT a_string(500), a_string(200) FROM t1; INSERT INTO t1 SELECT a_string(500), a_string(200) FROM t1; } } {} do_test pager1-18.2 { set root [db one "SELECT rootpage FROM sqlite_master"] set lockingpage [expr (0x10000/1024) + 1] execsql { PRAGMA writable_schema = 1; UPDATE sqlite_master SET rootpage = $lockingpage; } sqlite3 db2 test.db catchsql { SELECT count(*) FROM t1 } db2 } {1 {database disk image is malformed}} db2 close do_test pager1-18.3 { execsql { CREATE TABLE t2(x); INSERT INTO t2 VALUES(a_string(5000)); } set pgno [expr ([file size test.db] / 1024)-2] hexio_write test.db [expr ($pgno-1)*1024] 00000000 sqlite3 db2 test.db catchsql { SELECT length(x) FROM t2 } db2 } {1 {database disk image is malformed}} db2 close do_test pager1-18.4 { hexio_write test.db [expr ($pgno-1)*1024] 90000000 sqlite3 db2 test.db catchsql { SELECT length(x) FROM t2 } db2 } {1 {database disk image is malformed}} db2 close do_test pager1-18.5 { sqlite3 db "" execsql { CREATE TABLE t1(a, b); CREATE TABLE t2(a, b); PRAGMA writable_schema = 1; UPDATE sqlite_master SET rootpage=5 WHERE tbl_name = 't1'; PRAGMA writable_schema = 0; ALTER TABLE t1 RENAME TO x1; } catchsql { SELECT * FROM x1 } } {1 {database disk image is malformed}} db close do_test pager1-18.6 { faultsim_delete_and_reopen db func a_string a_string execsql { PRAGMA page_size = 1024; CREATE TABLE t1(x); INSERT INTO t1 VALUES(a_string(800)); INSERT INTO t1 VALUES(a_string(800)); } set root [db one "SELECT rootpage FROM sqlite_master"] db close hexio_write test.db [expr ($root-1)*1024 + 8] 00000000 sqlite3 db test.db catchsql { SELECT length(x) FROM t1 } } {1 {database disk image is malformed}} do_test pager1-19.1 { sqlite3 db "" db func a_string a_string execsql { PRAGMA page_size = 512; PRAGMA auto_vacuum = 1; CREATE TABLE t1(aa, ab, ac, ad, ae, af, ag, ah, ai, aj, ak, al, am, an, ba, bb, bc, bd, be, bf, bg, bh, bi, bj, bk, bl, bm, bn, ca, cb, cc, cd, ce, cf, cg, ch, ci, cj, ck, cl, cm, cn, da, db, dc, dd, de, df, dg, dh, di, dj, dk, dl, dm, dn, ea, eb, ec, ed, ee, ef, eg, eh, ei, ej, ek, el, em, en, fa, fb, fc, fd, fe, ff, fg, fh, fi, fj, fk, fl, fm, fn, ga, gb, gc, gd, ge, gf, gg, gh, gi, gj, gk, gl, gm, gn, ha, hb, hc, hd, he, hf, hg, hh, hi, hj, hk, hl, hm, hn, ia, ib, ic, id, ie, if, ig, ih, ii, ij, ik, il, im, ix, ja, jb, jc, jd, je, jf, jg, jh, ji, jj, jk, jl, jm, jn, ka, kb, kc, kd, ke, kf, kg, kh, ki, kj, kk, kl, km, kn, la, lb, lc, ld, le, lf, lg, lh, li, lj, lk, ll, lm, ln, ma, mb, mc, md, me, mf, mg, mh, mi, mj, mk, ml, mm, mn ); CREATE TABLE t2(aa, ab, ac, ad, ae, af, ag, ah, ai, aj, ak, al, am, an, ba, bb, bc, bd, be, bf, bg, bh, bi, bj, bk, bl, bm, bn, ca, cb, cc, cd, ce, cf, cg, ch, ci, cj, ck, cl, cm, cn, da, db, dc, dd, de, df, dg, dh, di, dj, dk, dl, dm, dn, ea, eb, ec, ed, ee, ef, eg, eh, ei, ej, ek, el, em, en, fa, fb, fc, fd, fe, ff, fg, fh, fi, fj, fk, fl, fm, fn, ga, gb, gc, gd, ge, gf, gg, gh, gi, gj, gk, gl, gm, gn, ha, hb, hc, hd, he, hf, hg, hh, hi, hj, hk, hl, hm, hn, ia, ib, ic, id, ie, if, ig, ih, ii, ij, ik, il, im, ix, ja, jb, jc, jd, je, jf, jg, jh, ji, jj, jk, jl, jm, jn, ka, kb, kc, kd, ke, kf, kg, kh, ki, kj, kk, kl, km, kn, la, lb, lc, ld, le, lf, lg, lh, li, lj, lk, ll, lm, ln, ma, mb, mc, md, me, mf, mg, mh, mi, mj, mk, ml, mm, mn ); INSERT INTO t1(aa) VALUES( a_string(100000) ); INSERT INTO t2(aa) VALUES( a_string(100000) ); VACUUM; } } {} #------------------------------------------------------------------------- # Test a couple of special cases that come up while committing # transactions: # # pager1-20.1.*: Committing an in-memory database transaction when the # database has not been modified at all. # # pager1-20.2.*: As above, but with a normal db in exclusive-locking mode. # # pager1-20.3.*: Committing a transaction in WAL mode where the database has # been modified, but all dirty pages have been flushed to # disk before the commit. # do_test pager1-20.1.1 { catch {db close} sqlite3 db :memory: execsql { CREATE TABLE one(two, three); INSERT INTO one VALUES('a', 'b'); } } {} do_test pager1-20.1.2 { execsql { BEGIN EXCLUSIVE; COMMIT; } } {} do_test pager1-20.2.1 { faultsim_delete_and_reopen execsql { PRAGMA locking_mode = exclusive; PRAGMA journal_mode = persist; CREATE TABLE one(two, three); INSERT INTO one VALUES('a', 'b'); } } {exclusive persist} do_test pager1-20.2.2 { execsql { BEGIN EXCLUSIVE; COMMIT; } } {} do_test pager1-20.3.1 { faultsim_delete_and_reopen db func a_string a_string execsql { PRAGMA cache_size = 10; PRAGMA journal_mode = wal; BEGIN; CREATE TABLE t1(x); CREATE TABLE t2(y); INSERT INTO t1 VALUES(a_string(800)); INSERT INTO t1 SELECT a_string(800) FROM t1; /* 2 */ INSERT INTO t1 SELECT a_string(800) FROM t1; /* 4 */ INSERT INTO t1 SELECT a_string(800) FROM t1; /* 8 */ INSERT INTO t1 SELECT a_string(800) FROM t1; /* 16 */ INSERT INTO t1 SELECT a_string(800) FROM t1; /* 32 */ COMMIT; } } {wal} do_test pager1-20.3.2 { execsql { BEGIN; INSERT INTO t2 VALUES('xxxx'); } recursive_select 32 t1 execsql COMMIT } {} #------------------------------------------------------------------------- # Test that a WAL database may not be opened if: # # pager1-21.1.*: The VFS has an iVersion less than 2, or # pager1-21.2.*: The VFS does not provide xShmXXX() methods. # do_test pager1-21.0 { faultsim_delete_and_reopen execsql { PRAGMA journal_mode = WAL; CREATE TABLE ko(c DEFAULT 'abc', b DEFAULT 'def'); INSERT INTO ko DEFAULT VALUES; } } {wal} do_test pager1-21.1 { testvfs tv -noshm 1 sqlite3 db2 test.db -vfs tv catchsql { SELECT * FROM ko } db2 } {1 {unable to open database file}} db2 close tv delete do_test pager1-21.2 { testvfs tv -iversion 1 sqlite3 db2 test.db -vfs tv catchsql { SELECT * FROM ko } db2 } {1 {unable to open database file}} db2 close tv delete #------------------------------------------------------------------------- # Test that a "PRAGMA wal_checkpoint": # # pager1-22.1.*: is a no-op on a non-WAL db, and # pager1-22.2.*: does not cause xSync calls with a synchronous=off db. # do_test pager1-22.1.1 { faultsim_delete_and_reopen execsql { CREATE TABLE ko(c DEFAULT 'abc', b DEFAULT 'def'); INSERT INTO ko DEFAULT VALUES; } execsql { PRAGMA wal_checkpoint } } {} do_test pager1-22.2.1 { testvfs tv -default 1 tv filter xSync tv script xSyncCb proc xSyncCb {args} {incr ::synccount} set ::synccount 0 sqlite3 db test.db execsql { PRAGMA synchronous = off; PRAGMA journal_mode = WAL; INSERT INTO ko DEFAULT VALUES; } execsql { PRAGMA wal_checkpoint } set synccount } {0} db close tv delete #------------------------------------------------------------------------- # Tests for changing journal mode. # # pager1-23.1.*: Test that when changing from PERSIST to DELETE mode, # the journal file is deleted. # # pager1-23.2.*: Same test as above, but while a shared lock is held # on the database file. # # pager1-23.3.*: Same test as above, but while a reserved lock is held # on the database file. # # pager1-23.4.*: And, for fun, while holding an exclusive lock. # # pager1-23.5.*: Try to set various different journal modes with an # in-memory database (only MEMORY and OFF should work). # # pager1-23.6.*: Try to set locking_mode=normal on an in-memory database # (doesn't work - in-memory databases always use # locking_mode=exclusive). # do_test pager1-23.1.1 { faultsim_delete_and_reopen execsql { PRAGMA journal_mode = PERSIST; CREATE TABLE t1(a, b); } file exists test.db-journal } {1} do_test pager1-23.1.2 { execsql { PRAGMA journal_mode = DELETE } file exists test.db-journal } {0} do_test pager1-23.2.1 { execsql { PRAGMA journal_mode = PERSIST; INSERT INTO t1 VALUES('Canberra', 'ACT'); } db eval { SELECT * FROM t1 } { db eval { PRAGMA journal_mode = DELETE } } execsql { PRAGMA journal_mode } } {delete} do_test pager1-23.2.2 { file exists test.db-journal } {0} do_test pager1-23.3.1 { execsql { PRAGMA journal_mode = PERSIST; INSERT INTO t1 VALUES('Darwin', 'NT'); BEGIN IMMEDIATE; } db eval { PRAGMA journal_mode = DELETE } execsql { PRAGMA journal_mode } } {delete} do_test pager1-23.3.2 { file exists test.db-journal } {0} do_test pager1-23.3.3 { execsql COMMIT } {} do_test pager1-23.4.1 { execsql { PRAGMA journal_mode = PERSIST; INSERT INTO t1 VALUES('Adelaide', 'SA'); BEGIN EXCLUSIVE; } db eval { PRAGMA journal_mode = DELETE } execsql { PRAGMA journal_mode } } {delete} do_test pager1-23.4.2 { file exists test.db-journal } {0} do_test pager1-23.4.3 { execsql COMMIT } {} do_test pager1-23.5.1 { faultsim_delete_and_reopen sqlite3 db :memory: } {} foreach {tn mode possible} { 2 off 1 3 memory 1 4 persist 0 5 delete 0 6 wal 0 7 truncate 0 } { do_test pager1-23.5.$tn.1 { execsql "PRAGMA journal_mode = off" execsql "PRAGMA journal_mode = $mode" } [if $possible {list $mode} {list off}] do_test pager1-23.5.$tn.2 { execsql "PRAGMA journal_mode = memory" execsql "PRAGMA journal_mode = $mode" } [if $possible {list $mode} {list memory}] } do_test pager1-23.6.1 { execsql {PRAGMA locking_mode = normal} } {exclusive} do_test pager1-23.6.2 { execsql {PRAGMA locking_mode = exclusive} } {exclusive} do_test pager1-23.6.3 { execsql {PRAGMA locking_mode} } {exclusive} do_test pager1-23.6.4 { execsql {PRAGMA main.locking_mode} } {exclusive} #------------------------------------------------------------------------- # do_test pager1-24.1.1 { faultsim_delete_and_reopen db func a_string a_string execsql { PRAGMA cache_size = 10; PRAGMA auto_vacuum = FULL; CREATE TABLE x1(x, y, z, PRIMARY KEY(y, z)); CREATE TABLE x2(x, y, z, PRIMARY KEY(y, z)); INSERT INTO x2 VALUES(a_string(400), a_string(500), a_string(600)); INSERT INTO x2 SELECT a_string(600), a_string(400), a_string(500) FROM x2; INSERT INTO x2 SELECT a_string(500), a_string(600), a_string(400) FROM x2; INSERT INTO x2 SELECT a_string(400), a_string(500), a_string(600) FROM x2; INSERT INTO x2 SELECT a_string(600), a_string(400), a_string(500) FROM x2; INSERT INTO x2 SELECT a_string(500), a_string(600), a_string(400) FROM x2; INSERT INTO x2 SELECT a_string(400), a_string(500), a_string(600) FROM x2; INSERT INTO x1 SELECT * FROM x2; } } {} do_test pager1-24.1.2 { execsql { BEGIN; DELETE FROM x1 WHERE rowid<32; } recursive_select 64 x2 } {} do_test pager1-24.1.3 { execsql { UPDATE x1 SET z = a_string(300) WHERE rowid>40; COMMIT; PRAGMA integrity_check; SELECT count(*) FROM x1; } } {ok 33} do_test pager1-24.1.4 { execsql { DELETE FROM x1; INSERT INTO x1 SELECT * FROM x2; BEGIN; DELETE FROM x1 WHERE rowid<32; UPDATE x1 SET z = a_string(299) WHERE rowid>40; } recursive_select 64 x2 {db eval COMMIT} execsql { PRAGMA integrity_check; SELECT count(*) FROM x1; } } {ok 33} do_test pager1-24.1.5 { execsql { DELETE FROM x1; INSERT INTO x1 SELECT * FROM x2; } recursive_select 64 x2 { db eval {CREATE TABLE x3(x, y, z)} } execsql { SELECT * FROM x3 } } {} #------------------------------------------------------------------------- # do_test pager1-25-1 { faultsim_delete_and_reopen execsql { BEGIN; SAVEPOINT abc; CREATE TABLE t1(a, b); ROLLBACK TO abc; COMMIT; } db close } {} breakpoint do_test pager1-25-2 { faultsim_delete_and_reopen execsql { SAVEPOINT abc; CREATE TABLE t1(a, b); ROLLBACK TO abc; COMMIT; } db close } {} #------------------------------------------------------------------------- # Sector-size tests. # do_test pager1-26.1 { testvfs tv -default 1 tv sectorsize 4096 faultsim_delete_and_reopen db func a_string a_string execsql { PRAGMA page_size = 512; CREATE TABLE tbl(a PRIMARY KEY, b UNIQUE); BEGIN; INSERT INTO tbl VALUES(a_string(25), a_string(600)); INSERT INTO tbl SELECT a_string(25), a_string(600) FROM tbl; INSERT INTO tbl SELECT a_string(25), a_string(600) FROM tbl; INSERT INTO tbl SELECT a_string(25), a_string(600) FROM tbl; INSERT INTO tbl SELECT a_string(25), a_string(600) FROM tbl; INSERT INTO tbl SELECT a_string(25), a_string(600) FROM tbl; INSERT INTO tbl SELECT a_string(25), a_string(600) FROM tbl; INSERT INTO tbl SELECT a_string(25), a_string(600) FROM tbl; COMMIT; } } {} do_execsql_test pager1-26.1 { UPDATE tbl SET b = a_string(550); } {} db close tv delete #------------------------------------------------------------------------- do_test pager1.27.1 { faultsim_delete_and_reopen sqlite3_pager_refcounts db execsql { BEGIN; CREATE TABLE t1(a, b); } sqlite3_pager_refcounts db execsql COMMIT } {} finish_test |
Added test/pager2.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 | # 2010 June 15 # # The author disclaims copyright to this source code. In place of # a legal notice, here is a blessing: # # May you do good and not evil. # May you find forgiveness for yourself and forgive others. # May you share freely, never taking more than you give. # #*********************************************************************** # set testdir [file dirname $argv0] source $testdir/tester.tcl source $testdir/lock_common.tcl source $testdir/malloc_common.tcl set otn 0 testvfs tv -default 1 foreach code [list { set s 512 } { set s 1024 set sql { PRAGMA journal_mode = memory } } { set s 1024 set sql { PRAGMA journal_mode = memory; PRAGMA locking_mode = exclusive; } } { set s 2048 tv devchar safe_append } { set s 4096 } { set s 4096 set sql { PRAGMA journal_mode = WAL } } { set s 4096 set sql { PRAGMA auto_vacuum = 1 } } { set s 8192 set sql { PRAGMA synchronous = off } }] { incr otn set sql "" tv devchar {} eval $code tv sectorsize $s do_test pager2-1.$otn.0 { faultsim_delete_and_reopen execsql $sql execsql { PRAGMA cache_size = 10; CREATE TABLE t1(i INTEGER PRIMARY KEY, j blob); } } {} set tn 0 set lowpoint 0 foreach x { 100 x 0 100 x 70 22 96 59 96 50 22 56 21 16 37 64 43 40 0 38 22 38 55 0 6 43 62 32 93 54 18 13 29 45 66 29 25 61 31 53 82 75 25 96 86 10 69 2 29 6 60 80 95 42 82 85 50 68 96 90 39 78 69 87 97 48 74 65 43 x 86 34 26 50 41 85 58 44 89 22 6 51 45 46 58 32 97 6 1 12 32 2 69 39 48 71 33 31 5 58 90 43 24 54 12 9 18 57 4 38 91 42 27 45 50 38 56 29 10 0 26 37 83 1 78 15 47 30 75 62 46 29 68 5 30 4 27 96 33 95 79 75 56 10 29 70 32 75 52 88 5 36 50 57 46 63 88 65 x 44 95 64 20 24 35 69 61 61 2 35 92 42 46 23 98 78 1 38 72 79 35 94 37 13 59 5 93 27 58 80 75 58 7 67 13 10 76 84 4 8 70 81 45 8 41 98 5 60 26 92 29 91 90 2 62 40 4 5 22 80 15 83 76 52 88 29 5 68 73 72 7 54 17 89 32 81 94 51 28 53 71 8 42 54 59 70 79 x } { incr tn set now [db one {SELECT count(i) FROM t1}] if {$x == "x"} { execsql { COMMIT ; BEGIN } set lowpoint $now do_test pager2.1.$otn.$tn { sqlite3 db2 test.db execsql { SELECT COALESCE(max(i), 0) FROM t1; PRAGMA integrity_check; } } [list $lowpoint ok] db2 close } else { if {$now > $x } { if { $x>=$lowpoint } { execsql "ROLLBACK TO sp_$x" } else { execsql "DELETE FROM t1 WHERE i>$x" set lowpoint $x } } elseif {$now < $x} { for {set k $now} {$k < $x} {incr k} { execsql "SAVEPOINT sp_$k" execsql { INSERT INTO t1(j) VALUES(randomblob(1500)) } } } do_execsql_test pager2.1.$otn.$tn { SELECT COALESCE(max(i), 0) FROM t1; PRAGMA integrity_check; } [list $x ok] } } } db close tv delete finish_test |
Added test/pagerfault.test.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 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 | # 2010 June 15 # # The author disclaims copyright to this source code. In place of # a legal notice, here is a blessing: # # May you do good and not evil. # May you find forgiveness for yourself and forgive others. # May you share freely, never taking more than you give. # #*********************************************************************** # set testdir [file dirname $argv0] source $testdir/tester.tcl source $testdir/lock_common.tcl source $testdir/malloc_common.tcl if {[permutation] == "inmemory_journal"} { finish_test return } set a_string_counter 1 proc a_string {n} { global a_string_counter incr a_string_counter string range [string repeat "${a_string_counter}." $n] 1 $n } db func a_string a_string #------------------------------------------------------------------------- # Test fault-injection while rolling back a hot-journal file. # do_test pagerfault-1-pre1 { execsql { PRAGMA journal_mode = DELETE; PRAGMA cache_size = 10; CREATE TABLE t1(a UNIQUE, b UNIQUE); INSERT INTO t1 VALUES(a_string(200), a_string(300)); INSERT INTO t1 SELECT a_string(200), a_string(300) FROM t1; INSERT INTO t1 SELECT a_string(200), a_string(300) FROM t1; BEGIN; INSERT INTO t1 SELECT a_string(201), a_string(301) FROM t1; INSERT INTO t1 SELECT a_string(202), a_string(302) FROM t1; INSERT INTO t1 SELECT a_string(203), a_string(303) FROM t1; INSERT INTO t1 SELECT a_string(204), a_string(304) FROM t1; } faultsim_save_and_close } {} do_faultsim_test pagerfault-1 -prep { faultsim_restore_and_reopen } -body { execsql { SELECT count(*) FROM t1 } } -test { faultsim_test_result {0 4} faultsim_integrity_check if {[db one { SELECT count(*) FROM t1 }] != 4} { error "Database content appears incorrect" } } #------------------------------------------------------------------------- # Test fault-injection while rolling back a hot-journal file with a # page-size different from the current value stored on page 1 of the # database file. # do_test pagerfault-2-pre1 { testvfs tv -default 1 tv filter xSync tv script xSyncCb proc xSyncCb {filename args} { if {[string match *journal filename]==0} faultsim_save } faultsim_delete_and_reopen execsql { PRAGMA page_size = 4096; BEGIN; CREATE TABLE abc(a, b, c); INSERT INTO abc VALUES('o', 't', 't'); INSERT INTO abc VALUES('f', 'f', 's'); INSERT INTO abc SELECT * FROM abc; -- 4 INSERT INTO abc SELECT * FROM abc; -- 8 INSERT INTO abc SELECT * FROM abc; -- 16 INSERT INTO abc SELECT * FROM abc; -- 32 INSERT INTO abc SELECT * FROM abc; -- 64 INSERT INTO abc SELECT * FROM abc; -- 128 INSERT INTO abc SELECT * FROM abc; -- 256 COMMIT; PRAGMA page_size = 1024; VACUUM; } db close tv delete } {} do_faultsim_test pagerfault-2 -prep { faultsim_restore_and_reopen } -body { execsql { SELECT * FROM abc } } -test { set answer [split [string repeat "ottffs" 128] ""] faultsim_test_result [list 0 $answer] faultsim_integrity_check set res [db eval { SELECT * FROM abc }] if {$res != $answer} { error "Database content appears incorrect ($res)" } } #------------------------------------------------------------------------- # Test fault-injection while rolling back hot-journals that were created # as part of a multi-file transaction. # do_test pagerfault-3-pre1 { testvfs tstvfs -default 1 tstvfs filter xDelete tstvfs script xDeleteCallback proc xDeleteCallback {method file args} { set file [file tail $file] if { [string match *mj* $file] } { faultsim_save } } faultsim_delete_and_reopen db func a_string a_string execsql { ATTACH 'test.db2' AS aux; PRAGMA journal_mode = DELETE; PRAGMA main.cache_size = 10; PRAGMA aux.cache_size = 10; CREATE TABLE t1(a UNIQUE, b UNIQUE); CREATE TABLE aux.t2(a UNIQUE, b UNIQUE); INSERT INTO t1 VALUES(a_string(200), a_string(300)); INSERT INTO t1 SELECT a_string(200), a_string(300) FROM t1; INSERT INTO t1 SELECT a_string(200), a_string(300) FROM t1; INSERT INTO t2 SELECT * FROM t1; BEGIN; INSERT INTO t1 SELECT a_string(201), a_string(301) FROM t1; INSERT INTO t1 SELECT a_string(202), a_string(302) FROM t1; INSERT INTO t1 SELECT a_string(203), a_string(303) FROM t1; INSERT INTO t1 SELECT a_string(204), a_string(304) FROM t1; REPLACE INTO t2 SELECT * FROM t1; COMMIT; } db close tstvfs delete } {} do_faultsim_test pagerfault-3 -prep { faultsim_restore_and_reopen } -body { execsql { ATTACH 'test.db2' AS aux; SELECT count(*) FROM t2; SELECT count(*) FROM t1; } } -test { faultsim_test_result {0 {4 4}} {1 {unable to open database: test.db2}} faultsim_integrity_check catchsql { ATTACH 'test.db2' AS aux } if {[db one { SELECT count(*) FROM t1 }] != 4 || [db one { SELECT count(*) FROM t2 }] != 4 } { error "Database content appears incorrect" } } #------------------------------------------------------------------------- # Test fault-injection as part of a vanilla, no-transaction, INSERT # statement. # do_faultsim_test pagerfault-4 -prep { faultsim_delete_and_reopen } -body { execsql { CREATE TABLE x(y); INSERT INTO x VALUES('z'); SELECT * FROM x; } } -test { faultsim_test_result {0 z} faultsim_integrity_check } #------------------------------------------------------------------------- # Test fault-injection as part of a commit when using journal_mode=PERSIST. # Three different cases: # # pagerfault-5.1: With no journal_size_limit configured. # pagerfault-5.2: With a journal_size_limit configured. # pagerfault-5.4: Multi-file transaction. One connection has a # journal_size_limit of 0, the other has no limit. # do_test pagerfault-5-pre1 { faultsim_delete_and_reopen db func a_string a_string execsql { CREATE TABLE t1(a UNIQUE, b UNIQUE); INSERT INTO t1 VALUES(a_string(200), a_string(300)); INSERT INTO t1 SELECT a_string(200), a_string(300) FROM t1; INSERT INTO t1 SELECT a_string(200), a_string(300) FROM t1; } faultsim_save_and_close } {} do_faultsim_test pagerfault-5.1 -prep { faultsim_restore_and_reopen db func a_string a_string execsql { PRAGMA journal_mode = PERSIST } } -body { execsql { INSERT INTO t1 SELECT a_string(200), a_string(300) FROM t1 } } -test { faultsim_test_result {0 {}} faultsim_integrity_check } do_faultsim_test pagerfault-5.2 -prep { faultsim_restore_and_reopen db func a_string a_string execsql { PRAGMA journal_mode = PERSIST; PRAGMA journal_size_limit = 2048; } } -body { execsql { INSERT INTO t1 SELECT a_string(200), a_string(300) FROM t1 } } -test { faultsim_test_result {0 {}} faultsim_integrity_check } do_faultsim_test pagerfault-5.3 -faults oom-transient -prep { faultsim_restore_and_reopen db func a_string a_string file delete -force test2.db test2.db-journal test2.db-wal execsql { PRAGMA journal_mode = PERSIST; ATTACH 'test2.db' AS aux; PRAGMA aux.journal_mode = PERSIST; PRAGMA aux.journal_size_limit = 0; } } -body { execsql { BEGIN; INSERT INTO t1 SELECT a_string(200), a_string(300) FROM t1; CREATE TABLE aux.t2 AS SELECT * FROM t1; COMMIT; } } -test { faultsim_test_result {0 {}} catchsql { COMMIT } catchsql { ROLLBACK } faultsim_integrity_check set res "" set rc [catch { set res [db one { PRAGMA aux.integrity_check }] }] if {$rc!=0 || $res != "ok"} {error "integrity-check problem:$rc $res"} } #------------------------------------------------------------------------- # Test fault-injection as part of a commit when using # journal_mode=TRUNCATE. # do_test pagerfault-6-pre1 { faultsim_delete_and_reopen db func a_string a_string execsql { CREATE TABLE t1(a UNIQUE, b UNIQUE); INSERT INTO t1 VALUES(a_string(200), a_string(300)); } faultsim_save_and_close } {} do_faultsim_test pagerfault-6.1 -prep { faultsim_restore_and_reopen db func a_string a_string execsql { PRAGMA journal_mode = TRUNCATE } } -body { execsql { INSERT INTO t1 SELECT a_string(200), a_string(300) FROM t1 } execsql { INSERT INTO t1 SELECT a_string(200), a_string(300) FROM t1 } } -test { faultsim_test_result {0 {}} faultsim_integrity_check } # The unix vfs xAccess() method considers a file zero bytes in size to # "not exist". This proc overrides that behaviour so that a zero length # file is considered to exist. # proc xAccess {method filename op args} { if {$op != "SQLITE_ACCESS_EXISTS"} { return "" } return [file exists $filename] } do_faultsim_test pagerfault-6.2 -faults cantopen-* -prep { shmfault filter xAccess shmfault script xAccess faultsim_restore_and_reopen db func a_string a_string execsql { PRAGMA journal_mode = TRUNCATE } } -body { execsql { INSERT INTO t1 SELECT a_string(200), a_string(300) FROM t1 } execsql { INSERT INTO t1 SELECT a_string(200), a_string(300) FROM t1 } } -test { faultsim_test_result {0 {}} faultsim_integrity_check } # The following was an attempt to get a bitvec malloc to fail. Didn't work. # # do_test pagerfault-6-pre1 { # faultsim_delete_and_reopen # execsql { # CREATE TABLE t1(x, y, UNIQUE(x, y)); # INSERT INTO t1 VALUES(1, randomblob(1501)); # INSERT INTO t1 VALUES(2, randomblob(1502)); # INSERT INTO t1 VALUES(3, randomblob(1503)); # INSERT INTO t1 VALUES(4, randomblob(1504)); # INSERT INTO t1 # SELECT x, randomblob(1500+oid+(SELECT max(oid) FROM t1)) FROM t1; # INSERT INTO t1 # SELECT x, randomblob(1500+oid+(SELECT max(oid) FROM t1)) FROM t1; # INSERT INTO t1 # SELECT x, randomblob(1500+oid+(SELECT max(oid) FROM t1)) FROM t1; # INSERT INTO t1 # SELECT x, randomblob(1500+oid+(SELECT max(oid) FROM t1)) FROM t1; # } # faultsim_save_and_close # } {} # do_faultsim_test pagerfault-6 -prep { # faultsim_restore_and_reopen # } -body { # execsql { # BEGIN; # UPDATE t1 SET x=x+4 WHERE x=1; # SAVEPOINT one; # UPDATE t1 SET x=x+4 WHERE x=2; # SAVEPOINT three; # UPDATE t1 SET x=x+4 WHERE x=3; # SAVEPOINT four; # UPDATE t1 SET x=x+4 WHERE x=4; # RELEASE three; # COMMIT; # SELECT DISTINCT x FROM t1; # } # } -test { # faultsim_test_result {0 {5 6 7 8}} # faultsim_integrity_check # } # # This is designed to provoke a special case in the pager code: # # If an error (specifically, a FULL or IOERR error) occurs while writing a # dirty page to the file-system in order to free up memory, the pager enters # the "error state". An IO error causes SQLite to roll back the current # transaction (exiting the error state). A FULL error, however, may only # rollback the current statement. # # This block tests that nothing goes wrong if a FULL error occurs while # writing a dirty page out to free memory from within a statement that has # opened a statement transaction. # do_test pagerfault-7-pre1 { faultsim_delete_and_reopen execsql { CREATE TABLE t2(a INTEGER PRIMARY KEY, b); BEGIN; INSERT INTO t2 VALUES(NULL, randomblob(1500)); INSERT INTO t2 VALUES(NULL, randomblob(1500)); INSERT INTO t2 SELECT NULL, randomblob(1500) FROM t2; -- 4 INSERT INTO t2 SELECT NULL, randomblob(1500) FROM t2; -- 8 INSERT INTO t2 SELECT NULL, randomblob(1500) FROM t2; -- 16 INSERT INTO t2 SELECT NULL, randomblob(1500) FROM t2; -- 32 INSERT INTO t2 SELECT NULL, randomblob(1500) FROM t2; -- 64 COMMIT; CREATE TABLE t1(a PRIMARY KEY, b); INSERT INTO t1 SELECT * FROM t2; DROP TABLE t2; } faultsim_save_and_close } {} do_faultsim_test pagerfault-7 -prep { faultsim_restore_and_reopen execsql { PRAGMA cache_size = 10; BEGIN; UPDATE t1 SET b = randomblob(1500); } } -body { execsql { UPDATE t1 SET a = 65, b = randomblob(1500) WHERE (a+1)>200 } execsql COMMIT } -test { faultsim_test_result {0 {}} faultsim_integrity_check } do_test pagerfault-8-pre1 { faultsim_delete_and_reopen execsql { PRAGMA auto_vacuum = 1; CREATE TABLE t1(a INTEGER PRIMARY KEY, b); BEGIN; INSERT INTO t1 VALUES(NULL, randomblob(1500)); INSERT INTO t1 VALUES(NULL, randomblob(1500)); INSERT INTO t1 SELECT NULL, randomblob(1500) FROM t1; -- 4 INSERT INTO t1 SELECT NULL, randomblob(1500) FROM t1; -- 8 INSERT INTO t1 SELECT NULL, randomblob(1500) FROM t1; -- 16 INSERT INTO t1 SELECT NULL, randomblob(1500) FROM t1; -- 32 INSERT INTO t1 SELECT NULL, randomblob(1500) FROM t1; -- 64 COMMIT; } faultsim_save_and_close set filesize [file size test.db] set {} {} } {} do_test pagerfault-8-pre2 { faultsim_restore_and_reopen execsql { DELETE FROM t1 WHERE a>32 } expr {[file size test.db] < $filesize} } {1} do_faultsim_test pagerfault-8 -prep { faultsim_restore_and_reopen execsql { BEGIN; DELETE FROM t1 WHERE a>32; } } -body { execsql COMMIT } -test { faultsim_test_result {0 {}} faultsim_integrity_check } #------------------------------------------------------------------------- # This test case is specially designed so that during a savepoint # rollback, a new cache entry must be allocated (see comments surrounding # the call to sqlite3PagerAcquire() from within pager_playback_one_page() # for details). Test the effects of injecting an OOM at this point. # do_test pagerfault-9-pre1 { faultsim_delete_and_reopen execsql { PRAGMA auto_vacuum = incremental; CREATE TABLE t1(x); CREATE TABLE t2(y); CREATE TABLE t3(z); INSERT INTO t1 VALUES(randomblob(900)); INSERT INTO t1 VALUES(randomblob(900)); DELETE FROM t1; } faultsim_save_and_close } {} do_faultsim_test pagerfault-9.1 -prep { faultsim_restore_and_reopen execsql { BEGIN; INSERT INTO t1 VALUES(randomblob(900)); INSERT INTO t1 VALUES(randomblob(900)); DROP TABLE t3; DROP TABLE t2; SAVEPOINT abc; PRAGMA incremental_vacuum; } } -body { execsql { ROLLBACK TO abc; COMMIT; PRAGMA freelist_count } } -test { faultsim_test_result {0 2} faultsim_integrity_check set sl [db one { SELECT COALESCE(sum(length(x)), 'null') FROM t1 }] if {$sl!="null" && $sl!=1800} { error "Content looks no good... ($sl)" } } #------------------------------------------------------------------------- # Test fault injection with a temporary database file. # foreach v {a b} { do_faultsim_test pagerfault-10$v -prep { sqlite3 db "" db func a_string a_string; execsql { PRAGMA cache_size = 10; BEGIN; CREATE TABLE xx(a, b, UNIQUE(a, b)); INSERT INTO xx VALUES(a_string(200), a_string(200)); INSERT INTO xx SELECT a_string(200), a_string(200) FROM xx; INSERT INTO xx SELECT a_string(200), a_string(200) FROM xx; INSERT INTO xx SELECT a_string(200), a_string(200) FROM xx; INSERT INTO xx SELECT a_string(200), a_string(200) FROM xx; COMMIT; } } -body { execsql { UPDATE xx SET a = a_string(300) } } -test { faultsim_test_result {0 {}} if {$::v == "b"} { execsql { PRAGMA journal_mode = TRUNCATE } } faultsim_integrity_check faultsim_integrity_check } } #------------------------------------------------------------------------- # Test fault injection with transaction savepoints (savepoints created # when a SAVEPOINT command is executed outside of any other savepoint # or transaction context). # do_test pagerfault-9-pre1 { faultsim_delete_and_reopen db func a_string a_string; execsql { PRAGMA auto_vacuum = on; CREATE TABLE t1(x UNIQUE); CREATE TABLE t2(y UNIQUE); CREATE TABLE t3(z UNIQUE); BEGIN; INSERT INTO t1 VALUES(a_string(202)); INSERT INTO t2 VALUES(a_string(203)); INSERT INTO t3 VALUES(a_string(204)); INSERT INTO t1 SELECT a_string(202) FROM t1; INSERT INTO t1 SELECT a_string(203) FROM t1; INSERT INTO t1 SELECT a_string(204) FROM t1; INSERT INTO t1 SELECT a_string(205) FROM t1; INSERT INTO t2 SELECT a_string(length(x)) FROM t1; INSERT INTO t3 SELECT a_string(length(x)) FROM t1; COMMIT; } faultsim_save_and_close } {} do_faultsim_test pagerfault-11 -prep { faultsim_restore_and_reopen execsql { PRAGMA cache_size = 10 } } -body { execsql { SAVEPOINT trans; UPDATE t2 SET y = y||'2'; INSERT INTO t3 SELECT * FROM t2; DELETE FROM t1; ROLLBACK TO trans; UPDATE t1 SET x = x||'3'; INSERT INTO t2 SELECT * FROM t1; DELETE FROM t3; RELEASE trans; } } -test { faultsim_test_result {0 {}} faultsim_integrity_check } #------------------------------------------------------------------------- # Test fault injection when writing to a database file that resides on # a file-system with a sector-size larger than the database page-size. # do_test pagerfault-12-pre1 { testvfs ss_layer -default 1 ss_layer sectorsize 4096 faultsim_delete_and_reopen db func a_string a_string; execsql { PRAGMA page_size = 1024; PRAGMA journal_mode = PERSIST; PRAGMA cache_size = 10; BEGIN; CREATE TABLE t1(x, y UNIQUE); INSERT INTO t1 VALUES(a_string(333), a_string(444)); INSERT INTO t1 SELECT a_string(333+rowid), a_string(444+rowid) FROM t1; INSERT INTO t1 SELECT a_string(333+rowid), a_string(444+rowid) FROM t1; INSERT INTO t1 SELECT a_string(333+rowid), a_string(444+rowid) FROM t1; INSERT INTO t1 SELECT a_string(333+rowid), a_string(444+rowid) FROM t1; INSERT INTO t1 SELECT a_string(44), a_string(55) FROM t1 LIMIT 13; COMMIT; } faultsim_save_and_close } {} do_faultsim_test pagerfault-12a -prep { faultsim_restore_and_reopen execsql { PRAGMA cache_size = 10 } db func a_string a_string; } -body { execsql { UPDATE t1 SET x = a_string(length(x)), y = a_string(length(y)); } } -test { faultsim_test_result {0 {}} faultsim_integrity_check } do_test pagerfault-12-pre2 { faultsim_restore_and_reopen execsql { CREATE TABLE t2 AS SELECT * FROM t1 LIMIT 10; } faultsim_save_and_close } {} do_faultsim_test pagerfault-12b -prep { faultsim_restore_and_reopen db func a_string a_string; execsql { SELECT * FROM t1 } } -body { set sql(1) { UPDATE t2 SET x = a_string(280) } set sql(2) { UPDATE t1 SET x = a_string(280) WHERE rowid = 5 } db eval { SELECT rowid FROM t1 LIMIT 2 } { db eval $sql($rowid) } } -test { faultsim_test_result {0 {}} faultsim_integrity_check } catch { db close } ss_layer delete #------------------------------------------------------------------------- # Test fault injection when SQLite opens a database where the size of the # database file is zero bytes but the accompanying journal file is larger # than that. In this scenario SQLite should delete the journal file # without rolling it back, even if it is in all other respects a valid # hot-journal file. # do_test pagerfault-13-pre1 { faultsim_delete_and_reopen db func a_string a_string; execsql { PRAGMA journal_mode = PERSIST; BEGIN; CREATE TABLE t1(x, y UNIQUE); INSERT INTO t1 VALUES(a_string(333), a_string(444)); COMMIT; } db close file delete -force test.db faultsim_save } {} do_faultsim_test pagerfault-13 -prep { faultsim_restore_and_reopen } -body { execsql { CREATE TABLE xx(a, b) } } -test { faultsim_test_result {0 {}} } #--------------------------------------------------------------------------- # Test fault injection into a small backup operation. # do_test pagerfault-14-pre1 { faultsim_delete_and_reopen db func a_string a_string; execsql { PRAGMA journal_mode = PERSIST; ATTACH 'test.db2' AS two; BEGIN; CREATE TABLE t1(x, y UNIQUE); CREATE TABLE two.t2(x, y UNIQUE); INSERT INTO t1 VALUES(a_string(333), a_string(444)); INSERT INTO t2 VALUES(a_string(333), a_string(444)); COMMIT; } faultsim_save_and_close } {} do_faultsim_test pagerfault-14a -prep { faultsim_restore_and_reopen } -body { if {[catch {db backup test.db2} msg]} { error [regsub {.*: } $msg {}] } } -test { faultsim_test_result {0 {}} {1 {}} {1 {SQL logic error or missing database}} } do_faultsim_test pagerfault-14b -prep { catch { db2 close } faultsim_restore_and_reopen sqlite3 db2 "" db2 eval { PRAGMA page_size = 4096; CREATE TABLE xx(a) } } -body { sqlite3_backup B db2 main db main B step 200 set rc [B finish] if {[string match SQLITE_IOERR_* $rc]} {set rc SQLITE_IOERR} if {$rc != "SQLITE_OK"} { error [sqlite3_test_errstr $rc] } set {} {} } -test { faultsim_test_result {0 {}} } do_faultsim_test pagerfault-14c -prep { catch { db2 close } faultsim_restore_and_reopen sqlite3 db2 test.db2 db2 eval { PRAGMA synchronous = off; PRAGMA page_size = 4096; CREATE TABLE xx(a); } } -body { sqlite3_backup B db2 main db main B step 200 set rc [B finish] if {[string match SQLITE_IOERR_* $rc]} {set rc SQLITE_IOERR} if {$rc != "SQLITE_OK"} { error [sqlite3_test_errstr $rc] } set {} {} } -test { faultsim_test_result {0 {}} } do_test pagerfault-15-pre1 { faultsim_delete_and_reopen db func a_string a_string; execsql { BEGIN; CREATE TABLE t1(x, y UNIQUE); INSERT INTO t1 VALUES(a_string(11), a_string(22)); INSERT INTO t1 VALUES(a_string(11), a_string(22)); COMMIT; } faultsim_save_and_close } {} do_faultsim_test pagerfault-15 -prep { faultsim_restore_and_reopen db func a_string a_string; } -body { db eval { SELECT * FROM t1 LIMIT 1 } { execsql { BEGIN; INSERT INTO t1 VALUES(a_string(333), a_string(555)); COMMIT; BEGIN; INSERT INTO t1 VALUES(a_string(333), a_string(555)); COMMIT; } } } -test { faultsim_test_result {0 {}} faultsim_integrity_check } do_test pagerfault-16-pre1 { faultsim_delete_and_reopen execsql { CREATE TABLE t1(x, y UNIQUE) } faultsim_save_and_close } {} do_faultsim_test pagerfault-16 -prep { faultsim_restore_and_reopen } -body { execsql { PRAGMA locking_mode = exclusive; PRAGMA journal_mode = wal; INSERT INTO t1 VALUES(1, 2); INSERT INTO t1 VALUES(3, 4); PRAGMA journal_mode = delete; INSERT INTO t1 VALUES(4, 5); PRAGMA journal_mode = wal; INSERT INTO t1 VALUES(6, 7); PRAGMA journal_mode = persist; INSERT INTO t1 VALUES(8, 9); } } -test { faultsim_test_result {0 {exclusive wal delete wal persist}} faultsim_integrity_check } #------------------------------------------------------------------------- # Test fault injection while changing into and out of WAL mode. # do_test pagerfault-17-pre1 { faultsim_delete_and_reopen execsql { CREATE TABLE t1(a PRIMARY KEY, b); INSERT INTO t1 VALUES(1862, 'Botha'); INSERT INTO t1 VALUES(1870, 'Smuts'); INSERT INTO t1 VALUES(1866, 'Hertzog'); } faultsim_save_and_close } {} do_faultsim_test pagerfault-17a -prep { faultsim_restore_and_reopen } -body { execsql { PRAGMA journal_mode = wal; PRAGMA journal_mode = delete; } } -test { faultsim_test_result {0 {wal delete}} faultsim_integrity_check } do_faultsim_test pagerfault-17b -prep { faultsim_restore_and_reopen execsql { PRAGMA synchronous = OFF } } -body { execsql { PRAGMA journal_mode = wal; INSERT INTO t1 VALUES(22, 'Clarke'); PRAGMA journal_mode = delete; } } -test { faultsim_test_result {0 {wal delete}} faultsim_integrity_check } do_faultsim_test pagerfault-17c -prep { faultsim_restore_and_reopen execsql { PRAGMA locking_mode = exclusive; PRAGMA journal_mode = wal; } } -body { execsql { PRAGMA journal_mode = delete } } -test { faultsim_test_result {0 delete} faultsim_integrity_check } do_faultsim_test pagerfault-17d -prep { catch { db2 close } faultsim_restore_and_reopen sqlite3 db2 test.db execsql { PRAGMA journal_mode = delete } execsql { PRAGMA journal_mode = wal } execsql { INSERT INTO t1 VALUES(99, 'Bradman') } db2 } -body { execsql { PRAGMA journal_mode = delete } } -test { faultsim_test_result {1 {database is locked}} faultsim_integrity_check } do_faultsim_test pagerfault-17e -prep { catch { db2 close } faultsim_restore_and_reopen sqlite3 db2 test.db execsql { PRAGMA journal_mode = delete } execsql { PRAGMA journal_mode = wal } set ::chan [launch_testfixture] testfixture $::chan { sqlite3 db test.db db eval { INSERT INTO t1 VALUES(101, 'Latham') } } catch { testfixture $::chan sqlite_abort } catch { close $::chan } } -body { execsql { PRAGMA journal_mode = delete } } -test { faultsim_test_result {0 delete} faultsim_integrity_check } #------------------------------------------------------------------------- # Test fault-injection when changing from journal_mode=persist to # journal_mode=delete (this involves deleting the journal file). # do_test pagerfault-18-pre1 { faultsim_delete_and_reopen execsql { CREATE TABLE qq(x); INSERT INTO qq VALUES('Herbert'); INSERT INTO qq VALUES('Macalister'); INSERT INTO qq VALUES('Mackenzie'); INSERT INTO qq VALUES('Lilley'); INSERT INTO qq VALUES('Palmer'); } faultsim_save_and_close } {} do_faultsim_test pagerfault-18 -prep { faultsim_restore_and_reopen execsql { PRAGMA journal_mode = PERSIST; INSERT INTO qq VALUES('Beatty'); } } -body { execsql { PRAGMA journal_mode = delete } } -test { faultsim_test_result {0 delete} faultsim_integrity_check } do_faultsim_test pagerfault-19a -prep { sqlite3 db :memory: db func a_string a_string execsql { PRAGMA auto_vacuum = FULL; BEGIN; CREATE TABLE t1(a, b); INSERT INTO t1 VALUES(a_string(5000), a_string(6000)); COMMIT; } } -body { execsql { CREATE TABLE t2(a, b); INSERT INTO t2 SELECT * FROM t1; DELETE FROM t1; } } -test { faultsim_test_result {0 {}} } do_test pagerfault-19-pre1 { faultsim_delete_and_reopen execsql { PRAGMA auto_vacuum = FULL; CREATE TABLE t1(x); INSERT INTO t1 VALUES(1); CREATE TABLE t2(x); INSERT INTO t2 VALUES(2); CREATE TABLE t3(x); INSERT INTO t3 VALUES(3); CREATE TABLE t4(x); INSERT INTO t4 VALUES(4); CREATE TABLE t5(x); INSERT INTO t5 VALUES(5); CREATE TABLE t6(x); INSERT INTO t6 VALUES(6); } faultsim_save_and_close } {} do_faultsim_test pagerfault-19b -prep { faultsim_restore_and_reopen } -body { execsql { BEGIN; UPDATE t4 SET x = x+1; UPDATE t6 SET x = x+1; SAVEPOINT one; UPDATE t3 SET x = x+1; SAVEPOINT two; DROP TABLE t2; ROLLBACK TO one; COMMIT; SELECT * FROM t3; SELECT * FROM t4; SELECT * FROM t6; } } -test { faultsim_test_result {0 {3 5 7}} } #------------------------------------------------------------------------- # This tests fault-injection in a special case in the auto-vacuum code. # do_test pagerfault-20-pre1 { faultsim_delete_and_reopen execsql { PRAGMA cache_size = 10; PRAGMA auto_vacuum = FULL; CREATE TABLE t0(a, b); } faultsim_save_and_close } {} do_faultsim_test pagerfault-20 -prep { faultsim_restore_and_reopen } -body { execsql { BEGIN; CREATE TABLE t1(a, b); CREATE TABLE t2(a, b); DROP TABLE t1; COMMIT; } } -test { faultsim_test_result {0 {}} } do_test pagerfault-21-pre1 { faultsim_delete_and_reopen execsql { PRAGMA cache_size = 10; CREATE TABLE t0(a PRIMARY KEY, b); INSERT INTO t0 VALUES(1, 2); } faultsim_save_and_close } {} do_faultsim_test pagerfault-21 -prep { faultsim_restore_and_reopen } -body { db eval { SELECT * FROM t0 LIMIT 1 } { db eval { INSERT INTO t0 SELECT a+1, b FROM t0 } db eval { INSERT INTO t0 SELECT a+2, b FROM t0 } } } -test { faultsim_test_result {0 {}} } #------------------------------------------------------------------------- # Test fault-injection and rollback when the nReserve header value # is non-zero. # do_test pagerfault-21-pre1 { faultsim_delete_and_reopen execsql { PRAGMA page_size = 1024; PRAGMA journal_mode = WAL; PRAGMA journal_mode = DELETE; } db close hexio_write test.db 20 10 hexio_write test.db 105 03F0 sqlite3 db test.db db func a_string a_string execsql { CREATE TABLE t0(a PRIMARY KEY, b UNIQUE); INSERT INTO t0 VALUES(a_string(222), a_string(333)); INSERT INTO t0 VALUES(a_string(223), a_string(334)); INSERT INTO t0 VALUES(a_string(224), a_string(335)); INSERT INTO t0 VALUES(a_string(225), a_string(336)); } faultsim_save_and_close } {} do_faultsim_test pagerfault-21 -prep { faultsim_restore_and_reopen } -body { execsql { INSERT INTO t0 SELECT a||'x', b||'x' FROM t0 } } -test { faultsim_test_result {0 {}} faultsim_integrity_check } ifcapable crashtest { faultsim_delete_and_reopen execsql { PRAGMA page_size = 1024; PRAGMA journal_mode = WAL; PRAGMA journal_mode = DELETE; } db close hexio_write test.db 20 10 hexio_write test.db 105 03F0 sqlite3 db test.db db func a_string a_string execsql { CREATE TABLE t0(a PRIMARY KEY, b UNIQUE); INSERT INTO t0 VALUES(a_string(222), a_string(333)); INSERT INTO t0 VALUES(a_string(223), a_string(334)); } faultsim_save_and_close for {set iTest 1} {$iTest<50} {incr iTest} { do_test pagerfault-21.crash.$iTest.1 { crashsql -delay 1 -file test.db -seed $iTest { BEGIN; CREATE TABLE t1(a PRIMARY KEY, b UNIQUE); INSERT INTO t1 SELECT a, b FROM t0; COMMIT; } } {1 {child process exited abnormally}} do_test pagerfault-22.$iTest.2 { sqlite3 db test.db execsql { PRAGMA integrity_check } } {ok} db close } } #------------------------------------------------------------------------- # When a 3.7.0 client opens a write-transaction on a database file that # has been appended to or truncated by a pre-370 client, it updates # the db-size in the file header immediately. This test case provokes # errors during that operation. # do_test pagerfault-22-pre1 { faultsim_delete_and_reopen db func a_string a_string execsql { PRAGMA page_size = 1024; PRAGMA auto_vacuum = 0; CREATE TABLE t1(a); CREATE INDEX i1 ON t1(a); INSERT INTO t1 VALUES(a_string(3000)); CREATE TABLE t2(a); INSERT INTO t2 VALUES(1); } db close sql36231 { INSERT INTO t1 VALUES(a_string(3000)) } faultsim_save_and_close } {} do_faultsim_test pagerfault-22 -prep { faultsim_restore_and_reopen } -body { execsql { INSERT INTO t2 VALUES(2) } execsql { SELECT * FROM t2 } } -test { faultsim_test_result {0 {1 2}} faultsim_integrity_check } finish_test |
Added test/pagerfault2.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 | # 2010 June 15 # # The author disclaims copyright to this source code. In place of # a legal notice, here is a blessing: # # May you do good and not evil. # May you find forgiveness for yourself and forgive others. # May you share freely, never taking more than you give. # #*********************************************************************** # # The tests in this file test the pager modules response to various # fault conditions (OOM, IO error, disk full etc.). They are similar # to those in file pagerfault1.test. # # More specifically, the tests in this file are those deemed too slow to # run as part of pagerfault1.test. # set testdir [file dirname $argv0] source $testdir/tester.tcl source $testdir/lock_common.tcl source $testdir/malloc_common.tcl if {[permutation] == "inmemory_journal"} { finish_test return } sqlite3_memdebug_vfs_oom_test 0 set a_string_counter 1 proc a_string {n} { global a_string_counter incr a_string_counter string range [string repeat "${a_string_counter}." $n] 1 $n } db func a_string a_string do_test pagerfault2-1-pre1 { faultsim_delete_and_reopen db func a_string a_string execsql { PRAGMA auto_vacuum = 0; PRAGMA journal_mode = DELETE; PRAGMA page_size = 1024; CREATE TABLE t1(a, b); INSERT INTO t1 VALUES(a_string(401), a_string(402)); } for {set ii 0} {$ii < 13} {incr ii} { execsql { INSERT INTO t1 SELECT a_string(401), a_string(402) FROM t1 } } faultsim_save_and_close file size test.db } [expr 1024 * 8268] do_faultsim_test pagerfault2-1 -faults oom-transient -prep { faultsim_restore_and_reopen sqlite3_db_config_lookaside db 0 256 4096 execsql { BEGIN; SELECT * FROM t1; INSERT INTO t1 VALUES(5, 6); SAVEPOINT abc; UPDATE t1 SET a = a||'x' WHERE rowid<3700; } } -body { execsql { UPDATE t1 SET a = a||'x' WHERE rowid>=3700 AND rowid<=4200 } execsql { ROLLBACK TO abc } } -test { faultsim_test_result {0 {}} } do_test pagerfault2-2-pre1 { faultsim_restore_and_reopen execsql { DELETE FROM t1 } faultsim_save_and_close } {} do_faultsim_test pagerfault2-2 -faults oom-transient -prep { faultsim_restore_and_reopen sqlite3_db_config_lookaside db 0 256 4096 db func a_string a_string execsql { PRAGMA cache_size = 20; BEGIN; INSERT INTO t1 VALUES(a_string(401), a_string(402)); SAVEPOINT abc; } } -body { execsql { INSERT INTO t1 VALUES (a_string(2000000), a_string(2500000)) } } -test { faultsim_test_result {0 {}} } sqlite3_memdebug_vfs_oom_test 1 finish_test |
Changes to test/pageropt.test.
︙ | ︙ | |||
190 191 192 193 194 195 196 | db close sqlite3 db test.db pagercount_sql { DELETE FROM t1 } } {12 3 3} | | | 190 191 192 193 194 195 196 197 198 199 | db close sqlite3 db test.db pagercount_sql { DELETE FROM t1 } } {12 3 3} sqlite3_soft_heap_limit $cmdlinearg(soft-heap-limit) catch {db2 close} finish_test |
Changes to test/pcache.test.
︙ | ︙ | |||
12 13 14 15 16 17 18 19 20 21 22 23 24 25 | # This file is focused on testing the pcache module. # # $Id: pcache.test,v 1.5 2009/05/08 06:52:48 danielk1977 Exp $ set testdir [file dirname $argv0] source $testdir/tester.tcl # The pcache module limits the number of pages available to purgeable # caches to the sum of the 'cache_size' values for the set of open # caches. This block of tests, pcache-1.*, test that the library behaves # corrctly when it is forced to exceed this limit. # do_test pcache-1.1 { | > > > > | 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | # This file is focused on testing the pcache module. # # $Id: pcache.test,v 1.5 2009/05/08 06:52:48 danielk1977 Exp $ set testdir [file dirname $argv0] source $testdir/tester.tcl # Do not use a codec for tests in this file, as the database file is # manipulated directly using tcl scripts (using the [hexio_write] command). # do_not_use_codec # The pcache module limits the number of pages available to purgeable # caches to the sum of the 'cache_size' values for the set of open # caches. This block of tests, pcache-1.*, test that the library behaves # corrctly when it is forced to exceed this limit. # do_test pcache-1.1 { |
︙ | ︙ |
Changes to test/permutations.test.
1 2 3 4 5 6 7 8 9 10 11 | # 2008 June 21 # # The author disclaims copyright to this source code. In place of # a legal notice, here is a blessing: # # May you do good and not evil. # May you find forgiveness for yourself and forgive others. # May you share freely, never taking more than you give. # #*********************************************************************** # | < | < | < < < < < < < | < < | < < < < < < < < < < < < < < < < < < < < < < < < < < | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < > < < > | | | | | < < | | > > > | | > > > > > > | | | | > | | | > | > > | > > > > > > > | | < < > > > > | < < < < < < < < < < | | < > | | > > > | | > > > > > > > > > | > | > > > > > > > > > > | > | < < | < > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | < | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 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 | # 2008 June 21 # # The author disclaims copyright to this source code. In place of # a legal notice, here is a blessing: # # May you do good and not evil. # May you find forgiveness for yourself and forgive others. # May you share freely, never taking more than you give. # #*********************************************************************** # set testdir [file dirname $argv0] source $testdir/tester.tcl db close #------------------------------------------------------------------------- # test_suite NAME OPTIONS # # where available options are: # # -description TITLE (default "") # -initialize SCRIPT (default "") # -shutdown SCRIPT (default "") # -presql SQL (default "") # -files LIST-OF-FILES (default $::ALLTESTS) # -prefix NAME (default "$::NAME.") # proc test_suite {name args} { set default(-shutdown) "" set default(-initialize) "" set default(-presql) "" set default(-description) "no description supplied (fixme)" set default(-files) "" set default(-prefix) "${name}." array set options [array get default] if {[llength $args]%2} { error "uneven number of options/switches passed to test_suite" } foreach {k v} $args { set o [array names options ${k}*] if {[llength $o]>1} { error "ambiguous option: $k" } if {[llength $o]==0} { error "unknown option: $k" } set options([lindex $o 0]) $v } set ::testspec($name) [array get options] lappend ::testsuitelist $name } #------------------------------------------------------------------------- # test_set ARGS... # proc test_set {args} { set isExclude 0 foreach a $args { if {[string match -* $a]} { switch -- $a { -include { set isExclude 0 } -exclude { set isExclude 1 } default { error "Unknown switch: $a" } } } elseif {$isExclude == 0} { foreach f $a { set t($f) 1 } } else { foreach f $a { array unset t $f } } } return [array names t] } #------------------------------------------------------------------------- # Set up the following global list variables containing the names of # various test scripts: # # $alltests # $allquicktests # set alltests [list] foreach f [glob $testdir/*.test] { lappend alltests [file tail $f] } if {$::tcl_platform(platform)!="unix"} { set alltests [test_set $alltests -exclude crash.test crash2.test] } set alltests [test_set $alltests -exclude { all.test async.test quick.test veryquick.test memleak.test permutations.test soak.test fts3.test mallocAll.test }] set allquicktests [test_set $alltests -exclude { async2.test async3.test backup_ioerr.test corrupt.test corruptC.test crash.test crash2.test crash3.test crash4.test crash5.test crash6.test crash7.test delete3.test e_fts3.test fts3rnd.test fkey_malloc.test fuzz.test fuzz3.test fuzz_malloc.test in2.test loadext.test misc7.test mutex2.test notify2.test onefile.test pagerfault2.test savepoint4.test savepoint6.test select9.test speed1.test speed1p.test speed2.test speed3.test speed4.test speed4p.test sqllimits1.test tkt2686.test thread001.test thread002.test thread003.test thread004.test thread005.test trans2.test vacuum3.test incrvacuum_ioerr.test autovacuum_crash.test btree8.test shared_err.test vtab_err.test walslow.test walcrash.test walthread.test }] if {[info exists ::env(QUICKTEST_INCLUDE)]} { set allquicktests [concat $allquicktests $::env(QUICKTEST_INCLUDE)] } ############################################################################# # Start of tests # #------------------------------------------------------------------------- # Define the generic test suites: # # veryquick # quick # full # lappend ::testsuitelist xxx test_suite "veryquick" -prefix "" -description { "Very" quick test suite. Runs in less than 5 minutes on a workstation. This test suite is the same as the "quick" tests, except that some files that test malloc and IO errors are omitted. } -files [ test_set $allquicktests -exclude *malloc* *ioerr* *fault* ] test_suite "quick" -prefix "" -description { Quick test suite. Runs in around 10 minutes on a workstation. } -files [ test_set $allquicktests ] test_suite "full" -prefix "" -description { Full test suite. Takes a long time. } -files [ test_set $alltests ] -initialize { unset -nocomplain ::G(isquick) } test_suite "threads" -prefix "" -description { All multi-threaded tests. } -files { notify2.test thread001.test thread002.test thread003.test thread004.test thread005.test walthread.test } test_suite "fts3" -prefix "" -description { All FTS3 tests except fts3malloc.test and fts3rnd.test. } -files { fts3aa.test fts3ab.test fts3ac.test fts3ad.test fts3ae.test fts3af.test fts3ag.test fts3ah.test fts3ai.test fts3aj.test fts3ak.test fts3al.test fts3am.test fts3an.test fts3ao.test fts3atoken.test fts3b.test fts3c.test fts3cov.test fts3d.test fts3e.test fts3expr.test fts3expr2.test fts3near.test fts3query.test fts3snippet.test } lappend ::testsuitelist xxx #------------------------------------------------------------------------- # Define the coverage related test suites: # # coverage-wal # test_suite "coverage-wal" -description { Coverage tests for file wal.c. } -files { wal.test wal2.test wal3.test walmode.test walbak.test walhook.test walcrash2.test walcksum.test walfault.test walbig.test } test_suite "coverage-pager" -description { Coverage tests for file pager.c. } -files { pager1.test pager2.test pagerfault.test pagerfault2.test walfault.test walbak.test journal2.test tkt-9d68c883.test } lappend ::testsuitelist xxx #------------------------------------------------------------------------- # Define the permutation test suites: # # Run some tests using pre-allocated page and scratch blocks. # test_suite "memsubsys1" -description { Tests using pre-allocated page and scratch blocks } -files [ test_set $::allquicktests -exclude ioerr5.test malloc5.test ] -initialize { catch {db close} sqlite3_shutdown sqlite3_config_pagecache 4096 24 sqlite3_config_scratch 25000 1 sqlite3_initialize autoinstall_test_functions } -shutdown { |
︙ | ︙ | |||
193 194 195 196 197 198 199 | # Run some tests using pre-allocated page and scratch blocks. This time # the allocations are too small to use in most cases. # # Both ioerr5.test and malloc5.test are excluded because they test the # sqlite3_soft_heap_limit() and sqlite3_release_memory() functionality. # This functionality is disabled if a pre-allocated page block is provided. # | | | < | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | > > > > > > | > > > | | | | 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 | # Run some tests using pre-allocated page and scratch blocks. This time # the allocations are too small to use in most cases. # # Both ioerr5.test and malloc5.test are excluded because they test the # sqlite3_soft_heap_limit() and sqlite3_release_memory() functionality. # This functionality is disabled if a pre-allocated page block is provided. # test_suite "memsubsys2" -description { Tests using small pre-allocated page and scratch blocks } -files [ test_set $::allquicktests -exclude ioerr5.test malloc5.test ] -initialize { catch {db close} sqlite3_shutdown sqlite3_config_pagecache 512 5 sqlite3_config_scratch 1000 1 sqlite3_initialize autoinstall_test_functions } -shutdown { catch {db close} sqlite3_shutdown sqlite3_config_pagecache 0 0 sqlite3_config_scratch 0 0 sqlite3_initialize autoinstall_test_functions } # Run all tests with the lookaside allocator disabled. # test_suite "nolookaside" -description { OOM tests with lookaside disabled } -initialize { catch {db close} sqlite3_shutdown sqlite3_config_lookaside 0 0 sqlite3_initialize autoinstall_test_functions } -shutdown { catch {db close} sqlite3_shutdown sqlite3_config_lookaside 100 500 sqlite3_initialize autoinstall_test_functions } -files $::allquicktests # Run some tests in SQLITE_CONFIG_SINGLETHREAD mode. # test_suite "singlethread" -description { Tests run in SQLITE_CONFIG_SINGLETHREAD mode } -initialize { catch {db close} sqlite3_shutdown catch {sqlite3_config singlethread} sqlite3_initialize autoinstall_test_functions } -files { delete.test delete2.test insert.test rollback.test select1.test select2.test trans.test update.test vacuum.test types.test types2.test types3.test } -shutdown { catch {db close} sqlite3_shutdown catch {sqlite3_config serialized} sqlite3_initialize autoinstall_test_functions } test_suite "nomutex" -description { Tests run with the SQLITE_OPEN_MULTITHREADED flag passed to sqlite3_open(). } -initialize { rename sqlite3 sqlite3_nomutex proc sqlite3 {args} { if {[string range [lindex $args 0] 0 0] ne "-"} { lappend args -fullmutex 0 -nomutex 1 } uplevel [concat sqlite3_nomutex $args] } } -files { delete.test delete2.test insert.test rollback.test select1.test select2.test trans.test update.test vacuum.test types.test types2.test types3.test } -shutdown { rename sqlite3 {} rename sqlite3_nomutex sqlite3 } # Run some tests in SQLITE_CONFIG_MULTITHREAD mode. # test_suite "multithread" -description { Tests run in SQLITE_CONFIG_MULTITHREAD mode } -initialize { catch {db close} sqlite3_shutdown catch {sqlite3_config multithread} sqlite3_initialize autoinstall_test_functions } -files { delete.test delete2.test insert.test rollback.test select1.test select2.test trans.test update.test vacuum.test types.test types2.test types3.test } -shutdown { catch {db close} sqlite3_shutdown catch {sqlite3_config serialized} sqlite3_initialize autoinstall_test_functions } # Run some tests in SQLITE_OPEN_FULLMUTEX mode. # test_suite "fullmutex" -description { Tests run in SQLITE_OPEN_FULLMUTEX mode } -initialize { rename sqlite3 sqlite3_fullmutex proc sqlite3 {args} { if {[string range [lindex $args 0] 0 0] ne "-"} { lappend args -nomutex 0 -fullmutex 1 } uplevel [concat sqlite3_fullmutex $args] } } -files { delete.test delete2.test insert.test rollback.test select1.test select2.test trans.test update.test vacuum.test types.test types2.test types3.test } -shutdown { rename sqlite3 {} rename sqlite3_fullmutex sqlite3 } # Run some tests using the "onefile" demo. # test_suite "onefile" -description { Run some tests using the "test_onefile.c" demo } -initialize { rename sqlite3 sqlite3_onefile proc sqlite3 {args} { if {[string range [lindex $args 0] 0 0] ne "-"} { lappend args -vfs fs } uplevel [concat sqlite3_onefile $args] } } -files { conflict.test insert.test insert2.test insert3.test rollback.test select1.test select2.test select3.test } -shutdown { rename sqlite3 {} rename sqlite3_onefile sqlite3 } # Run some tests using UTF-16 databases. # test_suite "utf16" -description { Run tests using UTF-16 databases } -presql { pragma encoding = 'UTF-16' } -files { alter.test alter3.test auth.test bind.test blob.test capi2.test capi3.test collate1.test collate2.test collate3.test collate4.test collate5.test collate6.test conflict.test date.test delete.test expr.test fkey1.test func.test hook.test index.test insert2.test insert.test interrupt.test in.test intpkey.test ioerr.test join2.test join.test lastinsert.test laststmtchanges.test limit.test lock2.test lock.test main.test memdb.test minmax.test misc1.test misc2.test misc3.test notnull.test null.test progress.test quote.test rowid.test select1.test select2.test select3.test select4.test select5.test select6.test sort.test subselect.test tableapi.test table.test temptable.test trace.test trigger1.test trigger2.test trigger3.test trigger4.test types2.test types.test unique.test update.test vacuum.test view.test where.test } # Run some tests in exclusive locking mode. # test_suite "exclusive" -description { Run tests in exclusive locking mode. } -presql { pragma locking_mode = 'exclusive' } -files { rollback.test select1.test select2.test malloc.test ioerr.test } # Run some tests in exclusive locking mode with truncated journals. # test_suite "exclusive-truncate" -description { Run tests in exclusive locking mode and truncate journal mode. } -presql { pragma locking_mode = 'exclusive'; pragma journal_mode = TRUNCATE; } -files { delete.test delete2.test insert.test rollback.test select1.test select2.test update.test malloc.test ioerr.test } # Run some tests in persistent journal mode. # test_suite "persistent_journal" -description { Run tests in persistent-journal mode. } -presql { pragma journal_mode = persist } -files { delete.test delete2.test insert.test rollback.test select1.test select2.test trans.test update.test vacuum.test } # Run some tests in truncating journal mode. # test_suite "truncate_journal" -description { Run tests in persistent-journal mode. } -presql { pragma journal_mode = truncate } -files { delete.test delete2.test insert.test rollback.test select1.test select2.test trans.test update.test vacuum.test malloc.test ioerr.test } # Run some error tests in persistent journal mode. # test_suite "persistent_journal_error" -description { Run malloc.test and ioerr.test in persistent-journal mode. } -presql { pragma journal_mode = persist } -files { malloc.test ioerr.test } # Run some tests in no journal mode. # test_suite "no_journal" -description { Run tests in no-journal mode. } -presql { pragma journal_mode = persist } -files { delete.test delete2.test insert.test rollback.test select1.test select2.test trans.test update.test vacuum.test } # Run some error tests in no journal mode. # test_suite "no_journal_error" -description { Run malloc.test and ioerr.test in no-journal mode. } -presql { pragma journal_mode = persist } -files { malloc.test ioerr.test } # Run some crash-tests in autovacuum mode. # test_suite "autovacuum_crash" -description { Run crash.test in autovacuum mode. } -presql { pragma auto_vacuum = 1 } -files crash.test # Run some ioerr-tests in autovacuum mode. # test_suite "autovacuum_ioerr" -description { Run ioerr.test in autovacuum mode. } -presql { pragma auto_vacuum = 1 } -files ioerr.test # Run tests with an in-memory journal. # test_suite "inmemory_journal" -description { Run tests with an in-memory journal file. } -presql { pragma journal_mode = 'memory' } -files [test_set $::allquicktests -exclude { # Exclude all tests that simulate IO errors. autovacuum_ioerr2.test incrvacuum_ioerr.test ioerr.test ioerr.test ioerr2.test ioerr3.test ioerr4.test ioerr5.test vacuum3.test incrblob_err.test diskfull.test backup_ioerr.test e_fts3.test fts3cov.test fts3malloc.test fts3rnd.test fts3snippet.test # Exclude test scripts that use tcl IO to access journal files or count # the number of fsync() calls. pager.test exclusive.test jrnlmode.test sync.test misc1.test journal1.test conflict.test crash8.test tkt3457.test io.test journal3.test pager1.test async4.test corrupt.test filefmt.test pager2.test corrupt5.test corruptA.test pageropt.test # Exclude stmt.test, which expects sub-journals to use temporary files. stmt.test # WAL mode is different. wal* }] ifcapable mem3 { test_suite "memsys3" -description { Run tests using the allocator in mem3.c. } -files [test_set $::allquicktests -exclude { autovacuum.test delete3.test manydb.test bigrow.test incrblob2.test memdb.test bitvec.test index2.test memsubsys1.test capi3c.test ioerr.test memsubsys2.test capi3.test join3.test pagesize.test collate5.test limit.test backup_ioerr.test backup_malloc.test }] -initialize { catch {db close} sqlite3_reset_auto_extension sqlite3_shutdown sqlite3_config_heap 25000000 0 sqlite3_config_lookaside 0 0 ifcapable mem5 { # If both memsys3 and memsys5 are enabled in the build, the call to |
︙ | ︙ | |||
512 513 514 515 516 517 518 | install_malloc_faultsim 1 sqlite3_initialize autoinstall_test_functions } } ifcapable mem5 { | | | | | | | 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 | install_malloc_faultsim 1 sqlite3_initialize autoinstall_test_functions } } ifcapable mem5 { test_suite "memsys5" -description { Run tests using the allocator in mem5.c. } -files [test_set $::allquicktests -exclude { autovacuum.test delete3.test manydb.test bigrow.test incrblob2.test memdb.test bitvec.test index2.test memsubsys1.test capi3c.test ioerr.test memsubsys2.test capi3.test join3.test pagesize.test collate5.test limit.test zeroblob.test }] -initialize { catch {db close} sqlite3_shutdown sqlite3_config_heap 25000000 64 sqlite3_config_lookaside 0 0 install_malloc_faultsim 1 sqlite3_initialize autoinstall_test_functions } -shutdown { catch {db close} sqlite3_shutdown sqlite3_config_heap 0 0 sqlite3_config_lookaside 100 500 install_malloc_faultsim 1 sqlite3_initialize autoinstall_test_functions } test_suite "memsys5-2" -description { Run tests using the allocator in mem5.c in a different configuration. } -files { select1.test } -initialize { catch {db close} sqlite3_shutdown sqlite3_config_heap 40000000 16 sqlite3_config_lookaside 0 0 install_malloc_faultsim 1 |
︙ | ︙ | |||
563 564 565 566 567 568 569 | install_malloc_faultsim 1 sqlite3_initialize autoinstall_test_functions } } ifcapable threadsafe { | | > | | | 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 | install_malloc_faultsim 1 sqlite3_initialize autoinstall_test_functions } } ifcapable threadsafe { test_suite "no_mutex_try" -description { The sqlite3_mutex_try() interface always fails } -files [ test_set $::allquicktests -exclude mutex1.test mutex2.test ] -initialize { catch {db close} sqlite3_shutdown install_mutex_counters 1 set ::disable_mutex_try 1 sqlite3_initialize autoinstall_test_functions } -shutdown { |
︙ | ︙ | |||
602 603 604 605 606 607 608 | # puts $fd [string repeat 1234567890 100000] # close $fd # eval sa_crashsql $options # } # } -shutdown { # rename crashsql {} # rename sa_crashsql crashsql | | | | | > > | | | | | | | | | | | | | | | | | | < < < < < < < < < < < < < < < | < < < < < < < < < < < < < < | < < < < < < < < > | < | | > | > > | < > | < | < < < | < | < < | > > > > < | | | < < < < < | < < < < | | | | > > > > > | | | > > > > > | > > | > | > > > > > | > > > | > | > > > > | > > > > > | | > > > > > > > > > > > > > > > > > > > | > > > > | > > > > | | > > | > > | 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 | # puts $fd [string repeat 1234567890 100000] # close $fd # eval sa_crashsql $options # } # } -shutdown { # rename crashsql {} # rename sa_crashsql crashsql # } -files crash.test test_suite "safe_append" -description { Run some tests on a SAFE_APPEND file-system. } -initialize { rename sqlite3 sqlite3_safeappend proc sqlite3 {args} { if {[string range [lindex $args 0] 0 0] ne "-"} { lappend args -vfs devsym } uplevel [concat sqlite3_safeappend $args] } sqlite3_simulate_device -char safe_append } -shutdown { rename sqlite3 {} rename sqlite3_shutdown sqlite3 } -files [ test_set $::allquicktests shared_err.test -exclude async3.test ] # The set of tests to run on the alternative-pcache set perm-alt-pcache-testset { async.test attach.test delete.test delete2.test index.test insert.test insert2.test join.test join2.test rollback.test select1.test select2.test trans.test update.test } foreach discard_rate {0 10 50 90 100} { test_suite "pcache${discard_rate}" -description " Alternative pcache implementation with ${discard_rate}% random discard " -initialize " catch {db close} sqlite3_shutdown sqlite3_config_alt_pcache 1 $discard_rate 1 sqlite3_initialize autoinstall_test_functions " -shutdown { catch {db close} sqlite3_shutdown sqlite3_config_alt_pcache 0 0 0 sqlite3_config_lookaside 100 500 install_malloc_faultsim 1 sqlite3_initialize autoinstall_test_functions } -files ${perm-alt-pcache-testset} } test_suite "journaltest" -description { Check that pages are synced before being written (test_journal.c). } -initialize { catch {db close} register_jt_vfs -default "" } -shutdown { unregister_jt_vfs } -files [test_set $::allquicktests -exclude { wal* incrvacuum.test ioerr.test corrupt4.test io.test crash8.test async4.test bigfile.test }] if {[info commands register_demovfs] != ""} { test_suite "demovfs" -description { Check that the demovfs (code in test_demovfs.c) more or less works. } -initialize { register_demovfs } -shutdown { unregister_demovfs } -files { insert.test insert2.test insert3.test rollback.test select1.test select2.test select3.test } } test_suite "wal" -description { Run tests with journal_mode=WAL } -initialize { set ::G(savepoint6_iterations) 100 } -shutdown { unset -nocomplain ::G(savepoint6_iterations) } -files { savepoint.test savepoint2.test savepoint6.test trans.test avtrans.test fts3aa.test fts3ab.test fts3ac.test fts3ad.test fts3ae.test fts3af.test fts3ag.test fts3ah.test fts3ai.test fts3aj.test fts3ak.test fts3al.test fts3am.test fts3an.test fts3ao.test fts3b.test fts3c.test fts3d.test fts3e.test fts3query.test } # End of tests ############################################################################# # run_tests NAME OPTIONS # # where available options are: # # -description TITLE # -initialize SCRIPT # -shutdown SCRIPT # -presql SQL # -files LIST-OF-FILES # -prefix NAME # proc run_tests {name args} { array set options $args set ::G(perm:name) $name set ::G(perm:prefix) $options(-prefix) set ::G(perm:presql) $options(-presql) set ::G(isquick) 1 uplevel $options(-initialize) foreach file [lsort $options(-files)] { slave_test_file $::testdir/$file } uplevel $options(-shutdown) unset ::G(perm:name) unset ::G(perm:prefix) unset ::G(perm:presql) } proc run_test_suite {name} { if {[info exists ::testspec($name)]==0} { error "No such test suite: $name" } uplevel run_tests $name $::testspec($name) } proc help {} { puts "Usage: $::argv0 TESTSUITE ?TESTFILE?" puts "" puts "Available test-suites are:" foreach k $::testsuitelist { if {[info exists ::testspec($k)]==0} { puts " ----------------------------------------" puts "" } else { array set o $::testspec($k) puts "Test suite: \"$k\"" set d [string trim $o(-description)] set d [regsub {\n *} $d "\n "] puts " $d" puts "" } } exit -1 } if {[info script] == $argv0} { proc main {argv} { if {[llength $argv]==0} { help } else { set suite [lindex $argv 0] if {[info exists ::testspec($suite)]==0} help set extra "" if {[llength $argv]>1} { set extra [list -files [lrange $argv 1 end]] } eval run_tests $suite $::testspec($suite) $extra } } main $argv finish_test } |
Changes to test/pragma.test.
︙ | ︙ | |||
12 13 14 15 16 17 18 19 20 21 22 23 24 25 | # # This file implements tests for the PRAGMA command. # # $Id: pragma.test,v 1.73 2009/01/12 14:01:45 danielk1977 Exp $ set testdir [file dirname $argv0] source $testdir/tester.tcl # Test organization: # # pragma-1.*: Test cache_size, default_cache_size and synchronous on main db. # pragma-2.*: Test synchronous on attached db. # pragma-3.*: Test detection of table/index inconsistency by integrity_check. # pragma-4.*: Test cache_size and default_cache_size on attached db. | > > > > > | 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | # # This file implements tests for the PRAGMA command. # # $Id: pragma.test,v 1.73 2009/01/12 14:01:45 danielk1977 Exp $ set testdir [file dirname $argv0] source $testdir/tester.tcl # Do not use a codec for tests in this file, as the database file is # manipulated directly using tcl scripts (using the [hexio_write] command). # do_not_use_codec # Test organization: # # pragma-1.*: Test cache_size, default_cache_size and synchronous on main db. # pragma-2.*: Test synchronous on attached db. # pragma-3.*: Test detection of table/index inconsistency by integrity_check. # pragma-4.*: Test cache_size and default_cache_size on attached db. |
︙ | ︙ | |||
313 314 315 316 317 318 319 320 321 322 323 324 325 326 | set in [open test.db r] fconfigure $in -translation binary puts -nonewline $out [read $in] seek $in 0 puts -nonewline $out [read $in] close $in close $out execsql {REINDEX t2} execsql {PRAGMA integrity_check} } {ok} do_test pragma-3.8.1 { execsql {PRAGMA quick_check} } {ok} do_test pragma-3.9 { | > | 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 | set in [open test.db r] fconfigure $in -translation binary puts -nonewline $out [read $in] seek $in 0 puts -nonewline $out [read $in] close $in close $out hexio_write testerr.db 28 00000000 execsql {REINDEX t2} execsql {PRAGMA integrity_check} } {ok} do_test pragma-3.8.1 { execsql {PRAGMA quick_check} } {ok} do_test pragma-3.9 { |
︙ | ︙ | |||
630 631 632 633 634 635 636 | do_test pragma-7.1.2 { execsql { pragma index_list(t3_bogus); } } {} } ;# ifcapable schema_pragmas ifcapable {utf16} { | > | | | | | | | > | 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 | do_test pragma-7.1.2 { execsql { pragma index_list(t3_bogus); } } {} } ;# ifcapable schema_pragmas ifcapable {utf16} { if {[permutation] == ""} { do_test pragma-7.2 { db close sqlite3 db test.db catchsql { pragma encoding=bogus; } } {1 {unsupported encoding: bogus}} } } ifcapable tempdb { do_test pragma-7.3 { db close sqlite3 db test.db execsql { pragma lock_status; |
︙ | ︙ |
Changes to test/quick.test.
1 2 3 4 5 6 7 8 | # # May you do good and not evil. # May you find forgiveness for yourself and forgive others. # May you share freely, never taking more than you give. # #*********************************************************************** # This file runs all tests. # | < < < < < < < < < < < < < < < < < < < < < < < < < | < < < < | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | < < < < | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | # # May you do good and not evil. # May you find forgiveness for yourself and forgive others. # May you share freely, never taking more than you give. # #*********************************************************************** # This file runs all tests. # set testdir [file dirname $argv0] source $testdir/permutations.test run_test_suite quick finish_test |
Changes to test/rdonly.test.
︙ | ︙ | |||
14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 | # as readonly if its write version is set to high. # # $Id: rdonly.test,v 1.2 2008/07/08 10:19:58 danielk1977 Exp $ set testdir [file dirname $argv0] source $testdir/tester.tcl # Create a database. # do_test rdonly-1.1 { execsql { CREATE TABLE t1(x); INSERT INTO t1 VALUES(1); SELECT * FROM t1; } } {1} | > > > > | | | 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 | # as readonly if its write version is set to high. # # $Id: rdonly.test,v 1.2 2008/07/08 10:19:58 danielk1977 Exp $ set testdir [file dirname $argv0] source $testdir/tester.tcl # Do not use a codec for tests in this file, as the database file is # manipulated directly using tcl scripts (using the [hexio_write] command). # do_not_use_codec # Create a database. # do_test rdonly-1.1 { execsql { CREATE TABLE t1(x); INSERT INTO t1 VALUES(1); SELECT * FROM t1; } } {1} # Changes the write version from 1 to 3. Verify that the database # can be read but not written. # do_test rdonly-1.2 { db close hexio_get_int [hexio_read test.db 18 1] } 1 do_test rdonly-1.3 { hexio_write test.db 18 03 sqlite3 db test.db execsql { SELECT * FROM t1; } } {1} do_test rdonly-1.4 { catchsql { |
︙ | ︙ | |||
63 64 65 66 67 68 69 70 | } {0 {1 2}} # Now, after connection [db] has loaded the database schema, modify the # write-version of the file (and the change-counter, so that the # write-version is reloaded). This way, SQLite does not discover that # the database is read-only until after it is locked. # do_test rdonly-1.6 { | > > | | 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 | } {0 {1 2}} # Now, after connection [db] has loaded the database schema, modify the # write-version of the file (and the change-counter, so that the # write-version is reloaded). This way, SQLite does not discover that # the database is read-only until after it is locked. # set ro_version 02 ifcapable wal { set ro_version 03 } do_test rdonly-1.6 { hexio_write test.db 18 $ro_version ; # write-version hexio_write test.db 24 11223344 ; # change-counter catchsql { INSERT INTO t1 VALUES(2); } } {1 {attempt to write a readonly database}} finish_test |
Changes to test/rollback.test.
︙ | ︙ | |||
75 76 77 78 79 80 81 | do_test rollback-1.8 { sqlite3_step $STMT } {SQLITE_ROW} do_test rollback-1.9 { sqlite3_finalize $STMT } {SQLITE_OK} | < < | | | 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 | do_test rollback-1.8 { sqlite3_step $STMT } {SQLITE_ROW} do_test rollback-1.9 { sqlite3_finalize $STMT } {SQLITE_OK} if {$tcl_platform(platform) == "unix" && [permutation] ne "onefile" && [permutation] ne "inmemory_journal" } { do_test rollback-2.1 { execsql { BEGIN; INSERT INTO t3 VALUES('hello world'); } file copy -force test.db testA.db |
︙ | ︙ | |||
131 132 133 134 135 136 137 | # do_test rollback-2.2 { sqlite3 db2 testA.db execsql { SELECT distinct tbl_name FROM sqlite_master; } db2 } {t1 t3} | | | 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 | # do_test rollback-2.2 { sqlite3 db2 testA.db execsql { SELECT distinct tbl_name FROM sqlite_master; } db2 } {t1 t3} if {[lsearch {exclusive persistent_journal no_journal} [permutation]]<0} { do_test rollback-2.3 { file exists testA.db-journal } 0 } do_test rollback-2.4 { execsql { SELECT distinct tbl_name FROM sqlite_master; |
︙ | ︙ |
Changes to test/rowhash.test.
︙ | ︙ | |||
8 9 10 11 12 13 14 | # May you share freely, never taking more than you give. # #*********************************************************************** # # This file implements regression tests for SQLite library. The # focus of this file is the code in rowhash.c. # | | > > | 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | # May you share freely, never taking more than you give. # #*********************************************************************** # # This file implements regression tests for SQLite library. The # focus of this file is the code in rowhash.c. # # NB: The rowhash.c module is no longer part of the source tree. But # we might as well keep this test. # set testdir [file dirname $argv0] source $testdir/tester.tcl do_test rowhash-1.1 { execsql { CREATE TABLE t1(id INTEGER PRIMARY KEY, a, b, c); |
︙ | ︙ | |||
41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 | } do_keyset_test rowhash-2.1 {1 2 3} do_keyset_test rowhash-2.2 {0 1 2 3} do_keyset_test rowhash-2.3 {62 125 188} if {[working_64bit_int]} { expr srand(1) for {set i 4} {$i < 10} {incr i} { for {set j 0} {$j < 5000} {incr j} { lappend L [expr int(rand()*1000000000)] } do_keyset_test rowhash-2.$i $L } } finish_test | > | 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 | } do_keyset_test rowhash-2.1 {1 2 3} do_keyset_test rowhash-2.2 {0 1 2 3} do_keyset_test rowhash-2.3 {62 125 188} if {[working_64bit_int]} { expr srand(1) unset -nocomplain i L for {set i 4} {$i < 10} {incr i} { for {set j 0} {$j < 5000} {incr j} { lappend L [expr int(rand()*1000000000)] } do_keyset_test rowhash-2.$i $L } } finish_test |
Changes to test/rtree.test.
︙ | ︙ | |||
11 12 13 14 15 16 17 | set testdir [file dirname $argv0] source $testdir/tester.tcl rename finish_test rtree_finish_test proc finish_test {} {} set RTREE_EXCLUDE { } | | < | | 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 | set testdir [file dirname $argv0] source $testdir/tester.tcl rename finish_test rtree_finish_test proc finish_test {} {} set RTREE_EXCLUDE { } if {[info exists G(isquick)] && $G(isquick)} { set RTREE_EXCLUDE rtree3.test } set rtreedir [file join $testdir .. ext rtree] foreach testfile [lsort -dictionary [glob -nocomplain $rtreedir/*.test]] { set tail [file tail $testfile] if {[lsearch -exact $RTREE_EXCLUDE $tail]>=0} continue source $testfile catch {db close} if {$sqlite_open_file_count>0} { puts "$tail did not close all files: $sqlite_open_file_count" fail_test $tail set sqlite_open_file_count 0 } } set sqlite_open_file_count 0 rtree_finish_test rename finish_test {} |
︙ | ︙ |
Changes to test/savepoint.test.
︙ | ︙ | |||
9 10 11 12 13 14 15 | # #*********************************************************************** # # $Id: savepoint.test,v 1.13 2009/07/18 08:30:45 danielk1977 Exp $ set testdir [file dirname $argv0] source $testdir/tester.tcl | | > > | 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 | # #*********************************************************************** # # $Id: savepoint.test,v 1.13 2009/07/18 08:30:45 danielk1977 Exp $ set testdir [file dirname $argv0] source $testdir/tester.tcl source $testdir/lock_common.tcl source $testdir/malloc_common.tcl #---------------------------------------------------------------------- # The following tests - savepoint-1.* - test that the SAVEPOINT, RELEASE # and ROLLBACK TO comands are correctly parsed, and that the auto-commit # flag is correctly set and unset as a result. # do_test savepoint-1.1 { wal_set_journal_mode execsql { SAVEPOINT sp1; RELEASE sp1; } } {} do_test savepoint-1.2 { execsql { |
︙ | ︙ | |||
89 90 91 92 93 94 95 96 97 98 99 100 101 102 | SAVEPOINT sp1; ROLLBACK TO sp1; } } {} do_test savepoint-1.6 { execsql COMMIT } {} #------------------------------------------------------------------------ # These tests - savepoint-2.* - test rollbacks and releases of savepoints # with a very simple data set. # do_test savepoint-2.1 { | > | 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 | SAVEPOINT sp1; ROLLBACK TO sp1; } } {} do_test savepoint-1.6 { execsql COMMIT } {} wal_check_journal_mode savepoint-1.7 #------------------------------------------------------------------------ # These tests - savepoint-2.* - test rollbacks and releases of savepoints # with a very simple data set. # do_test savepoint-2.1 { |
︙ | ︙ | |||
171 172 173 174 175 176 177 178 179 180 181 182 | } {1 2 3 a b c d e f} do_test savepoint-2.11 { execsql { ROLLBACK; } execsql { SELECT * FROM t1 } } {} #------------------------------------------------------------------------ # This block of tests - savepoint-3.* - test that when a transaction # savepoint is rolled back, locks are not released from database files. # And that when a transaction savepoint is released, they are released. | > | > > > > | | | | | | | | | | | | | | | | | | | | | | | | > | 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 | } {1 2 3 a b c d e f} do_test savepoint-2.11 { execsql { ROLLBACK; } execsql { SELECT * FROM t1 } } {} wal_check_journal_mode savepoint-2.12 #------------------------------------------------------------------------ # This block of tests - savepoint-3.* - test that when a transaction # savepoint is rolled back, locks are not released from database files. # And that when a transaction savepoint is released, they are released. # # These tests do not work in WAL mode. WAL mode does not take RESERVED # locks on the database file. # if {[wal_is_wal_mode]==0} { do_test savepoint-3.1 { execsql { SAVEPOINT "transaction" } execsql { PRAGMA lock_status } } {main unlocked temp closed} do_test savepoint-3.2 { execsql { INSERT INTO t1 VALUES(1, 2, 3) } execsql { PRAGMA lock_status } } {main reserved temp closed} do_test savepoint-3.3 { execsql { ROLLBACK TO "transaction" } execsql { PRAGMA lock_status } } {main reserved temp closed} do_test savepoint-3.4 { execsql { INSERT INTO t1 VALUES(1, 2, 3) } execsql { PRAGMA lock_status } } {main reserved temp closed} do_test savepoint-3.5 { execsql { RELEASE "transaction" } execsql { PRAGMA lock_status } } {main unlocked temp closed} } #------------------------------------------------------------------------ # Test that savepoints that include schema modifications are handled # correctly. Test cases savepoint-4.*. # do_test savepoint-4.1 { execsql { |
︙ | ︙ | |||
260 261 262 263 264 265 266 267 268 269 270 271 272 273 | INSERT INTO t3 VALUES('value'); } execsql {SELECT * FROM t3} } {value} do_test savepoint-4.8 { execsql COMMIT } {} #------------------------------------------------------------------------ # Test some logic errors to do with the savepoint feature. # ifcapable incrblob { do_test savepoint-5.1.1 { | > | 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 | INSERT INTO t3 VALUES('value'); } execsql {SELECT * FROM t3} } {value} do_test savepoint-4.8 { execsql COMMIT } {} wal_check_journal_mode savepoint-4.9 #------------------------------------------------------------------------ # Test some logic errors to do with the savepoint feature. # ifcapable incrblob { do_test savepoint-5.1.1 { |
︙ | ︙ | |||
308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 | catchsql {release def} } {1 {cannot release savepoint - SQL statements in progress}} do_test savepoint-5.3.5 { close $fd execsql {release abc} } {} do_test savepoint-5.4.1 { execsql { SAVEPOINT main; INSERT INTO blobs VALUES('another blob'); } } {} do_test savepoint-5.4.2 { sqlite3 db2 test.db | > > > > > > > > > > > > | > > > > > > | | | | | | > | > > > > > > | < | 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 | catchsql {release def} } {1 {cannot release savepoint - SQL statements in progress}} do_test savepoint-5.3.5 { close $fd execsql {release abc} } {} # Rollback mode: # # Open a savepoint transaction and insert a row into the database. Then, # using a second database handle, open a read-only transaction on the # database file. Check that the savepoint transaction cannot be committed # until after the read-only transaction has been closed. # # WAL mode: # # As above, except that the savepoint transaction can be successfully # committed before the read-only transaction has been closed. # do_test savepoint-5.4.1 { execsql { SAVEPOINT main; INSERT INTO blobs VALUES('another blob'); } } {} do_test savepoint-5.4.2 { sqlite3 db2 test.db execsql { BEGIN ; SELECT count(*) FROM blobs } db2 } {1} if {[wal_is_wal_mode]} { do_test savepoint-5.4.3 { catchsql "RELEASE main" } {0 {}} do_test savepoint-5.4.4 { db2 close } {} } else { do_test savepoint-5.4.3 { catchsql { RELEASE main } } {1 {database is locked}} do_test savepoint-5.4.4 { db2 close catchsql { RELEASE main } } {0 {}} } do_test savepoint-5.4.5 { execsql { SELECT x FROM blobs WHERE rowid = 2 } } {{another blob}} do_test savepoint-5.4.6 { execsql { SELECT count(*) FROM blobs } } {2} } wal_check_journal_mode savepoint-5.5 #------------------------------------------------------------------------- # The following tests, savepoint-6.*, test an incr-vacuum inside of a # couple of nested savepoints. # ifcapable {autovacuum && pragma} { db close file delete -force test.db sqlite3 db test.db do_test savepoint-6.1 { execsql { PRAGMA auto_vacuum = incremental } wal_set_journal_mode execsql { CREATE TABLE t1(a, b, c); CREATE INDEX i1 ON t1(a, b); BEGIN; INSERT INTO t1 VALUES(randstr(10,400),randstr(10,400),randstr(10,400)); } set r "randstr(10,400)" for {set ii 0} {$ii < 10} {incr ii} { |
︙ | ︙ | |||
372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 | PRAGMA incr_vacuum; ROLLBACK TO one; COMMIT; } } {} integrity_check savepoint-6.4 } #------------------------------------------------------------------------- # The following tests, savepoint-7.*, attempt to break the logic # surrounding savepoints by growing and shrinking the database file. # db close file delete -force test.db sqlite3 db test.db do_test savepoint-7.1 { execsql { | > > > > < | 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 | PRAGMA incr_vacuum; ROLLBACK TO one; COMMIT; } } {} integrity_check savepoint-6.4 wal_check_journal_mode savepoint-6.5 } #------------------------------------------------------------------------- # The following tests, savepoint-7.*, attempt to break the logic # surrounding savepoints by growing and shrinking the database file. # db close file delete -force test.db sqlite3 db test.db do_test savepoint-7.1 { execsql { PRAGMA auto_vacuum = incremental } wal_set_journal_mode execsql { PRAGMA cache_size = 10; BEGIN; CREATE TABLE t1(a PRIMARY KEY, b); INSERT INTO t1(a) VALUES('alligator'); INSERT INTO t1(a) VALUES('angelfish'); INSERT INTO t1(a) VALUES('ant'); INSERT INTO t1(a) VALUES('antelope'); |
︙ | ︙ | |||
445 446 447 448 449 450 451 452 453 454 455 456 457 | SAVEPOINT two; INSERT INTO t2 SELECT a, b FROM t1; ROLLBACK TO two; COMMIT; } execsql { PRAGMA integrity_check } } {ok} do_test savepoint-7.4.1 { db close file delete -force test.db sqlite3 db test.db execsql { | > > > < | 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 | SAVEPOINT two; INSERT INTO t2 SELECT a, b FROM t1; ROLLBACK TO two; COMMIT; } execsql { PRAGMA integrity_check } } {ok} wal_check_journal_mode savepoint-7.3.3 do_test savepoint-7.4.1 { db close file delete -force test.db sqlite3 db test.db execsql { PRAGMA auto_vacuum = incremental } wal_set_journal_mode execsql { CREATE TABLE t1(a, b, PRIMARY KEY(a, b)); INSERT INTO t1 VALUES(randstr(1000,1000), randstr(1000,1000)); BEGIN; DELETE FROM t1; SAVEPOINT one; PRAGMA incremental_vacuum; ROLLBACK TO one; |
︙ | ︙ | |||
493 494 495 496 497 498 499 500 501 502 503 504 505 506 | } } {ok} do_test savepoint-7.5.2 { execsql { DROP TABLE t5; } } {} # Test oddly named and quoted savepoints. # do_test savepoint-8-1 { execsql { SAVEPOINT "save1" } execsql { RELEASE save1 } } {} | > | 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 | } } {ok} do_test savepoint-7.5.2 { execsql { DROP TABLE t5; } } {} wal_check_journal_mode savepoint-7.5.3 # Test oddly named and quoted savepoints. # do_test savepoint-8-1 { execsql { SAVEPOINT "save1" } execsql { RELEASE save1 } } {} |
︙ | ︙ | |||
594 595 596 597 598 599 600 | # The lock state of the TEMP database can vary if SQLITE_TEMP_STORE=3 # And the following set of tests is only really interested in the status # of the aux1 and aux2 locks. So record the current lock status of # TEMP for use in the answers. set templockstate [lindex [db eval {PRAGMA lock_status}] 3] | > | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | > > > < | 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 | # The lock state of the TEMP database can vary if SQLITE_TEMP_STORE=3 # And the following set of tests is only really interested in the status # of the aux1 and aux2 locks. So record the current lock status of # TEMP for use in the answers. set templockstate [lindex [db eval {PRAGMA lock_status}] 3] if {[wal_is_wal_mode]==0} { do_test savepoint-10.2.1 { file delete -force test3.db file delete -force test2.db execsql { ATTACH 'test2.db' AS aux1; ATTACH 'test3.db' AS aux2; DROP TABLE t1; CREATE TABLE main.t1(x, y); CREATE TABLE aux1.t2(x, y); CREATE TABLE aux2.t3(x, y); SELECT name FROM sqlite_master UNION ALL SELECT name FROM aux1.sqlite_master UNION ALL SELECT name FROM aux2.sqlite_master; } } {t1 t2 t3} do_test savepoint-10.2.2 { execsql { PRAGMA lock_status } } [list main unlocked temp $templockstate aux1 unlocked aux2 unlocked] do_test savepoint-10.2.3 { execsql { SAVEPOINT one; INSERT INTO t1 VALUES(1, 2); PRAGMA lock_status; } } [list main reserved temp $templockstate aux1 unlocked aux2 unlocked] do_test savepoint-10.2.4 { execsql { INSERT INTO t3 VALUES(3, 4); PRAGMA lock_status; } } [list main reserved temp $templockstate aux1 unlocked aux2 reserved] do_test savepoint-10.2.5 { execsql { SAVEPOINT two; INSERT INTO t2 VALUES(5, 6); PRAGMA lock_status; } } [list main reserved temp $templockstate aux1 reserved aux2 reserved] do_test savepoint-10.2.6 { execsql { SELECT * FROM t2 } } {5 6} do_test savepoint-10.2.7 { execsql { ROLLBACK TO two } execsql { SELECT * FROM t2 } } {} do_test savepoint-10.2.8 { execsql { PRAGMA lock_status } } [list main reserved temp $templockstate aux1 reserved aux2 reserved] do_test savepoint-10.2.9 { execsql { SELECT 'a', * FROM t1 UNION ALL SELECT 'b', * FROM t3 } } {a 1 2 b 3 4} do_test savepoint-10.2.9 { execsql { INSERT INTO t2 VALUES(5, 6); RELEASE one; } execsql { SELECT * FROM t1; SELECT * FROM t2; SELECT * FROM t3; } } {1 2 5 6 3 4} do_test savepoint-10.2.9 { execsql { PRAGMA lock_status } } [list main unlocked temp $templockstate aux1 unlocked aux2 unlocked] do_test savepoint-10.2.10 { execsql { SAVEPOINT one; INSERT INTO t1 VALUES('a', 'b'); SAVEPOINT two; INSERT INTO t2 VALUES('c', 'd'); SAVEPOINT three; INSERT INTO t3 VALUES('e', 'f'); } execsql { SELECT * FROM t1; SELECT * FROM t2; SELECT * FROM t3; } } {1 2 a b 5 6 c d 3 4 e f} do_test savepoint-10.2.11 { execsql { ROLLBACK TO two } execsql { SELECT * FROM t1; SELECT * FROM t2; SELECT * FROM t3; } } {1 2 a b 5 6 3 4} do_test savepoint-10.2.12 { execsql { INSERT INTO t3 VALUES('g', 'h'); ROLLBACK TO two; } execsql { SELECT * FROM t1; SELECT * FROM t2; SELECT * FROM t3; } } {1 2 a b 5 6 3 4} do_test savepoint-10.2.13 { execsql { ROLLBACK } execsql { SELECT * FROM t1; SELECT * FROM t2; SELECT * FROM t3; } } {1 2 5 6 3 4} do_test savepoint-10.2.14 { execsql { PRAGMA lock_status } } [list main unlocked temp $templockstate aux1 unlocked aux2 unlocked] } #------------------------------------------------------------------------- # The following tests - savepoint-11.* - test the interaction of # savepoints and creating or dropping tables and indexes in # auto-vacuum mode. # do_test savepoint-11.1 { db close file delete -force test.db sqlite3 db test.db execsql { PRAGMA auto_vacuum = full; } wal_set_journal_mode execsql { CREATE TABLE t1(a, b, UNIQUE(a, b)); INSERT INTO t1 VALUES(1, randstr(1000,1000)); INSERT INTO t1 VALUES(2, randstr(1000,1000)); } } {} do_test savepoint-11.2 { execsql { |
︙ | ︙ | |||
747 748 749 750 751 752 753 754 755 | CREATE TABLE t3(a, b, UNIQUE(a, b)); ROLLBACK TO one; } } {} integrity_check savepoint-11.7 do_test savepoint-11.8 { execsql { ROLLBACK } file size test.db } {8192} | > < | 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 | CREATE TABLE t3(a, b, UNIQUE(a, b)); ROLLBACK TO one; } } {} integrity_check savepoint-11.7 do_test savepoint-11.8 { execsql { ROLLBACK } execsql { PRAGMA wal_checkpoint } file size test.db } {8192} do_test savepoint-11.9 { execsql { DROP TABLE IF EXISTS t1; DROP TABLE IF EXISTS t2; DROP TABLE IF EXISTS t3; } |
︙ | ︙ | |||
778 779 780 781 782 783 784 785 786 787 788 789 790 791 | } {1 2 3 4} do_test savepoint-11.11 { execsql COMMIT } {} do_test savepoint-11.12 { execsql {SELECT * FROM t2} } {1 2 3 4} #------------------------------------------------------------------------- # The following tests - savepoint-12.* - test the interaction of # savepoints and "ON CONFLICT ROLLBACK" clauses. # do_test savepoint-12.1 { execsql { | > | 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 | } {1 2 3 4} do_test savepoint-11.11 { execsql COMMIT } {} do_test savepoint-11.12 { execsql {SELECT * FROM t2} } {1 2 3 4} wal_check_journal_mode savepoint-11.13 #------------------------------------------------------------------------- # The following tests - savepoint-12.* - test the interaction of # savepoints and "ON CONFLICT ROLLBACK" clauses. # do_test savepoint-12.1 { execsql { |
︙ | ︙ | |||
811 812 813 814 815 816 817 818 819 820 821 822 | } {1 {column a is not unique}} do_test savepoint-12.3 { sqlite3_get_autocommit db } {1} do_test savepoint-12.4 { execsql { SAVEPOINT one } } {} #------------------------------------------------------------------------- # The following tests - savepoint-13.* - test the interaction of # savepoints and "journal_mode = off". # | > > | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | } {1 {column a is not unique}} do_test savepoint-12.3 { sqlite3_get_autocommit db } {1} do_test savepoint-12.4 { execsql { SAVEPOINT one } } {} wal_check_journal_mode savepoint-12.5 #------------------------------------------------------------------------- # The following tests - savepoint-13.* - test the interaction of # savepoints and "journal_mode = off". # if {[wal_is_wal_mode]==0} { do_test savepoint-13.1 { db close catch {file delete -force test.db} sqlite3 db test.db execsql { BEGIN; CREATE TABLE t1(a PRIMARY KEY, b); INSERT INTO t1 VALUES(1, 2); COMMIT; PRAGMA journal_mode = off; } } {off} do_test savepoint-13.2 { execsql { BEGIN; INSERT INTO t1 VALUES(3, 4); INSERT INTO t1 SELECT a+4,b+4 FROM t1; COMMIT; } } {} do_test savepoint-13.3 { execsql { BEGIN; INSERT INTO t1 VALUES(9, 10); SAVEPOINT s1; INSERT INTO t1 VALUES(11, 12); COMMIT; } } {} do_test savepoint-13.4 { execsql { BEGIN; INSERT INTO t1 VALUES(13, 14); SAVEPOINT s1; INSERT INTO t1 VALUES(15, 16); ROLLBACK TO s1; ROLLBACK; SELECT * FROM t1; } } {1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16} } db close file delete test.db do_multiclient_test tn { do_test savepoint-14.$tn.1 { sql1 { CREATE TABLE foo(x); INSERT INTO foo VALUES(1); INSERT INTO foo VALUES(2); } sql2 { BEGIN; SELECT * FROM foo; } } {1 2} do_test savepoint-14.$tn.2 { sql1 { SAVEPOINT one; INSERT INTO foo VALUES(1); } csql1 { RELEASE one } } {1 {database is locked}} do_test savepoint-14.$tn.3 { sql1 { ROLLBACK TO one } sql2 { COMMIT } sql1 { RELEASE one } } {} do_test savepoint-14.$tn.4 { sql2 { BEGIN; SELECT * FROM foo; } } {1 2} do_test savepoint-14.$tn.5 { sql1 { SAVEPOINT one; INSERT INTO foo VALUES(1); } csql1 { RELEASE one } } {1 {database is locked}} do_test savepoint-14.$tn.6 { sql2 { COMMIT } sql1 { ROLLBACK TO one; INSERT INTO foo VALUES(3); INSERT INTO foo VALUES(4); INSERT INTO foo VALUES(5); RELEASE one; } } {} do_test savepoint-14.$tn.7 { sql2 { CREATE INDEX fooidx ON foo(x); } sql3 { PRAGMA integrity_check } } {ok} } do_multiclient_test tn { do_test savepoint-15.$tn.1 { sql1 { CREATE TABLE foo(x); INSERT INTO foo VALUES(1); INSERT INTO foo VALUES(2); } sql2 { BEGIN; SELECT * FROM foo; } } {1 2} do_test savepoint-15.$tn.2 { sql1 { PRAGMA locking_mode = EXCLUSIVE; BEGIN; INSERT INTO foo VALUES(3); } csql1 { COMMIT } } {1 {database is locked}} do_test savepoint-15.$tn.3 { sql1 { ROLLBACK } sql2 { COMMIT } sql1 { INSERT INTO foo VALUES(3); PRAGMA locking_mode = NORMAL; INSERT INTO foo VALUES(4); } sql2 { CREATE INDEX fooidx ON foo(x); } sql3 { PRAGMA integrity_check } } {ok} } do_multiclient_test tn { do_test savepoint-16.$tn.1 { sql1 { CREATE TABLE foo(x); INSERT INTO foo VALUES(1); INSERT INTO foo VALUES(2); } } {} do_test savepoint-16.$tn.2 { db eval {SELECT * FROM foo} { sql1 { INSERT INTO foo VALUES(3) } sql2 { SELECT * FROM foo } sql1 { INSERT INTO foo VALUES(4) } break } sql2 { CREATE INDEX fooidx ON foo(x); } sql3 { PRAGMA integrity_check } } {ok} do_test savepoint-16.$tn.3 { sql1 { SELECT * FROM foo } } {1 2 3 4} } finish_test |
Changes to test/savepoint2.test.
︙ | ︙ | |||
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 | # #*********************************************************************** # # $Id: savepoint2.test,v 1.5 2009/06/05 17:09:12 drh Exp $ set testdir [file dirname $argv0] source $testdir/tester.tcl # Tests in this file are quite similar to those run by trans.test and # avtrans.test. # proc signature {} { return [db eval {SELECT count(*), md5sum(x) FROM t3}] } do_test savepoint2-1 { execsql { PRAGMA cache_size=10; BEGIN; CREATE TABLE t3(x TEXT); INSERT INTO t3 VALUES(randstr(10,400)); INSERT INTO t3 VALUES(randstr(10,400)); INSERT INTO t3 SELECT randstr(10,400) FROM t3; INSERT INTO t3 SELECT randstr(10,400) FROM t3; INSERT INTO t3 SELECT randstr(10,400) FROM t3; INSERT INTO t3 SELECT randstr(10,400) FROM t3; INSERT INTO t3 SELECT randstr(10,400) FROM t3; INSERT INTO t3 SELECT randstr(10,400) FROM t3; INSERT INTO t3 SELECT randstr(10,400) FROM t3; INSERT INTO t3 SELECT randstr(10,400) FROM t3; INSERT INTO t3 SELECT randstr(10,400) FROM t3; COMMIT; SELECT count(*) FROM t3; } } {1024} unset -nocomplain ::sig unset -nocomplain SQL set iterations 20 set SQL(1) { | > > > | 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 | # #*********************************************************************** # # $Id: savepoint2.test,v 1.5 2009/06/05 17:09:12 drh Exp $ set testdir [file dirname $argv0] source $testdir/tester.tcl # Tests in this file are quite similar to those run by trans.test and # avtrans.test. # proc signature {} { return [db eval {SELECT count(*), md5sum(x) FROM t3}] } do_test savepoint2-1 { wal_set_journal_mode execsql { PRAGMA cache_size=10; BEGIN; CREATE TABLE t3(x TEXT); INSERT INTO t3 VALUES(randstr(10,400)); INSERT INTO t3 VALUES(randstr(10,400)); INSERT INTO t3 SELECT randstr(10,400) FROM t3; INSERT INTO t3 SELECT randstr(10,400) FROM t3; INSERT INTO t3 SELECT randstr(10,400) FROM t3; INSERT INTO t3 SELECT randstr(10,400) FROM t3; INSERT INTO t3 SELECT randstr(10,400) FROM t3; INSERT INTO t3 SELECT randstr(10,400) FROM t3; INSERT INTO t3 SELECT randstr(10,400) FROM t3; INSERT INTO t3 SELECT randstr(10,400) FROM t3; INSERT INTO t3 SELECT randstr(10,400) FROM t3; COMMIT; SELECT count(*) FROM t3; } } {1024} wal_check_journal_mode savepoint2-1.1 unset -nocomplain ::sig unset -nocomplain SQL set iterations 20 set SQL(1) { |
︙ | ︙ | |||
136 137 138 139 140 141 142 143 144 145 146 147 148 | # do_test savepoint2-$ii.6 { execsql $SQL(4) execsql COMMIT sqlite3_get_autocommit db } {1} integrity_check savepoint2-$ii.6.1 } unset -nocomplain ::sig unset -nocomplain SQL finish_test | > > > | 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 | # do_test savepoint2-$ii.6 { execsql $SQL(4) execsql COMMIT sqlite3_get_autocommit db } {1} integrity_check savepoint2-$ii.6.1 # Check that the connection is still running in WAL mode. wal_check_journal_mode savepoint2-$ii.7 } unset -nocomplain ::sig unset -nocomplain SQL finish_test |
Changes to test/savepoint6.test.
︙ | ︙ | |||
21 22 23 24 25 26 27 28 29 30 31 32 33 34 | set DATABASE_SCHEMA { PRAGMA auto_vacuum = incremental; CREATE TABLE t1(x, y); CREATE UNIQUE INDEX i1 ON t1(x); CREATE INDEX i2 ON t1(y); } #-------------------------------------------------------------------------- # In memory database state. # # ::lSavepoint is a list containing one entry for each active savepoint. The # first entry in the list corresponds to the most recently opened savepoint. # Each entry consists of two elements: | > > > > | 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 | set DATABASE_SCHEMA { PRAGMA auto_vacuum = incremental; CREATE TABLE t1(x, y); CREATE UNIQUE INDEX i1 ON t1(x); CREATE INDEX i2 ON t1(y); } if {0==[info exists ::G(savepoint6_iterations)]} { set ::G(savepoint6_iterations) 1000 } #-------------------------------------------------------------------------- # In memory database state. # # ::lSavepoint is a list containing one entry for each active savepoint. The # first entry in the list corresponds to the most recently opened savepoint. # Each entry consists of two elements: |
︙ | ︙ | |||
218 219 220 221 222 223 224 225 226 227 | execsql {SELECT count(*) FROM t1} } {44} foreach zSetup [list { set testname normal sqlite3 db test.db } { set testname tempdb sqlite3 db "" } { | > | | > > | > > | 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 | execsql {SELECT count(*) FROM t1} } {44} foreach zSetup [list { set testname normal sqlite3 db test.db } { if {[wal_is_wal_mode]} continue set testname tempdb sqlite3 db "" } { if {[permutation] eq "journaltest"} { continue } set testname nosync sqlite3 db test.db sql { PRAGMA synchronous = off } } { set testname smallcache sqlite3 db test.db sql { PRAGMA cache_size = 10 } }] { unset -nocomplain ::lSavepoint unset -nocomplain ::aEntry catch { db close } file delete -force test.db test.db-wal test.db-journal eval $zSetup sql $DATABASE_SCHEMA wal_set_journal_mode do_test savepoint6-$testname.setup { savepoint one insert_rows [random_integers 100 1000] release one checkdb } {ok} for {set i 0} {$i < $::G(savepoint6_iterations)} {incr i} { do_test savepoint6-$testname.$i.1 { savepoint_op checkdb } {ok} do_test savepoint6-$testname.$i.2 { database_op database_op checkdb } {ok} } wal_check_journal_mode savepoint6-$testname.walok } unset -nocomplain ::lSavepoint unset -nocomplain ::aEntry finish_test |
Added test/schema3.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 | # 2010 Jun 28 # # The author disclaims copyright to this source code. In place of # a legal notice, here is a blessing: # # May you do good and not evil. # May you find forgiveness for yourself and forgive others. # May you share freely, never taking more than you give. # #*********************************************************************** # This file implements regression tests for SQLite library. # set testdir [file dirname $argv0] source $testdir/tester.tcl source $testdir/malloc_common.tcl source $testdir/lock_common.tcl # This block tests that if one client modifies the database schema, a # second client updates its internal cache of the database schema before # executing any queries. Specifically, it does not return a "no such column" # or "no such table" error if the table or column in question does exist # but was added after the second client loaded its cache of the database # schema. # # Types of schema modifications: # # 1. Adding a database table. # 2. Adding a database view. # 3. Adding a database index. # 4. Adding a database trigger. # 5. Adding a column to an existing table (ALTER TABLE). # do_multiclient_test tn { # Have connections [db1] and [db2] load the current database schema. # sql1 { SELECT * FROM sqlite_master } sql2 { SELECT * FROM sqlite_master } foreach {tn2 c1 c2} { 1 { CREATE TABLE t1(a, b) } { SELECT * FROM t1 } 2 { CREATE TABLE t2(a, b) } { UPDATE t2 SET a = b } 3 { CREATE TABLE t3(a, b) } { DELETE FROM t3 } 4 { CREATE TABLE t4(a, b) } { INSERT INTO t4 VALUES(1, 2) } 5 { CREATE TABLE t5(a, b) } { DROP TABLE t5 } 6 { CREATE TABLE t6(a, b) } { CREATE INDEX i1 ON t6(a) } 7 { ALTER TABLE t1 ADD COLUMN c } { SELECT a, b, c FROM t1 } 8 { ALTER TABLE t2 ADD COLUMN c } { UPDATE t2 SET a = c } 9 { ALTER TABLE t2 ADD COLUMN d } { UPDATE t2 SET d = c } 10 { ALTER TABLE t3 ADD COLUMN c } { DELETE FROM t3 WHERE c>10 } 11 { ALTER TABLE t4 ADD COLUMN c } { INSERT INTO t4(a,b,c) VALUES(1,2,3) } 12 { ALTER TABLE t6 ADD COLUMN c } { CREATE INDEX i2 ON t6(c) } 13 { ALTER TABLE t6 ADD COLUMN d } { CREATE TRIGGER tr1 AFTER UPDATE OF d ON t6 BEGIN SELECT 1, 2, 3; END; } 14 { CREATE INDEX i3 ON t1(a) } { DROP INDEX i3 } 15 { CREATE INDEX i4 ON t2(a) } { SELECT * FROM t2 INDEXED BY i4 ORDER BY a } 16 { CREATE TRIGGER tr2 AFTER INSERT ON t3 BEGIN SELECT 1 ; END } { DROP TRIGGER tr2 } 17 { CREATE VIEW v1 AS SELECT * FROM t1 } { SELECT a,b,c FROM v1 } 18 { ALTER TABLE t1 ADD COLUMN d } { SELECT a,b,c,d FROM v1 } 19 { CREATE TABLE t7(a, b) } { DROP TABLE IF EXISTS t7; CREATE TABLE t7(c, d); } 20 { CREATE INDEX i5 ON t7(c, d) } { DROP INDEX IF EXISTS i5; CREATE INDEX i5 ON t7(c) } 21 { CREATE TRIGGER tr3 BEFORE DELETE ON t7 BEGIN SELECT 1, 2, 3 ; END } { DROP TRIGGER IF EXISTS tr3; CREATE TRIGGER tr3 AFTER INSERT ON t7 BEGIN SELECT 1, 2, 3 ; END } 22 { CREATE TABLE t8(a, b) } { CREATE TRIGGER tr4 AFTER UPDATE OF a ON t8 BEGIN SELECT 1, 2, 3; END; } } { do_test schema3-1.$tn.$tn2 { sql1 $c1 sql2 $c2 } {} } } finish_test |
Changes to test/select2.test.
︙ | ︙ | |||
149 150 151 152 153 154 155 | INSERT INTO bb VALUES(4); SELECT * FROM aa, bb WHERE max(a,b)>2; } } {1 4 3 2 3 4} do_test select2-4.2 { execsql { INSERT INTO bb VALUES(0); | | | | 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 | INSERT INTO bb VALUES(4); SELECT * FROM aa, bb WHERE max(a,b)>2; } } {1 4 3 2 3 4} do_test select2-4.2 { execsql { INSERT INTO bb VALUES(0); SELECT * FROM aa CROSS JOIN bb WHERE b; } } {1 2 1 4 3 2 3 4} do_test select2-4.3 { execsql { SELECT * FROM aa CROSS JOIN bb WHERE NOT b; } } {1 0 3 0} do_test select2-4.4 { execsql { SELECT * FROM aa, bb WHERE min(a,b); } } {1 2 1 4 3 2 3 4} |
︙ | ︙ |
Changes to test/select9.test.
︙ | ︙ | |||
21 22 23 24 25 26 27 | # TODO Points: # # * Are there any "column affinity" issues to consider? set testdir [file dirname $argv0] source $testdir/tester.tcl | < < | 21 22 23 24 25 26 27 28 29 30 31 32 33 34 | # TODO Points: # # * Are there any "column affinity" issues to consider? set testdir [file dirname $argv0] source $testdir/tester.tcl #------------------------------------------------------------------------- # test_compound_select TESTNAME SELECT RESULT # # This command is used to run multiple LIMIT/OFFSET test cases based on # the single SELECT statement passed as the second argument. The SELECT # statement may not contain a LIMIT or OFFSET clause. This proc tests # many statements of the form: |
︙ | ︙ | |||
58 59 60 61 62 63 64 | do_test $testname { execsql $::compound_sql } $result #return set iLimitIncr 1 set iOffsetIncr 1 | | | 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 | do_test $testname { execsql $::compound_sql } $result #return set iLimitIncr 1 set iOffsetIncr 1 if {[info exists ::G(isquick)] && $::G(isquick) && $nRow>=5} { set iOffsetIncr [expr $nRow / 5] set iLimitIncr [expr $nRow / 5] } set iLimitEnd [expr $nRow+$iLimitIncr] set iOffsetEnd [expr $nRow+$iOffsetIncr] |
︙ | ︙ |
Changes to test/selectC.test.
︙ | ︙ | |||
161 162 163 164 165 166 167 168 169 | SELECT a FROM t21a WHERE a>new.x UNION ALL SELECT b FROM t21a WHERE b>new.x ORDER BY 1 LIMIT 2; END; INSERT INTO t21b VALUES(6); } } {1 {no such column: new.x}} } finish_test | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | SELECT a FROM t21a WHERE a>new.x UNION ALL SELECT b FROM t21a WHERE b>new.x ORDER BY 1 LIMIT 2; END; INSERT INTO t21b VALUES(6); } } {1 {no such column: new.x}} } # Check that ticket [883034dcb5] is fixed. # do_test selectC-3.1 { execsql { CREATE TABLE person ( org_id TEXT NOT NULL, nickname TEXT NOT NULL, license TEXT, CONSTRAINT person_pk PRIMARY KEY (org_id, nickname), CONSTRAINT person_license_uk UNIQUE (license) ); INSERT INTO person VALUES('meyers', 'jack', '2GAT123'); INSERT INTO person VALUES('meyers', 'hill', 'V345FMP'); INSERT INTO person VALUES('meyers', 'jim', '2GAT138'); INSERT INTO person VALUES('smith', 'maggy', ''); INSERT INTO person VALUES('smith', 'jose', 'JJZ109'); INSERT INTO person VALUES('smith', 'jack', 'THX138'); INSERT INTO person VALUES('lakeside', 'dave', '953OKG'); INSERT INTO person VALUES('lakeside', 'amy', NULL); INSERT INTO person VALUES('lake-apts', 'tom', NULL); INSERT INTO person VALUES('acorn', 'hideo', 'CQB421'); SELECT org_id, count((NOT (org_id IS NULL)) AND (NOT (nickname IS NULL))) FROM person WHERE (CASE WHEN license != '' THEN 1 ELSE 0 END) GROUP BY 1; } } {acorn 1 lakeside 1 meyers 3 smith 2} do_test selectC-3.2 { execsql { CREATE TABLE t2(a PRIMARY KEY, b); INSERT INTO t2 VALUES('abc', 'xxx'); INSERT INTO t2 VALUES('def', 'yyy'); SELECT a, max(b || a) FROM t2 WHERE (b||b||b)!='value' GROUP BY a; } } {abc xxxabc def yyydef} do_test selectC-3.3 { execsql { SELECT b, max(a || b) FROM t2 WHERE (b||b||b)!='value' GROUP BY a; } } {xxx abcxxx yyy defyyy} finish_test |
Changes to test/shared3.test.
︙ | ︙ | |||
86 87 88 89 90 91 92 | catchsql {select count(*) from sqlite_master} db3 } {0 1} do_test shared3-2.8 { db3 close execsql { INSERT INTO t1 VALUES(10, randomblob(10000)) } db1 | < > | 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 | catchsql {select count(*) from sqlite_master} db3 } {0 1} do_test shared3-2.8 { db3 close execsql { INSERT INTO t1 VALUES(10, randomblob(10000)) } db1 # If the pager-cache is really still limited to 10 pages, then the INSERT # statement above should have caused the pager to grab an exclusive lock # on the database file so that the cache could be spilled. # catch { sqlite3 db3 $alternative_name } catchsql {select count(*) from sqlite_master} db3 } {1 {database is locked}} db1 close db2 close db3 close |
︙ | ︙ |
Changes to test/soak.test.
︙ | ︙ | |||
61 62 63 64 65 66 67 | set SOAKTESTS { fuzz.test fuzz_malloc.test trans.test corruptC.test } | | | < | > | 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 | set SOAKTESTS { fuzz.test fuzz_malloc.test trans.test corruptC.test } set G(isquick) 1 set soak_starttime [clock seconds] set soak_finishtime [expr {$soak_starttime + $TIMEOUT}] # Loop until the timeout is reached or an error occurs. # for {set iRun 0} {[clock seconds] < $soak_finishtime} {incr iRun} { set iIdx [expr {$iRun % [llength $SOAKTESTS]}] source [file join $testdir [lindex $SOAKTESTS $iIdx]] catch {db close} if {$sqlite_open_file_count>0} { puts "$tail did not close all files: $sqlite_open_file_count" fail_test $tail set sqlite_open_file_count 0 } if {[set_test_counter errors]>0} break } really_finish_test |
Changes to test/softheap1.test.
︙ | ︙ | |||
41 42 43 44 45 46 47 | ROLLBACK; } execsql { PRAGMA integrity_check; } } {ok} | | < | 41 42 43 44 45 46 47 48 49 | ROLLBACK; } execsql { PRAGMA integrity_check; } } {ok} sqlite3_soft_heap_limit $cmdlinearg(soft-heap-limit) finish_test |
Added test/stat.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 | # 2010 July 09 # # The author disclaims copyright to this source code. In place of # a legal notice, here is a blessing: # # May you do good and not evil. # May you find forgiveness for yourself and forgive others. # May you share freely, never taking more than you give. # #*********************************************************************** # This file implements regression tests for SQLite library. The # focus of this file is testing the SELECT statement. # set testdir [file dirname $argv0] source $testdir/tester.tcl ifcapable !vtab { finish_test return } set ::asc 1 proc a_string {n} { string range [string repeat [incr ::asc]. $n] 1 $n } db func a_string a_string register_dbstat_vtab db do_execsql_test stat-0.0 { CREATE VIRTUAL TABLE temp.stat USING dbstat; SELECT * FROM stat; } {} do_execsql_test stat-0.1 { PRAGMA journal_mode = WAL; PRAGMA journal_mode = delete; SELECT * FROM stat; } {wal delete sqlite_master / 1 leaf 0 0 916 0} do_test stat-1.0 { execsql { CREATE TABLE t1(a, b); CREATE INDEX i1 ON t1(b); INSERT INTO t1(rowid, a, b) VALUES(2, 2, 3); INSERT INTO t1(rowid, a, b) VALUES(3, 4, 5); } } {} do_test stat-1.1 { execsql { SELECT * FROM stat WHERE name = 't1'; } } {t1 / 2 leaf 2 10 998 5} do_test stat-1.2 { execsql { SELECT * FROM stat WHERE name = 'i1'; } } {i1 / 3 leaf 2 10 1000 5} do_test stat-1.3 { execsql { SELECT * FROM stat WHERE name = 'sqlite_master'; } } {sqlite_master / 1 leaf 2 77 831 40} do_test stat-1.4 { execsql { DROP TABLE t1; } } {} do_execsql_test stat-2.1 { CREATE TABLE t3(a PRIMARY KEY, b); INSERT INTO t3(rowid, a, b) VALUES(2, a_string(111), a_string(222)); INSERT INTO t3 SELECT a_string(110+rowid), a_string(221+rowid) FROM t3; INSERT INTO t3 SELECT a_string(110+rowid), a_string(221+rowid) FROM t3; INSERT INTO t3 SELECT a_string(110+rowid), a_string(221+rowid) FROM t3; INSERT INTO t3 SELECT a_string(110+rowid), a_string(221+rowid) FROM t3; INSERT INTO t3 SELECT a_string(110+rowid), a_string(221+rowid) FROM t3; SELECT * FROM stat WHERE name != 'sqlite_master'; } [list \ sqlite_autoindex_t3_1 / 3 internal 3 368 623 125 \ sqlite_autoindex_t3_1 /000/ 8 leaf 8 946 46 123 \ sqlite_autoindex_t3_1 /001/ 9 leaf 8 988 2 131 \ sqlite_autoindex_t3_1 /002/ 15 leaf 7 857 137 132 \ sqlite_autoindex_t3_1 /003/ 20 leaf 6 739 257 129 \ t3 / 2 internal 15 0 907 0 \ t3 /000/ 4 leaf 2 678 328 340 \ t3 /001/ 5 leaf 2 682 324 342 \ t3 /002/ 6 leaf 2 682 324 342 \ t3 /003/ 7 leaf 2 690 316 346 \ t3 /004/ 10 leaf 2 682 324 342 \ t3 /005/ 11 leaf 2 690 316 346 \ t3 /006/ 12 leaf 2 698 308 350 \ t3 /007/ 13 leaf 2 706 300 354 \ t3 /008/ 14 leaf 2 682 324 342 \ t3 /009/ 16 leaf 2 690 316 346 \ t3 /00a/ 17 leaf 2 698 308 350 \ t3 /00b/ 18 leaf 2 706 300 354 \ t3 /00c/ 19 leaf 2 714 292 358 \ t3 /00d/ 21 leaf 2 722 284 362 \ t3 /00e/ 22 leaf 2 730 276 366 \ t3 /00f/ 23 leaf 2 738 268 370 \ ] do_execsql_test stat-2.2 { DROP TABLE t3 } {} do_execsql_test stat-3.1 { CREATE TABLE t4(x); CREATE INDEX i4 ON t4(x); INSERT INTO t4(rowid, x) VALUES(2, a_string(7777)); SELECT * FROM stat WHERE name != 'sqlite_master'; } [list \ i4 / 3 leaf 1 103 905 7782 \ i4 /000+000000 9 overflow 0 1020 0 0 \ i4 /000+000001 10 overflow 0 1020 0 0 \ i4 /000+000002 11 overflow 0 1020 0 0 \ i4 /000+000003 12 overflow 0 1020 0 0 \ i4 /000+000004 13 overflow 0 1020 0 0 \ i4 /000+000005 14 overflow 0 1020 0 0 \ i4 /000+000006 15 overflow 0 1020 0 0 \ i4 /000+000007 16 overflow 0 539 481 0 \ t4 / 2 leaf 1 640 367 7780 \ t4 /000+000000 22 overflow 0 1020 0 0 \ t4 /000+000001 23 overflow 0 1020 0 0 \ t4 /000+000002 21 overflow 0 1020 0 0 \ t4 /000+000003 20 overflow 0 1020 0 0 \ t4 /000+000004 19 overflow 0 1020 0 0 \ t4 /000+000005 18 overflow 0 1020 0 0 \ t4 /000+000006 17 overflow 0 1020 0 0 \ ] do_execsql_test stat-4.1 { CREATE TABLE t5(x); CREATE INDEX i5 ON t5(x); SELECT * FROM stat WHERE name = 't5' OR name = 'i5'; } [list \ i5 / 5 leaf 0 0 1016 0 \ t5 / 4 leaf 0 0 1016 0 \ ] db close file delete -force test.db sqlite3 db test.db register_dbstat_vtab db breakpoint do_execsql_test stat-5.1 { CREATE VIRTUAL TABLE temp.stat USING dbstat; CREATE TABLE t1(x); INSERT INTO t1 VALUES(zeroblob(1513)); INSERT INTO t1 VALUES(zeroblob(1514)); SELECT * FROM stat WHERE name = 't1'; } [list \ t1 / 2 leaf 2 993 5 1517 \ t1 /000+000000 3 overflow 0 1020 0 0 \ t1 /001+000000 4 overflow 0 1020 0 0 \ ] finish_test |
Changes to test/stmt.test.
︙ | ︙ | |||
46 47 48 49 50 51 52 | } set sqlite_open_file_count } {3} do_test stmt-1.5 { execsql COMMIT set sqlite_open_file_count } {1} | | > > > > | | | > > > > | | > > > | 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 | } set sqlite_open_file_count } {3} do_test stmt-1.5 { execsql COMMIT set sqlite_open_file_count } {1} do_test stmt-1.6.1 { execsql { BEGIN; INSERT INTO t1 SELECT a+2, b+2 FROM t1; } set sqlite_open_file_count } {2} do_test stmt-1.6.2 { execsql { INSERT INTO t1 SELECT a+4, b+4 FROM t1 } set sqlite_open_file_count } {3} do_test stmt-1.7 { execsql COMMIT set sqlite_open_file_count } {1} proc filecount {testname sql expected} { uplevel [list do_test $testname [subst -nocommand { execsql BEGIN execsql { $sql } set ret [set sqlite_open_file_count] execsql ROLLBACK set ret }] $expected] } filecount stmt-2.1 { INSERT INTO t1 VALUES(9, 9) } 2 filecount stmt-2.2 { REPLACE INTO t1 VALUES(9, 9) } 2 filecount stmt-2.3 { INSERT INTO t1 SELECT 9, 9 } 2 filecount stmt-2.4 { INSERT INTO t1 SELECT 9, 9; INSERT INTO t1 SELECT 10, 10; } 3 do_test stmt-2.5 { execsql { CREATE INDEX i1 ON t1(b) } } {} filecount stmt-2.6 { REPLACE INTO t1 VALUES(5, 5); REPLACE INTO t1 VALUES(5, 5); } 3 finish_test |
Changes to test/table.test.
︙ | ︙ | |||
600 601 602 603 604 605 606 607 608 609 610 611 612 613 | dt text DEFAULT CURRENT_DATE, dttm text DEFAULT CURRENT_TIMESTAMP ); SELECT * FROM tablet8; } } {} set i 0 foreach {date time seconds} { 1976-07-04 12:00:00 205329600 1994-04-16 14:00:00 766504800 2000-01-01 00:00:00 946684800 2003-12-31 12:34:56 1072874096 } { incr i | > | 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 | dt text DEFAULT CURRENT_DATE, dttm text DEFAULT CURRENT_TIMESTAMP ); SELECT * FROM tablet8; } } {} set i 0 unset -nocomplain date time seconds foreach {date time seconds} { 1976-07-04 12:00:00 205329600 1994-04-16 14:00:00 766504800 2000-01-01 00:00:00 946684800 2003-12-31 12:34:56 1072874096 } { incr i |
︙ | ︙ |
Changes to test/tclsqlite.test.
︙ | ︙ | |||
21 22 23 24 25 26 27 | source $testdir/tester.tcl # Check the error messages generated by tclsqlite # if {[sqlite3 -has-codec]} { set r "sqlite_orig HANDLE FILENAME ?-key CODEC-KEY?" } else { | | | | 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 | source $testdir/tester.tcl # Check the error messages generated by tclsqlite # if {[sqlite3 -has-codec]} { set r "sqlite_orig HANDLE FILENAME ?-key CODEC-KEY?" } else { set r "sqlite_orig HANDLE FILENAME ?-vfs VFSNAME? ?-readonly BOOLEAN? ?-create BOOLEAN? ?-nomutex BOOLEAN? ?-fullmutex BOOLEAN?" } do_test tcl-1.1 { set v [catch {sqlite3 bogus} msg] regsub {really_sqlite3} $msg {sqlite3} msg lappend v $msg } [list 1 "wrong # args: should be \"$r\""] do_test tcl-1.2 { set v [catch {db bogus} msg] lappend v $msg } {1 {bad option "bogus": must be authorizer, backup, busy, cache, changes, close, collate, collation_needed, commit_hook, complete, copy, enable_load_extension, errorcode, eval, exists, function, incrblob, interrupt, last_insert_rowid, nullvalue, onecolumn, profile, progress, rekey, restore, rollback_hook, status, timeout, total_changes, trace, transaction, unlock_notify, update_hook, version, or wal_hook}} do_test tcl-1.2.1 { set v [catch {db cache bogus} msg] lappend v $msg } {1 {bad option "bogus": must be flush or size}} do_test tcl-1.2.2 { set v [catch {db cache} msg] lappend v $msg |
︙ | ︙ |
Changes to test/tempdb.test.
︙ | ︙ | |||
54 55 56 57 58 59 60 | do_test tempdb-2.1 { # Set $::jrnl_in_memory if the journal file is expected to be in-memory. # Similarly, set $::subj_in_memory if the sub-journal file is expected # to be in memory. These variables are used to calculate the expected # number of open files in the test cases below. # | | < < < > | 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 | do_test tempdb-2.1 { # Set $::jrnl_in_memory if the journal file is expected to be in-memory. # Similarly, set $::subj_in_memory if the sub-journal file is expected # to be in memory. These variables are used to calculate the expected # number of open files in the test cases below. # set jrnl_in_memory [expr {[permutation] eq "inmemory_journal"}] set subj_in_memory [expr {$jrnl_in_memory || $TEMP_STORE == 3}] db close sqlite3 db test.db } {} do_test tempdb-2.2 { execsql { CREATE TABLE t1 (a PRIMARY KEY, b, c); CREATE TABLE t2 (a, b, c); BEGIN; INSERT INTO t1 VALUES(1, 2, 3); INSERT INTO t1 VALUES(4, 5, 6); INSERT INTO t2 VALUES(7, 8, 9); INSERT INTO t2 SELECT * FROM t1; } catchsql { INSERT INTO t1 SELECT * FROM t2 } set sqlite_open_file_count } [expr 1 + (0==$jrnl_in_memory) + (0==$subj_in_memory)] do_test tempdb-2.3 { execsql { |
︙ | ︙ |
Changes to test/tester.tcl.
︙ | ︙ | |||
9 10 11 12 13 14 15 16 | # #*********************************************************************** # This file implements some common TCL routines used for regression # testing the SQLite library # # $Id: tester.tcl,v 1.143 2009/04/09 01:23:49 drh Exp $ # | > > > < > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > > > > > > > > > | < < < > > > | < > > > > > > > > > | < > > > > > | | < < < < < < < < < < < < < < < < < < | < < < < < < < < < < < < < < < < < < < < < | | < < < < | < < < < < < < < < < < < < < < < < < < < | | < < < < < < < < < < < | | < < | | > > > > > > | | > > > > > > > > > > | | | | > > > > > > | > > > > > > > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > | > | > > > > > > > > > > > > > | | > > > | | | | | > | > > | > | > > > > > > > > > > > > > > > > > > > > > > > > | < < < | < | < | | | | | | | | | > > > | > > | | < | < | < < | < > | < > > | > > > | 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 | # #*********************************************************************** # This file implements some common TCL routines used for regression # testing the SQLite library # # $Id: tester.tcl,v 1.143 2009/04/09 01:23:49 drh Exp $ #------------------------------------------------------------------------- # The commands provided by the code in this file to help with creating # test cases are as follows: # # Commands to manipulate the db and the file-system at a high level: # # copy_file FROM TO # drop_all_table ?DB? # forcedelete FILENAME # # Test the capability of the SQLite version built into the interpreter to # determine if a specific test can be run: # # ifcapable EXPR # # Calulate checksums based on database contents: # # dbcksum DB DBNAME # allcksum ?DB? # cksum ?DB? # # Commands to execute/explain SQL statements: # # stepsql DB SQL # execsql2 SQL # explain_no_trace SQL # explain SQL ?DB? # catchsql SQL ?DB? # execsql SQL ?DB? # # Commands to run test cases: # # do_ioerr_test TESTNAME ARGS... # crashsql ARGS... # integrity_check TESTNAME ?DB? # do_test TESTNAME SCRIPT EXPECTED # do_execsql_test TESTNAME SQL EXPECTED # do_catchsql_test TESTNAME SQL EXPECTED # # Commands providing a lower level interface to the global test counters: # # set_test_counter COUNTER ?VALUE? # omit_test TESTNAME REASON # fail_test TESTNAME # incr_ntest # # Command run at the end of each test file: # # finish_test # # Commands to help create test files that run with the "WAL" and other # permutations (see file permutations.test): # # wal_is_wal_mode # wal_set_journal_mode ?DB? # wal_check_journal_mode TESTNAME?DB? # permutation # # Set the precision of FP arithmatic used by the interpreter. And # configure SQLite to take database file locks on the page that begins # 64KB into the database file instead of the one 1GB in. This means # the code that handles that special case can be tested without creating # very large database files. # set tcl_precision 15 sqlite3_test_control_pending_byte 0x0010000 # If the pager codec is available, create a wrapper for the [sqlite3] # command that appends "-key {xyzzy}" to the command line. i.e. this: # # sqlite3 db test.db # # becomes # # sqlite3 db test.db -key {xyzzy} # if {[info command sqlite_orig]==""} { rename sqlite3 sqlite_orig proc sqlite3 {args} { if {[llength $args]>=2 && [string index [lindex $args 0] 0]!="-"} { # This command is opening a new database connection. # if {[info exists ::G(perm:sqlite3_args)]} { set args [concat $args $::G(perm:sqlite3_args)] } if {[sqlite_orig -has-codec] && ![info exists ::do_not_use_codec]} { lappend args -key {xyzzy} } set res [uplevel 1 sqlite_orig $args] if {[info exists ::G(perm:presql)]} { [lindex $args 0] eval $::G(perm:presql) } set res } else { # This command is not opening a new database connection. Pass the # arguments through to the C implemenation as the are. # uplevel 1 sqlite_orig $args } } } proc execpresql {handle args} { trace remove execution $handle enter [list execpresql $handle] if {[info exists ::G(perm:presql)]} { $handle eval $::G(perm:presql) } } # This command should be called after loading tester.tcl from within # all test scripts that are incompatible with encryption codecs. # proc do_not_use_codec {} { set ::do_not_use_codec 1 reset_db } # The following block only runs the first time this file is sourced. It # does not run in slave interpreters (since the ::cmdlinearg array is # populated before the test script is run in slave interpreters). # if {[info exists cmdlinearg]==0} { # Parse any options specified in the $argv array. This script accepts the # following options: # # --pause # --soft-heap-limit=NN # --maxerror=NN # --malloctrace=N # --backtrace=N # --binarylog=N # --soak=N # set cmdlinearg(soft-heap-limit) 0 set cmdlinearg(maxerror) 1000 set cmdlinearg(malloctrace) 0 set cmdlinearg(backtrace) 10 set cmdlinearg(binarylog) 0 set cmdlinearg(soak) 0 set leftover [list] foreach a $argv { switch -regexp -- $a { {^-+pause$} { # Wait for user input before continuing. This is to give the user an # opportunity to connect profiling tools to the process. puts -nonewline "Press RETURN to begin..." flush stdout gets stdin } {^-+soft-heap-limit=.+$} { foreach {dummy cmdlinearg(soft-heap-limit)} [split $a =] break } {^-+maxerror=.+$} { foreach {dummy cmdlinearg(maxerror)} [split $a =] break } {^-+malloctrace=.+$} { foreach {dummy cmdlinearg(malloctrace)} [split $a =] break if {$cmdlinearg(malloctrace)} { sqlite3_memdebug_log start } } {^-+backtrace=.+$} { foreach {dummy cmdlinearg(backtrace)} [split $a =] break sqlite3_memdebug_backtrace $value } {^-+binarylog=.+$} { foreach {dummy cmdlinearg(binarylog)} [split $a =] break } {^-+soak=.+$} { foreach {dummy cmdlinearg(soak)} [split $a =] break set ::G(issoak) $cmdlinearg(soak) } default { lappend leftover $a } } } set argv $leftover # Install the malloc layer used to inject OOM errors. And the 'automatic' # extensions. This only needs to be done once for the process. # sqlite3_shutdown install_malloc_faultsim 1 sqlite3_initialize autoinstall_test_functions # If the --binarylog option was specified, create the logging VFS. This # call installs the new VFS as the default for all SQLite connections. # if {$cmdlinearg(binarylog)} { vfslog new binarylog {} vfslog.bin } # Set the backtrace depth, if malloc tracing is enabled. # if {$cmdlinearg(malloctrace)} { sqlite3_memdebug_backtrace $cmdlinearg(backtrace) } } # Update the soft-heap-limit each time this script is run. In that # way if an individual test file changes the soft-heap-limit, it # will be reset at the start of the next test file. # sqlite3_soft_heap_limit $cmdlinearg(soft-heap-limit) # Create a test database # proc reset_db {} { catch {db close} file delete -force test.db file delete -force test.db-journal file delete -force test.db-wal sqlite3 db ./test.db set ::DB [sqlite3_connection_pointer db] if {[info exists ::SETUP_SQL]} { db eval $::SETUP_SQL } } reset_db # Abort early if this script has been run before. # if {[info exists TC(count)]} return # Initialize the test counters and set up commands to access them. # Or, if this is a slave interpreter, set up aliases to write the # counters in the parent interpreter. # if {0==[info exists ::SLAVE]} { set TC(errors) 0 set TC(count) 0 set TC(fail_list) [list] set TC(omit_list) [list] proc set_test_counter {counter args} { if {[llength $args]} { set ::TC($counter) [lindex $args 0] } set ::TC($counter) } } # Record the fact that a sequence of tests were omitted. # proc omit_test {name reason} { set omitList [set_test_counter omit_list] lappend omitList [list $name $reason] set_test_counter omit_list $omitList } # Record the fact that a test failed. # proc fail_test {name} { set f [set_test_counter fail_list] lappend f $name set_test_counter fail_list $f set_test_counter errors [expr [set_test_counter errors] + 1] set nFail [set_test_counter errors] if {$nFail>=$::cmdlinearg(maxerror)} { puts "*** Giving up..." finalize_testing } } # Increment the number of tests run # proc incr_ntest {} { set_test_counter count [expr [set_test_counter count] + 1] } # Invoke the do_test procedure to run a single test # proc do_test {name cmd expected} { global argv cmdlinearg sqlite3_memdebug_settitle $name # if {[llength $argv]==0} { # set go 1 # } else { # set go 0 # foreach pattern $argv { # if {[string match $pattern $name]} { # set go 1 # break # } # } # } if {[info exists ::G(perm:prefix)]} { set name "$::G(perm:prefix)$name" } incr_ntest puts -nonewline $name... flush stdout if {[catch {uplevel #0 "$cmd;\n"} result]} { puts "\nError: $result" fail_test $name } elseif {[string compare $result $expected]} { puts "\nExpected: \[$expected\]\n Got: \[$result\]" fail_test $name } else { puts " Ok" } flush stdout } proc do_execsql_test {testname sql result} { uplevel do_test $testname [list "execsql {$sql}"] [list $result] } proc do_catchsql_test {testname sql result} { uplevel do_test $testname [list "catchsql {$sql}"] [list $result] } # Run an SQL script. # Return the number of microseconds per statement. # proc speed_trial {name numstmt units sql} { puts -nonewline [format {%-21.21s } $name...] flush stdout |
︙ | ︙ | |||
251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 | puts [format {%12d uS %s %s} $tm $rate $u2] global total_time set total_time [expr {$total_time+$tm}] } proc speed_trial_init {name} { global total_time set total_time 0 } proc speed_trial_summary {name} { global total_time puts [format {%-21.21s %12d uS TOTAL} $name $total_time] } # Run this routine last # proc finish_test {} { | > > > > > > > | | > > < < < < < < | | > > | | | < < < < < < < < < < < < < < | | 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 | puts [format {%12d uS %s %s} $tm $rate $u2] global total_time set total_time [expr {$total_time+$tm}] } proc speed_trial_init {name} { global total_time set total_time 0 sqlite3 versdb :memory: set vers [versdb one {SELECT sqlite_source_id()}] versdb close puts "SQLite $vers" } proc speed_trial_summary {name} { global total_time puts [format {%-21.21s %12d uS TOTAL} $name $total_time] } # Run this routine last # proc finish_test {} { catch {db close} catch {db2 close} catch {db3 close} if {0==[info exists ::SLAVE]} { finalize_testing } } proc finalize_testing {} { global sqlite_open_file_count set omitList [set_test_counter omit_list] catch {db close} catch {db2 close} catch {db3 close} vfs_unlink_test sqlite3 db {} # sqlite3_clear_tsd_memdebug db close sqlite3_reset_auto_extension sqlite3_soft_heap_limit 0 set nTest [incr_ntest] set nErr [set_test_counter errors] puts "$nErr errors out of $nTest tests" if {$nErr>0} { puts "Failures on these tests: [set_test_counter fail_list]" } run_thread_tests 1 if {[llength $omitList]>0} { puts "Omitted test cases:" set prec {} foreach {rec} [lsort $omitList] { if {$rec==$prec} continue set prec $rec puts [format { %-12s %s} [lindex $rec 0] [lindex $rec 1]] } } if {$nErr>0 && ![working_64bit_int]} { puts "******************************************************************" puts "N.B.: The version of TCL that you used to build this test harness" puts "is defective in that it does not support 64-bit integers. Some or" puts "all of the test failures above might be a result from this defect" puts "in your TCL build." puts "******************************************************************" } if {$::cmdlinearg(binarylog)} { vfslog finalize binarylog } if {$sqlite_open_file_count} { puts "$sqlite_open_file_count files were left open" incr nErr } if {[sqlite3_memory_used]>0} { puts "Unfreed memory: [sqlite3_memory_used] bytes" incr nErr ifcapable memdebug||mem5||(mem3&&debug) { puts "Writing unfreed memory log to \"./memleak.txt\"" sqlite3_memdebug_dump ./memleak.txt } } else { puts "All memory allocations freed - no leaks" ifcapable memdebug||mem5 { sqlite3_memdebug_dump ./memusage.txt } } show_memstats puts "Maximum memory usage: [sqlite3_memory_highwater 1] bytes" puts "Current memory usage: [sqlite3_memory_highwater] bytes" if {[info commands sqlite3_memdebug_malloc_count] ne ""} { puts "Number of malloc() : [sqlite3_memdebug_malloc_count] calls" } if {$::cmdlinearg(malloctrace)} { puts "Writing mallocs.sql..." memdebug_log_sql sqlite3_memdebug_log stop sqlite3_memdebug_log clear if {[sqlite3_memory_used]>0} { puts "Writing leaks.sql..." |
︙ | ︙ | |||
543 544 545 546 547 548 549 | # reported some other error. The second element in the returned list is the # error message. This is "child process exited abnormally" if the crash # occured. # # crashsql -delay CRASHDELAY -file CRASHFILE ?-blocksize BLOCKSIZE? $sql # proc crashsql {args} { | < < < | 654 655 656 657 658 659 660 661 662 663 664 665 666 667 | # reported some other error. The second element in the returned list is the # error message. This is "child process exited abnormally" if the crash # occured. # # crashsql -delay CRASHDELAY -file CRASHFILE ?-blocksize BLOCKSIZE? $sql # proc crashsql {args} { set blocksize "" set crashdelay 1 set prngseed 0 set tclbody {} set crashfile "" set dc "" |
︙ | ︙ | |||
573 574 575 576 577 578 579 | else { error "Unrecognized option: $z" } } if {$crashfile eq ""} { error "Compulsory option -file missing" } | > > > | | 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 | else { error "Unrecognized option: $z" } } if {$crashfile eq ""} { error "Compulsory option -file missing" } # $crashfile gets compared to the native filename in # cfSync(), which can be different then what TCL uses by # default, so here we force it to the "nativename" format. set cfile [string map {\\ \\\\} [file nativename [file join [pwd] $crashfile]]] set f [open crash.tcl w] puts $f "sqlite3_crash_enable 1" puts $f "sqlite3_crashparams $blocksize $dc $crashdelay $cfile" puts $f "sqlite3_test_control_pending_byte $::sqlite_pending_byte" puts $f "sqlite3 db test.db -vfs crash" |
︙ | ︙ | |||
602 603 604 605 606 607 608 | } if {[string length $sql]>0} { puts $f "db eval {" puts $f "$sql" puts $f "}" } close $f | < > > > > > > > > > > > | 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 | } if {[string length $sql]>0} { puts $f "db eval {" puts $f "$sql" puts $f "}" } close $f set r [catch { exec [info nameofexec] crash.tcl >@stdout } msg] # Windows/ActiveState TCL returns a slightly different # error message. We map that to the expected message # so that we don't have to change all of the test # cases. if {$::tcl_platform(platform)=="windows"} { if {$msg=="child killed: unknown signal"} { set msg "child process exited abnormally" } } lappend r $msg } # Usage: do_ioerr_test <test number> <options...> # # This proc is used to implement test cases that check that IO errors # are correctly handled. The first argument, <test number>, is an integer |
︙ | ︙ | |||
748 749 750 751 752 753 754 755 756 757 758 759 760 761 | } set ::sqlite_io_error_hit 0 # One of two things must have happened. either # 1. We never hit the IO error and the SQL returned OK # 2. An IO error was hit and the SQL failed # expr { ($s && !$r && !$q) || (!$s && $r && $q) } } {1} set ::sqlite_io_error_hit 0 set ::sqlite_io_error_pending 0 # Check that no page references were leaked. There should be | > | 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 | } set ::sqlite_io_error_hit 0 # One of two things must have happened. either # 1. We never hit the IO error and the SQL returned OK # 2. An IO error was hit and the SQL failed # #puts "s=$s r=$r q=$q" expr { ($s && !$r && !$q) || (!$s && $r && $q) } } {1} set ::sqlite_io_error_hit 0 set ::sqlite_io_error_pending 0 # Check that no page references were leaked. There should be |
︙ | ︙ | |||
982 983 984 985 986 987 988 | } } ifcapable trigger&&foreignkey { $db eval "PRAGMA foreign_keys = $pk" } } | > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | } } ifcapable trigger&&foreignkey { $db eval "PRAGMA foreign_keys = $pk" } } #------------------------------------------------------------------------- # If a test script is executed with global variable $::G(perm:name) set to # "wal", then the tests are run in WAL mode. Otherwise, they should be run # in rollback mode. The following Tcl procs are used to make this less # intrusive: # # wal_set_journal_mode ?DB? # # If running a WAL test, execute "PRAGMA journal_mode = wal" using # connection handle DB. Otherwise, this command is a no-op. # # wal_check_journal_mode TESTNAME ?DB? # # If running a WAL test, execute a tests case that fails if the main # database for connection handle DB is not currently a WAL database. # Otherwise (if not running a WAL permutation) this is a no-op. # # wal_is_wal_mode # # Returns true if this test should be run in WAL mode. False otherwise. # proc wal_is_wal_mode {} { expr {[permutation] eq "wal"} } proc wal_set_journal_mode {{db db}} { if { [wal_is_wal_mode] } { $db eval "PRAGMA journal_mode = WAL" } } proc wal_check_journal_mode {testname {db db}} { if { [wal_is_wal_mode] } { $db eval { SELECT * FROM sqlite_master } do_test $testname [list $db eval "PRAGMA main.journal_mode"] {wal} } } proc permutation {} { set perm "" catch {set perm $::G(perm:name)} set perm } #------------------------------------------------------------------------- # proc slave_test_script {script} { # Create the interpreter used to run the test script. interp create tinterp # Populate some global variables that tester.tcl expects to see. foreach {var value} [list \ ::argv0 $::argv0 \ ::argv {} \ ::SLAVE 1 \ ] { interp eval tinterp [list set $var $value] } # The alias used to access the global test counters. tinterp alias set_test_counter set_test_counter # Set up the ::cmdlinearg array in the slave. interp eval tinterp [list array set ::cmdlinearg [array get ::cmdlinearg]] # Set up the ::G array in the slave. interp eval tinterp [list array set ::G [array get ::G]] # Load the various test interfaces implemented in C. load_testfixture_extensions tinterp # Run the test script. interp eval tinterp $script # Check if the interpreter call [run_thread_tests] if { [interp eval tinterp {info exists ::run_thread_tests_called}] } { set ::run_thread_tests_called 1 } # Delete the interpreter used to run the test script. interp delete tinterp } proc slave_test_file {zFile} { set tail [file tail $zFile] # Remember the value of the shared-cache setting. So that it is possible # to check afterwards that it was not modified by the test script. # ifcapable shared_cache { set scs [sqlite3_enable_shared_cache] } # Run the test script in a slave interpreter. # unset -nocomplain ::run_thread_tests_called reset_prng_state set ::sqlite_open_file_count 0 set time [time { slave_test_script [list source $zFile] }] set ms [expr [lindex $time 0] / 1000] # Test that all files opened by the test script were closed. Omit this # if the test script has "thread" in its name. The open file counter # is not thread-safe. # if {[info exists ::run_thread_tests_called]==0} { do_test ${tail}-closeallfiles { expr {$::sqlite_open_file_count>0} } {0} } set ::sqlite_open_file_count 0 # Test that the global "shared-cache" setting was not altered by # the test script. # ifcapable shared_cache { set res [expr {[sqlite3_enable_shared_cache] == $scs}] do_test ${tail}-sharedcachesetting [list set {} $res] 1 } # Add some info to the output. # puts "Time: $tail $ms ms" show_memstats } # Open a new connection on database test.db and execute the SQL script # supplied as an argument. Before returning, close the new conection and # restore the 4 byte fields starting at header offsets 28, 92 and 96 # to the values they held before the SQL was executed. This simulates # a write by a pre-3.7.0 client. # proc sql36231 {sql} { set B [hexio_read test.db 92 8] set A [hexio_read test.db 28 4] sqlite3 db36231 test.db catch { db36231 func a_string a_string } execsql $sql db36231 db36231 close hexio_write test.db 28 $A hexio_write test.db 92 $B return "" } # If the library is compiled with the SQLITE_DEFAULT_AUTOVACUUM macro set # to non-zero, then set the global variable $AUTOVACUUM to 1. set AUTOVACUUM $sqlite_options(default_autovacuum) source $testdir/thread_common.tcl |
Changes to test/thread2.test.
︙ | ︙ | |||
25 26 27 28 29 30 31 | # Skip this whole file if the thread testing code is not enabled # if {[llength [info command thread_step]]==0 || [sqlite3 -has-codec]} { finish_test return } | < < < < | 25 26 27 28 29 30 31 32 33 34 35 36 37 38 | # Skip this whole file if the thread testing code is not enabled # if {[llength [info command thread_step]]==0 || [sqlite3 -has-codec]} { finish_test return } # Create some data to work with # do_test thread1-1.1 { execsql { CREATE TABLE t1(a,b); INSERT INTO t1 VALUES(1,'abcdefgh'); |
︙ | ︙ | |||
117 118 119 120 121 122 123 | do_test thread2-2.9 { thread_finalize B thread_result B } {SQLITE_OK} thread_halt A thread_halt B | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | 113 114 115 116 117 118 119 120 121 122 123 124 | do_test thread2-2.9 { thread_finalize B thread_result B } {SQLITE_OK} thread_halt A thread_halt B # Also important to halt the worker threads, which are using spin # locks and eating away CPU cycles. # thread_halt * finish_test |
Changes to test/thread_common.tcl.
︙ | ︙ | |||
76 77 78 79 80 81 82 | if {$res ne $result} { error "$name failed: expected \"$result\" got \"$res\"" } } } proc thread_spawn {varname args} { | | < < | < < < < < > | 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 | if {$res ne $result} { error "$name failed: expected \"$result\" got \"$res\"" } } } proc thread_spawn {varname args} { sqlthread spawn $varname [join $args {;}] } # Return true if this build can run the multi-threaded tests. # proc run_thread_tests {{print_warning 0}} { ifcapable !mutex { set zProblem "SQLite build is not threadsafe" } if {[info commands sqlthread] eq ""} { set zProblem "SQLite build is not threadsafe" } if {![info exists ::tcl_platform(threaded)]} { set zProblem "Linked against a non-threadsafe Tcl build" } if {[info exists zProblem]} { puts "WARNING: Multi-threaded tests skipped: $zProblem" return 0 } set ::run_thread_tests_called 1 return 1; } return 0 |
Added test/tkt-02a8e81d44.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 | # 2010 April 15 # # The author disclaims copyright to this source code. In place of # a legal notice, here is a blessing: # # May you do good and not evil. # May you find forgiveness for yourself and forgive others. # May you share freely, never taking more than you give. # #*********************************************************************** # This file implements regression tests for SQLite library. # # This file implements tests to verify that ticket [02a8e81d44] has been # fixed. # set testdir [file dirname $argv0] source $testdir/tester.tcl do_test tkt-02a838-1.1 { execsql { CREATE TABLE t1(a); INSERT INTO t1 VALUES(1); INSERT INTO t1 VALUES(2); INSERT INTO t1 VALUES(4); INSERT INTO t1 VALUES(5); SELECT * FROM (SELECT a FROM t1 LIMIT 1) UNION ALL SELECT 3; } } {1 3} finish_test |
Added test/tkt-26ff0c2d1e.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 | # 2010 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 testing a bug found in the OP_Variable optimizer # set testdir [file dirname $argv0] source $testdir/tester.tcl do_test bug-20100512-1 { set DB [sqlite3_connection_pointer db] set SQL {SELECT case when 1 then 99 else ? end + ?} set STMT [sqlite3_prepare_v2 $DB $SQL -1 TAIL] set TAIL } {} do_test bug-20100512-2 { sqlite3_bind_parameter_count $STMT } 2 do_test bug-20100512-3 { sqlite3_bind_int $STMT 1 123 sqlite3_bind_int $STMT 2 456 sqlite3_step $STMT sqlite3_column_int $STMT 0 } {555} sqlite3_finalize $STMT |
Added test/tkt-80e031a00f.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 | # 2010 July 14 # # The author disclaims copyright to this source code. In place of # a legal notice, here is a blessing: # # May you do good and not evil. # May you find forgiveness for yourself and forgive others. # May you share freely, never taking more than you give. # #*********************************************************************** # This file implements regression tests for SQLite library. Specifically, # it tests that ticket [80e031a00f45dca877ed92b225209cfa09280f4f] has been # resolved. That ticket is about IN and NOT IN operators with empty-set # right-hand sides. Such expressions should always return TRUE or FALSE # even if the left-hand side is NULL. # set testdir [file dirname $argv0] source $testdir/tester.tcl source $testdir/lock_common.tcl source $testdir/malloc_common.tcl # EVIDENCE-OF: R-58875-56087 The IN and NOT IN operators take a single # scalar operand on the left and a vector operand on the right formed by # an explicit list of zero or more scalars or by a single subquery. # # EVIDENCE-OF: R-52275-55503 When the right operand is an empty set, the # result of IN is false and the result of NOT IN is true, regardless of # the left operand and even if the left operand is NULL. # # EVIDENCE-OF: R-13595-45863 Note that SQLite allows the parenthesized # list of scalar values on the right-hand side of an IN or NOT IN # operator to be an empty list but most other SQL database database # engines and the SQL92 standard require the list to contain at least # one element. # do_execsql_test tkt-80e031a00f.1 {SELECT 1 IN ()} 0 do_execsql_test tkt-80e031a00f.1b {SELECT 1 IN (2)} 0 do_execsql_test tkt-80e031a00f.1c {SELECT 1 IN (2,3,4,5,6,7,8,9)} 0 do_execsql_test tkt-80e031a00f.2 {SELECT 1 NOT IN ()} 1 do_execsql_test tkt-80e031a00f.2b {SELECT 1 NOT IN (2)} 1 do_execsql_test tkt-80e031a00f.2c {SELECT 1 NOT IN (2,3,4,5,6,7,8,9)} 1 do_execsql_test tkt-80e031a00f.3 {SELECT null IN ()} 0 do_execsql_test tkt-80e031a00f.4 {SELECT null NOT IN ()} 1 do_execsql_test tkt-80e031a00f.5 { CREATE TABLE t1(x); SELECT 1 IN t1; } 0 do_execsql_test tkt-80e031a00f.6 {SELECT 1 NOT IN t1} 1 do_execsql_test tkt-80e031a00f.7 {SELECT null IN t1} 0 do_execsql_test tkt-80e031a00f.8 {SELECT null NOT IN t1} 1 do_execsql_test tkt-80e031a00f.9 { CREATE TABLE t2(y INTEGER PRIMARY KEY); SELECT 1 IN t2; } 0 do_execsql_test tkt-80e031a00f.10 {SELECT 1 NOT IN t2} 1 do_execsql_test tkt-80e031a00f.11 {SELECT null IN t2} 0 do_execsql_test tkt-80e031a00f.12 {SELECT null NOT IN t2} 1 do_execsql_test tkt-80e031a00f.13 { CREATE TABLE t3(z INT UNIQUE); SELECT 1 IN t3; } 0 do_execsql_test tkt-80e031a00f.14 {SELECT 1 NOT IN t3} 1 do_execsql_test tkt-80e031a00f.15 {SELECT null IN t3} 0 do_execsql_test tkt-80e031a00f.16 {SELECT null NOT IN t3} 1 do_execsql_test tkt-80e031a00f.17 {SELECT 1 IN (SELECT x+y FROM t1, t2)} 0 do_execsql_test tkt-80e031a00f.18 {SELECT 1 NOT IN (SELECT x+y FROM t1,t2)} 1 do_execsql_test tkt-80e031a00f.19 {SELECT null IN (SELECT x+y FROM t1,t2)} 0 do_execsql_test tkt-80e031a00f.20 {SELECT null NOT IN (SELECT x+y FROM t1,t2)} 1 do_execsql_test tkt-80e031a00f.21 {SELECT 1.23 IN ()} 0 do_execsql_test tkt-80e031a00f.22 {SELECT 1.23 NOT IN ()} 1 do_execsql_test tkt-80e031a00f.23 {SELECT 1.23 IN t1} 0 do_execsql_test tkt-80e031a00f.24 {SELECT 1.23 NOT IN t1} 1 do_execsql_test tkt-80e031a00f.25 {SELECT 'hello' IN ()} 0 do_execsql_test tkt-80e031a00f.26 {SELECT 'hello' NOT IN ()} 1 do_execsql_test tkt-80e031a00f.27 {SELECT 'hello' IN t1} 0 do_execsql_test tkt-80e031a00f.28 {SELECT 'hello' NOT IN t1} 1 do_execsql_test tkt-80e031a00f.29 {SELECT x'303132' IN ()} 0 do_execsql_test tkt-80e031a00f.30 {SELECT x'303132' NOT IN ()} 1 do_execsql_test tkt-80e031a00f.31 {SELECT x'303132' IN t1} 0 do_execsql_test tkt-80e031a00f.32 {SELECT x'303132' NOT IN t1} 1 # EVIDENCE-OF: R-50221-42915 The result of an IN or NOT IN operator is # determined by the following matrix: Left operand is NULL Right operand # contains NULL Right operand is an empty set Left operand found within # right operand Result of IN operator Result of NOT IN operator no no no # no false true does not matter no yes no false true no does not matter # no yes true false no yes no no NULL NULL yes does not matter no does # not matter NULL NULL # # Row 1: do_execsql_test tkt-80e031a00f.100 {SELECT 1 IN (2,3,4)} 0 do_execsql_test tkt-80e031a00f.101 {SELECT 1 NOT IN (2,3,4)} 1 do_execsql_test tkt-80e031a00f.102 {SELECT 'a' IN ('b','c','d')} 0 do_execsql_test tkt-80e031a00f.103 {SELECT 'a' NOT IN ('b','c','d')} 1 do_test tkt-80e031a00f.104 { db eval { CREATE TABLE t4(a UNIQUE); CREATE TABLE t5(b INTEGER PRIMARY KEY); CREATE TABLE t6(c); INSERT INTO t4 VALUES(2); INSERT INTO t4 VALUES(3); INSERT INTO t4 VALUES(4); INSERT INTO t5 SELECT * FROM t4; INSERT INTO t6 SELECT * FROM t4; CREATE TABLE t4n(a UNIQUE); CREATE TABLE t6n(c); INSERT INTO t4n SELECT * FROM t4; INSERT INTO t4n VALUES(null); INSERT INTO t6n SELECT * FROM t4n; CREATE TABLE t7(a UNIQUE); CREATE TABLE t8(c); INSERT INTO t7 VALUES('b'); INSERT INTO t7 VALUES('c'); INSERT INTO t7 VALUES('d'); INSERT INTO t8 SELECT * FROM t7; CREATE TABLE t7n(a UNIQUE); CREATE TABLE t8n(c); INSERT INTO t7n SELECT * FROM t7; INSERT INTO t7n VALUES(null); INSERT INTO t8n SELECT * FROM t7n; } execsql {SELECT 1 IN t4} } 0 do_execsql_test tkt-80e031a00f.105 {SELECT 1 NOT IN t4} 1 do_execsql_test tkt-80e031a00f.106 {SELECT 1 IN t5} 0 do_execsql_test tkt-80e031a00f.107 {SELECT 1 NOT IN t5} 1 do_execsql_test tkt-80e031a00f.108 {SELECT 1 IN t6} 0 do_execsql_test tkt-80e031a00f.109 {SELECT 1 NOT IN t6} 1 do_execsql_test tkt-80e031a00f.110 {SELECT 'a' IN t7} 0 do_execsql_test tkt-80e031a00f.111 {SELECT 'a' NOT IN t7} 1 do_execsql_test tkt-80e031a00f.112 {SELECT 'a' IN t8} 0 do_execsql_test tkt-80e031a00f.113 {SELECT 'a' NOT IN t8} 1 # # Row 2 is tested by cases 1-32 above. # Row 3: do_execsql_test tkt-80e031a00f.300 {SELECT 2 IN (2,3,4,null)} 1 do_execsql_test tkt-80e031a00f.301 {SELECT 3 NOT IN (2,3,4,null)} 0 do_execsql_test tkt-80e031a00f.302 {SELECT 4 IN (2,3,4)} 1 do_execsql_test tkt-80e031a00f.303 {SELECT 2 NOT IN (2,3,4)} 0 do_execsql_test tkt-80e031a00f.304 {SELECT 'b' IN ('b','c','d')} 1 do_execsql_test tkt-80e031a00f.305 {SELECT 'c' NOT IN ('b','c','d')} 0 do_execsql_test tkt-80e031a00f.306 {SELECT 'd' IN ('b','c',null,'d')} 1 do_execsql_test tkt-80e031a00f.307 {SELECT 'b' NOT IN (null,'b','c','d')} 0 do_execsql_test tkt-80e031a00f.308 {SELECT 2 IN t4} 1 do_execsql_test tkt-80e031a00f.309 {SELECT 3 NOT IN t4} 0 do_execsql_test tkt-80e031a00f.310 {SELECT 4 IN t4n} 1 do_execsql_test tkt-80e031a00f.311 {SELECT 2 NOT IN t4n} 0 do_execsql_test tkt-80e031a00f.312 {SELECT 2 IN t5} 1 do_execsql_test tkt-80e031a00f.313 {SELECT 3 NOT IN t5} 0 do_execsql_test tkt-80e031a00f.314 {SELECT 2 IN t6} 1 do_execsql_test tkt-80e031a00f.315 {SELECT 3 NOT IN t6} 0 do_execsql_test tkt-80e031a00f.316 {SELECT 4 IN t6n} 1 do_execsql_test tkt-80e031a00f.317 {SELECT 2 NOT IN t6n} 0 do_execsql_test tkt-80e031a00f.318 {SELECT 'b' IN t7} 1 do_execsql_test tkt-80e031a00f.319 {SELECT 'c' NOT IN t7} 0 do_execsql_test tkt-80e031a00f.320 {SELECT 'c' IN t7n} 1 do_execsql_test tkt-80e031a00f.321 {SELECT 'd' NOT IN t7n} 0 do_execsql_test tkt-80e031a00f.322 {SELECT 'b' IN t8} 1 do_execsql_test tkt-80e031a00f.323 {SELECT 'c' NOT IN t8} 0 do_execsql_test tkt-80e031a00f.324 {SELECT 'c' IN t8n} 1 do_execsql_test tkt-80e031a00f.325 {SELECT 'd' NOT IN t8n} 0 # # Row 4: do_execsql_test tkt-80e031a00f.400 {SELECT 1 IN (2,3,4,null)} {{}} do_execsql_test tkt-80e031a00f.401 {SELECT 1 NOT IN (2,3,4,null)} {{}} do_execsql_test tkt-80e031a00f.402 {SELECT 'a' IN ('b','c',null,'d')} {{}} do_execsql_test tkt-80e031a00f.403 {SELECT 'a' NOT IN (null,'b','c','d')} {{}} do_execsql_test tkt-80e031a00f.404 {SELECT 1 IN t4n} {{}} do_execsql_test tkt-80e031a00f.405 {SELECT 5 NOT IN t4n} {{}} do_execsql_test tkt-80e031a00f.406 {SELECT 6 IN t6n} {{}} do_execsql_test tkt-80e031a00f.407 {SELECT 7 NOT IN t6n} {{}} do_execsql_test tkt-80e031a00f.408 {SELECT 'a' IN t7n} {{}} do_execsql_test tkt-80e031a00f.409 {SELECT 'e' NOT IN t7n} {{}} do_execsql_test tkt-80e031a00f.410 {SELECT 'f' IN t8n} {{}} do_execsql_test tkt-80e031a00f.411 {SELECT 'g' NOT IN t8n} {{}} # # Row 5: do_execsql_test tkt-80e031a00f.500 {SELECT null IN (2,3,4,null)} {{}} do_execsql_test tkt-80e031a00f.501 {SELECT null NOT IN (2,3,4,null)} {{}} do_execsql_test tkt-80e031a00f.502 {SELECT null IN (2,3,4)} {{}} do_execsql_test tkt-80e031a00f.503 {SELECT null NOT IN (2,3,4)} {{}} do_execsql_test tkt-80e031a00f.504 {SELECT null IN ('b','c','d')} {{}} do_execsql_test tkt-80e031a00f.505 {SELECT null NOT IN ('b','c','d')} {{}} do_execsql_test tkt-80e031a00f.506 {SELECT null IN ('b','c',null,'d')} {{}} do_execsql_test tkt-80e031a00f.507 {SELECT null NOT IN (null,'b','c','d')} {{}} do_execsql_test tkt-80e031a00f.508 {SELECT null IN t4} {{}} do_execsql_test tkt-80e031a00f.509 {SELECT null NOT IN t4} {{}} do_execsql_test tkt-80e031a00f.510 {SELECT null IN t4n} {{}} do_execsql_test tkt-80e031a00f.511 {SELECT null NOT IN t4n} {{}} do_execsql_test tkt-80e031a00f.512 {SELECT null IN t5} {{}} do_execsql_test tkt-80e031a00f.513 {SELECT null NOT IN t5} {{}} do_execsql_test tkt-80e031a00f.514 {SELECT null IN t6} {{}} do_execsql_test tkt-80e031a00f.515 {SELECT null NOT IN t6} {{}} do_execsql_test tkt-80e031a00f.516 {SELECT null IN t6n} {{}} do_execsql_test tkt-80e031a00f.517 {SELECT null NOT IN t6n} {{}} do_execsql_test tkt-80e031a00f.518 {SELECT null IN t7} {{}} do_execsql_test tkt-80e031a00f.519 {SELECT null NOT IN t7} {{}} do_execsql_test tkt-80e031a00f.520 {SELECT null IN t7n} {{}} do_execsql_test tkt-80e031a00f.521 {SELECT null NOT IN t7n} {{}} do_execsql_test tkt-80e031a00f.522 {SELECT null IN t8} {{}} do_execsql_test tkt-80e031a00f.523 {SELECT null NOT IN t8} {{}} do_execsql_test tkt-80e031a00f.524 {SELECT null IN t8n} {{}} do_execsql_test tkt-80e031a00f.525 {SELECT null NOT IN t8n} {{}} finish_test |
Added test/tkt-9d68c883.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 | # 2010 April 10 # # The author disclaims copyright to this source code. In place of # a legal notice, here is a blessing: # # May you do good and not evil. # May you find forgiveness for yourself and forgive others. # May you share freely, never taking more than you give. # #*********************************************************************** # This file tests that bug 9d68c883132c8e9ffcd5b0c148c990807b5df1b7 # is fixed. # set testdir [file dirname $argv0] source $testdir/tester.tcl do_test tkt-9d68c88-1.1 { execsql { PRAGMA page_size = 1024; PRAGMA auto_vacuum = 2; CREATE TABLE t3(x); CREATE TABLE t4(x); CREATE TABLE t5(x); INSERT INTO t5 VALUES(randomblob(1500)); CREATE TABLE t7(x); CREATE TABLE t8(x); } } {} for {set i 0} {$i < 100} {incr i} { db close sqlite3_simulate_device -sectorsize 8192 sqlite3 db test.db -vfs devsym do_test tkt-9d68c88-2.$i { execsql { BEGIN; DELETE FROM t5; INSERT INTO t8 VALUES('hello world'); } sqlite3_memdebug_fail $i -repeat 0 catchsql { DROP TABLE t7; } sqlite3_memdebug_fail -1 catchsql { ROLLBACK } execsql { PRAGMA integrity_check } } {ok} } finish_test |
Added test/tkt-cbd054fa6b.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 | # 2010 March 25 # # The author disclaims copyright to this source code. In place of # a legal notice, here is a blessing: # # May you do good and not evil. # May you find forgiveness for yourself and forgive others. # May you share freely, never taking more than you give. # #*********************************************************************** # # This file implements tests to verify that ticket [cbd054fa6b] has been # fixed. # set testdir [file dirname $argv0] source $testdir/tester.tcl ifcapable !stat2 { finish_test return } do_test tkt-cbd05-1.1 { db eval { CREATE TABLE t1(a INTEGER PRIMARY KEY, b TEXT UNIQUE NOT NULL); CREATE INDEX t1_x ON t1(b); INSERT INTO t1 VALUES (NULL, ''); INSERT INTO t1 VALUES (NULL, 'A'); INSERT INTO t1 VALUES (NULL, 'B'); INSERT INTO t1 VALUES (NULL, 'C'); INSERT INTO t1 VALUES (NULL, 'D'); INSERT INTO t1 VALUES (NULL, 'E'); INSERT INTO t1 VALUES (NULL, 'F'); INSERT INTO t1 VALUES (NULL, 'G'); INSERT INTO t1 VALUES (NULL, 'H'); INSERT INTO t1 VALUES (NULL, 'I'); SELECT count(*) FROM t1; } } {10} do_test tkt-cbd05-1.2 { db eval { ANALYZE; } } {} do_test tkt-cbd05-1.3 { execsql { SELECT tbl,idx,group_concat(sample,' ') FROM sqlite_stat2 WHERE idx = 't1_x' GROUP BY tbl,idx } } {t1 t1_x { A B C D E F G H I}} do_test tkt-cbd05-2.1 { db eval { DROP TABLE t1; CREATE TABLE t1(a INTEGER PRIMARY KEY, b BLOB UNIQUE NOT NULL); CREATE INDEX t1_x ON t1(b); INSERT INTO t1 VALUES(NULL, X''); INSERT INTO t1 VALUES(NULL, X'41'); INSERT INTO t1 VALUES(NULL, X'42'); INSERT INTO t1 VALUES(NULL, X'43'); INSERT INTO t1 VALUES(NULL, X'44'); INSERT INTO t1 VALUES(NULL, X'45'); INSERT INTO t1 VALUES(NULL, X'46'); INSERT INTO t1 VALUES(NULL, X'47'); INSERT INTO t1 VALUES(NULL, X'48'); INSERT INTO t1 VALUES(NULL, X'49'); SELECT count(*) FROM t1; } } {10} do_test tkt-cbd05-2.2 { db eval { ANALYZE; } } {} do_test tkt-cbd05-2.3 { execsql { SELECT tbl,idx,group_concat(sample,' ') FROM sqlite_stat2 WHERE idx = 't1_x' GROUP BY tbl,idx } } {t1 t1_x { A B C D E F G H I}} finish_test |
Added test/tkt-d11f09d36e.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 | # 2010 June 26 # # The author disclaims copyright to this source code. In place of # a legal notice, here is a blessing: # # May you do good and not evil. # May you find forgiveness for yourself and forgive others. # May you share freely, never taking more than you give. # #*********************************************************************** # # Test that the bug reported by ticket d11f09d36e7cb0821e01f4 has # been fixed. # set testdir [file dirname $argv0] source $testdir/tester.tcl set a_string_counter 1 proc a_string {n} { global a_string_counter incr a_string_counter string range [string repeat "${a_string_counter}." $n] 1 $n } db func a_string a_string do_test tkt-d11f09d36e.1 { execsql { PRAGMA synchronous = NORMAL; PRAGMA cache_size = 10; CREATE TABLE t1(x, y, UNIQUE(x, y)); BEGIN; } for {set i 0} {$i < 10000} {incr i} { execsql { INSERT INTO t1 VALUES($i, $i) } } execsql COMMIT } {} do_test tkt-d11f09d36e.2 { execsql { BEGIN; UPDATE t1 set x = x+10000; ROLLBACK; } } {} do_test tkt-d11f09d36e.3 { execsql { PRAGMA integrity_check } } {ok} do_test tkt-d11f09d36e.4 { execsql { SAVEPOINT tr; UPDATE t1 set x = x+10000; ROLLBACK TO tr; RELEASE tr; } } {} do_test tkt-d11f09d36e.5 { execsql { PRAGMA integrity_check } } {ok} finish_test |
Added test/tkt-f973c7ac31.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 | # 2010 June 09 # # The author disclaims copyright to this source code. In place of # a legal notice, here is a blessing: # # May you do good and not evil. # May you find forgiveness for yourself and forgive others. # May you share freely, never taking more than you give. # #*********************************************************************** # This file implements regression tests for SQLite library. # set testdir [file dirname $argv0] source $testdir/tester.tcl do_test tkt-f973c7ac3-1.0 { execsql { CREATE TABLE t(c1 INTEGER, c2 INTEGER); INSERT INTO t VALUES(5, 5); INSERT INTO t VALUES(5, 4); } } {} foreach {tn sql} { 1 "" 2 "CREATE INDEX i1 ON t(c1, c2)" } { execsql $sql do_test tkt-f973c7ac3-1.$tn.1 { execsql { SELECT * FROM t WHERE c1 = 5 AND c2>0 AND c2<='2' ORDER BY c2 DESC } } {} do_test tkt-f973c7ac3-1.$tn.2 { execsql { SELECT * FROM t WHERE c1 = 5 AND c2>0 AND c2<=5 ORDER BY c2 DESC } } {5 5 5 4} do_test tkt-f973c7ac3-1.$tn.3 { execsql { SELECT * FROM t WHERE c1 = 5 AND c2>0 AND c2<='5' ORDER BY c2 DESC } } {5 5 5 4} do_test tkt-f973c7ac3-1.$tn.4 { execsql { SELECT * FROM t WHERE c1 = 5 AND c2>'0' AND c2<=5 ORDER BY c2 DESC } } {5 5 5 4} do_test tkt-f973c7ac3-1.$tn.5 { execsql { SELECT * FROM t WHERE c1 = 5 AND c2>'0' AND c2<='5' ORDER BY c2 DESC } } {5 5 5 4} do_test tkt-f973c7ac3-1.$tn.6 { execsql { SELECT * FROM t WHERE c1 = 5 AND c2>0 AND c2<='2' ORDER BY c2 ASC } } {} do_test tkt-f973c7ac3-1.$tn.7 { execsql { SELECT * FROM t WHERE c1 = 5 AND c2>0 AND c2<=5 ORDER BY c2 ASC } } {5 4 5 5} do_test tkt-f973c7ac3-1.$tn.8 { execsql { SELECT * FROM t WHERE c1 = 5 AND c2>0 AND c2<='5' ORDER BY c2 ASC } } {5 4 5 5} do_test tkt-f973c7ac3-1.$tn.9 { execsql { SELECT * FROM t WHERE c1 = 5 AND c2>'0' AND c2<=5 ORDER BY c2 ASC } } {5 4 5 5} do_test tkt-f973c7ac3-1.$tn.10 { execsql { SELECT * FROM t WHERE c1 = 5 AND c2>'0' AND c2<='5' ORDER BY c2 ASC } } {5 4 5 5} } finish_test |
Added test/tkt-fc62af4523.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 | # 2010 June 16 # # The author disclaims copyright to this source code. In place of # a legal notice, here is a blessing: # # May you do good and not evil. # May you find forgiveness for yourself and forgive others. # May you share freely, never taking more than you give. # #*********************************************************************** # This file implements regression tests for SQLite library. Specifically, # it tests that ticket [fc62af4523] has been resolved. # set testdir [file dirname $argv0] source $testdir/tester.tcl source $testdir/lock_common.tcl source $testdir/malloc_common.tcl do_test tkt-fc62af4523.1 { execsql { PRAGMA cache_size = 10; PRAGMA journal_mode = persist; CREATE TABLE t1(a UNIQUE, b UNIQUE); INSERT INTO t1 SELECT randomblob(200), randomblob(300); INSERT INTO t1 SELECT randomblob(200), randomblob(300) FROM t1; -- 2 INSERT INTO t1 SELECT randomblob(200), randomblob(300) FROM t1; -- 4 INSERT INTO t1 SELECT randomblob(200), randomblob(300) FROM t1; -- 8 INSERT INTO t1 SELECT randomblob(200), randomblob(300) FROM t1; -- 16 INSERT INTO t1 SELECT randomblob(200), randomblob(300) FROM t1; -- 32 INSERT INTO t1 SELECT randomblob(200), randomblob(300) FROM t1; -- 64 } execsql { PRAGMA integrity_check; SELECT count(*) FROM t1; } } {ok 64} # Launch an external process. Have it write (but not commit) a large # transaction to the database. # set ::chan [launch_testfixture] proc buddy {code} { testfixture $::chan $code } do_test tkt-fc62af4523.2 { testfixture $::chan { sqlite3 db test.db db eval { PRAGMA cache_size = 10; BEGIN; UPDATE t1 SET b = randomblob(400); UPDATE t1 SET a = randomblob(201); } } file exists test.db-journal } {1} # Now do "PRAGMA journal_mode = DELETE" in this process. At one point # this was causing SQLite to delete the journal file from the file-system, # even though the external process is currently using it. # do_test tkt-fc62af4523.3 { execsql { PRAGMA journal_mode = DELETE } } {delete} do_test tkt-fc62af4523.4 { file exists test.db-journal } {1} # Cause the external process to crash. Since it has already written # uncommitted data into the database file, the next reader will have # to do a hot-journal rollback to recover the database. # # Or, if this test is run in a version with the bug present, the journal # file has already been deleted. In this case we are left with a corrupt # database file and no hot-journal to fix it with. # do_test tkt-fc62af4523.5 { testfixture $::chan sqlite_abort } {ERROR: Child process hung up} after 200 do_test tkt-fc62af4523.6 { execsql { PRAGMA integrity_check; SELECT count(*) FROM t1; } } {ok 64} catch { close $::chan } finish_test |
Deleted test/tkt3472.test.
|
| < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < |
Changes to test/trans.test.
︙ | ︙ | |||
15 16 17 18 19 20 21 22 23 24 25 26 27 28 | set testdir [file dirname $argv0] source $testdir/tester.tcl # Create several tables to work with. # do_test trans-1.0 { execsql { CREATE TABLE one(a int PRIMARY KEY, b text); INSERT INTO one VALUES(1,'one'); INSERT INTO one VALUES(2,'two'); INSERT INTO one VALUES(3,'three'); SELECT b FROM one ORDER BY a; | > | 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | set testdir [file dirname $argv0] source $testdir/tester.tcl # Create several tables to work with. # wal_set_journal_mode do_test trans-1.0 { execsql { CREATE TABLE one(a int PRIMARY KEY, b text); INSERT INTO one VALUES(1,'one'); INSERT INTO one VALUES(2,'two'); INSERT INTO one VALUES(3,'three'); SELECT b FROM one ORDER BY a; |
︙ | ︙ | |||
42 43 44 45 46 47 48 49 50 51 52 53 54 55 | sqlite3 altdb test.db execsql {SELECT b FROM one ORDER BY a} altdb } {one two three} do_test trans-1.10 { execsql {SELECT b FROM two ORDER BY a} altdb } {I V X} integrity_check trans-1.11 # Basic transactions # do_test trans-2.1 { set v [catch {execsql {BEGIN}} msg] lappend v $msg } {0 {}} | > | 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 | sqlite3 altdb test.db execsql {SELECT b FROM one ORDER BY a} altdb } {one two three} do_test trans-1.10 { execsql {SELECT b FROM two ORDER BY a} altdb } {I V X} integrity_check trans-1.11 wal_check_journal_mode trans-1.12 # Basic transactions # do_test trans-2.1 { set v [catch {execsql {BEGIN}} msg] lappend v $msg } {0 {}} |
︙ | ︙ | |||
78 79 80 81 82 83 84 85 86 87 88 89 90 91 | BEGIN; SELECT a FROM one ORDER BY a; SELECT a FROM two ORDER BY a; END; } } {1 2 3 1 5 10} integrity_check trans-2.11 # Check the locking behavior # do_test trans-3.1 { execsql { BEGIN; UPDATE one SET a = 0 WHERE 0; | > | 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 | BEGIN; SELECT a FROM one ORDER BY a; SELECT a FROM two ORDER BY a; END; } } {1 2 3 1 5 10} integrity_check trans-2.11 wal_check_journal_mode trans-2.12 # Check the locking behavior # do_test trans-3.1 { execsql { BEGIN; UPDATE one SET a = 0 WHERE 0; |
︙ | ︙ | |||
158 159 160 161 162 163 164 165 166 167 168 169 170 171 | do_test trans-3.14 { set v [catch {execsql { SELECT a FROM one ORDER BY a; } db} msg] lappend v $msg } {0 {1 2 3 4}} integrity_check trans-3.15 do_test trans-4.1 { set v [catch {execsql { COMMIT; } db} msg] lappend v $msg } {1 {cannot commit - no transaction is active}} | > | 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 | do_test trans-3.14 { set v [catch {execsql { SELECT a FROM one ORDER BY a; } db} msg] lappend v $msg } {0 {1 2 3 4}} integrity_check trans-3.15 wal_check_journal_mode trans-3.16 do_test trans-4.1 { set v [catch {execsql { COMMIT; } db} msg] lappend v $msg } {1 {cannot commit - no transaction is active}} |
︙ | ︙ | |||
224 225 226 227 228 229 230 231 232 233 234 235 236 237 | do_test trans-4.11 { set v [catch {execsql { SELECT a FROM one ORDER BY a; } altdb} msg] lappend v $msg } {0 {1 2 3 4}} integrity_check trans-4.12 do_test trans-4.98 { altdb close execsql { DROP TABLE one; DROP TABLE two; } } {} | > > | 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 | do_test trans-4.11 { set v [catch {execsql { SELECT a FROM one ORDER BY a; } altdb} msg] lappend v $msg } {0 {1 2 3 4}} integrity_check trans-4.12 wal_check_journal_mode trans-4.13 wal_check_journal_mode trans-4.14 altdb do_test trans-4.98 { altdb close execsql { DROP TABLE one; DROP TABLE two; } } {} |
︙ | ︙ | |||
771 772 773 774 775 776 777 778 779 780 781 782 783 784 | } } $checksum } do_test trans-7.14 { execsql {SELECT md5sum(type,name,tbl_name,rootpage,sql) FROM sqlite_master} } $checksum2 integrity_check trans-7.15 # Arrange for another process to begin modifying the database but abort # and die in the middle of the modification. Then have this process read # the database. This process should detect the journal file and roll it # back. Verify that this happens correctly. # set fd [open test.tcl w] | > | 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 | } } $checksum } do_test trans-7.14 { execsql {SELECT md5sum(type,name,tbl_name,rootpage,sql) FROM sqlite_master} } $checksum2 integrity_check trans-7.15 wal_check_journal_mode trans-7.16 # Arrange for another process to begin modifying the database but abort # and die in the middle of the modification. Then have this process read # the database. This process should detect the journal file and roll it # back. Verify that this happens correctly. # set fd [open test.tcl w] |
︙ | ︙ | |||
820 821 822 823 824 825 826 | catch {exec [info nameofexec] test.tcl} execsql {SELECT md5sum(x,y,z) FROM t2} } $checksum do_test trans-8.5 { execsql {SELECT md5sum(type,name,tbl_name,rootpage,sql) FROM sqlite_master} } $checksum2 integrity_check trans-8.6 | | | 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 | catch {exec [info nameofexec] test.tcl} execsql {SELECT md5sum(x,y,z) FROM t2} } $checksum do_test trans-8.5 { execsql {SELECT md5sum(type,name,tbl_name,rootpage,sql) FROM sqlite_master} } $checksum2 integrity_check trans-8.6 wal_check_journal_mode trans-8.7 # In the following sequence of tests, compute the MD5 sum of the content # of a table, make lots of modifications to that table, then do a rollback. # Verify that after the rollback, the MD5 checksum is unchanged. # do_test trans-9.1 { execsql { |
︙ | ︙ | |||
850 851 852 853 854 855 856 857 858 859 860 861 862 863 | INSERT INTO t3 SELECT randstr(10,400) FROM t3; INSERT INTO t3 SELECT randstr(10,400) FROM t3; INSERT INTO t3 SELECT randstr(10,400) FROM t3; COMMIT; SELECT count(*) FROM t3; } } {1024} # The following procedure computes a "signature" for table "t3". If # T3 changes in any way, the signature should change. # # This is used to test ROLLBACK. We gather a signature for t3, then # make lots of changes to t3, then rollback and take another signature. # The two signatures should be the same. | > | 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 | INSERT INTO t3 SELECT randstr(10,400) FROM t3; INSERT INTO t3 SELECT randstr(10,400) FROM t3; INSERT INTO t3 SELECT randstr(10,400) FROM t3; COMMIT; SELECT count(*) FROM t3; } } {1024} wal_check_journal_mode trans-9.1.1 # The following procedure computes a "signature" for table "t3". If # T3 changes in any way, the signature should change. # # This is used to test ROLLBACK. We gather a signature for t3, then # make lots of changes to t3, then rollback and take another signature. # The two signatures should be the same. |
︙ | ︙ | |||
871 872 873 874 875 876 877 | # t3 a little larger, and thus takes a little longer, so doing 40 tests # is more than 2.0 times slower than doing 20 tests. Considerably more. # # Also, if temporary tables are stored in memory and the test pcache # is in use, only 20 iterations. Otherwise the test pcache runs out # of page slots and SQLite reports "out of memory". # | | < | | | 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 | # t3 a little larger, and thus takes a little longer, so doing 40 tests # is more than 2.0 times slower than doing 20 tests. Considerably more. # # Also, if temporary tables are stored in memory and the test pcache # is in use, only 20 iterations. Otherwise the test pcache runs out # of page slots and SQLite reports "out of memory". # if {[info exists G(isquick)] || ( $TEMP_STORE==3 && [regexp {^pcache[[:digit:]]*$} [permutation]] ) } { set limit 20 } elseif {[info exists G(issoak)]} { set limit 100 } else { set limit 40 } # Do rollbacks. Make sure the signature does not change. # |
︙ | ︙ | |||
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 | } $sig if {$i<$limit} { do_test trans-9.$i.3-$cnt { execsql { INSERT INTO t3 SELECT randstr(10,400) FROM t3 WHERE random()%10==0; } } {} if {$tcl_platform(platform)=="unix"} { do_test trans-9.$i.4-$cnt { expr {$sqlite_sync_count>0} } 1 ifcapable pager_pragmas { do_test trans-9.$i.5-$cnt { expr {$sqlite_fullsync_count>0} } [expr {$i%2==0}] } else { do_test trans-9.$i.5-$cnt { expr {$sqlite_fullsync_count==0} } {1} } } } set ::pager_old_format 0 } finish_test | > > > | 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 | } $sig if {$i<$limit} { do_test trans-9.$i.3-$cnt { execsql { INSERT INTO t3 SELECT randstr(10,400) FROM t3 WHERE random()%10==0; } } {} catch flush_async_queue if {$tcl_platform(platform)=="unix"} { do_test trans-9.$i.4-$cnt { expr {$sqlite_sync_count>0} } 1 ifcapable pager_pragmas { do_test trans-9.$i.5-$cnt { expr {$sqlite_fullsync_count>0} } [expr {$i%2==0}] } else { do_test trans-9.$i.5-$cnt { expr {$sqlite_fullsync_count==0} } {1} } } } wal_check_journal_mode trans-9.$i.6-$cnt set ::pager_old_format 0 } finish_test |
Changes to test/trigger7.test.
︙ | ︙ | |||
108 109 110 111 112 113 114 | # do_test trigger7-99.1 { execsql { PRAGMA writable_schema=on; UPDATE sqlite_master SET sql='nonsense'; } db close | | | < < | 108 109 110 111 112 113 114 115 116 117 118 119 | # do_test trigger7-99.1 { execsql { PRAGMA writable_schema=on; UPDATE sqlite_master SET sql='nonsense'; } db close catch { sqlite3 db test.db } catchsql { DROP TRIGGER t2r5 } } {1 {malformed database schema (t2r12) - near "nonsense": syntax error}} finish_test |
Changes to test/triggerA.test.
︙ | ︙ | |||
73 74 75 76 77 78 79 | UNION SELECT y FROM t1 WHERE x BETWEEN 3 and 5; SELECT * FROM v4 ORDER BY 1; } } {1 10 2 3 4 5 6 7 8 9 five four three} do_test triggerA-1.6 { db eval { CREATE VIEW v5 AS SELECT x, b FROM t1, t2 WHERE y=c; | | | 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 | UNION SELECT y FROM t1 WHERE x BETWEEN 3 and 5; SELECT * FROM v4 ORDER BY 1; } } {1 10 2 3 4 5 6 7 8 9 five four three} do_test triggerA-1.6 { db eval { CREATE VIEW v5 AS SELECT x, b FROM t1, t2 WHERE y=c; SELECT * FROM v5 ORDER BY x DESC; } } {10 1003 9 904 8 805 7 705 6 603 5 504 4 404 3 305 2 203 1 103} # Create INSTEAD OF triggers on the views. Run UPDATE and DELETE statements # using those triggers. Verify correct operation. # do_test triggerA-2.1 { |
︙ | ︙ |
Changes to test/triggerC.test.
︙ | ︙ | |||
850 851 852 853 854 855 856 857 858 859 | # 1 written to column "updatecnt" was clobbered by the old value 0). # execsql { UPDATE t11 SET c4=35, c33=22, c1=5; SELECT * FROM t11; } } {5 2 3 35 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 32 33 22 34 35 36 37 38 39 40} finish_test | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | # 1 written to column "updatecnt" was clobbered by the old value 0). # execsql { UPDATE t11 SET c4=35, c33=22, c1=5; SELECT * FROM t11; } } {5 2 3 35 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 32 33 22 34 35 36 37 38 39 40} #------------------------------------------------------------------------- # Test that bug [371bab5d65] has been fixed. BEFORE INSERT and INSTEAD OF # INSERT triggers with the DEFAULT VALUES INSERT syntax. # do_test triggerC-11.0 { catchsql { DROP TABLE log } execsql { CREATE TABLE log(a, b) } } {} foreach {testno tbl defaults} { 1 "CREATE TABLE t1(a, b)" {{} {}} 2 "CREATE TABLE t1(a DEFAULT 1, b DEFAULT 'abc')" {1 abc} 3 "CREATE TABLE t1(a, b DEFAULT 4.5)" {{} 4.5} } { do_test triggerC-11.$testno.1 { catchsql { DROP TABLE t1 } execsql { DELETE FROM log } execsql $tbl execsql { CREATE TRIGGER tt1 BEFORE INSERT ON t1 BEGIN INSERT INTO log VALUES(new.a, new.b); END; INSERT INTO t1 DEFAULT VALUES; SELECT * FROM log; } } $defaults do_test triggerC-11.$testno.2 { execsql { DELETE FROM log } execsql { CREATE TRIGGER tt2 AFTER INSERT ON t1 BEGIN INSERT INTO log VALUES(new.a, new.b); END; INSERT INTO t1 DEFAULT VALUES; SELECT * FROM log; } } [concat $defaults $defaults] do_test triggerC-11.$testno.3 { execsql { DROP TRIGGER tt1 } execsql { DELETE FROM log } execsql { INSERT INTO t1 DEFAULT VALUES; SELECT * FROM log; } } $defaults } do_test triggerC-11.4 { catchsql { DROP TABLE t2 } execsql { DELETE FROM log; CREATE TABLE t2(a, b); CREATE VIEW v2 AS SELECT * FROM t2; CREATE TRIGGER tv2 INSTEAD OF INSERT ON v2 BEGIN INSERT INTO log VALUES(new.a, new.b); END; INSERT INTO v2 DEFAULT VALUES; SELECT a, b, a IS NULL, b IS NULL FROM log; } } {{} {} 1 1} finish_test |
Changes to test/vacuum.test.
︙ | ︙ | |||
50 51 52 53 54 55 56 57 58 59 60 61 62 63 | COMMIT; DROP TABLE t2; } set ::size1 [file size test.db] set ::cksum [cksum] expr {$::cksum!=""} } {1} do_test vacuum-1.2 { execsql { VACUUM; } cksum } $cksum ifcapable vacuum { | > > > > > > > > > > > > > | 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 | COMMIT; DROP TABLE t2; } set ::size1 [file size test.db] set ::cksum [cksum] expr {$::cksum!=""} } {1} # Create bogus application-defined functions for functions used # internally by VACUUM, to ensure that VACUUM falls back # to the built-in functions. # proc failing_app_func {args} {error "bad function"} do_test vacuum-1.1b { db func substr failing_app_func db func like failing_app_func db func quote failing_app_func catchsql {SELECT substr(name,1,3) FROM sqlite_master} } {1 {bad function}} do_test vacuum-1.2 { execsql { VACUUM; } cksum } $cksum ifcapable vacuum { |
︙ | ︙ |
Changes to test/veryquick.test.
1 2 3 4 5 6 7 8 9 10 11 12 13 | # # May you do good and not evil. # May you find forgiveness for yourself and forgive others. # May you share freely, never taking more than you give. # #*********************************************************************** # This file runs all the tests run by quick.test except for those related # to malloc or IO error simulation. With these tests omitted, the overall # run time is reduced by about 75%. # # $Id: veryquick.test,v 1.9 2008/07/12 14:52:21 drh Exp $ set testdir [file dirname $argv0] | < | > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | # # May you do good and not evil. # May you find forgiveness for yourself and forgive others. # May you share freely, never taking more than you give. # #*********************************************************************** # This file runs all the tests run by quick.test except for those related # to malloc or IO error simulation. With these tests omitted, the overall # run time is reduced by about 75%. # # $Id: veryquick.test,v 1.9 2008/07/12 14:52:21 drh Exp $ set testdir [file dirname $argv0] source $testdir/permutations.test run_test_suite veryquick finish_test |
Added test/wal.test.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 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 | # 2010 April 13 # # The author disclaims copyright to this source code. In place of # a legal notice, here is a blessing: # # May you do good and not evil. # May you find forgiveness for yourself and forgive others. # May you share freely, never taking more than you give. # #*********************************************************************** # This file implements regression tests for SQLite library. The # focus of this file is testing the operation of the library in # "PRAGMA journal_mode=WAL" mode. # set testdir [file dirname $argv0] source $testdir/tester.tcl source $testdir/lock_common.tcl source $testdir/malloc_common.tcl source $testdir/wal_common.tcl ifcapable !wal {finish_test ; return } proc reopen_db {} { catch { db close } file delete -force test.db test.db-wal test.db-wal-summary sqlite3_wal db test.db } set ::blobcnt 0 proc blob {nByte} { incr ::blobcnt return [string range [string repeat "${::blobcnt}x" $nByte] 1 $nByte] } proc sqlite3_wal {args} { eval sqlite3 $args [lindex $args 0] eval { PRAGMA auto_vacuum = 0 } [lindex $args 0] eval { PRAGMA page_size = 1024 } [lindex $args 0] eval { PRAGMA journal_mode = wal } [lindex $args 0] eval { PRAGMA synchronous = normal } [lindex $args 0] function blob blob } proc log_deleted {logfile} { return [expr [file exists $logfile]==0] } # # These are 'warm-body' tests used while developing the WAL code. They # serve to prove that a few really simple cases work: # # wal-1.*: Read and write the database. # wal-2.*: Test MVCC with one reader, one writer. # wal-3.*: Test transaction rollback. # wal-4.*: Test savepoint/statement rollback. # wal-5.*: Test the temp database. # wal-6.*: Test creating databases with different page sizes. # # do_test wal-0.1 { execsql { PRAGMA auto_vacuum = 0 } execsql { PRAGMA synchronous = normal } execsql { PRAGMA journal_mode = wal } } {wal} do_test wal-0.2 { file size test.db } {1024} do_test wal-1.0 { execsql { BEGIN; CREATE TABLE t1(a, b); } list [file exists test.db-journal] \ [file exists test.db-wal] \ [file size test.db] } {0 1 1024} do_test wal-1.1 { execsql COMMIT list [file exists test.db-journal] [file exists test.db-wal] } {0 1} do_test wal-1.2 { # There are now two pages in the log. file size test.db-wal } [wal_file_size 2 1024] do_test wal-1.3 { execsql { SELECT * FROM sqlite_master } } {table t1 t1 2 {CREATE TABLE t1(a, b)}} do_test wal-1.4 { execsql { INSERT INTO t1 VALUES(1, 2) } execsql { INSERT INTO t1 VALUES(3, 4) } execsql { INSERT INTO t1 VALUES(5, 6) } execsql { INSERT INTO t1 VALUES(7, 8) } execsql { INSERT INTO t1 VALUES(9, 10) } } {} do_test wal-1.5 { execsql { SELECT * FROM t1 } } {1 2 3 4 5 6 7 8 9 10} do_test wal-2.1 { sqlite3_wal db2 ./test.db execsql { BEGIN; SELECT * FROM t1 } db2 } {1 2 3 4 5 6 7 8 9 10} do_test wal-2.2 { execsql { INSERT INTO t1 VALUES(11, 12) } execsql { SELECT * FROM t1 } } {1 2 3 4 5 6 7 8 9 10 11 12} do_test wal-2.3 { execsql { SELECT * FROM t1 } db2 } {1 2 3 4 5 6 7 8 9 10} do_test wal-2.4 { execsql { INSERT INTO t1 VALUES(13, 14) } execsql { SELECT * FROM t1 } } {1 2 3 4 5 6 7 8 9 10 11 12 13 14} do_test wal-2.5 { execsql { SELECT * FROM t1 } db2 } {1 2 3 4 5 6 7 8 9 10} do_test wal-2.6 { execsql { COMMIT; SELECT * FROM t1 } db2 } {1 2 3 4 5 6 7 8 9 10 11 12 13 14} do_test wal-3.1 { execsql { BEGIN; DELETE FROM t1 } execsql { SELECT * FROM t1 } } {} do_test wal-3.2 { execsql { SELECT * FROM t1 } db2 } {1 2 3 4 5 6 7 8 9 10 11 12 13 14} do_test wal-3.3 { execsql { ROLLBACK } execsql { SELECT * FROM t1 } } {1 2 3 4 5 6 7 8 9 10 11 12 13 14} db2 close #------------------------------------------------------------------------- # The following tests, wal-4.*, test that savepoints work with WAL # databases. # do_test wal-4.1 { execsql { DELETE FROM t1; BEGIN; INSERT INTO t1 VALUES('a', 'b'); SAVEPOINT sp; INSERT INTO t1 VALUES('c', 'd'); SELECT * FROM t1; } } {a b c d} do_test wal-4.2 { execsql { ROLLBACK TO sp; SELECT * FROM t1; } } {a b} do_test wal-4.3 { execsql { COMMIT; SELECT * FROM t1; } } {a b} do_test wal-4.4.1 { db close sqlite3 db test.db db func blob blob list [execsql { SELECT * FROM t1 }] [file size test.db-wal] } {{a b} 0} do_test wal-4.4.2 { execsql { PRAGMA cache_size = 10 } execsql { CREATE TABLE t2(a, b); INSERT INTO t2 VALUES(blob(400), blob(400)); SAVEPOINT tr; INSERT INTO t2 SELECT blob(400), blob(400) FROM t2; /* 2 */ INSERT INTO t2 SELECT blob(400), blob(400) FROM t2; /* 4 */ INSERT INTO t2 SELECT blob(400), blob(400) FROM t2; /* 8 */ INSERT INTO t2 SELECT blob(400), blob(400) FROM t2; /* 16 */ INSERT INTO t2 SELECT blob(400), blob(400) FROM t2; /* 32 */ INSERT INTO t1 SELECT blob(400), blob(400) FROM t1; /* 2 */ INSERT INTO t1 SELECT blob(400), blob(400) FROM t1; /* 4 */ INSERT INTO t1 SELECT blob(400), blob(400) FROM t1; /* 8 */ INSERT INTO t1 SELECT blob(400), blob(400) FROM t1; /* 16 */ INSERT INTO t1 SELECT blob(400), blob(400) FROM t1; /* 32 */ SELECT count(*) FROM t2; } } {32} do_test wal-4.4.3 { execsql { ROLLBACK TO tr } } {} do_test wal-4.4.4 { set logsize [file size test.db-wal] execsql { INSERT INTO t1 VALUES('x', 'y'); RELEASE tr; } expr { $logsize == [file size test.db-wal] } } {1} do_test wal-4.4.5 { execsql { SELECT count(*) FROM t2 } } {1} do_test wal-4.4.6 { file copy -force test.db test2.db file copy -force test.db-wal test2.db-wal sqlite3 db2 test2.db execsql { SELECT count(*) FROM t2 ; SELECT count(*) FROM t1 } db2 } {1 2} do_test wal-4.4.7 { execsql { PRAGMA integrity_check } db2 } {ok} db2 close do_test wal-4.5.1 { reopen_db db func blob blob execsql { PRAGMA journal_mode = WAL; CREATE TABLE t1(a, b); INSERT INTO t1 VALUES('a', 'b'); } sqlite3 db test.db db func blob blob list [execsql { SELECT * FROM t1 }] [file size test.db-wal] } {{a b} 0} do_test wal-4.5.2 { execsql { PRAGMA cache_size = 10 } execsql { CREATE TABLE t2(a, b); BEGIN; INSERT INTO t2 VALUES(blob(400), blob(400)); SAVEPOINT tr; INSERT INTO t2 SELECT blob(400), blob(400) FROM t2; /* 2 */ INSERT INTO t2 SELECT blob(400), blob(400) FROM t2; /* 4 */ INSERT INTO t2 SELECT blob(400), blob(400) FROM t2; /* 8 */ INSERT INTO t2 SELECT blob(400), blob(400) FROM t2; /* 16 */ INSERT INTO t2 SELECT blob(400), blob(400) FROM t2; /* 32 */ INSERT INTO t1 SELECT blob(400), blob(400) FROM t1; /* 2 */ INSERT INTO t1 SELECT blob(400), blob(400) FROM t1; /* 4 */ INSERT INTO t1 SELECT blob(400), blob(400) FROM t1; /* 8 */ INSERT INTO t1 SELECT blob(400), blob(400) FROM t1; /* 16 */ INSERT INTO t1 SELECT blob(400), blob(400) FROM t1; /* 32 */ SELECT count(*) FROM t2; } } {32} do_test wal-4.5.3 { execsql { ROLLBACK TO tr } } {} do_test wal-4.5.4 { set logsize [file size test.db-wal] execsql { INSERT INTO t1 VALUES('x', 'y'); RELEASE tr; COMMIT; } expr { $logsize == [file size test.db-wal] } } {1} do_test wal-4.5.5 { execsql { SELECT count(*) FROM t2 ; SELECT count(*) FROM t1 } } {1 2} do_test wal-4.5.6 { file copy -force test.db test2.db file copy -force test.db-wal test2.db-wal sqlite3 db2 test2.db execsql { SELECT count(*) FROM t2 ; SELECT count(*) FROM t1 } db2 } {1 2} do_test wal-4.5.7 { execsql { PRAGMA integrity_check } db2 } {ok} db2 close do_test wal-4.6.1 { execsql { DELETE FROM t2; PRAGMA wal_checkpoint; BEGIN; INSERT INTO t2 VALUES('w', 'x'); SAVEPOINT save; INSERT INTO t2 VALUES('y', 'z'); ROLLBACK TO save; COMMIT; SELECT * FROM t2; } } {w x} reopen_db do_test wal-5.1 { execsql { CREATE TEMP TABLE t2(a, b); INSERT INTO t2 VALUES(1, 2); } } {} do_test wal-5.2 { execsql { BEGIN; INSERT INTO t2 VALUES(3, 4); SELECT * FROM t2; } } {1 2 3 4} do_test wal-5.3 { execsql { ROLLBACK; SELECT * FROM t2; } } {1 2} do_test wal-5.4 { execsql { CREATE TEMP TABLE t3(x UNIQUE); BEGIN; INSERT INTO t2 VALUES(3, 4); INSERT INTO t3 VALUES('abc'); } catchsql { INSERT INTO t3 VALUES('abc') } } {1 {column x is not unique}} do_test wal-5.5 { execsql { COMMIT; SELECT * FROM t2; } } {1 2 3 4} db close foreach sector {512 4096} { sqlite3_simulate_device -sectorsize $sector foreach pgsz {512 1024 2048 4096} { file delete -force test.db test.db-wal do_test wal-6.$sector.$pgsz.1 { sqlite3 db test.db -vfs devsym execsql " PRAGMA page_size = $pgsz; PRAGMA auto_vacuum = 0; PRAGMA journal_mode = wal; " execsql " CREATE TABLE t1(a, b); INSERT INTO t1 VALUES(1, 2); " db close file size test.db } [expr $pgsz*2] do_test wal-6.$sector.$pgsz.2 { log_deleted test.db-wal } {1} } } do_test wal-7.1 { file delete -force test.db test.db-wal sqlite3_wal db test.db execsql { PRAGMA page_size = 1024; CREATE TABLE t1(a, b); INSERT INTO t1 VALUES(1, 2); } list [file size test.db] [file size test.db-wal] } [list 1024 [wal_file_size 3 1024]] do_test wal-7.2 { execsql { PRAGMA wal_checkpoint } list [file size test.db] [file size test.db-wal] } [list 2048 [wal_file_size 3 1024]] # Execute some transactions in auto-vacuum mode to test database file # truncation. # do_test wal-8.1 { reopen_db catch { db close } file delete -force test.db test.db-wal sqlite3 db test.db db function blob blob execsql { PRAGMA auto_vacuum = 1; PRAGMA journal_mode = wal; PRAGMA auto_vacuum; } } {wal 1} do_test wal-8.2 { execsql { PRAGMA page_size = 1024; CREATE TABLE t1(x); INSERT INTO t1 VALUES(blob(900)); INSERT INTO t1 VALUES(blob(900)); INSERT INTO t1 SELECT blob(900) FROM t1; /* 4 */ INSERT INTO t1 SELECT blob(900) FROM t1; /* 8 */ INSERT INTO t1 SELECT blob(900) FROM t1; /* 16 */ INSERT INTO t1 SELECT blob(900) FROM t1; /* 32 */ INSERT INTO t1 SELECT blob(900) FROM t1; /* 64 */ PRAGMA wal_checkpoint; } file size test.db } [expr 68*1024] do_test wal-8.3 { execsql { DELETE FROM t1 WHERE rowid<54; PRAGMA wal_checkpoint; } file size test.db } [expr 14*1024] # Run some "warm-body" tests to ensure that log-summary files with more # than 256 entries (log summaries that contain index blocks) work Ok. # do_test wal-9.1 { reopen_db execsql { CREATE TABLE t1(x PRIMARY KEY); INSERT INTO t1 VALUES(blob(900)); INSERT INTO t1 VALUES(blob(900)); INSERT INTO t1 SELECT blob(900) FROM t1; /* 4 */ INSERT INTO t1 SELECT blob(900) FROM t1; /* 8 */ INSERT INTO t1 SELECT blob(900) FROM t1; /* 16 */ INSERT INTO t1 SELECT blob(900) FROM t1; /* 32 */ INSERT INTO t1 SELECT blob(900) FROM t1; /* 64 */ INSERT INTO t1 SELECT blob(900) FROM t1; /* 128 */ INSERT INTO t1 SELECT blob(900) FROM t1; /* 256 */ } file size test.db } 1024 do_test wal-9.2 { sqlite3_wal db2 test.db execsql {PRAGMA integrity_check } db2 } {ok} do_test wal-9.3 { file delete -force test2.db test2.db-wal file copy test.db test2.db file copy test.db-wal test2.db-wal sqlite3_wal db3 test2.db execsql {PRAGMA integrity_check } db3 } {ok} db3 close do_test wal-9.4 { execsql { PRAGMA wal_checkpoint } db2 close sqlite3_wal db2 test.db execsql {PRAGMA integrity_check } db2 } {ok} foreach handle {db db2 db3} { catch { $handle close } } unset handle #------------------------------------------------------------------------- # The following block of tests - wal-10.* - test that the WAL locking # scheme works in simple cases. This block of tests is run twice. Once # using multiple connections in the address space of the current process, # and once with all connections except one running in external processes. # do_multiclient_test tn { # Initialize the database schema and contents. # do_test wal-10.$tn.1 { execsql { PRAGMA journal_mode = wal; CREATE TABLE t1(a, b); INSERT INTO t1 VALUES(1, 2); SELECT * FROM t1; } } {wal 1 2} # Open a transaction and write to the database using [db]. Check that [db2] # is still able to read the snapshot before the transaction was opened. # do_test wal-10.$tn.2 { execsql { BEGIN; INSERT INTO t1 VALUES(3, 4); } sql2 {SELECT * FROM t1} } {1 2} # Have [db] commit the transaction. Check that [db2] is now seeing the # new, updated snapshot. # do_test wal-10.$tn.3 { execsql { COMMIT } sql2 {SELECT * FROM t1} } {1 2 3 4} # Have [db2] open a read transaction. Then write to the db via [db]. Check # that [db2] is still seeing the original snapshot. Then read with [db3]. # [db3] should see the newly committed data. # do_test wal-10.$tn.4 { sql2 { BEGIN ; SELECT * FROM t1} } {1 2 3 4} do_test wal-10.$tn.5 { execsql { INSERT INTO t1 VALUES(5, 6); } sql2 {SELECT * FROM t1} } {1 2 3 4} do_test wal-10.$tn.6 { sql3 {SELECT * FROM t1} } {1 2 3 4 5 6} do_test wal-10.$tn.7 { sql2 COMMIT } {} # Have [db2] open a write transaction. Then attempt to write to the # database via [db]. This should fail (writer lock cannot be obtained). # # Then open a read-transaction with [db]. Commit the [db2] transaction # to disk. Verify that [db] still cannot write to the database (because # it is reading an old snapshot). # # Close the current [db] transaction. Open a new one. [db] can now write # to the database (as it is not locked and [db] is reading the latest # snapshot). # do_test wal-10.$tn.7 { sql2 { BEGIN; INSERT INTO t1 VALUES(7, 8) ; } catchsql { INSERT INTO t1 VALUES(9, 10) } } {1 {database is locked}} do_test wal-10.$tn.8 { execsql { BEGIN ; SELECT * FROM t1 } } {1 2 3 4 5 6} do_test wal-10.$tn.9 { sql2 COMMIT catchsql { INSERT INTO t1 VALUES(9, 10) } } {1 {database is locked}} do_test wal-10.$tn.10 { execsql { COMMIT } execsql { BEGIN } execsql { INSERT INTO t1 VALUES(9, 10) } execsql { COMMIT } execsql { SELECT * FROM t1 } } {1 2 3 4 5 6 7 8 9 10} # Open a read transaction with [db2]. Check that this prevents [db] from # checkpointing the database. But not from writing to it. # do_test wal-10.$tn.11 { sql2 { BEGIN; SELECT * FROM t1 } } {1 2 3 4 5 6 7 8 9 10} do_test wal-10.$tn.12 { catchsql { PRAGMA wal_checkpoint } } {0 {}} ;# Reader no longer block checkpoints do_test wal-10.$tn.13 { execsql { INSERT INTO t1 VALUES(11, 12) } sql2 {SELECT * FROM t1} } {1 2 3 4 5 6 7 8 9 10} # Writers do not block checkpoints any more either. # do_test wal-10.$tn.14 { catchsql { PRAGMA wal_checkpoint } } {0 {}} # The following series of test cases used to verify another blocking # case in WAL - a case which no longer blocks. # do_test wal-10.$tn.15 { sql2 { COMMIT; BEGIN; SELECT * FROM t1; } } {1 2 3 4 5 6 7 8 9 10 11 12} do_test wal-10.$tn.16 { catchsql { PRAGMA wal_checkpoint } } {0 {}} do_test wal-10.$tn.17 { execsql { PRAGMA wal_checkpoint } } {} do_test wal-10.$tn.18 { sql3 { BEGIN; SELECT * FROM t1 } } {1 2 3 4 5 6 7 8 9 10 11 12} do_test wal-10.$tn.19 { catchsql { INSERT INTO t1 VALUES(13, 14) } } {0 {}} do_test wal-10.$tn.20 { execsql { SELECT * FROM t1 } } {1 2 3 4 5 6 7 8 9 10 11 12 13 14} do_test wal-10.$tn.21 { sql3 COMMIT sql2 COMMIT } {} do_test wal-10.$tn.22 { execsql { SELECT * FROM t1 } } {1 2 3 4 5 6 7 8 9 10 11 12 13 14} # Another series of tests that used to demonstrate blocking behavior # but which now work. # do_test wal-10.$tn.23 { execsql { PRAGMA wal_checkpoint } } {} do_test wal-10.$tn.24 { sql2 { BEGIN; SELECT * FROM t1; } } {1 2 3 4 5 6 7 8 9 10 11 12 13 14} do_test wal-10.$tn.25 { execsql { PRAGMA wal_checkpoint } } {} do_test wal-10.$tn.26 { catchsql { INSERT INTO t1 VALUES(15, 16) } } {0 {}} do_test wal-10.$tn.27 { sql3 { INSERT INTO t1 VALUES(17, 18) } } {} do_test wal-10.$tn.28 { code3 { set ::STMT [sqlite3_prepare db3 "SELECT * FROM t1" -1 TAIL] sqlite3_step $::STMT } execsql { SELECT * FROM t1 } } {1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18} do_test wal-10.$tn.29 { execsql { INSERT INTO t1 VALUES(19, 20) } catchsql { PRAGMA wal_checkpoint } } {0 {}} do_test wal-10.$tn.30 { code3 { sqlite3_finalize $::STMT } execsql { PRAGMA wal_checkpoint } } {} # At one point, if a reader failed to upgrade to a writer because it # was reading an old snapshot, the write-locks were not being released. # Test that this bug has been fixed. # do_test wal-10.$tn.31 { sql2 COMMIT execsql { BEGIN ; SELECT * FROM t1 } sql2 { INSERT INTO t1 VALUES(21, 22) } catchsql { INSERT INTO t1 VALUES(23, 24) } } {1 {database is locked}} do_test wal-10.$tn.32 { # This statement would fail when the bug was present. sql2 { INSERT INTO t1 VALUES(23, 24) } } {} do_test wal-10.$tn.33 { execsql { SELECT * FROM t1 ; COMMIT } } {1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20} do_test wal-10.$tn.34 { execsql { SELECT * FROM t1 } } {1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24} # Test that if a checkpointer cannot obtain the required locks, it # releases all locks before returning a busy error. # do_test wal-10.$tn.35 { execsql { DELETE FROM t1; INSERT INTO t1 VALUES('a', 'b'); INSERT INTO t1 VALUES('c', 'd'); } sql2 { BEGIN; SELECT * FROM t1; } } {a b c d} do_test wal-10.$tn.36 { catchsql { PRAGMA wal_checkpoint } } {0 {}} do_test wal-10.$tn.36 { sql3 { INSERT INTO t1 VALUES('e', 'f') } sql2 { SELECT * FROM t1 } } {a b c d} do_test wal-10.$tn.37 { sql2 COMMIT execsql { PRAGMA wal_checkpoint } } {} } #------------------------------------------------------------------------- # This block of tests, wal-11.*, test that nothing goes terribly wrong # if frames must be written to the log file before a transaction is # committed (in order to free up memory). # do_test wal-11.1 { reopen_db execsql { PRAGMA cache_size = 10; PRAGMA page_size = 1024; CREATE TABLE t1(x PRIMARY KEY); } list [expr [file size test.db]/1024] [expr [file size test.db-wal]/1044] } {1 3} do_test wal-11.2 { execsql { PRAGMA wal_checkpoint } list [expr [file size test.db]/1024] [file size test.db-wal] } [list 3 [wal_file_size 3 1024]] do_test wal-11.3 { execsql { INSERT INTO t1 VALUES( blob(900) ) } list [expr [file size test.db]/1024] [file size test.db-wal] } [list 3 [wal_file_size 4 1024]] do_test wal-11.4 { execsql { BEGIN; INSERT INTO t1 SELECT blob(900) FROM t1; -- 2 INSERT INTO t1 SELECT blob(900) FROM t1; -- 4 INSERT INTO t1 SELECT blob(900) FROM t1; -- 8 INSERT INTO t1 SELECT blob(900) FROM t1; -- 16 } list [expr [file size test.db]/1024] [file size test.db-wal] } [list 3 [wal_file_size 32 1024]] do_test wal-11.5 { execsql { SELECT count(*) FROM t1; PRAGMA integrity_check; } } {16 ok} do_test wal-11.6 { execsql COMMIT list [expr [file size test.db]/1024] [file size test.db-wal] } [list 3 [wal_file_size 41 1024]] do_test wal-11.7 { execsql { SELECT count(*) FROM t1; PRAGMA integrity_check; } } {16 ok} do_test wal-11.8 { execsql { PRAGMA wal_checkpoint } list [expr [file size test.db]/1024] [file size test.db-wal] } [list 37 [wal_file_size 41 1024]] do_test wal-11.9 { db close list [expr [file size test.db]/1024] [log_deleted test.db-wal] } {37 1} sqlite3_wal db test.db do_test wal-11.10 { execsql { PRAGMA cache_size = 10; BEGIN; INSERT INTO t1 SELECT blob(900) FROM t1; -- 32 SELECT count(*) FROM t1; } list [expr [file size test.db]/1024] [file size test.db-wal] } [list 37 [wal_file_size 37 1024]] do_test wal-11.11 { execsql { SELECT count(*) FROM t1; ROLLBACK; SELECT count(*) FROM t1; } } {32 16} do_test wal-11.12 { list [expr [file size test.db]/1024] [file size test.db-wal] } [list 37 [wal_file_size 37 1024]] do_test wal-11.13 { execsql { INSERT INTO t1 VALUES( blob(900) ); SELECT count(*) FROM t1; PRAGMA integrity_check; } } {17 ok} do_test wal-11.14 { list [expr [file size test.db]/1024] [file size test.db-wal] } [list 37 [wal_file_size 37 1024]] #------------------------------------------------------------------------- # This block of tests, wal-12.*, tests the fix for a problem that # could occur if a log that is a prefix of an older log is written # into a reused log file. # reopen_db do_test wal-12.1 { execsql { PRAGMA page_size = 1024; CREATE TABLE t1(x, y); CREATE TABLE t2(x, y); INSERT INTO t1 VALUES('A', 1); } list [expr [file size test.db]/1024] [file size test.db-wal] } [list 1 [wal_file_size 5 1024]] do_test wal-12.2 { db close sqlite3 db test.db execsql { PRAGMA synchronous = normal; UPDATE t1 SET y = 0 WHERE x = 'A'; } list [expr [file size test.db]/1024] [expr [file size test.db-wal]/1044] } {3 1} do_test wal-12.3 { execsql { INSERT INTO t2 VALUES('B', 1) } list [expr [file size test.db]/1024] [expr [file size test.db-wal]/1044] } {3 2} do_test wal-12.4 { file copy -force test.db test2.db file copy -force test.db-wal test2.db-wal sqlite3_wal db2 test2.db execsql { SELECT * FROM t2 } db2 } {B 1} db2 close do_test wal-12.5 { execsql { PRAGMA wal_checkpoint; UPDATE t2 SET y = 2 WHERE x = 'B'; PRAGMA wal_checkpoint; UPDATE t1 SET y = 1 WHERE x = 'A'; PRAGMA wal_checkpoint; UPDATE t1 SET y = 0 WHERE x = 'A'; SELECT * FROM t2; } } {B 2} do_test wal-12.6 { file copy -force test.db test2.db file copy -force test.db-wal test2.db-wal sqlite3_wal db2 test2.db execsql { SELECT * FROM t2 } db2 } {B 2} db2 close db close #------------------------------------------------------------------------- # Test large log summaries. # # In this case "large" usually means a log file that requires a wal-index # mapping larger than 64KB (the default initial allocation). A 64KB wal-index # is large enough for a log file that contains approximately 13100 frames. # So the following tests create logs containing at least this many frames. # # wal-13.1.*: This test case creates a very large log file within the # file-system (around 200MB). The log file does not contain # any valid frames. Test that the database file can still be # opened and queried, and that the invalid log file causes no # problems. # # wal-13.2.*: Test that a process may create a large log file and query # the database (including the log file that it itself created). # # wal-13.3.*: Test that if a very large log file is created, and then a # second connection is opened on the database file, it is possible # to query the database (and the very large log) using the # second connection. # # wal-13.4.*: Same test as wal-13.3.*. Except in this case the second # connection is opened by an external process. # do_test wal-13.1.1 { list [file exists test.db] [file exists test.db-wal] } {1 0} do_test wal-13.1.2 { set fd [open test.db-wal w] seek $fd [expr 200*1024*1024] puts $fd "" close $fd sqlite3 db test.db execsql { SELECT * FROM t2 } } {B 2} do_test wal-13.1.3 { db close file exists test.db-wal } {0} do_test wal-13.2.1 { sqlite3 db test.db execsql { SELECT count(*) FROM t2 } } {1} do_test wal-13.2.2 { db function blob blob for {set i 0} {$i < 16} {incr i} { execsql { INSERT INTO t2 SELECT blob(400), blob(400) FROM t2 } } execsql { SELECT count(*) FROM t2 } } [expr int(pow(2, 16))] do_test wal-13.2.3 { expr [file size test.db-wal] > [wal_file_size 33000 1024] } 1 do_multiclient_test tn { incr tn 2 do_test wal-13.$tn.0 { sql1 { PRAGMA journal_mode = WAL; CREATE TABLE t1(x); INSERT INTO t1 SELECT randomblob(800); } sql1 { SELECT count(*) FROM t1 } } {1} for {set ii 1} {$ii<16} {incr ii} { do_test wal-13.$tn.$ii.a { sql2 { INSERT INTO t1 SELECT randomblob(800) FROM t1 } sql2 { SELECT count(*) FROM t1 } } [expr (1<<$ii)] do_test wal-13.$tn.$ii.b { sql1 { SELECT count(*) FROM t1 } } [expr (1<<$ii)] do_test wal-13.$tn.$ii.c { sql1 { SELECT count(*) FROM t1 } } [expr (1<<$ii)] do_test wal-13.$tn.$ii.d { sql1 { PRAGMA integrity_check } } {ok} } } #------------------------------------------------------------------------- # Check a fun corruption case has been fixed. # # The problem was that after performing a checkpoint using a connection # that had an out-of-date pager-cache, the next time the connection was # used it did not realize the cache was out-of-date and proceeded to # operate with an inconsistent cache. Leading to corruption. # catch { db close } catch { db2 close } catch { db3 close } file delete -force test.db test.db-wal sqlite3 db test.db sqlite3 db2 test.db do_test wal-14 { execsql { PRAGMA journal_mode = WAL; CREATE TABLE t1(a PRIMARY KEY, b); INSERT INTO t1 VALUES(randomblob(10), randomblob(100)); INSERT INTO t1 SELECT randomblob(10), randomblob(100) FROM t1; INSERT INTO t1 SELECT randomblob(10), randomblob(100) FROM t1; INSERT INTO t1 SELECT randomblob(10), randomblob(100) FROM t1; } db2 eval { INSERT INTO t1 SELECT randomblob(10), randomblob(100); INSERT INTO t1 SELECT randomblob(10), randomblob(100); INSERT INTO t1 SELECT randomblob(10), randomblob(100); INSERT INTO t1 SELECT randomblob(10), randomblob(100); } # After executing the "PRAGMA wal_checkpoint", connection [db] was being # left with an inconsistent cache. Running the CREATE INDEX statement # in this state led to database corruption. catchsql { PRAGMA wal_checkpoint; CREATE INDEX i1 on t1(b); } db2 eval { PRAGMA integrity_check } } {ok} catch { db close } catch { db2 close } #------------------------------------------------------------------------- # The following block of tests - wal-15.* - focus on testing the # implementation of the sqlite3_wal_checkpoint() interface. # file delete -force test.db test.db-wal sqlite3 db test.db do_test wal-15.1 { execsql { PRAGMA auto_vacuum = 0; PRAGMA page_size = 1024; PRAGMA journal_mode = WAL; } execsql { CREATE TABLE t1(a, b); INSERT INTO t1 VALUES(1, 2); } } {} # Test that an error is returned if the database name is not recognized # do_test wal-15.2.1 { sqlite3_wal_checkpoint db aux } {SQLITE_ERROR} do_test wal-15.2.2 { sqlite3_errcode db } {SQLITE_ERROR} do_test wal-15.2.3 { sqlite3_errmsg db } {unknown database: aux} # Test that an error is returned if an attempt is made to checkpoint # if a transaction is open on the database. # do_test wal-15.3.1 { execsql { BEGIN; INSERT INTO t1 VALUES(3, 4); } sqlite3_wal_checkpoint db main } {SQLITE_LOCKED} do_test wal-15.3.2 { sqlite3_errcode db } {SQLITE_LOCKED} do_test wal-15.3.3 { sqlite3_errmsg db } {database table is locked} # Earlier versions returned an error is returned if the db cannot be # checkpointed because of locks held by another connection. Check that # this is no longer the case. # sqlite3 db2 test.db do_test wal-15.4.1 { execsql { BEGIN; SELECT * FROM t1; } db2 } {1 2} do_test wal-15.4.2 { execsql { COMMIT } sqlite3_wal_checkpoint db } {SQLITE_OK} do_test wal-15.4.3 { sqlite3_errmsg db } {not an error} # After [db2] drops its lock, [db] may checkpoint the db. # do_test wal-15.4.4 { execsql { COMMIT } db2 sqlite3_wal_checkpoint db } {SQLITE_OK} do_test wal-15.4.5 { sqlite3_errmsg db } {not an error} do_test wal-15.4.6 { file size test.db } [expr 1024*2] catch { db2 close } catch { db close } #------------------------------------------------------------------------- # The following block of tests - wal-16.* - test that if a NULL pointer or # an empty string is passed as the second argument of the wal_checkpoint() # API, an attempt is made to checkpoint all attached databases. # foreach {tn ckpt_cmd ckpt_res ckpt_main ckpt_aux} { 1 {sqlite3_wal_checkpoint db} SQLITE_OK 1 1 2 {sqlite3_wal_checkpoint db ""} SQLITE_OK 1 1 3 {db eval "PRAGMA wal_checkpoint"} {} 1 1 4 {sqlite3_wal_checkpoint db main} SQLITE_OK 1 0 5 {sqlite3_wal_checkpoint db aux} SQLITE_OK 0 1 6 {sqlite3_wal_checkpoint db temp} SQLITE_OK 0 0 7 {db eval "PRAGMA main.wal_checkpoint"} {} 1 0 8 {db eval "PRAGMA aux.wal_checkpoint"} {} 0 1 9 {db eval "PRAGMA temp.wal_checkpoint"} {} 0 0 } { do_test wal-16.$tn.1 { file delete -force test2.db test2.db-wal test2.db-journal file delete -force test.db test.db-wal test.db-journal sqlite3 db test.db execsql { ATTACH 'test2.db' AS aux; PRAGMA main.auto_vacuum = 0; PRAGMA aux.auto_vacuum = 0; PRAGMA main.journal_mode = WAL; PRAGMA aux.journal_mode = WAL; PRAGMA synchronous = NORMAL; } } {wal wal} do_test wal-16.$tn.2 { execsql { CREATE TABLE main.t1(a, b, PRIMARY KEY(a, b)); CREATE TABLE aux.t2(a, b, PRIMARY KEY(a, b)); INSERT INTO t2 VALUES(1, randomblob(1000)); INSERT INTO t2 VALUES(2, randomblob(1000)); INSERT INTO t1 SELECT * FROM t2; } list [file size test.db] [file size test.db-wal] } [list [expr 1*1024] [wal_file_size 10 1024]] do_test wal-16.$tn.3 { list [file size test2.db] [file size test2.db-wal] } [list [expr 1*1024] [wal_file_size 16 1024]] do_test wal-16.$tn.4 [list eval $ckpt_cmd] $ckpt_res do_test wal-16.$tn.5 { list [file size test.db] [file size test.db-wal] } [list [expr ($ckpt_main ? 7 : 1)*1024] [wal_file_size 10 1024]] do_test wal-16.$tn.6 { list [file size test2.db] [file size test2.db-wal] } [list [expr ($ckpt_aux ? 7 : 1)*1024] [wal_file_size 16 1024]] catch { db close } } #------------------------------------------------------------------------- # The following tests - wal-17.* - attempt to verify that the correct # number of "padding" frames are appended to the log file when a transaction # is committed in synchronous=FULL mode. # # Do this by creating a database that uses 512 byte pages. Then writing # a transaction that modifies 171 pages. In synchronous=NORMAL mode, this # produces a log file of: # # 32 + (24+512)*171 = 90312 bytes. # # Slightly larger than 11*8192 = 90112 bytes. # # Run the test using various different sector-sizes. In each case, the # WAL code should write the 90300 bytes of log file containing the # transaction, then append as may frames as are required to extend the # log file so that no part of the next transaction will be written into # a disk-sector used by transaction just committed. # set old_pending_byte [sqlite3_test_control_pending_byte 0x10000000] catch { db close } foreach {tn sectorsize logsize} " 1 128 [wal_file_size 172 512] 2 256 [wal_file_size 172 512] 3 512 [wal_file_size 172 512] 4 1024 [wal_file_size 172 512] 5 2048 [wal_file_size 172 512] 6 4096 [wal_file_size 176 512] 7 8192 [wal_file_size 184 512] " { file delete -force test.db test.db-wal test.db-journal sqlite3_simulate_device -sectorsize $sectorsize sqlite3 db test.db -vfs devsym do_test wal-17.$tn.1 { execsql { PRAGMA auto_vacuum = 0; PRAGMA page_size = 512; PRAGMA journal_mode = WAL; PRAGMA synchronous = FULL; } execsql { BEGIN; CREATE TABLE t(x); } for {set i 0} {$i<166} {incr i} { execsql { INSERT INTO t VALUES(randomblob(400)) } } execsql COMMIT file size test.db-wal } $logsize do_test wal-17.$tn.2 { file size test.db } 512 do_test wal-17.$tn.3 { db close file size test.db } [expr 512*171] } sqlite3_test_control_pending_byte $old_pending_byte #------------------------------------------------------------------------- # This test - wal-18.* - verifies a couple of specific conditions that # may be encountered while recovering a log file are handled correctly: # # wal-18.1.* When the first 32-bits of a frame checksum is correct but # the second 32-bits are false, and # # wal-18.2.* When the page-size field that occurs at the start of a log # file is a power of 2 greater than 16384 or smaller than 512. # file delete -force test.db test.db-wal test.db-journal do_test wal-18.0 { sqlite3 db test.db execsql { PRAGMA page_size = 1024; PRAGMA auto_vacuum = 0; PRAGMA journal_mode = WAL; PRAGMA synchronous = OFF; CREATE TABLE t1(a, b, UNIQUE(a, b)); INSERT INTO t1 VALUES(0, 0); PRAGMA wal_checkpoint; INSERT INTO t1 VALUES(1, 2); -- frames 1 and 2 INSERT INTO t1 VALUES(3, 4); -- frames 3 and 4 INSERT INTO t1 VALUES(5, 6); -- frames 5 and 6 } file copy -force test.db testX.db file copy -force test.db-wal testX.db-wal db close list [file size testX.db] [file size testX.db-wal] } [list [expr 3*1024] [wal_file_size 6 1024]] unset -nocomplain nFrame result foreach {nFrame result} { 0 {0 0} 1 {0 0} 2 {0 0 1 2} 3 {0 0 1 2} 4 {0 0 1 2 3 4} 5 {0 0 1 2 3 4} 6 {0 0 1 2 3 4 5 6} } { do_test wal-18.1.$nFrame { file copy -force testX.db test.db file copy -force testX.db-wal test.db-wal hexio_write test.db-wal [expr 24 + $nFrame*(24+1024) + 20] 00000000 sqlite3 db test.db execsql { SELECT * FROM t1; PRAGMA integrity_check; } } [concat $result ok] db close } proc randomblob {pgsz} { sqlite3 rbdb :memory: set blob [rbdb one {SELECT randomblob($pgsz)}] rbdb close set blob } proc logcksum {ckv1 ckv2 blob} { upvar $ckv1 c1 upvar $ckv2 c2 set scanpattern I* if {$::tcl_platform(byteOrder) eq "littleEndian"} { set scanpattern i* } binary scan $blob $scanpattern values foreach {v1 v2} $values { set c1 [expr {($c1 + $v1 + $c2)&0xFFFFFFFF}] set c2 [expr {($c2 + $v2 + $c1)&0xFFFFFFFF}] } } file copy -force test.db testX.db foreach {tn pgsz works} { 1 128 0 2 256 0 3 512 1 4 1024 1 5 2048 1 6 4096 1 7 8192 1 8 16384 1 9 32768 1 10 65536 0 11 1016 0 } { if {$::SQLITE_MAX_PAGE_SIZE < $pgsz} { set works 0 } for {set pg 1} {$pg <= 3} {incr pg} { file copy -force testX.db test.db file delete -force test.db-wal # Check that the database now exists and consists of three pages. And # that there is no associated wal file. # do_test wal-18.2.$tn.$pg.1 { file exists test.db-wal } 0 do_test wal-18.2.$tn.$pg.2 { file exists test.db } 1 do_test wal-18.2.$tn.$pg.3 { file size test.db } [expr 1024*3] do_test wal-18.2.$tn.$pg.4 { # Create a wal file that contains a single frame (database page # number $pg) with the commit flag set. The frame checksum is # correct, but the contents of the database page are corrupt. # # The page-size in the log file header is set to $pgsz. If the # WAL code considers $pgsz to be a valid SQLite database file page-size, # the database will be corrupt (because the garbage frame contents # will be treated as valid content). If $pgsz is invalid (too small # or too large), the db will not be corrupt as the log file will # be ignored. # set walhdr [binary format IIIIII 931071618 3007000 $pgsz 1234 22 23] set framebody [randomblob $pgsz] set framehdr [binary format IIII $pg 5 22 23] set c1 0 set c2 0 logcksum c1 c2 $walhdr append walhdr [binary format II $c1 $c2] logcksum c1 c2 [string range $framehdr 0 7] logcksum c1 c2 $framebody set framehdr [binary format IIIIII $pg 5 22 23 $c1 $c2] set fd [open test.db-wal w] fconfigure $fd -encoding binary -translation binary puts -nonewline $fd $walhdr puts -nonewline $fd $framehdr puts -nonewline $fd $framebody close $fd file size test.db-wal } [wal_file_size 1 $pgsz] do_test wal-18.2.$tn.$pg.5 { sqlite3 db test.db set rc [catch { db one {PRAGMA integrity_check} } msg] expr { $rc!=0 || $msg!="ok" } } $works db close } } #------------------------------------------------------------------------- # The following test - wal-19.* - fixes a bug that was present during # development. # # When a database connection in WAL mode is closed, it attempts an # EXCLUSIVE lock on the database file. If the lock is obtained, the # connection knows that it is the last connection to disconnect from # the database, so it runs a checkpoint operation. The bug was that # the connection was not updating its private copy of the wal-index # header before doing so, meaning that it could checkpoint an old # snapshot. # do_test wal-19.1 { file delete -force test.db test.db-wal test.db-journal sqlite3 db test.db sqlite3 db2 test.db execsql { PRAGMA journal_mode = WAL; CREATE TABLE t1(a, b); INSERT INTO t1 VALUES(1, 2); INSERT INTO t1 VALUES(3, 4); } execsql { SELECT * FROM t1 } db2 } {1 2 3 4} do_test wal-19.2 { execsql { INSERT INTO t1 VALUES(5, 6); SELECT * FROM t1; } } {1 2 3 4 5 6} do_test wal-19.3 { db close db2 close file exists test.db-wal } {0} do_test wal-19.4 { # When the bug was present, the following was returning {1 2 3 4} only, # as [db2] had an out-of-date copy of the wal-index header when it was # closed. # sqlite3 db test.db execsql { SELECT * FROM t1 } } {1 2 3 4 5 6} #------------------------------------------------------------------------- # This test - wal-20.* - uses two connections. One in this process and # the other in an external process. The procedure is: # # 1. Using connection 1, create the database schema. # # 2. Using connection 2 (in an external process), add so much # data to the database without checkpointing that a wal-index # larger than 64KB is required. # # 3. Using connection 1, checkpoint the database. Make sure all # the data is present and the database is not corrupt. # # At one point, SQLite was failing to grow the mapping of the wal-index # file in step 3 and the checkpoint was corrupting the database file. # do_test wal-20.1 { catch {db close} file delete -force test.db test.db-wal test.db-journal sqlite3 db test.db execsql { PRAGMA journal_mode = WAL; CREATE TABLE t1(x); INSERT INTO t1 VALUES(randomblob(900)); SELECT count(*) FROM t1; } } {wal 1} do_test wal-20.2 { set ::buddy [launch_testfixture] testfixture $::buddy { sqlite3 db test.db db transaction { db eval { PRAGMA wal_autocheckpoint = 0; INSERT INTO t1 SELECT randomblob(900) FROM t1; /* 2 */ INSERT INTO t1 SELECT randomblob(900) FROM t1; /* 4 */ INSERT INTO t1 SELECT randomblob(900) FROM t1; /* 8 */ INSERT INTO t1 SELECT randomblob(900) FROM t1; /* 16 */ INSERT INTO t1 SELECT randomblob(900) FROM t1; /* 32 */ INSERT INTO t1 SELECT randomblob(900) FROM t1; /* 64 */ INSERT INTO t1 SELECT randomblob(900) FROM t1; /* 128 */ INSERT INTO t1 SELECT randomblob(900) FROM t1; /* 256 */ INSERT INTO t1 SELECT randomblob(900) FROM t1; /* 512 */ INSERT INTO t1 SELECT randomblob(900) FROM t1; /* 1024 */ INSERT INTO t1 SELECT randomblob(900) FROM t1; /* 2048 */ INSERT INTO t1 SELECT randomblob(900) FROM t1; /* 4096 */ INSERT INTO t1 SELECT randomblob(900) FROM t1; /* 8192 */ INSERT INTO t1 SELECT randomblob(900) FROM t1; /* 16384 */ } } } } {0} do_test wal-20.3 { close $::buddy execsql { PRAGMA wal_checkpoint; SELECT count(*) FROM t1; } } {16384} do_test wal-20.4 { db close sqlite3 db test.db execsql { SELECT count(*) FROM t1 } } {16384} integrity_check wal-20.5 catch { db2 close } catch { db close } do_test wal-21.1 { faultsim_delete_and_reopen execsql { PRAGMA journal_mode = WAL; CREATE TABLE t1(a, b); INSERT INTO t1 VALUES(1, 2); INSERT INTO t1 VALUES(3, 4); INSERT INTO t1 VALUES(5, 6); INSERT INTO t1 VALUES(7, 8); INSERT INTO t1 VALUES(9, 10); INSERT INTO t1 VALUES(11, 12); } } {wal} do_test wal-21.2 { execsql { PRAGMA cache_size = 10; PRAGMA wal_checkpoint; BEGIN; SAVEPOINT s; INSERT INTO t1 SELECT randomblob(900), randomblob(900) FROM t1; ROLLBACK TO s; COMMIT; SELECT * FROM t1; } } {1 2 3 4 5 6 7 8 9 10 11 12} do_test wal-21.3 { execsql { PRAGMA integrity_check } } {ok} finish_test |
Added test/wal2.test.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 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 | # 2010 May 5 # # The author disclaims copyright to this source code. In place of # a legal notice, here is a blessing: # # May you do good and not evil. # May you find forgiveness for yourself and forgive others. # May you share freely, never taking more than you give. # #*********************************************************************** # This file implements regression tests for SQLite library. The # focus of this file is testing the operation of the library in # "PRAGMA journal_mode=WAL" mode. # set testdir [file dirname $argv0] source $testdir/tester.tcl source $testdir/lock_common.tcl source $testdir/malloc_common.tcl source $testdir/wal_common.tcl ifcapable !wal {finish_test ; return } proc set_tvfs_hdr {file args} { # Set $nHdr to the number of bytes in the wal-index header: set nHdr 48 set nInt [expr {$nHdr/4}] if {[llength $args]>2} { error {wrong # args: should be "set_tvfs_hdr fileName ?val1? ?val2?"} } set blob [tvfs shm $file] if {[llength $args]} { set ia [lindex $args 0] set ib $ia if {[llength $args]==2} { set ib [lindex $args 1] } binary scan $blob a[expr $nHdr*2]a* dummy tail set blob [binary format i${nInt}i${nInt}a* $ia $ib $tail] tvfs shm $file $blob } binary scan $blob i${nInt} ints return $ints } proc incr_tvfs_hdr {file idx incrval} { set ints [set_tvfs_hdr $file] set v [lindex $ints $idx] incr v $incrval lset ints $idx $v set_tvfs_hdr $file $ints } #------------------------------------------------------------------------- # Test case wal2-1.*: # # Set up a small database containing a single table. The database is not # checkpointed during the test - all content resides in the log file. # # Two connections are established to the database file - a writer ([db]) # and a reader ([db2]). For each of the 8 integer fields in the wal-index # header (6 fields and 2 checksum values), do the following: # # 1. Modify the database using the writer. # # 2. Attempt to read the database using the reader. Before the reader # has a chance to snapshot the wal-index header, increment one # of the the integer fields (so that the reader ends up with a corrupted # header). # # 3. Check that the reader recovers the wal-index and reads the correct # database content. # do_test wal2-1.0 { proc tvfs_cb {method filename args} { set ::filename $filename return SQLITE_OK } testvfs tvfs tvfs script tvfs_cb tvfs filter xShmOpen sqlite3 db test.db -vfs tvfs sqlite3 db2 test.db -vfs tvfs execsql { PRAGMA journal_mode = WAL; CREATE TABLE t1(a); } db2 execsql { INSERT INTO t1 VALUES(1); INSERT INTO t1 VALUES(2); INSERT INTO t1 VALUES(3); INSERT INTO t1 VALUES(4); SELECT count(a), sum(a) FROM t1; } } {4 10} do_test wal2-1.1 { execsql { SELECT count(a), sum(a) FROM t1 } db2 } {4 10} set RECOVER [list \ {0 1 lock exclusive} {1 7 lock exclusive} \ {1 7 unlock exclusive} {0 1 unlock exclusive} \ ] set READ [list \ {4 1 lock exclusive} {4 1 unlock exclusive} \ {4 1 lock shared} {4 1 unlock shared} \ ] foreach {tn iInsert res wal_index_hdr_mod wal_locks} " 2 5 {5 15} 0 {$RECOVER $READ} 3 6 {6 21} 1 {$RECOVER $READ} 4 7 {7 28} 2 {$RECOVER $READ} 5 8 {8 36} 3 {$RECOVER $READ} 6 9 {9 45} 4 {$RECOVER $READ} 7 10 {10 55} 5 {$RECOVER $READ} 8 11 {11 66} 6 {$RECOVER $READ} 9 12 {12 78} 7 {$RECOVER $READ} 10 13 {13 91} 8 {$RECOVER $READ} 11 14 {14 105} 9 {$RECOVER $READ} 12 15 {15 120} -1 {$READ} " { do_test wal2-1.$tn.1 { execsql { INSERT INTO t1 VALUES($iInsert) } set ::locks [list] proc tvfs_cb {method args} { lappend ::locks [lindex $args 2] return SQLITE_OK } tvfs filter xShmLock if {$::wal_index_hdr_mod >= 0} { incr_tvfs_hdr $::filename $::wal_index_hdr_mod 1 } execsql { SELECT count(a), sum(a) FROM t1 } db2 } $res do_test wal2-1.$tn.2 { set ::locks } $wal_locks } db close db2 close tvfs delete file delete -force test.db test.db-wal test.db-journal #------------------------------------------------------------------------- # This test case is very similar to the previous one, except, after # the reader reads the corrupt wal-index header, but before it has # a chance to re-read it under the cover of the RECOVER lock, the # wal-index header is replaced with a valid, but out-of-date, header. # # Because the header checksum looks Ok, the reader does not run recovery, # it simply drops back to a READ lock and proceeds. But because the # header is out-of-date, the reader reads the out-of-date snapshot. # # After this, the header is corrupted again and the reader is allowed # to run recovery. This time, it sees an up-to-date snapshot of the # database file. # set WRITER [list 0 1 lock exclusive] set LOCKS [list \ {0 1 lock exclusive} {0 1 unlock exclusive} \ {4 1 lock exclusive} {4 1 unlock exclusive} \ {4 1 lock shared} {4 1 unlock shared} \ ] do_test wal2-2.0 { testvfs tvfs tvfs script tvfs_cb tvfs filter xShmOpen proc tvfs_cb {method args} { set ::filename [lindex $args 0] return SQLITE_OK } sqlite3 db test.db -vfs tvfs sqlite3 db2 test.db -vfs tvfs execsql { PRAGMA journal_mode = WAL; CREATE TABLE t1(a); } db2 execsql { INSERT INTO t1 VALUES(1); INSERT INTO t1 VALUES(2); INSERT INTO t1 VALUES(3); INSERT INTO t1 VALUES(4); SELECT count(a), sum(a) FROM t1; } } {4 10} do_test wal2-2.1 { execsql { SELECT count(a), sum(a) FROM t1 } db2 } {4 10} foreach {tn iInsert res0 res1 wal_index_hdr_mod} { 2 5 {4 10} {5 15} 0 3 6 {5 15} {6 21} 1 4 7 {6 21} {7 28} 2 5 8 {7 28} {8 36} 3 6 9 {8 36} {9 45} 4 7 10 {9 45} {10 55} 5 8 11 {10 55} {11 66} 6 9 12 {11 66} {12 78} 7 } { tvfs filter xShmLock do_test wal2-2.$tn.1 { set oldhdr [set_tvfs_hdr $::filename] execsql { INSERT INTO t1 VALUES($iInsert) } execsql { SELECT count(a), sum(a) FROM t1 } } $res1 do_test wal2-2.$tn.2 { set ::locks [list] proc tvfs_cb {method args} { set lock [lindex $args 2] lappend ::locks $lock if {$lock == $::WRITER} { set_tvfs_hdr $::filename $::oldhdr } return SQLITE_OK } if {$::wal_index_hdr_mod >= 0} { incr_tvfs_hdr $::filename $::wal_index_hdr_mod 1 } execsql { SELECT count(a), sum(a) FROM t1 } db2 } $res0 do_test wal2-2.$tn.3 { set ::locks } $LOCKS do_test wal2-2.$tn.4 { set ::locks [list] proc tvfs_cb {method args} { set lock [lindex $args 2] lappend ::locks $lock return SQLITE_OK } if {$::wal_index_hdr_mod >= 0} { incr_tvfs_hdr $::filename $::wal_index_hdr_mod 1 } execsql { SELECT count(a), sum(a) FROM t1 } db2 } $res1 } db close db2 close tvfs delete file delete -force test.db test.db-wal test.db-journal if 0 { #------------------------------------------------------------------------- # This test case - wal2-3.* - tests the response of the library to an # SQLITE_BUSY when attempting to obtain a READ or RECOVER lock. # # wal2-3.0 - 2: SQLITE_BUSY when obtaining a READ lock # wal2-3.3 - 6: SQLITE_BUSY when obtaining a RECOVER lock # do_test wal2-3.0 { proc tvfs_cb {method args} { if {$method == "xShmLock"} { if {[info exists ::locked]} { return SQLITE_BUSY } } return SQLITE_OK } proc busyhandler x { if {$x>3} { unset -nocomplain ::locked } return 0 } testvfs tvfs tvfs script tvfs_cb sqlite3 db test.db -vfs tvfs db busy busyhandler execsql { PRAGMA journal_mode = WAL; CREATE TABLE t1(a); INSERT INTO t1 VALUES(1); INSERT INTO t1 VALUES(2); INSERT INTO t1 VALUES(3); INSERT INTO t1 VALUES(4); } set ::locked 1 info exists ::locked } {1} do_test wal2-3.1 { execsql { SELECT count(a), sum(a) FROM t1 } } {4 10} do_test wal2-3.2 { info exists ::locked } {0} do_test wal2-3.3 { proc tvfs_cb {method args} { if {$method == "xShmLock"} { if {[info exists ::sabotage]} { unset -nocomplain ::sabotage incr_tvfs_hdr [lindex $args 0] 1 1 } if {[info exists ::locked] && [lindex $args 2] == "RECOVER"} { return SQLITE_BUSY } } return SQLITE_OK } set ::sabotage 1 set ::locked 1 list [info exists ::sabotage] [info exists ::locked] } {1 1} do_test wal2-3.4 { execsql { SELECT count(a), sum(a) FROM t1 } } {4 10} do_test wal2-3.5 { list [info exists ::sabotage] [info exists ::locked] } {0 0} db close tvfs delete file delete -force test.db test.db-wal test.db-journal } #------------------------------------------------------------------------- # Test that a database connection using a VFS that does not support the # xShmXXX interfaces cannot open a WAL database. # do_test wal2-4.1 { sqlite3 db test.db execsql { PRAGMA journal_mode = WAL; CREATE TABLE data(x); INSERT INTO data VALUES('need xShmOpen to see this'); PRAGMA wal_checkpoint; } } {wal} do_test wal2-4.2 { db close testvfs tvfs -noshm 1 sqlite3 db test.db -vfs tvfs catchsql { SELECT * FROM data } } {1 {unable to open database file}} do_test wal2-4.3 { db close testvfs tvfs sqlite3 db test.db -vfs tvfs catchsql { SELECT * FROM data } } {0 {{need xShmOpen to see this}}} db close tvfs delete #------------------------------------------------------------------------- # Test that if a database connection is forced to run recovery before it # can perform a checkpoint, it does not transition into RECOVER state. # # UPDATE: This has now changed. When running a checkpoint, if recovery is # required the client grabs all exclusive locks (just as it would for a # recovery performed as a pre-cursor to a normal database transaction). # set expected_locks [list] lappend expected_locks {1 1 lock exclusive} ;# Lock checkpoint lappend expected_locks {0 1 lock exclusive} ;# Lock writer lappend expected_locks {2 6 lock exclusive} ;# Lock recovery & all aReadMark[] lappend expected_locks {2 6 unlock exclusive} ;# Unlock recovery & aReadMark[] lappend expected_locks {0 1 unlock exclusive} ;# Unlock writer lappend expected_locks {3 1 lock exclusive} ;# Lock aReadMark[0] lappend expected_locks {3 1 unlock exclusive} ;# Unlock aReadMark[0] lappend expected_locks {1 1 unlock exclusive} ;# Unlock checkpoint do_test wal2-5.1 { proc tvfs_cb {method args} { set ::shm_file [lindex $args 0] if {$method == "xShmLock"} { lappend ::locks [lindex $args 2] } return $::tvfs_cb_return } set tvfs_cb_return SQLITE_OK testvfs tvfs tvfs script tvfs_cb sqlite3 db test.db -vfs tvfs execsql { PRAGMA journal_mode = WAL; CREATE TABLE x(y); INSERT INTO x VALUES(1); } incr_tvfs_hdr $::shm_file 1 1 set ::locks [list] execsql { PRAGMA wal_checkpoint } set ::locks } $expected_locks db close tvfs delete #------------------------------------------------------------------------- # This block, test cases wal2-6.*, tests the operation of WAL with # "PRAGMA locking_mode=EXCLUSIVE" set. # # wal2-6.1.*: Changing to WAL mode before setting locking_mode=exclusive. # # wal2-6.2.*: Changing to WAL mode after setting locking_mode=exclusive. # # wal2-6.3.*: Changing back to rollback mode from WAL mode after setting # locking_mode=exclusive. # # wal2-6.4.*: Check that xShmLock calls are omitted in exclusive locking # mode. # # wal2-6.5.*: # # wal2-6.6.*: Check that if the xShmLock() to reaquire a WAL read-lock when # exiting exclusive mode fails (i.e. SQLITE_IOERR), then the # connection silently remains in exclusive mode. # do_test wal2-6.1.1 { file delete -force test.db test.db-wal test.db-journal sqlite3 db test.db execsql { Pragma Journal_Mode = Wal; Pragma Locking_Mode = Exclusive; } } {wal exclusive} do_test wal2-6.1.2 { execsql { PRAGMA lock_status } } {main unlocked temp closed} do_test wal2-6.1.3 { execsql { BEGIN; CREATE TABLE t1(a, b); INSERT INTO t1 VALUES(1, 2); COMMIT; PRAGMA lock_status; } } {main exclusive temp closed} do_test wal2-6.1.4 { execsql { PRAGMA locking_mode = normal; PRAGMA lock_status; } } {normal main exclusive temp closed} do_test wal2-6.1.5 { execsql { SELECT * FROM t1; PRAGMA lock_status; } } {1 2 main exclusive temp closed} do_test wal2-6.1.6 { execsql { INSERT INTO t1 VALUES(3, 4); PRAGMA lock_status; } } {main shared temp closed} db close do_test wal2-6.2.1 { file delete -force test.db test.db-wal test.db-journal sqlite3 db test.db execsql { Pragma Locking_Mode = Exclusive; Pragma Journal_Mode = Wal; Pragma Lock_Status; } } {exclusive wal main exclusive temp closed} do_test wal2-6.2.2 { execsql { BEGIN; CREATE TABLE t1(a, b); INSERT INTO t1 VALUES(1, 2); COMMIT; Pragma loCK_STATus; } } {main exclusive temp closed} do_test wal2-6.2.3 { db close sqlite3 db test.db execsql { PRAGMA LOCKING_MODE = EXCLUSIVE } } {exclusive} do_test wal2-6.2.4 { execsql { SELECT * FROM t1; pragma lock_status; } } {1 2 main shared temp closed} do_test wal2-6.2.5 { execsql { INSERT INTO t1 VALUES(3, 4); pragma lock_status; } } {main exclusive temp closed} do_test wal2-6.2.6 { execsql { PRAGMA locking_mode = NORMAL; pragma lock_status; } } {normal main exclusive temp closed} do_test wal2-6.2.7 { execsql { BEGIN IMMEDIATE; COMMIT; pragma lock_status; } } {main shared temp closed} do_test wal2-6.2.8 { execsql { PRAGMA locking_mode = EXCLUSIVE; BEGIN IMMEDIATE; COMMIT; PRAGMA locking_mode = NORMAL; } execsql { SELECT * FROM t1; pragma lock_status; } } {1 2 3 4 main exclusive temp closed} do_test wal2-6.2.9 { execsql { INSERT INTO t1 VALUES(5, 6); SELECT * FROM t1; pragma lock_status; } } {1 2 3 4 5 6 main shared temp closed} db close do_test wal2-6.3.1 { file delete -force test.db test.db-wal test.db-journal sqlite3 db test.db execsql { PRAGMA journal_mode = WAL; PRAGMA locking_mode = exclusive; BEGIN; CREATE TABLE t1(x); INSERT INTO t1 VALUES('Chico'); INSERT INTO t1 VALUES('Harpo'); COMMIT; } list [file exists test.db-wal] [file exists test.db-journal] } {1 0} do_test wal2-6.3.2 { execsql { PRAGMA journal_mode = DELETE } file exists test.db-wal } {0} do_test wal2-6.3.3 { execsql { PRAGMA lock_status } } {main exclusive temp closed} do_test wal2-6.3.4 { execsql { BEGIN; INSERT INTO t1 VALUES('Groucho'); } list [file exists test.db-wal] [file exists test.db-journal] } {0 1} do_test wal2-6.3.5 { execsql { PRAGMA lock_status } } {main exclusive temp closed} do_test wal2-6.3.6 { execsql { COMMIT } list [file exists test.db-wal] [file exists test.db-journal] } {0 1} do_test wal2-6.3.7 { execsql { PRAGMA lock_status } } {main exclusive temp closed} db close # This test - wal2-6.4.* - uses a single database connection and the # [testvfs] instrumentation to test that xShmLock() is being called # as expected when a WAL database is used with locking_mode=exclusive. # do_test wal2-6.4.1 { file delete -force test.db test.db-wal test.db-journal proc tvfs_cb {method args} { set ::shm_file [lindex $args 0] if {$method == "xShmLock"} { lappend ::locks [lindex $args 2] } return "SQLITE_OK" } testvfs tvfs tvfs script tvfs_cb sqlite3 db test.db -vfs tvfs } {} set RECOVERY { {0 1 lock exclusive} {1 7 lock exclusive} {1 7 unlock exclusive} {0 1 unlock exclusive} } set READMARK0_READ { {3 1 lock shared} {3 1 unlock shared} } set READMARK0_WRITE { {3 1 lock shared} {0 1 lock exclusive} {3 1 unlock shared} {4 1 lock exclusive} {4 1 unlock exclusive} {4 1 lock shared} {0 1 unlock exclusive} {4 1 unlock shared} } set READMARK1_SET { {4 1 lock exclusive} {4 1 unlock exclusive} } set READMARK1_READ { {4 1 lock shared} {4 1 unlock shared} } foreach {tn sql res expected_locks} { 2 { PRAGMA journal_mode = WAL; BEGIN; CREATE TABLE t1(x); INSERT INTO t1 VALUES('Leonard'); INSERT INTO t1 VALUES('Arthur'); COMMIT; } {wal} { $RECOVERY $READMARK0_WRITE } 3 { # This test should do the READMARK1_SET locking to populate the # aReadMark[1] slot with the current mxFrame value. Followed by # READMARK1_READ to read the database. # SELECT * FROM t1 } {Leonard Arthur} { $READMARK1_SET $READMARK1_READ } 4 { # aReadMark[1] is already set to mxFrame. So just READMARK1_READ # this time, not READMARK1_SET. # SELECT * FROM t1 ORDER BY x } {Arthur Leonard} { $READMARK1_READ } 5 { PRAGMA locking_mode = exclusive } {exclusive} { } 6 { INSERT INTO t1 VALUES('Julius Henry'); SELECT * FROM t1; } {Leonard Arthur {Julius Henry}} { $READMARK1_READ } 7 { INSERT INTO t1 VALUES('Karl'); SELECT * FROM t1; } {Leonard Arthur {Julius Henry} Karl} { } 8 { PRAGMA locking_mode = normal } {normal} { } 9 { SELECT * FROM t1 ORDER BY x } {Arthur {Julius Henry} Karl Leonard} { } 10 { DELETE FROM t1 } {} { $READMARK1_READ } 11 { SELECT * FROM t1 } {} { $READMARK1_SET $READMARK1_READ } } { set L [list] foreach el [subst $expected_locks] { lappend L $el } set S "" foreach sq [split $sql "\n"] { set sq [string trim $sq] if {[string match {#*} $sq]==0} {append S "$sq\n"} } set ::locks [list] do_test wal2-6.4.$tn.1 { execsql $S } $res do_test wal2-6.4.$tn.2 { set ::locks } $L } db close tvfs delete do_test wal2-6.5.1 { sqlite3 db test.db execsql { PRAGMA journal_mode = wal; PRAGMA locking_mode = exclusive; CREATE TABLE t2(a, b); PRAGMA wal_checkpoint; INSERT INTO t2 VALUES('I', 'II'); PRAGMA journal_mode; } } {wal exclusive wal} do_test wal2-6.5.2 { execsql { PRAGMA locking_mode = normal; INSERT INTO t2 VALUES('III', 'IV'); PRAGMA locking_mode = exclusive; SELECT * FROM t2; } } {normal exclusive I II III IV} do_test wal2-6.5.3 { execsql { PRAGMA wal_checkpoint } } {} db close proc lock_control {method filename handle spec} { foreach {start n op type} $spec break if {$op == "lock"} { return SQLITE_IOERR } return SQLITE_OK } do_test wal2-6.6.1 { testvfs T T script lock_control T filter {} sqlite3 db test.db -vfs T execsql { PRAGMA locking_mode = exclusive } execsql { INSERT INTO t2 VALUES('V', 'VI') } } {} do_test wal2-6.6.2 { execsql { PRAGMA locking_mode = normal } T filter xShmLock execsql { INSERT INTO t2 VALUES('VII', 'VIII') } } {} do_test wal2-6.6.3 { # At this point the connection should still be in exclusive-mode, even # though it tried to exit exclusive-mode when committing the INSERT # statement above. To exit exclusive mode, SQLite has to take a read-lock # on the WAL file using xShmLock(). Since that call failed, it remains # in exclusive mode. # sqlite3 db2 test.db -vfs T catchsql { SELECT * FROM t2 } db2 } {1 {database is locked}} do_test wal2-6.6.2 { db2 close T filter {} execsql { INSERT INTO t2 VALUES('IX', 'X') } } {} do_test wal2-6.6.3 { # This time, we have successfully exited exclusive mode. So the second # connection can read the database. sqlite3 db2 test.db -vfs T catchsql { SELECT * FROM t2 } db2 } {0 {I II III IV V VI VII VIII IX X}} db close db2 close T delete #------------------------------------------------------------------------- # Test a theory about the checksum algorithm. Theory was false and this # test did not provoke a bug. # file delete -force test.db test.db-wal test.db-journal do_test wal2-7.1.1 { sqlite3 db test.db execsql { PRAGMA page_size = 4096; PRAGMA journal_mode = WAL; CREATE TABLE t1(a, b); } file size test.db } {4096} do_test wal2-7.1.2 { file copy -force test.db test2.db file copy -force test.db-wal test2.db-wal hexio_write test2.db-wal 48 FF } {1} do_test wal2-7.1.3 { sqlite3 db2 test2.db execsql { PRAGMA wal_checkpoint } db2 execsql { SELECT * FROM sqlite_master } db2 } {} db close db2 close file delete -force test.db test.db-wal test.db-journal do_test wal2-8.1.2 { sqlite3 db test.db execsql { PRAGMA auto_vacuum=OFF; PRAGMA page_size = 1024; PRAGMA journal_mode = WAL; CREATE TABLE t1(x); INSERT INTO t1 VALUES(zeroblob(8188*1020)); CREATE TABLE t2(y); } execsql { PRAGMA wal_checkpoint; SELECT rootpage>=8192 FROM sqlite_master WHERE tbl_name = 't2'; } } {1} do_test wal2-8.1.3 { execsql { PRAGMA cache_size = 10; CREATE TABLE t3(z); BEGIN; INSERT INTO t3 VALUES(randomblob(900)); INSERT INTO t3 SELECT randomblob(900) FROM t3; INSERT INTO t2 VALUES('hello'); INSERT INTO t3 SELECT randomblob(900) FROM t3; INSERT INTO t3 SELECT randomblob(900) FROM t3; INSERT INTO t3 SELECT randomblob(900) FROM t3; INSERT INTO t3 SELECT randomblob(900) FROM t3; INSERT INTO t3 SELECT randomblob(900) FROM t3; INSERT INTO t3 SELECT randomblob(900) FROM t3; ROLLBACK; } execsql { INSERT INTO t2 VALUES('goodbye'); INSERT INTO t3 SELECT randomblob(900) FROM t3; INSERT INTO t3 SELECT randomblob(900) FROM t3; } } {} do_test wal2-8.1.4 { sqlite3 db2 test.db execsql { SELECT * FROM t2 } } {goodbye} db2 close db close #------------------------------------------------------------------------- # Test that even if the checksums for both are valid, if the two copies # of the wal-index header in the wal-index do not match, the client # runs (or at least tries to run) database recovery. # # proc get_name {method args} { set ::filename [lindex $args 0] ; tvfs filter {} } testvfs tvfs tvfs script get_name tvfs filter xShmOpen file delete -force test.db test.db-wal test.db-journal do_test wal2-9.1 { sqlite3 db test.db -vfs tvfs execsql { PRAGMA journal_mode = WAL; CREATE TABLE x(y); INSERT INTO x VALUES('Barton'); INSERT INTO x VALUES('Deakin'); } # Set $wih(1) to the contents of the wal-index header after # the frames associated with the first two rows in table 'x' have # been inserted. Then insert one more row and set $wih(2) # to the new value of the wal-index header. # # If the $wih(1) is written into the wal-index before running # a read operation, the client will see only the first two rows. If # $wih(2) is written into the wal-index, the client will see # three rows. If an invalid header is written into the wal-index, then # the client will run recovery and see three rows. # set wih(1) [set_tvfs_hdr $::filename] execsql { INSERT INTO x VALUES('Watson') } set wih(2) [set_tvfs_hdr $::filename] sqlite3 db2 test.db -vfs tvfs execsql { SELECT * FROM x } db2 } {Barton Deakin Watson} foreach {tn hdr1 hdr2 res} [list \ 3 $wih(1) $wih(1) {Barton Deakin} \ 4 $wih(1) $wih(2) {Barton Deakin Watson} \ 5 $wih(2) $wih(1) {Barton Deakin Watson} \ 6 $wih(2) $wih(2) {Barton Deakin Watson} \ 7 $wih(1) $wih(1) {Barton Deakin} \ 8 {0 0 0 0 0 0 0 0 0 0 0 0} {0 0 0 0 0 0 0 0 0 0 0 0} {Barton Deakin Watson} ] { do_test wal2-9.$tn { set_tvfs_hdr $::filename $hdr1 $hdr2 execsql { SELECT * FROM x } db2 } $res } db2 close db close #------------------------------------------------------------------------- # This block of tests - wal2-10.* - focus on the libraries response to # new versions of the wal or wal-index formats. # # wal2-10.1.*: Test that the library refuses to "recover" a new WAL # format. # # wal2-10.2.*: Test that the library refuses to read or write a database # if the wal-index version is newer than it understands. # # At time of writing, the only versions of the wal and wal-index formats # that exist are versions 3007000 (corresponding to SQLite version 3.7.0, # the first version of SQLite to feature wal mode). # do_test wal2-10.1.1 { faultsim_delete_and_reopen execsql { PRAGMA journal_mode = WAL; CREATE TABLE t1(a, b); PRAGMA wal_checkpoint; INSERT INTO t1 VALUES(1, 2); INSERT INTO t1 VALUES(3, 4); } faultsim_save_and_close } {} do_test wal2-10.1.2 { faultsim_restore_and_reopen execsql { SELECT * FROM t1 } } {1 2 3 4} do_test wal2-10.1.3 { faultsim_restore_and_reopen set hdr [wal_set_walhdr test.db-wal] lindex $hdr 1 } {3007000} do_test wal2-10.1.4 { lset hdr 1 3007001 wal_set_walhdr test.db-wal $hdr catchsql { SELECT * FROM t1 } } {1 {unable to open database file}} testvfs tvfs -default 1 do_test wal2-10.2.1 { faultsim_restore_and_reopen execsql { SELECT * FROM t1 } } {1 2 3 4} do_test wal2-10.2.2 { set hdr [set_tvfs_hdr $::filename] lindex $hdr 0 } {3007000} breakpoint do_test wal2-10.2.3 { lset hdr 0 3007001 wal_fix_walindex_cksum hdr set_tvfs_hdr $::filename $hdr catchsql { SELECT * FROM t1 } } {1 {unable to open database file}} db close tvfs delete #------------------------------------------------------------------------- # This block of tests - wal2-11.* - tests that it is not possible to put # the library into an infinite loop by presenting it with a corrupt # hash table (one that appears to contain a single chain of infinite # length). # # wal2-11.1.*: While reading the hash-table. # # wal2-11.2.*: While writing the hash-table. # testvfs tvfs -default 1 do_test wal2-11.0 { faultsim_delete_and_reopen execsql { PRAGMA journal_mode = WAL; CREATE TABLE t1(a, b, c); INSERT INTO t1 VALUES(1, 2, 3); INSERT INTO t1 VALUES(4, 5, 6); INSERT INTO t1 VALUES(7, 8, 9); SELECT * FROM t1; } } {wal 1 2 3 4 5 6 7 8 9} do_test wal2-11.1.1 { sqlite3 db2 test.db execsql { SELECT name FROM sqlite_master } db2 } {t1} # Set all zeroed slots in the first hash table to invalid values. # set blob [string range [tvfs shm $::filename] 0 16383] set I [string range [tvfs shm $::filename] 16384 end] binary scan $I t* L set I [list] foreach p $L { lappend I [expr $p ? $p : 400] } append blob [binary format t* $I] tvfs shm $::filename $blob do_test wal2-11.2 { catchsql { INSERT INTO t1 VALUES(10, 11, 12) } } {1 {database disk image is malformed}} # Fill up the hash table on the first page of shared memory with 0x55 bytes. # set blob [string range [tvfs shm $::filename] 0 16383] append blob [string repeat [binary format c 55] 16384] tvfs shm $::filename $blob do_test wal2-11.3 { catchsql { SELECT * FROM t1 } db2 } {1 {database disk image is malformed}} db close db2 close tvfs delete #------------------------------------------------------------------------- # If a connection is required to create a WAL or SHM file, it creates # the new files with the same file-system permissions as the database # file itself. Test this. # if {$::tcl_platform(platform) == "unix"} { faultsim_delete_and_reopen set umask [exec /bin/sh -c umask] do_test wal2-12.1 { sqlite3 db test.db execsql { CREATE TABLE tx(y, z); PRAGMA journal_mode = WAL; } db close list [file exists test.db-wal] [file exists test.db-shm] } {0 0} foreach {tn permissions} { 1 00644 2 00666 3 00600 4 00755 } { set effective [format %.5o [expr $permissions & ~$umask]] do_test wal2-12.2.$tn.1 { file attributes test.db -permissions $permissions file attributes test.db -permissions } $permissions do_test wal2-12.2.$tn.2 { list [file exists test.db-wal] [file exists test.db-shm] } {0 0} do_test wal2-12.2.$tn.3 { sqlite3 db test.db execsql { INSERT INTO tx DEFAULT VALUES } list [file exists test.db-wal] [file exists test.db-shm] } {1 1} do_test wal2-12.2.$tn.4 { list [file attr test.db-wal -perm] [file attr test.db-shm -perm] } [list $effective $effective] do_test wal2-12.2.$tn.5 { db close list [file exists test.db-wal] [file exists test.db-shm] } {0 0} } } #------------------------------------------------------------------------- # Test the libraries response to discovering that one or more of the # database, wal or shm files cannot be opened, or can only be opened # read-only. # if {$::tcl_platform(platform) == "unix"} { proc perm {} { set L [list] foreach f {test.db test.db-wal test.db-shm} { if {[file exists $f]} { lappend L [file attr $f -perm] } else { lappend L {} } } set L } faultsim_delete_and_reopen execsql { PRAGMA journal_mode = WAL; CREATE TABLE t1(a, b); PRAGMA wal_checkpoint; INSERT INTO t1 VALUES('3.14', '2.72'); } do_test wal2-13.1.1 { list [file exists test.db-shm] [file exists test.db-wal] } {1 1} faultsim_save_and_close foreach {tn db_perm wal_perm shm_perm can_open can_read can_write} { 2 00644 00644 00644 1 1 1 3 00644 00400 00644 1 1 0 4 00644 00644 00400 1 0 0 5 00400 00644 00644 1 1 0 7 00644 00000 00644 1 0 0 8 00644 00644 00000 1 0 0 9 00000 00644 00644 0 0 0 } { faultsim_restore do_test wal2-13.$tn.1 { file attr test.db -perm $db_perm file attr test.db-wal -perm $wal_perm file attr test.db-shm -perm $shm_perm set L [file attr test.db -perm] lappend L [file attr test.db-wal -perm] lappend L [file attr test.db-shm -perm] } [list $db_perm $wal_perm $shm_perm] # If $can_open is true, then it should be possible to open a database # handle. Otherwise, if $can_open is 0, attempting to open the db # handle throws an "unable to open database file" exception. # set r(1) {0 ok} set r(0) {1 {unable to open database file}} do_test wal2-13.$tn.2 { list [catch {sqlite3 db test.db ; set {} ok} msg] $msg } $r($can_open) if {$can_open} { # If $can_read is true, then the client should be able to read from # the database file. If $can_read is false, attempting to read should # throw the "unable to open database file" exception. # set a(0) {1 {unable to open database file}} set a(1) {0 {3.14 2.72}} do_test wal2-13.$tn.3 { catchsql { SELECT * FROM t1 } } $a($can_read) # Now try to write to the db file. If the client can read but not # write, then it should throw the familiar "unable to open db file" # exception. If it can read but not write, the exception should # be "attempt to write a read only database". # # If the client can read and write, the operation should succeed. # set b(0,0) {1 {unable to open database file}} set b(1,0) {1 {attempt to write a readonly database}} set b(1,1) {0 {}} do_test wal2-13.$tn.4 { catchsql { INSERT INTO t1 DEFAULT VALUES } } $b($can_read,$can_write) } catch { db close } } } finish_test |
Added test/wal3.test.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 | # 2010 April 13 # # The author disclaims copyright to this source code. In place of # a legal notice, here is a blessing: # # May you do good and not evil. # May you find forgiveness for yourself and forgive others. # May you share freely, never taking more than you give. # #*********************************************************************** # This file implements regression tests for SQLite library. The # focus of this file is testing the operation of the library in # "PRAGMA journal_mode=WAL" mode. # set testdir [file dirname $argv0] source $testdir/tester.tcl source $testdir/lock_common.tcl source $testdir/wal_common.tcl source $testdir/malloc_common.tcl ifcapable !wal {finish_test ; return } set a_string_counter 1 proc a_string {n} { global a_string_counter incr a_string_counter string range [string repeat "${a_string_counter}." $n] 1 $n } db func a_string a_string #------------------------------------------------------------------------- # When a rollback or savepoint rollback occurs, the client may remove # elements from one of the hash tables in the wal-index. This block # of test cases tests that nothing appears to go wrong when this is # done. # do_test wal3-1.0 { execsql { PRAGMA cache_size = 2000; PRAGMA page_size = 1024; PRAGMA auto_vacuum = off; PRAGMA synchronous = normal; PRAGMA journal_mode = WAL; PRAGMA wal_autocheckpoint = 0; BEGIN; CREATE TABLE t1(x); INSERT INTO t1 VALUES( a_string(800) ); /* 1 */ INSERT INTO t1 SELECT a_string(800) FROM t1; /* 2 */ INSERT INTO t1 SELECT a_string(800) FROM t1; /* 4 */ INSERT INTO t1 SELECT a_string(800) FROM t1; /* 8 */ INSERT INTO t1 SELECT a_string(800) FROM t1; /* 16 */ INSERT INTO t1 SELECT a_string(800) FROM t1; /* 32 */ INSERT INTO t1 SELECT a_string(800) FROM t1; /* 64 */ INSERT INTO t1 SELECT a_string(800) FROM t1; /* 128*/ INSERT INTO t1 SELECT a_string(800) FROM t1; /* 256 */ INSERT INTO t1 SELECT a_string(800) FROM t1; /* 512 */ INSERT INTO t1 SELECT a_string(800) FROM t1; /* 1024 */ INSERT INTO t1 SELECT a_string(800) FROM t1; /* 2048 */ INSERT INTO t1 SELECT a_string(800) FROM t1 LIMIT 1970; /* 4018 */ COMMIT; PRAGMA cache_size = 10; } wal_frame_count test.db-wal 1024 } 4056 for {set i 1} {$i < 50} {incr i} { do_test wal3-1.$i.1 { set str [a_string 800] execsql { UPDATE t1 SET x = $str WHERE rowid = $i } lappend L [wal_frame_count test.db-wal 1024] execsql { BEGIN; INSERT INTO t1 SELECT a_string(800) FROM t1 LIMIT 100; ROLLBACK; PRAGMA integrity_check; } } {ok} # Check that everything looks OK from the point of view of an # external connection. # sqlite3 db2 test.db do_test wal3-1.$i.2 { execsql { SELECT count(*) FROM t1 } db2 } 4018 do_test wal3-1.$i.3 { execsql { SELECT x FROM t1 WHERE rowid = $i } } $str do_test wal3-1.$i.4 { execsql { PRAGMA integrity_check } db2 } {ok} db2 close # Check that the file-system in its current state can be recovered. # file copy -force test.db test2.db file copy -force test.db-wal test2.db-wal file delete -force test2.db-journal sqlite3 db2 test2.db do_test wal3-1.$i.5 { execsql { SELECT count(*) FROM t1 } db2 } 4018 do_test wal3-1.$i.6 { execsql { SELECT x FROM t1 WHERE rowid = $i } } $str do_test wal3-1.$i.7 { execsql { PRAGMA integrity_check } db2 } {ok} db2 close } do_multiclient_test i { set testname(1) multiproc set testname(2) singleproc set tn $testname($i) do_test wal3-2.$tn.1 { sql1 { PRAGMA page_size = 1024; PRAGMA journal_mode = WAL; } sql1 { CREATE TABLE t1(a, b); INSERT INTO t1 VALUES(1, 'one'); BEGIN; SELECT * FROM t1; } } {1 one} do_test wal3-2.$tn.2 { sql2 { CREATE TABLE t2(a, b); INSERT INTO t2 VALUES(2, 'two'); BEGIN; SELECT * FROM t2; } } {2 two} do_test wal3-2.$tn.3 { sql3 { CREATE TABLE t3(a, b); INSERT INTO t3 VALUES(3, 'three'); BEGIN; SELECT * FROM t3; } } {3 three} # Try to checkpoint the database using [db]. It should be possible to # checkpoint everything except the table added by [db3] (checkpointing # these frames would clobber the snapshot currently being used by [db2]). # # After [db2] has committed, a checkpoint can copy the entire log to the # database file. Checkpointing after [db3] has committed is therefore a # no-op, as the entire log has already been backfilled. # do_test wal3-2.$tn.4 { sql1 { COMMIT; PRAGMA wal_checkpoint; } file size test.db } [expr $AUTOVACUUM ? 4*1024 : 3*1024] do_test wal3-2.$tn.5 { sql2 { COMMIT; PRAGMA wal_checkpoint; } file size test.db } [expr $AUTOVACUUM ? 5*1024 : 4*1024] do_test wal3-2.$tn.6 { sql3 { COMMIT; PRAGMA wal_checkpoint; } file size test.db } [expr $AUTOVACUUM ? 5*1024 : 4*1024] } catch {db close} #------------------------------------------------------------------------- # Test that that for the simple test: # # CREATE TABLE x(y); # INSERT INTO x VALUES('z'); # PRAGMA wal_checkpoint; # # in WAL mode the xSync method is invoked as expected for each of # synchronous=off, synchronous=normal and synchronous=full. # foreach {tn syncmode synccount} { 1 off {} 2 normal {test.db-wal normal test.db normal} 3 full {test.db-wal normal test.db-wal normal test.db-wal normal test.db normal} } { proc sync_counter {args} { foreach {method filename id flags} $args break lappend ::syncs [file tail $filename] $flags } do_test wal3-3.$tn { file delete -force test.db test.db-wal test.db-journal testvfs T T filter {} T script sync_counter sqlite3 db test.db -vfs T execsql "PRAGMA synchronous = $syncmode" execsql { PRAGMA journal_mode = WAL } set ::syncs [list] T filter xSync execsql { CREATE TABLE x(y); INSERT INTO x VALUES('z'); PRAGMA wal_checkpoint; } T filter {} set ::syncs } $synccount db close T delete } #------------------------------------------------------------------------- # When recovering the contents of a WAL file, a process obtains the WRITER # lock, then locks all other bytes before commencing recovery. If it fails # to lock all other bytes (because some other process is holding a read # lock) it should retry up to 100 times. Then return SQLITE_PROTOCOL to the # caller. Test this (test case wal3-4.3). # # Also test the effect of hitting an SQLITE_BUSY while attempting to obtain # the WRITER lock (should be the same). Test case wal3-4.4. # proc lock_callback {method filename handle lock} { lappend ::locks $lock } do_test wal3-4.1 { testvfs T T filter xShmLock T script lock_callback set ::locks [list] sqlite3 db test.db -vfs T execsql { SELECT * FROM x } lrange $::locks 0 3 } [list {0 1 lock exclusive} {1 7 lock exclusive} \ {1 7 unlock exclusive} {0 1 unlock exclusive} \ ] do_test wal3-4.2 { db close set ::locks [list] sqlite3 db test.db -vfs T execsql { SELECT * FROM x } lrange $::locks 0 3 } [list {0 1 lock exclusive} {1 7 lock exclusive} \ {1 7 unlock exclusive} {0 1 unlock exclusive} \ ] proc lock_callback {method filename handle lock} { if {$lock == "1 7 lock exclusive"} { return SQLITE_BUSY } return SQLITE_OK } puts " Warning: This next test case causes SQLite to call xSleep(1) 100 times." puts " Normally this equates to a 100ms delay, but if SQLite is built on unix" puts " without HAVE_USLEEP defined, it may be 100 seconds." do_test wal3-4.3 { db close set ::locks [list] sqlite3 db test.db -vfs T catchsql { SELECT * FROM x } } {1 {locking protocol}} puts " Warning: Same again!" proc lock_callback {method filename handle lock} { if {$lock == "0 1 lock exclusive"} { return SQLITE_BUSY } return SQLITE_OK } do_test wal3-4.4 { db close set ::locks [list] sqlite3 db test.db -vfs T catchsql { SELECT * FROM x } } {1 {locking protocol}} db close T delete #------------------------------------------------------------------------- # Only one client may run recovery at a time. Test this mechanism. # # When client-2 tries to open a read transaction while client-1 is # running recovery, it fails to obtain a lock on an aReadMark[] slot # (because they are all locked by recovery). It then tries to obtain # a shared lock on the RECOVER lock to see if there really is a # recovery running or not. # # This block of tests checks the effect of an SQLITE_BUSY or SQLITE_IOERR # being returned when client-2 attempts a shared lock on the RECOVER byte. # # An SQLITE_BUSY should be converted to an SQLITE_BUSY_RECOVERY. An # SQLITE_IOERR should be returned to the caller. # do_test wal3-5.1 { faultsim_delete_and_reopen execsql { PRAGMA journal_mode = WAL; CREATE TABLE t1(a, b); INSERT INTO t1 VALUES(1, 2); INSERT INTO t1 VALUES(3, 4); } faultsim_save_and_close } {} testvfs T -default 1 T script method_callback proc method_callback {method args} { if {$method == "xShmBarrier"} { incr ::barrier_count if {$::barrier_count == 2} { # This code is executed within the xShmBarrier() callback invoked # by the client running recovery as part of writing the recovered # wal-index header. If a second client attempts to access the # database now, it reads a corrupt (partially written) wal-index # header. But it cannot even get that far, as the first client # is still holding all the locks (recovery takes an exclusive lock # on *all* db locks, preventing access by any other client). # # If global variable ::wal3_do_lockfailure is non-zero, then set # things up so that an IO error occurs within an xShmLock() callback # made by the second client (aka [db2]). # sqlite3 db2 test.db if { $::wal3_do_lockfailure } { T filter xShmLock } set ::testrc [ catch { db2 eval "SELECT * FROM t1" } ::testmsg ] T filter {} db2 close } } if {$method == "xShmLock"} { foreach {file handle spec} $args break if { $spec == "2 1 lock shared" } { return SQLITE_IOERR } } return SQLITE_OK } # Test a normal SQLITE_BUSY return. # T filter xShmBarrier set testrc "" set testmsg "" set barrier_count 0 set wal3_do_lockfailure 0 do_test wal3-5.2 { faultsim_restore_and_reopen execsql { SELECT * FROM t1 } } {1 2 3 4} do_test wal3-5.3 { list $::testrc $::testmsg } {1 {database is locked}} db close # Test an SQLITE_IOERR return. # T filter xShmBarrier set barrier_count 0 set wal3_do_lockfailure 1 set testrc "" set testmsg "" do_test wal3-5.4 { faultsim_restore_and_reopen execsql { SELECT * FROM t1 } } {1 2 3 4} do_test wal3-5.5 { list $::testrc $::testmsg } {1 {disk I/O error}} db close T delete #------------------------------------------------------------------------- # When opening a read-transaction on a database, if the entire log has # already been copied to the database file, the reader grabs a special # kind of read lock (on aReadMark[0]). This set of test cases tests the # outcome of the following: # # + The reader discovering that between the time when it determined # that the log had been completely backfilled and the lock is obtained # that a writer has written to the log. In this case the reader should # acquire a different read-lock (not aReadMark[0]) and read the new # snapshot. # # + The attempt to obtain the lock on aReadMark[0] fails with SQLITE_BUSY. # This can happen if a checkpoint is ongoing. In this case also simply # obtain a different read-lock. # catch {db close} testvfs T -default 1 do_test wal3-6.1.1 { file delete -force test.db test.db-journal test.db wal sqlite3 db test.db execsql { PRAGMA journal_mode = WAL } execsql { CREATE TABLE t1(a, b); INSERT INTO t1 VALUES('o', 't'); INSERT INTO t1 VALUES('t', 'f'); } } {} do_test wal3-6.1.2 { sqlite3 db2 test.db sqlite3 db3 test.db execsql { BEGIN ; SELECT * FROM t1 } db3 } {o t t f} do_test wal3-6.1.3 { execsql { PRAGMA wal_checkpoint } db2 } {} # At this point the log file has been fully checkpointed. However, # connection [db3] holds a lock that prevents the log from being wrapped. # Test case 3.6.1.4 has [db] attempt a read-lock on aReadMark[0]. But # as it is obtaining the lock, [db2] appends to the log file. # T filter xShmLock T script lock_callback proc lock_callback {method file handle spec} { if {$spec == "3 1 lock shared"} { # This is the callback for [db] to obtain the read lock on aReadMark[0]. # Disable future callbacks using [T filter {}] and write to the log # file using [db2]. [db3] is preventing [db2] from wrapping the log # here, so this is an append. T filter {} db2 eval { INSERT INTO t1 VALUES('f', 's') } } return SQLITE_OK } do_test wal3-6.1.4 { execsql { BEGIN; SELECT * FROM t1; } } {o t t f f s} # [db] should be left holding a read-lock on some slot other than # aReadMark[0]. Test this by demonstrating that the read-lock is preventing # the log from being wrapped. # do_test wal3-6.1.5 { db3 eval COMMIT db2 eval { PRAGMA wal_checkpoint } set sz1 [file size test.db-wal] db2 eval { INSERT INTO t1 VALUES('s', 'e') } set sz2 [file size test.db-wal] expr {$sz2>$sz1} } {1} # Test that if [db2] had not interfered when [db] was trying to grab # aReadMark[0], it would have been possible to wrap the log in 3.6.1.5. # do_test wal3-6.1.6 { execsql { COMMIT } execsql { PRAGMA wal_checkpoint } db2 execsql { BEGIN; SELECT * FROM t1; } } {o t t f f s s e} do_test wal3-6.1.7 { db2 eval { PRAGMA wal_checkpoint } set sz1 [file size test.db-wal] db2 eval { INSERT INTO t1 VALUES('n', 't') } set sz2 [file size test.db-wal] expr {$sz2==$sz1} } {1} db3 close db2 close db close do_test wal3-6.2.1 { file delete -force test.db test.db-journal test.db wal sqlite3 db test.db sqlite3 db2 test.db execsql { PRAGMA journal_mode = WAL } execsql { CREATE TABLE t1(a, b); INSERT INTO t1 VALUES('h', 'h'); INSERT INTO t1 VALUES('l', 'b'); } } {} T filter xShmLock T script lock_callback proc lock_callback {method file handle spec} { if {$spec == "3 1 unlock exclusive"} { T filter {} set ::R [db2 eval { BEGIN; SELECT * FROM t1; }] } } do_test wal3-6.2.2 { execsql { PRAGMA wal_checkpoint } } {} do_test wal3-6.2.3 { set ::R } {h h l b} do_test wal3-6.2.4 { set sz1 [file size test.db-wal] execsql { INSERT INTO t1 VALUES('b', 'c'); } set sz2 [file size test.db-wal] expr {$sz2 > $sz1} } {1} do_test wal3-6.2.5 { db2 eval { COMMIT } execsql { PRAGMA wal_checkpoint } set sz1 [file size test.db-wal] execsql { INSERT INTO t1 VALUES('n', 'o'); } set sz2 [file size test.db-wal] expr {$sz2 == $sz1} } {1} db2 close db close T delete #------------------------------------------------------------------------- # When opening a read-transaction on a database, if the entire log has # not yet been copied to the database file, the reader grabs a read # lock on aReadMark[x], where x>0. The following test cases experiment # with the outcome of the following: # # + The reader discovering that between the time when it read the # wal-index header and the lock was obtained that a writer has # written to the log. In this case the reader should re-read the # wal-index header and lock a snapshot corresponding to the new # header. # # + The value in the aReadMark[x] slot has been modified since it was # read. # catch {db close} testvfs T -default 1 do_test wal3-7.1.1 { file delete -force test.db test.db-journal test.db wal sqlite3 db test.db execsql { PRAGMA journal_mode = WAL; CREATE TABLE blue(red PRIMARY KEY, green); } } {wal} T script method_callback T filter xOpen proc method_callback {method args} { if {$method == "xOpen"} { return "reader" } } do_test wal3-7.1.2 { sqlite3 db2 test.db execsql { SELECT * FROM blue } db2 } {} T filter xShmLock set ::locks [list] proc method_callback {method file handle spec} { if {$handle != "reader" } { return } if {$method == "xShmLock"} { catch { execsql { INSERT INTO blue VALUES(1, 2) } } catch { execsql { INSERT INTO blue VALUES(3, 4) } } } lappend ::locks $spec } do_test wal3-7.1.3 { execsql { SELECT * FROM blue } db2 } {1 2 3 4} do_test wal3-7.1.4 { set ::locks } {{4 1 lock shared} {4 1 unlock shared} {5 1 lock shared} {5 1 unlock shared}} set ::locks [list] proc method_callback {method file handle spec} { if {$handle != "reader" } { return } if {$method == "xShmLock"} { catch { execsql { INSERT INTO blue VALUES(5, 6) } } } lappend ::locks $spec } do_test wal3-7.2.1 { execsql { SELECT * FROM blue } db2 } {1 2 3 4 5 6} do_test wal3-7.2.2 { set ::locks } {{5 1 lock shared} {5 1 unlock shared} {4 1 lock shared} {4 1 unlock shared}} db close db2 close T delete #------------------------------------------------------------------------- # do_test wal3-8.1 { file delete -force test.db test.db-journal test.db wal sqlite3 db test.db sqlite3 db2 test.db execsql { PRAGMA journal_mode = WAL; CREATE TABLE b(c); INSERT INTO b VALUES('Tehran'); INSERT INTO b VALUES('Qom'); INSERT INTO b VALUES('Markazi'); PRAGMA wal_checkpoint; } } {wal} do_test wal3-8.2 { execsql { SELECT * FROM b } } {Tehran Qom Markazi} do_test wal3-8.3 { db eval { SELECT * FROM b } { db eval { INSERT INTO b VALUES('Qazvin') } set r [db2 eval { SELECT * FROM b }] break } set r } {Tehran Qom Markazi Qazvin} do_test wal3-8.4 { execsql { INSERT INTO b VALUES('Gilan'); INSERT INTO b VALUES('Ardabil'); } } {} db2 close faultsim_save_and_close testvfs T -default 1 faultsim_restore_and_reopen T filter xShmLock T script lock_callback proc lock_callback {method file handle spec} { if {$spec == "4 1 unlock exclusive"} { T filter {} set ::r [catchsql { SELECT * FROM b } db2] } } sqlite3 db test.db sqlite3 db2 test.db do_test wal3-8.5 { execsql { SELECT * FROM b } } {Tehran Qom Markazi Qazvin Gilan Ardabil} do_test wal3-8.6 { set ::r } {1 {locking protocol}} db close db2 close faultsim_restore_and_reopen sqlite3 db2 test.db T filter xShmLock T script lock_callback proc lock_callback {method file handle spec} { if {$spec == "1 7 unlock exclusive"} { T filter {} set ::r [catchsql { SELECT * FROM b } db2] } } unset ::r do_test wal3-8.5 { execsql { SELECT * FROM b } } {Tehran Qom Markazi Qazvin Gilan Ardabil} do_test wal3-8.6 { set ::r } {1 {locking protocol}} db close db2 close T delete #------------------------------------------------------------------------- # When a connection opens a read-lock on the database, it searches for # an aReadMark[] slot that is already set to the mxFrame value for the # new transaction. If it cannot find one, it attempts to obtain an # exclusive lock on an aReadMark[] slot for the purposes of modifying # the value, then drops back to a shared-lock for the duration of the # transaction. # # This test case verifies that if an exclusive lock cannot be obtained # on any aReadMark[] slot (because there are already several readers), # the client takes a shared-lock on a slot without modifying the value # and continues. # do_test wal3-9.0 { file delete -force test.db test.db-journal test.db wal sqlite3 db test.db execsql { PRAGMA journal_mode = WAL; CREATE TABLE whoami(x); INSERT INTO whoami VALUES('nobody'); } } {wal} for {set i 0} {$i < 50} {incr i} { set c db$i do_test wal3-9.1.$i { sqlite3 $c test.db execsql { UPDATE whoami SET x = $c } execsql { BEGIN; SELECT * FROM whoami } $c } $c } for {set i 0} {$i < 50} {incr i} { set c db$i do_test wal3-9.2.$i { execsql { SELECT * FROM whoami } $c } $c } do_test wal3-9.3 { for {set i 0} {$i < 49} {incr i} { db$i close } execsql { PRAGMA wal_checkpoint } set sz1 [file size test.db] db49 close execsql { PRAGMA wal_checkpoint } set sz2 [file size test.db] expr {$sz2 > $sz1} } {1} db close finish_test |
Added test/wal4.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 | # 2010 July 1 # # The author disclaims copyright to this source code. In place of # a legal notice, here is a blessing: # # May you do good and not evil. # May you find forgiveness for yourself and forgive others. # May you share freely, never taking more than you give. # #*********************************************************************** # Verify that an empty database and a non-empty WAL file do not # result in database corruption # set testdir [file dirname $argv0] source $testdir/tester.tcl source $testdir/malloc_common.tcl ifcapable !wal {finish_test ; return } do_test wal4-1.1 { execsql { PRAGMA journal_mode=WAL; CREATE TABLE t1(x); INSERT INTO t1 VALUES(1); INSERT INTO t1 VALUES(2); SELECT x FROM t1 ORDER BY x; } } {wal 1 2} do_test wal4-1.2 { # Save a copy of the file-system containing the wal and wal-index files # only (no database file). faultsim_save_and_close file delete -force sv_test.db } {} do_test wal4-1.3 { faultsim_restore_and_reopen catchsql { SELECT * FROM t1 } } {1 {no such table: t1}} do_faultsim_test wal4-2 -prep { faultsim_restore_and_reopen } -body { execsql { SELECT name FROM sqlite_master } } -test { # Result should be zero rows (empty db file). # faultsim_test_result {0 {}} # If the SELECT finished successfully, the WAL file should have been # deleted. In no case should the database file have been written, so # it should still be zero bytes in size regardless of whether or not # a fault was injected. Test these assertions: # if { $testrc==0 && [file exists test.db-wal] } { error "Wal file was not deleted" } if { [file size test.db]!=0 } { error "Db file grew to [file size test.db] bytes" } } finish_test |
Added test/wal_common.tcl.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 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 | # 2010 June 03 # # The author disclaims copyright to this source code. In place of # a legal notice, here is a blessing: # # May you do good and not evil. # May you find forgiveness for yourself and forgive others. # May you share freely, never taking more than you give. # #*********************************************************************** # # This file contains common code used by many different malloc tests # within the test suite. # proc wal_file_size {nFrame pgsz} { expr {32 + ($pgsz+24)*$nFrame} } proc wal_frame_count {zFile pgsz} { set f [file size $zFile] expr {($f - 32) / ($pgsz+24)} } proc wal_cksum_intlist {ckv1 ckv2 intlist} { upvar $ckv1 c1 upvar $ckv2 c2 foreach {v1 v2} $intlist { set c1 [expr {($c1 + $v1 + $c2)&0xFFFFFFFF}] set c2 [expr {($c2 + $v2 + $c1)&0xFFFFFFFF}] } } # This proc calculates checksums in the same way as those used by SQLite # in WAL files. If the $endian argument is "big", then checksums are # calculated by interpreting data as an array of big-endian integers. If # it is "little", data is interpreted as an array of little-endian integers. # proc wal_cksum {endian ckv1 ckv2 blob} { upvar $ckv1 c1 upvar $ckv2 c2 if {$endian!="big" && $endian!="little"} { return -error "Bad value \"$endian\" - must be \"big\" or \"little\"" } set scanpattern I* if {$endian == "little"} { set scanpattern i* } binary scan $blob $scanpattern values wal_cksum_intlist c1 c2 $values } proc wal_set_walhdr {filename {intlist {}}} { if {[llength $intlist]==6} { set blob [binary format I6 $intlist] set endian little if {[lindex $intlist 0] & 0x00000001} { set endian big } set c1 0 set c2 0 wal_cksum $endian c1 c2 $blob append blob [binary format II $c1 $c2] set fd [open $filename r+] fconfigure $fd -translation binary fconfigure $fd -encoding binary seek $fd 0 puts -nonewline $fd $blob close $fd } set fd [open $filename] fconfigure $fd -translation binary fconfigure $fd -encoding binary set blob [read $fd 24] close $fd binary scan $blob I6 ints set ints } proc wal_fix_walindex_cksum {hdrvar} { upvar $hdrvar hdr set c1 0 set c2 0 wal_cksum_intlist c1 c2 [lrange $hdr 0 9] lset hdr 10 $c1 lset hdr 11 $c2 } |
Added test/walbak.test.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 | # 2010 April 22 # # The author disclaims copyright to this source code. In place of # a legal notice, here is a blessing: # # May you do good and not evil. # May you find forgiveness for yourself and forgive others. # May you share freely, never taking more than you give. # #*********************************************************************** # This file implements regression tests for SQLite library. The # focus of this file is testing the operation of the library in # "PRAGMA journal_mode=WAL" mode. # set testdir [file dirname $argv0] source $testdir/tester.tcl source $testdir/wal_common.tcl source $testdir/malloc_common.tcl do_not_use_codec ifcapable !wal {finish_test ; return } # Test organization: # # walback-1.*: Simple tests. # # walback-2.*: Test backups when the source db is modified mid-backup. # # walback-3.*: Backup of WAL sources into rollback destinations, and # vice-versa. # # Make sure a simple backup from a WAL database works. # do_test walbak-1.0 { execsql { PRAGMA synchronous = NORMAL; PRAGMA page_size = 1024; PRAGMA auto_vacuum = 0; PRAGMA journal_mode = wal; BEGIN; CREATE TABLE t1(a PRIMARY KEY, b); INSERT INTO t1 VALUES('I', 'one'); COMMIT; } } {wal} do_test walbak-1.1 { file delete -force bak.db bak.db-journal bak.db-wal db backup bak.db file size bak.db } [expr 3*1024] do_test walbak-1.2 { sqlite3 db2 bak.db execsql { SELECT * FROM t1; PRAGMA main.journal_mode; } db2 } {I one wal} do_test walbak-1.3 { execsql { PRAGMA integrity_check } db2 } {ok} db2 close # Try a VACUUM on a WAL database. # do_test walbak-1.4 { execsql { VACUUM; PRAGMA main.journal_mode; } } {wal} do_test walbak-1.5 { list [file size test.db] [file size test.db-wal] } [list 1024 [wal_file_size 6 1024]] do_test walbak-1.6 { execsql { PRAGMA wal_checkpoint } list [file size test.db] [file size test.db-wal] } [list [expr 3*1024] [wal_file_size 6 1024]] do_test walbak-1.7 { execsql { CREATE TABLE t2(a, b); INSERT INTO t2 SELECT * FROM t1; DROP TABLE t1; } list [file size test.db] [file size test.db-wal] } [list [expr 3*1024] [wal_file_size 6 1024]] do_test walbak-1.8 { execsql { VACUUM } list [file size test.db] [file size test.db-wal] } [list [expr 3*1024] [wal_file_size 8 1024]] do_test walbak-1.9 { execsql { PRAGMA wal_checkpoint } list [file size test.db] [file size test.db-wal] } [list [expr 2*1024] [wal_file_size 8 1024]] #------------------------------------------------------------------------- # Backups when the source db is modified mid-backup. # proc sig {{db db}} { $db eval { PRAGMA integrity_check; SELECT md5sum(a, b) FROM t1; } } db close file delete test.db sqlite3 db test.db do_test walbak-2.1 { execsql { PRAGMA journal_mode = WAL } execsql { CREATE TABLE t1(a PRIMARY KEY, b); BEGIN; INSERT INTO t1 VALUES(randomblob(500), randomblob(500)); INSERT INTO t1 SELECT randomblob(500), randomblob(500) FROM t1; /* 2 */ INSERT INTO t1 SELECT randomblob(500), randomblob(500) FROM t1; /* 4 */ INSERT INTO t1 SELECT randomblob(500), randomblob(500) FROM t1; /* 8 */ INSERT INTO t1 SELECT randomblob(500), randomblob(500) FROM t1; /* 16 */ INSERT INTO t1 SELECT randomblob(500), randomblob(500) FROM t1; /* 32 */ INSERT INTO t1 SELECT randomblob(500), randomblob(500) FROM t1; /* 64 */ COMMIT; } } {} do_test walbak-2.2 { db backup abc.db sqlite3 db2 abc.db string compare [sig db] [sig db2] } {0} do_test walbak-2.3 { sqlite3_backup B db2 main db main B step 50 execsql { UPDATE t1 SET b = randomblob(500) } list [B step 1000] [B finish] } {SQLITE_DONE SQLITE_OK} do_test walbak-2.4 { string compare [sig db] [sig db2] } {0} do_test walbak-2.5 { db close sqlite3 db test.db execsql { PRAGMA cache_size = 10 } sqlite3_backup B db2 main db main B step 50 execsql { BEGIN; UPDATE t1 SET b = randomblob(500); } expr [file size test.db-wal] > 10*1024 } {1} do_test walbak-2.6 { B step 1000 } {SQLITE_BUSY} do_test walbak-2.7 { execsql COMMIT list [B step 1000] [B finish] } {SQLITE_DONE SQLITE_OK} do_test walbak-2.8 { string compare [sig db] [sig db2] } {0} do_test walbak-2.9 { db close sqlite3 db test.db execsql { PRAGMA cache_size = 10 } sqlite3_backup B db2 main db main B step 50 execsql { BEGIN; UPDATE t1 SET b = randomblob(500); } expr [file size test.db-wal] > 10*1024 } {1} do_test walbak-2.10 { B step 1000 } {SQLITE_BUSY} do_test walbak-2.11 { execsql ROLLBACK set sigB [sig db] list [B step 1000] [B finish] } {SQLITE_DONE SQLITE_OK} do_test walbak-2.12 { string compare [sig db] [sig db2] } {0} db2 close db close #------------------------------------------------------------------------- # Run some backup operations to copy back and forth between WAL and: # # walbak-3.1.*: an in-memory database # # walbak-3.2.*: a temporary database # # walbak-3.3.*: a database in rollback mode. # # walbak-3.4.*: a database in rollback mode that (initially) uses a # different page-size. # # Check that this does not confuse any connected clients. # foreach {tn setup} { 1 { sqlite3 db test.db sqlite3 db2 :memory: db eval { PRAGMA page_size = 1024 ; PRAGMA journal_mode = WAL } db2 eval { PRAGMA page_size = 1024 } } 2 { sqlite3 db test.db sqlite3 db2 "" db eval { PRAGMA page_size = 1024 ; PRAGMA journal_mode = WAL } db2 eval { PRAGMA page_size = 1024 } } 3 { sqlite3 db test.db sqlite3 db2 test.db2 db eval { PRAGMA page_size = 1024 ; PRAGMA journal_mode = WAL } db2 eval { PRAGMA page_size = 1024 ; PRAGMA journal_mode = PERSIST } } 4 { sqlite3 db test.db sqlite3 db2 test.db2 db eval { PRAGMA page_size = 1024 ; PRAGMA journal_mode = WAL } db2 eval { PRAGMA page_size = 2048; PRAGMA journal_mode = PERSIST; CREATE TABLE xx(x); } } } { foreach f [glob -nocomplain test.db*] { file delete -force $f } eval $setup do_test walbak-3.$tn.1 { execsql { CREATE TABLE t1(a, b); INSERT INTO t1 VALUES(1, 2); INSERT INTO t1 VALUES(3, 4); SELECT * FROM t1; } } {1 2 3 4} do_test walbak-3.$tn.2 { sqlite3_backup B db2 main db main B step 10000 B finish execsql { SELECT * FROM t1 } db2 } {1 2 3 4} do_test walbak-3.$tn.3 { execsql { INSERT INTO t1 VALUES(5, 6); INSERT INTO t1 VALUES(7, 8); SELECT * FROM t1; } db2 } {1 2 3 4 5 6 7 8} do_test walbak-3.$tn.4 { sqlite3_backup B db main db2 main B step 10000 B finish execsql { SELECT * FROM t1 } } {1 2 3 4 5 6 7 8} db close db2 close } finish_test |
Added test/walbig.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 | # 2010 July 07 # # The author disclaims copyright to this source code. In place of # a legal notice, here is a blessing: # # May you do good and not evil. # May you find forgiveness for yourself and forgive others. # May you share freely, never taking more than you give. # #*********************************************************************** # This file implements regression tests for SQLite library. The # focus of this script testing the ability of SQLite to handle database # files larger than 4GB in WAL mode. # set testdir [file dirname $argv0] source $testdir/tester.tcl # Do not use a codec for this file, as the database is manipulated using # external methods (the [fake_big_file] and [hexio_write] commands). # do_not_use_codec # If SQLITE_DISABLE_LFS is defined, omit this file. ifcapable !lfs { finish_test return } set a_string_counter 1 proc a_string {n} { incr ::a_string_counter string range [string repeat "${::a_string_counter}." $n] 1 $n } db func a_string a_string do_test walbig-1.0 { execsql { PRAGMA journal_mode = WAL; CREATE TABLE t1(a PRIMARY KEY, b UNIQUE); INSERT INTO t1 VALUES(a_string(300), a_string(500)); INSERT INTO t1 SELECT a_string(300), a_string(500) FROM t1; INSERT INTO t1 SELECT a_string(300), a_string(500) FROM t1; INSERT INTO t1 SELECT a_string(300), a_string(500) FROM t1; } } {wal} db close if {[catch {fake_big_file 5000 [pwd]/test.db}]} { puts "**** Unable to create a file larger than 5000 MB. *****" finish_test return } hexio_write test.db 28 00000000 sqlite3 db test.db db func a_string a_string do_test walbig-1.1 { execsql { INSERT INTO t1 SELECT a_string(300), a_string(500) FROM t1 } } {} db close sqlite3 db test.db do_test walbig-1.2 { execsql { SELECT a FROM t1 ORDER BY a } } [lsort [execsql { SELECT a FROM t1 ORDER BY rowid }]] do_test walbig-1.3 { execsql { SELECT b FROM t1 ORDER BY b } } [lsort [execsql { SELECT b FROM t1 ORDER BY rowid }]] finish_test |
Added test/walcksum.test.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 | # 2010 May 24 # # The author disclaims copyright to this source code. In place of # a legal notice, here is a blessing: # # May you do good and not evil. # May you find forgiveness for yourself and forgive others. # May you share freely, never taking more than you give. # #*********************************************************************** # set testdir [file dirname $argv0] source $testdir/tester.tcl source $testdir/lock_common.tcl source $testdir/wal_common.tcl ifcapable !wal {finish_test ; return } # Read and return the contents of file $filename. Treat the content as # binary data. # proc readfile {filename} { set fd [open $filename] fconfigure $fd -encoding binary fconfigure $fd -translation binary set data [read $fd] close $fd return $data } # # File $filename must be a WAL file on disk. Check that the checksum of frame # $iFrame in the file is correct when interpreting data as $endian-endian # integers ($endian must be either "big" or "little"). If the checksum looks # correct, return 1. Otherwise 0. # proc log_checksum_verify {filename iFrame endian} { set data [readfile $filename] foreach {offset c1 c2} [log_checksum_calc $data $iFrame $endian] {} binary scan [string range $data $offset [expr $offset+7]] II expect1 expect2 set expect1 [expr $expect1&0xFFFFFFFF] set expect2 [expr $expect2&0xFFFFFFFF] expr {$c1==$expect1 && $c2==$expect2} } # File $filename must be a WAL file on disk. Compute the checksum for frame # $iFrame in the file by interpreting data as $endian-endian integers # ($endian must be either "big" or "little"). Then write the computed # checksum into the file. # proc log_checksum_write {filename iFrame endian} { set data [readfile $filename] foreach {offset c1 c2} [log_checksum_calc $data $iFrame $endian] {} set bin [binary format II $c1 $c2] set fd [open $filename r+] fconfigure $fd -encoding binary fconfigure $fd -translation binary seek $fd $offset puts -nonewline $fd $bin close $fd } # Calculate and return the checksum for a particular frame in a WAL. # # Arguments are: # # $data Blob containing the entire contents of a WAL. # # $iFrame Frame number within the $data WAL. Frames are numbered # starting at 1. # # $endian One of "big" or "little". # # Returns a list of three elements, as follows: # # * The byte offset of the checksum belonging to frame $iFrame in the WAL. # * The first integer in the calculated version of the checksum. # * The second integer in the calculated version of the checksum. # proc log_checksum_calc {data iFrame endian} { binary scan [string range $data 8 11] I pgsz if {$iFrame > 1} { set n [wal_file_size [expr $iFrame-2] $pgsz] binary scan [string range $data [expr $n+16] [expr $n+23]] II c1 c2 } else { set c1 0 set c2 0 wal_cksum $endian c1 c2 [string range $data 0 23] } set n [wal_file_size [expr $iFrame-1] $pgsz] wal_cksum $endian c1 c2 [string range $data $n [expr $n+7]] wal_cksum $endian c1 c2 [string range $data [expr $n+24] [expr $n+24+$pgsz-1]] list [expr $n+16] $c1 $c2 } # # File $filename must be a WAL file on disk. Set the 'magic' field of the # WAL header to indicate that checksums are $endian-endian ($endian must be # either "big" or "little"). # # Also update the wal header checksum (since the wal header contents may # have changed). # proc log_checksum_writemagic {filename endian} { set val [expr {0x377f0682 | ($endian == "big" ? 1 : 0)}] set bin [binary format I $val] set fd [open $filename r+] fconfigure $fd -encoding binary fconfigure $fd -translation binary puts -nonewline $fd $bin seek $fd 0 set blob [read $fd 24] set c1 0 set c2 0 wal_cksum $endian c1 c2 $blob seek $fd 24 puts -nonewline $fd [binary format II $c1 $c2] close $fd } #------------------------------------------------------------------------- # Test cases walcksum-1.* attempt to verify the following: # # * That both native and non-native order checksum log files can # be recovered. # # * That when appending to native or non-native checksum log files # SQLite continues to use the right kind of checksums. # # * Test point 2 when the appending process is not one that recovered # the log file. # # * Test that both native and non-native checksum log files can be # checkpointed. And that after doing so the next write to the log # file occurs using native byte-order checksums. # set native "big" if {$::tcl_platform(byteOrder) == "littleEndian"} { set native "little" } foreach endian {big little} { # Create a database. Leave some data in the log file. # do_test walcksum-1.$endian.1 { catch { db close } file delete -force test.db test.db-wal test.db-journal sqlite3 db test.db execsql { PRAGMA page_size = 1024; PRAGMA auto_vacuum = 0; PRAGMA synchronous = NORMAL; CREATE TABLE t1(a PRIMARY KEY, b); INSERT INTO t1 VALUES(1, 'one'); INSERT INTO t1 VALUES(2, 'two'); INSERT INTO t1 VALUES(3, 'three'); INSERT INTO t1 VALUES(5, 'five'); PRAGMA journal_mode = WAL; INSERT INTO t1 VALUES(8, 'eight'); INSERT INTO t1 VALUES(13, 'thirteen'); INSERT INTO t1 VALUES(21, 'twentyone'); } file copy -force test.db test2.db file copy -force test.db-wal test2.db-wal db close list [file size test2.db] [file size test2.db-wal] } [list [expr 1024*3] [wal_file_size 6 1024]] # Verify that the checksums are valid for all frames and that they # are calculated by interpreting data in native byte-order. # for {set f 1} {$f <= 6} {incr f} { do_test walcksum-1.$endian.2.$f { log_checksum_verify test2.db-wal $f $native } 1 } # Replace all checksums in the current WAL file with $endian versions. # Then check that it is still possible to recover and read the database. # log_checksum_writemagic test2.db-wal $endian for {set f 1} {$f <= 6} {incr f} { do_test walcksum-1.$endian.3.$f { log_checksum_write test2.db-wal $f $endian log_checksum_verify test2.db-wal $f $endian } {1} } do_test walcksum-1.$endian.4.1 { file copy -force test2.db test.db file copy -force test2.db-wal test.db-wal sqlite3 db test.db execsql { SELECT a FROM t1 } } {1 2 3 5 8 13 21} # Following recovery, any frames written to the log should use the same # endianness as the existing frames. Check that this is the case. # do_test walcksum-1.$endian.5.0 { execsql { PRAGMA synchronous = NORMAL; INSERT INTO t1 VALUES(34, 'thirtyfour'); } list [file size test.db] [file size test.db-wal] } [list [expr 1024*3] [wal_file_size 8 1024]] for {set f 1} {$f <= 8} {incr f} { do_test walcksum-1.$endian.5.$f { log_checksum_verify test.db-wal $f $endian } {1} } # Now connect a second connection to the database. Check that this one # (not the one that did recovery) also appends frames to the log using # the same endianness for checksums as the existing frames. # do_test walcksum-1.$endian.6 { sqlite3 db2 test.db execsql { PRAGMA integrity_check; SELECT a FROM t1; } db2 } {ok 1 2 3 5 8 13 21 34} do_test walcksum-1.$endian.7.0 { execsql { PRAGMA synchronous = NORMAL; INSERT INTO t1 VALUES(55, 'fiftyfive'); } db2 list [file size test.db] [file size test.db-wal] } [list [expr 1024*3] [wal_file_size 10 1024]] for {set f 1} {$f <= 10} {incr f} { do_test walcksum-1.$endian.7.$f { log_checksum_verify test.db-wal $f $endian } {1} } # Now that both the recoverer and non-recoverer have added frames to the # log file, check that it can still be recovered. # file copy -force test.db test2.db file copy -force test.db-wal test2.db-wal do_test walcksum-1.$endian.7.11 { sqlite3 db3 test2.db execsql { PRAGMA integrity_check; SELECT a FROM t1; } db3 } {ok 1 2 3 5 8 13 21 34 55} db3 close # Run a checkpoint on the database file. Then, check that any frames written # to the start of the log use native byte-order checksums. # do_test walcksum-1.$endian.8.1 { execsql { PRAGMA wal_checkpoint; INSERT INTO t1 VALUES(89, 'eightynine'); } log_checksum_verify test.db-wal 1 $native } {1} do_test walcksum-1.$endian.8.2 { log_checksum_verify test.db-wal 2 $native } {1} do_test walcksum-1.$endian.8.3 { log_checksum_verify test.db-wal 3 $native } {0} do_test walcksum-1.$endian.9 { execsql { PRAGMA integrity_check; SELECT a FROM t1; } db2 } {ok 1 2 3 5 8 13 21 34 55 89} catch { db close } catch { db2 close } } #------------------------------------------------------------------------- # Test case walcksum-2.* tests that if a statement transaction is rolled # back after frames are written to the WAL, and then (after writing some # more) the outer transaction is committed, the WAL file is still correctly # formatted (and can be recovered by a second process if required). # do_test walcksum-2.1 { file delete -force test.db test.db-wal test.db-journal sqlite3 db test.db execsql { PRAGMA synchronous = NORMAL; PRAGMA page_size = 1024; PRAGMA journal_mode = WAL; PRAGMA cache_size = 10; CREATE TABLE t1(x PRIMARY KEY); PRAGMA wal_checkpoint; INSERT INTO t1 VALUES(randomblob(800)); BEGIN; INSERT INTO t1 SELECT randomblob(800) FROM t1; /* 2 */ INSERT INTO t1 SELECT randomblob(800) FROM t1; /* 4 */ INSERT INTO t1 SELECT randomblob(800) FROM t1; /* 8 */ INSERT INTO t1 SELECT randomblob(800) FROM t1; /* 16 */ SAVEPOINT one; INSERT INTO t1 SELECT randomblob(800) FROM t1; /* 32 */ INSERT INTO t1 SELECT randomblob(800) FROM t1; /* 64 */ INSERT INTO t1 SELECT randomblob(800) FROM t1; /* 128 */ INSERT INTO t1 SELECT randomblob(800) FROM t1; /* 256 */ ROLLBACK TO one; INSERT INTO t1 SELECT randomblob(800) FROM t1; /* 32 */ INSERT INTO t1 SELECT randomblob(800) FROM t1; /* 64 */ INSERT INTO t1 SELECT randomblob(800) FROM t1; /* 128 */ INSERT INTO t1 SELECT randomblob(800) FROM t1; /* 256 */ COMMIT; } file copy -force test.db test2.db file copy -force test.db-wal test2.db-wal sqlite3 db2 test2.db execsql { PRAGMA integrity_check; SELECT count(*) FROM t1; } db2 } {ok 256} catch { db close } catch { db2 close } #------------------------------------------------------------------------- # Test case walcksum-3.* tests that the checksum calculation detects single # byte changes to frame or frame-header data and considers the frame # invalid as a result. # do_test walcksum-3.1 { file delete -force test.db test.db-wal test.db-journal sqlite3 db test.db execsql { PRAGMA synchronous = NORMAL; PRAGMA page_size = 1024; CREATE TABLE t1(a, b); INSERT INTO t1 VALUES(1, randomblob(300)); INSERT INTO t1 VALUES(2, randomblob(300)); PRAGMA journal_mode = WAL; INSERT INTO t1 VALUES(3, randomblob(300)); } file size test.db-wal } [wal_file_size 1 1024] do_test walcksum-3.2 { file copy -force test.db-wal test2.db-wal file copy -force test.db test2.db sqlite3 db2 test2.db execsql { SELECT a FROM t1 } db2 } {1 2 3} db2 close file copy -force test.db test2.db foreach incr {1 2 3 20 40 60 80 100 120 140 160 180 200 220 240 253 254 255} { do_test walcksum-3.3.$incr { set FAIL 0 for {set iOff 0} {$iOff < [wal_file_size 1 1024]} {incr iOff} { file copy -force test.db-wal test2.db-wal set fd [open test2.db-wal r+] fconfigure $fd -encoding binary fconfigure $fd -translation binary seek $fd $iOff binary scan [read $fd 1] c x seek $fd $iOff puts -nonewline $fd [binary format c [expr {($x+$incr)&0xFF}]] close $fd sqlite3 db2 test2.db if { [execsql { SELECT a FROM t1 } db2] != "1 2" } {set FAIL 1} db2 close } set FAIL } {0} } finish_test |
Added test/walcrash.test.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 | # 2010 February 8 # # The author disclaims copyright to this source code. In place of # a legal notice, here is a blessing: # # May you do good and not evil. # May you find forgiveness for yourself and forgive others. # May you share freely, never taking more than you give. # #*********************************************************************** # This file implements regression tests for SQLite library. The # focus of this file is testing the operation of the library when # recovering a database following a simulated system failure in # "PRAGMA journal_mode=WAL" mode. # # # These are 'warm-body' tests of database recovery used while developing # the WAL code. They serve to prove that a few really simple cases work: # # walcrash-1.*: Recover a database. # walcrash-2.*: Recover a database where the failed transaction spanned more # than one page. # walcrash-3.*: Recover multiple databases where the failed transaction # was a multi-file transaction. # set testdir [file dirname $argv0] source $testdir/tester.tcl ifcapable !wal {finish_test ; return } db close set seed 0 set REPEATS 100 # walcrash-1.* # for {set i 1} {$i < $REPEATS} {incr i} { file delete -force test.db test.db-wal do_test walcrash-1.$i.1 { crashsql -delay 4 -file test.db-wal -seed [incr seed] { PRAGMA journal_mode = WAL; CREATE TABLE t1(a, b); INSERT INTO t1 VALUES(1, 1); INSERT INTO t1 VALUES(2, 3); INSERT INTO t1 VALUES(3, 6); } } {1 {child process exited abnormally}} do_test walcrash-1.$i.2 { sqlite3 db test.db execsql { SELECT sum(a)==max(b) FROM t1 } } {1} integrity_check walcrash-1.$i.3 db close do_test walcrash-1.$i.4 { crashsql -delay 2 -file test.db-wal -seed [incr seed] { INSERT INTO t1 VALUES(4, (SELECT sum(a) FROM t1) + 4); INSERT INTO t1 VALUES(5, (SELECT sum(a) FROM t1) + 5); } } {1 {child process exited abnormally}} do_test walcrash-1.$i.5 { sqlite3 db test.db execsql { SELECT sum(a)==max(b) FROM t1 } } {1} integrity_check walcrash-1.$i.6 do_test walcrash-1.$i.7 { execsql { PRAGMA main.journal_mode } } {wal} db close } # walcrash-2.* # for {set i 1} {$i < $REPEATS} {incr i} { file delete -force test.db test.db-wal do_test walcrash-2.$i.1 { crashsql -delay 4 -file test.db-wal -seed [incr seed] { PRAGMA journal_mode = WAL; CREATE TABLE t1(a PRIMARY KEY, b); INSERT INTO t1 VALUES(1, 2); INSERT INTO t1 VALUES(3, 4); INSERT INTO t1 VALUES(5, 9); } } {1 {child process exited abnormally}} do_test walcrash-2.$i.2 { sqlite3 db test.db execsql { SELECT sum(a)==max(b) FROM t1 } } {1} integrity_check walcrash-2.$i.3 db close do_test walcrash-2.$i.4 { crashsql -delay 2 -file test.db-wal -seed [incr seed] { INSERT INTO t1 VALUES(6, (SELECT sum(a) FROM t1) + 6); INSERT INTO t1 VALUES(7, (SELECT sum(a) FROM t1) + 7); } } {1 {child process exited abnormally}} do_test walcrash-2.$i.5 { sqlite3 db test.db execsql { SELECT sum(a)==max(b) FROM t1 } } {1} integrity_check walcrash-2.$i.6 do_test walcrash-2.$i.6 { execsql { PRAGMA main.journal_mode } } {wal} db close } # walcrash-3.* # # for {set i 1} {$i < $REPEATS} {incr i} { # file delete -force test.db test.db-wal # file delete -force test2.db test2.db-wal # # do_test walcrash-3.$i.1 { # crashsql -delay 2 -file test2.db-wal -seed [incr seed] { # PRAGMA journal_mode = WAL; # ATTACH 'test2.db' AS aux; # CREATE TABLE t1(a PRIMARY KEY, b); # CREATE TABLE aux.t2(a PRIMARY KEY, b); # BEGIN; # INSERT INTO t1 VALUES(1, 2); # INSERT INTO t2 VALUES(1, 2); # COMMIT; # } # } {1 {child process exited abnormally}} # # do_test walcrash-3.$i.2 { # sqlite3_wal db test.db # execsql { # ATTACH 'test2.db' AS aux; # SELECT * FROM t1 EXCEPT SELECT * FROM t2; # } # } {} # do_test walcrash-3.$i.3 { execsql { PRAGMA main.integrity_check } } {ok} # do_test walcrash-3.$i.4 { execsql { PRAGMA aux.integrity_check } } {ok} # # db close # } # walcrash-4.* # for {set i 1} {$i < $REPEATS} {incr i} { file delete -force test.db test.db-wal file delete -force test2.db test2.db-wal do_test walcrash-4.$i.1 { crashsql -delay 3 -file test.db-wal -seed [incr seed] -blocksize 4096 { PRAGMA journal_mode = WAL; PRAGMA page_size = 1024; CREATE TABLE t1(a PRIMARY KEY, b); INSERT INTO t1 VALUES(1, 2); INSERT INTO t1 VALUES(3, 4); } } {1 {child process exited abnormally}} do_test walcrash-4.$i.2 { sqlite3 db test.db execsql { SELECT * FROM t1 WHERE a = 1; } } {1 2} do_test walcrash-4.$i.3 { execsql { PRAGMA main.integrity_check } } {ok} do_test walcrash-4.$i.4 { execsql { PRAGMA main.journal_mode } } {wal} db close } # walcrash-5.* # for {set i 1} {$i < $REPEATS} {incr i} { file delete -force test.db test.db-wal file delete -force test2.db test2.db-wal do_test walcrash-5.$i.1 { crashsql -delay 11 -file test.db-wal -seed [incr seed] -blocksize 4096 { PRAGMA journal_mode = WAL; PRAGMA page_size = 1024; BEGIN; CREATE TABLE t1(x PRIMARY KEY); INSERT INTO t1 VALUES(randomblob(900)); INSERT INTO t1 VALUES(randomblob(900)); INSERT INTO t1 SELECT randomblob(900) FROM t1; /* 4 */ COMMIT; INSERT INTO t1 SELECT randomblob(900) FROM t1 LIMIT 4; /* 8 */ INSERT INTO t1 SELECT randomblob(900) FROM t1 LIMIT 4; /* 12 */ INSERT INTO t1 SELECT randomblob(900) FROM t1 LIMIT 4; /* 16 */ INSERT INTO t1 SELECT randomblob(900) FROM t1 LIMIT 4; /* 20 */ INSERT INTO t1 SELECT randomblob(900) FROM t1 LIMIT 4; /* 24 */ INSERT INTO t1 SELECT randomblob(900) FROM t1 LIMIT 4; /* 28 */ INSERT INTO t1 SELECT randomblob(900) FROM t1 LIMIT 4; /* 32 */ PRAGMA wal_checkpoint; INSERT INTO t1 VALUES(randomblob(900)); INSERT INTO t1 VALUES(randomblob(900)); INSERT INTO t1 VALUES(randomblob(900)); } } {1 {child process exited abnormally}} do_test walcrash-5.$i.2 { sqlite3 db test.db execsql { SELECT count(*)==33 OR count(*)==34 FROM t1 WHERE x != 1 } } {1} do_test walcrash-5.$i.3 { execsql { PRAGMA main.integrity_check } } {ok} do_test walcrash-5.$i.4 { execsql { PRAGMA main.journal_mode } } {wal} db close } # walcrash-6.* # for {set i 1} {$i < $REPEATS} {incr i} { file delete -force test.db test.db-wal file delete -force test2.db test2.db-wal do_test walcrash-6.$i.1 { crashsql -delay 12 -file test.db-wal -seed [incr seed] -blocksize 512 { PRAGMA journal_mode = WAL; PRAGMA page_size = 1024; BEGIN; CREATE TABLE t1(x PRIMARY KEY); INSERT INTO t1 VALUES(randomblob(900)); INSERT INTO t1 VALUES(randomblob(900)); INSERT INTO t1 SELECT randomblob(900) FROM t1; /* 4 */ COMMIT; INSERT INTO t1 SELECT randomblob(900) FROM t1 LIMIT 4; /* 8 */ INSERT INTO t1 SELECT randomblob(900) FROM t1 LIMIT 4; /* 12 */ INSERT INTO t1 SELECT randomblob(900) FROM t1 LIMIT 4; /* 16 */ INSERT INTO t1 SELECT randomblob(900) FROM t1 LIMIT 4; /* 20 */ INSERT INTO t1 SELECT randomblob(900) FROM t1 LIMIT 4; /* 24 */ INSERT INTO t1 SELECT randomblob(900) FROM t1 LIMIT 4; /* 28 */ INSERT INTO t1 SELECT randomblob(900) FROM t1 LIMIT 4; /* 32 */ PRAGMA wal_checkpoint; INSERT INTO t1 VALUES(randomblob(900)); INSERT INTO t1 VALUES(randomblob(900)); INSERT INTO t1 VALUES(randomblob(900)); } } {1 {child process exited abnormally}} do_test walcrash-6.$i.2 { sqlite3 db test.db execsql { SELECT count(*)==34 OR count(*)==35 FROM t1 WHERE x != 1 } } {1} do_test walcrash-6.$i.3 { execsql { PRAGMA main.integrity_check } } {ok} do_test walcrash-6.$i.4 { execsql { PRAGMA main.journal_mode } } {wal} db close } #------------------------------------------------------------------------- # This test case simulates a crash while checkpointing the database. Page # 1 is one of the pages overwritten by the checkpoint. This is a special # case because it means the content of page 1 may be damaged. SQLite will # have to determine: # # (a) that the database is a WAL database, and # (b) the database page-size # # based on the log file. # for {set i 1} {$i < $REPEATS} {incr i} { file delete -force test.db test.db-wal # Select a page-size for this test. # set pgsz [lindex {512 1024 2048 4096 8192 16384} [expr $i%6]] do_test walcrash-7.$i.1 { crashsql -delay 3 -file test.db -seed [incr seed] -blocksize 512 " PRAGMA page_size = $pgsz; PRAGMA journal_mode = wal; BEGIN; CREATE TABLE t1(a, b); INSERT INTO t1 VALUES(1, 2); COMMIT; PRAGMA wal_checkpoint; CREATE INDEX i1 ON t1(a); PRAGMA wal_checkpoint; " } {1 {child process exited abnormally}} do_test walcrash-7.$i.2 { sqlite3 db test.db execsql { SELECT b FROM t1 WHERE a = 1 } } {2} do_test walcrash-7.$i.3 { execsql { PRAGMA main.integrity_check } } {ok} do_test walcrash-7.$i.4 { execsql { PRAGMA main.journal_mode } } {wal} db close } finish_test |
Added test/walcrash2.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 | # 2010 May 25 # # The author disclaims copyright to this source code. In place of # a legal notice, here is a blessing: # # May you do good and not evil. # May you find forgiveness for yourself and forgive others. # May you share freely, never taking more than you give. # #*********************************************************************** # set testdir [file dirname $argv0] source $testdir/tester.tcl source $testdir/lock_common.tcl source $testdir/wal_common.tcl ifcapable !wal {finish_test ; return } #------------------------------------------------------------------------- # This test case demonstrates a flaw in the wal-index manipulation that # existed at one point: If a process crashes mid-transaction, it may have # already added some entries to one of the hash-tables in the wal-index. # If the transaction were to be explicitly rolled back at this point, the # hash-table entries would be removed as part of the rollback. However, # if the process crashes, the transaction is implicitly rolled back and # the rogue entries remain in the hash table. # # Normally, this causes no problem - readers can tell the difference # between committed and uncommitted entries in the hash table. However, # if it happens often enough that all slots in the hash-table become # non-zero, the next process that attempts to read or write the hash # table falls into an infinite loop. # # Even if run with an SQLite version affected by the bug, this test case # only goes into an infinite loop if SQLite is compiled without SQLITE_DEBUG # defined. If SQLITE_DEBUG is defined, the program is halted by a failing # assert() before entering the infinite loop. # # walcrash2-1.1: Create a database. Commit a transaction that adds 8 frames # to the WAL (and 8 entry to the first hash-table in the # wal-index). # # walcrash2-1.2: Have an external process open a transaction, add 8 entries # to the wal-index hash-table, then crash. Repeat this 1023 # times (so that the wal-index contains 8192 entries - all # slots are non-zero). # # walcrash2-1.3: Using a new database connection, attempt to query the # database. This should cause the process to go into the # infinite loop. # do_test walcrash2-1.1 { execsql { PRAGMA page_size = 1024; PRAGMA auto_vacuum = off; PRAGMA journal_mode = WAL; PRAGMA synchronous = NORMAL; BEGIN; CREATE TABLE t1(x); CREATE TABLE t2(x); CREATE TABLE t3(x); CREATE TABLE t4(x); CREATE TABLE t5(x); CREATE TABLE t6(x); CREATE TABLE t7(x); COMMIT; } file size test.db-wal } [wal_file_size 8 1024] for {set nEntry 8} {$nEntry < 8192} {incr nEntry 8} { do_test walcrash2-1.2.[expr $nEntry/8] { set C [launch_testfixture] testfixture $C { sqlite3 db test.db db eval { PRAGMA cache_size = 15; BEGIN; INSERT INTO t1 VALUES(randomblob(900)); -- 1 row, 1 page INSERT INTO t1 SELECT * FROM t1; -- 2 rows, 3 pages INSERT INTO t1 SELECT * FROM t1; -- 4 rows, 5 pages INSERT INTO t1 SELECT * FROM t1; -- 8 rows, 9 pages INSERT INTO t1 SELECT * FROM t1; -- 16 rows, 17 pages INSERT INTO t1 SELECT * FROM t1 LIMIT 3; -- 20 rows, 20 pages } } close $C file size test.db-wal } [wal_file_size 16 1024] } do_test walcrash2-1.3 { sqlite3 db2 test.db execsql { SELECT count(*) FROM t1 } db2 } {0} catch { db2 close } finish_test |
Added test/walfault.test.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 | # 2010 May 03 # # The author disclaims copyright to this source code. In place of # a legal notice, here is a blessing: # # May you do good and not evil. # May you find forgiveness for yourself and forgive others. # May you share freely, never taking more than you give. # #*********************************************************************** # This file implements regression tests for SQLite library. The # focus of this file is testing the operation of the library in # "PRAGMA journal_mode=WAL" mode. # set testdir [file dirname $argv0] source $testdir/tester.tcl source $testdir/malloc_common.tcl source $testdir/lock_common.tcl ifcapable !wal {finish_test ; return } #------------------------------------------------------------------------- # This test case, walfault-1-*, simulates faults while executing a # # PRAGMA journal_mode = WAL; # # statement immediately after creating a new database. # do_test walfault-1-pre-1 { faultsim_delete_and_reopen faultsim_save_and_close } {} do_faultsim_test walfault-1 -prep { faultsim_restore_and_reopen } -body { db eval { PRAGMA main.journal_mode = WAL } } -test { faultsim_test_result {0 wal} # Test that the connection that encountered an error as part of # "PRAGMA journal_mode = WAL" and a new connection use the same # journal mode when accessing the database. # # If "PRAGMA journal_mode" is executed immediately, connection [db] (the # one that hit the error in journal_mode="WAL") might return "wal" even # if it failed to switch the database to WAL mode. This is not considered # a problem. When it tries to read the database, connection [db] correctly # recognizes that it is a rollback database and switches back to a # rollback compatible journal mode. # if {[permutation] != "inmemory_journal"} { set jm [db one {SELECT * FROM sqlite_master ; PRAGMA main.journal_mode}] sqlite3 db2 test.db set jm2 [db2 one {SELECT * FROM sqlite_master ; PRAGMA main.journal_mode}] db2 close if { $jm!=$jm2 } { error "Journal modes do not match: $jm $jm2" } if { $testrc==0 && $jm!="wal" } { error "Journal mode is not WAL" } } } #-------------------------------------------------------------------------- # Test case walfault-2-* tests fault injection during recovery of a # short WAL file (a dozen frames or thereabouts). # do_test walfault-2-pre-1 { sqlite3 db test.db execsql { PRAGMA journal_mode = WAL; BEGIN; CREATE TABLE x(y, z, UNIQUE(y, z)); INSERT INTO x VALUES(randomblob(100), randomblob(100)); COMMIT; PRAGMA wal_checkpoint; INSERT INTO x SELECT randomblob(100), randomblob(100) FROM x; INSERT INTO x SELECT randomblob(100), randomblob(100) FROM x; INSERT INTO x SELECT randomblob(100), randomblob(100) FROM x; } execsql { SELECT count(*) FROM x } } {8} do_test walfault-2-pre-2 { faultsim_save_and_close faultsim_restore_and_reopen execsql { SELECT count(*) FROM x } } {8} do_faultsim_test walfault-2 -prep { faultsim_restore_and_reopen } -body { execsql { SELECT count(*) FROM x } } -test { faultsim_test_result {0 8} faultsim_integrity_check } #-------------------------------------------------------------------------- # Test fault injection while writing and checkpointing a small WAL file. # do_test walfault-3-pre-1 { sqlite3 db test.db execsql { PRAGMA auto_vacuum = 1; PRAGMA journal_mode = WAL; CREATE TABLE abc(a PRIMARY KEY); INSERT INTO abc VALUES(randomblob(1500)); } db close faultsim_save_and_close } {} do_faultsim_test walfault-3 -prep { faultsim_restore_and_reopen } -body { db eval { DELETE FROM abc; PRAGMA wal_checkpoint; } } -test { faultsim_test_result {0 {}} } #-------------------------------------------------------------------------- # if {[permutation] != "inmemory_journal"} { faultsim_delete_and_reopen faultsim_save_and_close do_faultsim_test walfault-4 -prep { faultsim_restore_and_reopen } -body { execsql { PRAGMA journal_mode = WAL; CREATE TABLE t1(a PRIMARY KEY, b); INSERT INTO t1 VALUES('a', 'b'); PRAGMA wal_checkpoint; SELECT * FROM t1; } } -test { faultsim_test_result {0 {wal a b}} faultsim_integrity_check } } #-------------------------------------------------------------------------- # do_test walfault-5-pre-1 { faultsim_delete_and_reopen execsql { PRAGMA page_size = 512; PRAGMA journal_mode = WAL; } faultsim_save_and_close } {} do_faultsim_test walfault-5 -faults shmerr* -prep { faultsim_restore_and_reopen execsql { PRAGMA wal_autocheckpoint = 0 } shmfault filter xShmMap } -body { execsql { CREATE TABLE t1(x); BEGIN; INSERT INTO t1 VALUES(randomblob(400)); /* 1 */ INSERT INTO t1 SELECT randomblob(400) FROM t1; /* 2 */ INSERT INTO t1 SELECT randomblob(400) FROM t1; /* 4 */ INSERT INTO t1 SELECT randomblob(400) FROM t1; /* 8 */ INSERT INTO t1 SELECT randomblob(400) FROM t1; /* 16 */ INSERT INTO t1 SELECT randomblob(400) FROM t1; /* 32 */ INSERT INTO t1 SELECT randomblob(400) FROM t1; /* 64 */ INSERT INTO t1 SELECT randomblob(400) FROM t1; /* 128 */ INSERT INTO t1 SELECT randomblob(400) FROM t1; /* 256 */ INSERT INTO t1 SELECT randomblob(400) FROM t1; /* 512 */ INSERT INTO t1 SELECT randomblob(400) FROM t1; /* 1024 */ INSERT INTO t1 SELECT randomblob(400) FROM t1; /* 2048 */ INSERT INTO t1 SELECT randomblob(400) FROM t1; /* 4096 */ INSERT INTO t1 SELECT randomblob(400) FROM t1; /* 8192 */ INSERT INTO t1 SELECT randomblob(400) FROM t1; /* 16384 */ COMMIT; SELECT count(*) FROM t1; } } -test { faultsim_test_result {0 16384} faultsim_integrity_check } #-------------------------------------------------------------------------- # do_test walfault-6-pre-1 { faultsim_delete_and_reopen execsql { PRAGMA page_size = 512; PRAGMA journal_mode = WAL; PRAGMA wal_autocheckpoint = 0; CREATE TABLE t1(x); BEGIN; INSERT INTO t1 VALUES(randomblob(400)); /* 1 */ INSERT INTO t1 SELECT randomblob(400) FROM t1; /* 2 */ INSERT INTO t1 SELECT randomblob(400) FROM t1; /* 4 */ INSERT INTO t1 SELECT randomblob(400) FROM t1; /* 8 */ INSERT INTO t1 SELECT randomblob(400) FROM t1; /* 16 */ INSERT INTO t1 SELECT randomblob(400) FROM t1; /* 32 */ INSERT INTO t1 SELECT randomblob(400) FROM t1; /* 64 */ INSERT INTO t1 SELECT randomblob(400) FROM t1; /* 128 */ INSERT INTO t1 SELECT randomblob(400) FROM t1; /* 256 */ INSERT INTO t1 SELECT randomblob(400) FROM t1; /* 512 */ INSERT INTO t1 SELECT randomblob(400) FROM t1; /* 1024 */ INSERT INTO t1 SELECT randomblob(400) FROM t1; /* 2048 */ INSERT INTO t1 SELECT randomblob(400) FROM t1; /* 4096 */ INSERT INTO t1 SELECT randomblob(400) FROM t1; /* 8192 */ INSERT INTO t1 SELECT randomblob(400) FROM t1; /* 16384 */ COMMIT; } faultsim_save_and_close } {} do_faultsim_test walfault-6 -faults shmerr* -prep { faultsim_restore_and_reopen shmfault filter xShmMap } -body { execsql { SELECT count(*) FROM t1 } } -test { faultsim_test_result {0 16384} faultsim_integrity_check set n [db one {SELECT count(*) FROM t1}] if {$n != 16384 && $n != 0} { error "Incorrect number of rows: $n" } } #-------------------------------------------------------------------------- # do_test walfault-7-pre-1 { faultsim_delete_and_reopen execsql { PRAGMA page_size = 512; PRAGMA journal_mode = WAL; PRAGMA wal_autocheckpoint = 0; CREATE TABLE t1(x); BEGIN; INSERT INTO t1 VALUES(randomblob(400)); /* 1 */ INSERT INTO t1 SELECT randomblob(400) FROM t1; /* 2 */ INSERT INTO t1 SELECT randomblob(400) FROM t1; /* 4 */ COMMIT; } faultsim_save_and_close } {} do_faultsim_test walfault-7 -prep { faultsim_restore_and_reopen } -body { execsql { SELECT count(*) FROM t1 } } -test { faultsim_test_result {0 4} set n [db one {SELECT count(*) FROM t1}] if {$n != 4 && $n != 0} { error "Incorrect number of rows: $n" } } #-------------------------------------------------------------------------- # do_test walfault-8-pre-1 { faultsim_delete_and_reopen execsql { PRAGMA journal_mode = WAL; CREATE TABLE abc(a PRIMARY KEY); INSERT INTO abc VALUES(randomblob(900)); } faultsim_save_and_close } {} do_faultsim_test walfault-8 -prep { faultsim_restore_and_reopen execsql { PRAGMA cache_size = 10 } } -body { execsql { BEGIN; INSERT INTO abc SELECT randomblob(900) FROM abc; /* 1 */ --INSERT INTO abc SELECT randomblob(900) FROM abc; /* 2 */ --INSERT INTO abc SELECT randomblob(900) FROM abc; /* 4 */ --INSERT INTO abc SELECT randomblob(900) FROM abc; /* 8 */ ROLLBACK; SELECT count(*) FROM abc; } } -test { faultsim_test_result {0 1} faultsim_integrity_check catch { db eval ROLLBACK } faultsim_integrity_check set n [db one {SELECT count(*) FROM abc}] if {$n != 1} { error "Incorrect number of rows: $n" } } #-------------------------------------------------------------------------- # do_test walfault-9-pre-1 { faultsim_delete_and_reopen execsql { PRAGMA journal_mode = WAL; CREATE TABLE abc(a PRIMARY KEY); INSERT INTO abc VALUES(randomblob(900)); } faultsim_save_and_close } {} do_faultsim_test walfault-9 -prep { #if {$iFail<73} { set iFail 73 } #if {$iFail>73} { exit } faultsim_restore_and_reopen execsql { PRAGMA cache_size = 10 } } -body { execsql { BEGIN; INSERT INTO abc SELECT randomblob(900) FROM abc; /* 1 */ SAVEPOINT spoint; INSERT INTO abc SELECT randomblob(900) FROM abc; /* 2 */ INSERT INTO abc SELECT randomblob(900) FROM abc; /* 4 */ INSERT INTO abc SELECT randomblob(900) FROM abc; /* 8 */ ROLLBACK TO spoint; COMMIT; SELECT count(*) FROM abc; } } -test { faultsim_test_result {0 2} faultsim_integrity_check catch { db eval { ROLLBACK TO spoint } } catch { db eval { COMMIT } } set n [db one {SELECT count(*) FROM abc}] if {$n != 1 && $n != 2} { error "Incorrect number of rows: $n" } } do_test walfault-10-pre1 { faultsim_delete_and_reopen execsql { PRAGMA journal_mode = WAL; PRAGMA wal_autocheckpoint = 0; CREATE TABLE z(zz INTEGER PRIMARY KEY, zzz BLOB); CREATE INDEX zzzz ON z(zzz); INSERT INTO z VALUES(NULL, randomblob(800)); INSERT INTO z VALUES(NULL, randomblob(800)); INSERT INTO z SELECT NULL, randomblob(800) FROM z; INSERT INTO z SELECT NULL, randomblob(800) FROM z; INSERT INTO z SELECT NULL, randomblob(800) FROM z; INSERT INTO z SELECT NULL, randomblob(800) FROM z; INSERT INTO z SELECT NULL, randomblob(800) FROM z; } faultsim_save_and_close } {} do_faultsim_test walfault-10 -prep { faultsim_restore_and_reopen execsql { PRAGMA cache_size = 10; BEGIN; UPDATE z SET zzz = randomblob(799); } set ::stmt [sqlite3_prepare db "SELECT zzz FROM z WHERE zz IN (1, 2, 3)" -1] sqlite3_step $::stmt } -body { execsql { INSERT INTO z VALUES(NULL, NULL) } } -test { sqlite3_finalize $::stmt faultsim_integrity_check faultsim_test_result {0 {}} catch { db eval { ROLLBACK } } faultsim_integrity_check set n [db eval {SELECT count(*), sum(length(zzz)) FROM z}] if {$n != "64 51200"} { error "Incorrect data: $n" } } #-------------------------------------------------------------------------- # Test fault injection while checkpointing a large WAL file, if the # checkpoint is the first operation run after opening the database. # This means that some of the required wal-index pages are mapped as part of # the checkpoint process, which means there are a few more opportunities # for IO errors. # # To speed this up, IO errors are only simulated within xShmMap() calls. # do_test walfault-11-pre-1 { sqlite3 db test.db execsql { PRAGMA journal_mode = WAL; PRAGMA wal_autocheckpoint = 0; BEGIN; CREATE TABLE abc(a PRIMARY KEY); INSERT INTO abc VALUES(randomblob(1500)); INSERT INTO abc VALUES(randomblob(1500)); INSERT INTO abc SELECT randomblob(1500) FROM abc; -- 4 INSERT INTO abc SELECT randomblob(1500) FROM abc; -- 8 INSERT INTO abc SELECT randomblob(1500) FROM abc; -- 16 INSERT INTO abc SELECT randomblob(1500) FROM abc; -- 32 INSERT INTO abc SELECT randomblob(1500) FROM abc; -- 64 INSERT INTO abc SELECT randomblob(1500) FROM abc; -- 128 INSERT INTO abc SELECT randomblob(1500) FROM abc; -- 256 INSERT INTO abc SELECT randomblob(1500) FROM abc; -- 512 INSERT INTO abc SELECT randomblob(1500) FROM abc; -- 1024 INSERT INTO abc SELECT randomblob(1500) FROM abc; -- 2048 INSERT INTO abc SELECT randomblob(1500) FROM abc; -- 4096 COMMIT; } faultsim_save_and_close } {} do_faultsim_test walfault-11 -faults shmerr* -prep { catch { db2 close } faultsim_restore_and_reopen shmfault filter xShmMap } -body { db eval { SELECT count(*) FROM abc } sqlite3 db2 test.db -vfs shmfault db2 eval { PRAGMA wal_checkpoint } } -test { faultsim_test_result {0 {}} } #------------------------------------------------------------------------- # Test the handling of the various IO/OOM/SHM errors that may occur during # a log recovery operation undertaken as part of a call to # sqlite3_wal_checkpoint(). # do_test walfault-12-pre-1 { faultsim_delete_and_reopen execsql { PRAGMA journal_mode = WAL; PRAGMA wal_autocheckpoint = 0; BEGIN; CREATE TABLE abc(a PRIMARY KEY); INSERT INTO abc VALUES(randomblob(1500)); INSERT INTO abc VALUES(randomblob(1500)); COMMIT; } faultsim_save_and_close } {} do_faultsim_test walfault-12 -prep { if {[info commands shmfault] == ""} { testvfs shmfault -default true } faultsim_restore_and_reopen db eval { SELECT * FROM sqlite_master } shmfault shm test.db [string repeat "\000" 40] } -body { set rc [sqlite3_wal_checkpoint db] if {$rc != "SQLITE_OK"} { error [sqlite3_errmsg db] } } -test { db close faultsim_test_result {0 {}} } finish_test |
Added test/walhook.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 | # 2010 April 19 # # The author disclaims copyright to this source code. In place of # a legal notice, here is a blessing: # # May you do good and not evil. # May you find forgiveness for yourself and forgive others. # May you share freely, never taking more than you give. # #*********************************************************************** # This file implements regression tests for SQLite library. The # focus of this file is testing the operation of the library in # "PRAGMA journal_mode=WAL" mode. # # More specifically, this file contains regression tests for the # sqlite3_wal_hook() mechanism, including the sqlite3_wal_autocheckpoint() # and "PRAGMA wal_autocheckpoint" convenience interfaces. # set testdir [file dirname $argv0] source $testdir/tester.tcl source $testdir/wal_common.tcl ifcapable !wal {finish_test ; return } set ::wal_hook [list] proc wal_hook {zDb nEntry} { lappend ::wal_hook $zDb $nEntry return 0 } db wal_hook wal_hook do_test walhook-1.1 { execsql { PRAGMA page_size = 1024; PRAGMA auto_vacuum = 0; PRAGMA journal_mode = wal; PRAGMA synchronous = normal; CREATE TABLE t1(i PRIMARY KEY, j); } set ::wal_hook } {main 3} do_test walhook-1.2 { set ::wal_hook [list] execsql { INSERT INTO t1 VALUES(1, 'one') } set ::wal_hook } {main 5} do_test walhook-1.3 { proc wal_hook {args} { db eval {PRAGMA wal_checkpoint}; return 0 } execsql { INSERT INTO t1 VALUES(2, 'two') } file size test.db } [expr 3*1024] do_test walhook-1.4 { proc wal_hook {zDb nEntry} { execsql { PRAGMA wal_checkpoint } return 0 } execsql { CREATE TABLE t2(a, b) } file size test.db } [expr 4*1024] do_test walhook-1.5 { sqlite3 db2 test.db proc wal_hook {zDb nEntry} { execsql { PRAGMA wal_checkpoint } db2 return 0 } execsql { CREATE TABLE t3(a PRIMARY KEY, b) } file size test.db } [expr 6*1024] db2 close db close sqlite3 db test.db do_test walhook-2.1 { execsql { PRAGMA synchronous = NORMAL } execsql { PRAGMA wal_autocheckpoint } } {1000} do_test walhook-2.2 { execsql { PRAGMA wal_autocheckpoint = 10} } {10} do_test walhook-2.3 { execsql { PRAGMA wal_autocheckpoint } } {10} # # The database connection is configured with "PRAGMA wal_autocheckpoint = 10". # Check that transactions are written to the log file until it contains at # least 10 frames, then the database is checkpointed. Subsequent transactions # are written into the start of the log file. # foreach {tn sql dbpages logpages} { 4 "CREATE TABLE t4(x PRIMARY KEY, y)" 6 3 5 "INSERT INTO t4 VALUES(1, 'one')" 6 5 6 "INSERT INTO t4 VALUES(2, 'two')" 6 7 7 "INSERT INTO t4 VALUES(3, 'three')" 6 9 8 "INSERT INTO t4 VALUES(4, 'four')" 8 11 9 "INSERT INTO t4 VALUES(5, 'five')" 8 11 } { do_test walhook-2.$tn { execsql $sql list [file size test.db] [file size test.db-wal] } [list [expr $dbpages*1024] [wal_file_size $logpages 1024]] } catch { db2 close } catch { db close } finish_test |
Added test/walmode.test.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 | # 2010 April 19 # # The author disclaims copyright to this source code. In place of # a legal notice, here is a blessing: # # May you do good and not evil. # May you find forgiveness for yourself and forgive others. # May you share freely, never taking more than you give. # #*********************************************************************** # This file implements regression tests for SQLite library. The # focus of this file is testing the operation of the library in # "PRAGMA journal_mode=WAL" mode. # set testdir [file dirname $argv0] source $testdir/tester.tcl source $testdir/malloc_common.tcl # If the library was compiled without WAL support, check that the # "PRAGMA journal_mode=WAL" treats "WAL" as an unrecognized mode. # ifcapable !wal { do_test walmode-0.1 { execsql { PRAGMA journal_mode = wal } } {delete} do_test walmode-0.2 { execsql { PRAGMA main.journal_mode = wal } } {delete} do_test walmode-0.3 { execsql { PRAGMA main.journal_mode } } {delete} finish_test return } do_test walmode-1.1 { set sqlite_sync_count 0 execsql { PRAGMA page_size = 1024 } execsql { PRAGMA journal_mode = wal } } {wal} do_test walmode-1.2 { file size test.db } {1024} set expected_sync_count 3 if {$::tcl_platform(platform)!="windows"} { ifcapable dirsync { incr expected_sync_count } } do_test walmode-1.3 { set sqlite_sync_count } $expected_sync_count do_test walmode-1.4 { file exists test.db-wal } {0} do_test walmode-1.5 { execsql { CREATE TABLE t1(a, b) } file size test.db } {1024} do_test walmode-1.6 { file exists test.db-wal } {1} do_test walmode-1.7 { db close file exists test.db-wal } {0} # There is now a database file with the read and write versions set to 2 # in the file system. This file should default to WAL mode. # do_test walmode-2.1 { sqlite3 db test.db file exists test.db-wal } {0} do_test walmode-2.2 { execsql { SELECT * FROM sqlite_master } file exists test.db-wal } {1} do_test walmode-2.3 { db close file exists test.db-wal } {0} # If the first statement executed is "PRAGMA journal_mode = wal", and # the file is already configured for WAL (read and write versions set # to 2), then there should be no need to write the database. The # statement should cause the client to connect to the log file. # set sqlite_sync_count 0 do_test walmode-3.1 { sqlite3 db test.db execsql { PRAGMA journal_mode = wal } } {wal} do_test walmode-3.2 { list $sqlite_sync_count [file exists test.db-wal] [file size test.db-wal] } {0 1 0} # Test that changing back to journal_mode=persist works. # do_test walmode-4.1 { execsql { INSERT INTO t1 VALUES(1, 2) } execsql { PRAGMA journal_mode = persist } } {persist} do_test walmode-4.2 { list [file exists test.db-journal] [file exists test.db-wal] } {1 0} do_test walmode-4.3 { execsql { SELECT * FROM t1 } } {1 2} do_test walmode-4.4 { db close sqlite3 db test.db execsql { SELECT * FROM t1 } } {1 2} do_test walmode-4.5 { list [file exists test.db-journal] [file exists test.db-wal] } {1 0} # Test that nothing goes wrong if a connection is prevented from changing # from WAL to rollback mode because a second connection has the database # open. Or from rollback to WAL. # do_test walmode-4.6 { sqlite3 db2 test.db execsql { PRAGMA main.journal_mode } db2 } {delete} do_test walmode-4.7 { execsql { PRAGMA main.journal_mode = wal } db } {wal} do_test walmode-4.8 { execsql { SELECT * FROM t1 } db2 } {1 2} do_test walmode-4.9 { catchsql { PRAGMA journal_mode = delete } db } {1 {database is locked}} do_test walmode-4.10 { execsql { PRAGMA main.journal_mode } db } {wal} do_test walmode-4.11 { db2 close execsql { PRAGMA journal_mode = delete } db } {delete} do_test walmode-4.12 { execsql { PRAGMA main.journal_mode } db } {delete} do_test walmode-4.13 { list [file exists test.db-journal] [file exists test.db-wal] } {0 0} do_test walmode-4.14 { sqlite3 db2 test.db execsql { BEGIN; SELECT * FROM t1; } db2 } {1 2} do_test walmode-4.16 { execsql { PRAGMA main.journal_mode } db } {delete} do_test walmode-4.17 { execsql { PRAGMA main.journal_mode } db2 } {delete} do_test walmode-4.17 { catchsql { PRAGMA main.journal_mode = wal } db } {1 {database is locked}} do_test walmode-4.18 { execsql { PRAGMA main.journal_mode } db } {delete} catch { db close } catch { db2 close } # Test that it is not possible to change a temporary or in-memory database # to WAL mode. WAL mode is for persistent file-backed databases only. # # walmode-5.1.*: Try to set journal_mode=WAL on [sqlite3 db :memory:] database. # walmode-5.2.*: Try to set journal_mode=WAL on [sqlite3 db ""] database. # walmode-5.3.*: Try to set temp.journal_mode=WAL. # do_test walmode-5.1.1 { sqlite3 db :memory: execsql { PRAGMA main.journal_mode } } {memory} do_test walmode-5.1.2 { execsql { PRAGMA main.journal_mode = wal } } {memory} do_test walmode-5.1.3 { execsql { BEGIN; CREATE TABLE t1(a, b); INSERT INTO t1 VALUES(1, 2); COMMIT; SELECT * FROM t1; PRAGMA main.journal_mode; } } {1 2 memory} do_test walmode-5.1.4 { execsql { PRAGMA main.journal_mode = wal } } {memory} do_test walmode-5.1.5 { execsql { INSERT INTO t1 VALUES(3, 4); SELECT * FROM t1; PRAGMA main.journal_mode; } } {1 2 3 4 memory} do_test walmode-5.2.1 { sqlite3 db "" execsql { PRAGMA main.journal_mode } } {delete} do_test walmode-5.2.2 { execsql { PRAGMA main.journal_mode = wal } } {delete} do_test walmode-5.2.3 { execsql { BEGIN; CREATE TABLE t1(a, b); INSERT INTO t1 VALUES(1, 2); COMMIT; SELECT * FROM t1; PRAGMA main.journal_mode; } } {1 2 delete} do_test walmode-5.2.4 { execsql { PRAGMA main.journal_mode = wal } } {delete} do_test walmode-5.2.5 { execsql { INSERT INTO t1 VALUES(3, 4); SELECT * FROM t1; PRAGMA main.journal_mode; } } {1 2 3 4 delete} if {$TEMP_STORE>=2} { set tempJrnlMode memory } else { set tempJrnlMode delete } do_test walmode-5.3.1 { sqlite3 db test.db execsql { PRAGMA temp.journal_mode } } $tempJrnlMode do_test walmode-5.3.2 { execsql { PRAGMA temp.journal_mode = wal } } $tempJrnlMode do_test walmode-5.3.3 { execsql { BEGIN; CREATE TEMP TABLE t1(a, b); INSERT INTO t1 VALUES(1, 2); COMMIT; SELECT * FROM t1; PRAGMA temp.journal_mode; } } [list 1 2 $tempJrnlMode] do_test walmode-5.3.4 { execsql { PRAGMA temp.journal_mode = wal } } $tempJrnlMode do_test walmode-5.3.5 { execsql { INSERT INTO t1 VALUES(3, 4); SELECT * FROM t1; PRAGMA temp.journal_mode; } } [list 1 2 3 4 $tempJrnlMode] #------------------------------------------------------------------------- # Test changing to WAL mode from journal_mode=off or journal_mode=memory # foreach {tn mode} { 1 off 2 memory 3 persist 4 delete 5 truncate } { do_test walmode-6.$tn { faultsim_delete_and_reopen execsql " PRAGMA journal_mode = $mode; PRAGMA journal_mode = wal; " } [list $mode wal] } db close #------------------------------------------------------------------------- # Test the effect of a "PRAGMA journal_mode" command being the first # thing executed by a new connection. This means that the schema is not # loaded when sqlite3_prepare_v2() is called to compile the statement. # do_test walmode-7.0 { file delete -force test.db sqlite3 db test.db execsql { PRAGMA journal_mode = WAL; CREATE TABLE t1(a, b); } } {wal} foreach {tn sql result} { 1 "PRAGMA journal_mode" wal 2 "PRAGMA main.journal_mode" wal 3 "PRAGMA journal_mode = delete" delete 4 "PRAGMA journal_mode" delete 5 "PRAGMA main.journal_mode" delete 6 "PRAGMA journal_mode = wal" wal 7 "PRAGMA journal_mode" wal 8 "PRAGMA main.journal_mode" wal 9 "PRAGMA journal_mode" wal 10 "PRAGMA main.journal_mode" wal 11 "PRAGMA main.journal_mode = delete" delete 12 "PRAGMA journal_mode" delete 13 "PRAGMA main.journal_mode" delete 14 "PRAGMA main.journal_mode = wal" wal 15 "PRAGMA journal_mode" wal 16 "PRAGMA main.journal_mode" wal } { do_test walmode-7.$tn { db close sqlite3 db test.db execsql $sql } $result } db close #------------------------------------------------------------------------- # Test the effect of a "PRAGMA journal_mode" command on an attached # database. # faultsim_delete_and_reopen do_execsql_test walmode-8.1 { CREATE TABLE t1(a, b); PRAGMA journal_mode = WAL; ATTACH 'test.db2' AS two; CREATE TABLE two.t2(a, b); } {wal} do_execsql_test walmode-8.2 { PRAGMA main.journal_mode } {wal} do_execsql_test walmode-8.3 { PRAGMA two.journal_mode } {delete} do_execsql_test walmode-8.4 { PRAGMA two.journal_mode = DELETE } {delete} db close sqlite3 db test.db do_execsql_test walmode-8.5 { ATTACH 'test.db2' AS two } {} do_execsql_test walmode-8.6 { PRAGMA main.journal_mode } {wal} do_execsql_test walmode-8.7 { PRAGMA two.journal_mode } {delete} do_execsql_test walmode-8.8 { INSERT INTO two.t2 DEFAULT VALUES } {} do_execsql_test walmode-8.9 { PRAGMA two.journal_mode } {delete} do_execsql_test walmode-8.10 { INSERT INTO t1 DEFAULT VALUES } {} do_execsql_test walmode-8.11 { PRAGMA main.journal_mode } {wal} do_execsql_test walmode-8.12 { PRAGMA journal_mode } {wal} # Change to WAL mode on test2.db and make sure (in the tests that follow) # that this mode change persists. do_test walmode-8.x1 { execsql { PRAGMA two.journal_mode=WAL; PRAGMA two.journal_mode; } } {wal wal} db close sqlite3 db test.db do_execsql_test walmode-8.13 { PRAGMA journal_mode = WAL } {wal} do_execsql_test walmode-8.14 { ATTACH 'test.db2' AS two } {} do_execsql_test walmode-8.15 { PRAGMA main.journal_mode } {wal} do_execsql_test walmode-8.16 { PRAGMA two.journal_mode } {wal} do_execsql_test walmode-8.17 { INSERT INTO two.t2 DEFAULT VALUES } {} do_execsql_test walmode-8.18 { PRAGMA two.journal_mode } {wal} sqlite3 db2 test.db2 do_test walmode-8.19 { execsql { PRAGMA main.journal_mode } db2 } {wal} db2 close finish_test |
Added test/walslow.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 | # 2010 March 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 file is testing the operation of the library in # "PRAGMA journal_mode=WAL" mode. The tests in this file use # brute force methods, so may take a while to run. # set testdir [file dirname $argv0] source $testdir/tester.tcl ifcapable !wal {finish_test ; return } proc reopen_db {} { catch { db close } file delete -force test.db test.db-wal sqlite3 db test.db execsql { PRAGMA journal_mode = wal } } db close save_prng_state for {set seed 1} {$seed<10} {incr seed} { expr srand($seed) restore_prng_state reopen_db do_test walslow-1.seed=$seed.0 { execsql { CREATE TABLE t1(a, b) } execsql { CREATE INDEX i1 ON t1(a) } execsql { CREATE INDEX i2 ON t1(b) } } {} for {set iTest 1} {$iTest < 100} {incr iTest} { do_test walslow-1.seed=$seed.$iTest.1 { set w [expr int(rand()*2000)] set x [expr int(rand()*2000)] execsql { INSERT INTO t1 VALUES(randomblob($w), randomblob($x)) } execsql { PRAGMA integrity_check } } {ok} do_test walslow-1.seed=$seed.$iTest.2 { execsql "PRAGMA wal_checkpoint;" execsql { PRAGMA integrity_check } } {ok} do_test walslow-1.seed=$seed.$iTest.3 { file delete -force testX.db testX.db-wal file copy test.db testX.db file copy test.db-wal testX.db-wal sqlite3 db2 testX.db execsql { PRAGMA journal_mode = WAL } db2 execsql { PRAGMA integrity_check } db2 } {ok} do_test walslow-1.seed=$seed.$iTest.4 { execsql { SELECT count(*) FROM t1 WHERE a!=b } db2 } [execsql { SELECT count(*) FROM t1 WHERE a!=b }] db2 close } } finish_test |
Added test/walthread.test.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 | # 2010 April 13 # # The author disclaims copyright to this source code. In place of # a legal notice, here is a blessing: # # May you do good and not evil. # May you find forgiveness for yourself and forgive others. # May you share freely, never taking more than you give. # #*********************************************************************** # This file implements regression tests for SQLite library. The # focus of this file is testing the operation of the library in # "PRAGMA journal_mode=WAL" mode with multiple threads. # set testdir [file dirname $argv0] source $testdir/tester.tcl source $testdir/lock_common.tcl if {[run_thread_tests]==0} { finish_test ; return } ifcapable !wal { finish_test ; return } set sqlite_walsummary_mmap_incr 64 # How long, in seconds, to run each test for. If a test is set to run for # 0 seconds, it is omitted entirely. # unset -nocomplain seconds set seconds(walthread-1) 20 set seconds(walthread-2) 20 set seconds(walthread-3) 20 set seconds(walthread-4) 20 set seconds(walthread-5) 1 # The parameter is the name of a variable in the callers context. The # variable may or may not exist when this command is invoked. # # If the variable does exist, its value is returned. Otherwise, this # command uses [vwait] to wait until it is set, then returns the value. # In other words, this is a version of the [set VARNAME] command that # blocks until a variable exists. # proc wait_for_var {varname} { if {0==[uplevel [list info exists $varname]]} { uplevel [list vwait $varname] } uplevel [list set $varname] } # The argument is the name of a list variable in the callers context. The # first element of the list is removed and returned. For example: # # set L {a b c} # set x [lshift L] # assert { $x == "a" && $L == "b c" } # proc lshift {lvar} { upvar $lvar L set ret [lindex $L 0] set L [lrange $L 1 end] return $ret } #------------------------------------------------------------------------- # do_thread_test TESTNAME OPTIONS... # # where OPTIONS are: # # -seconds SECONDS How many seconds to run the test for # -init SCRIPT Script to run before test. # -thread NAME COUNT SCRIPT Scripts to run in threads (or processes). # -processes BOOLEAN True to use processes instead of threads. # -check SCRIPT Script to run after test. # proc do_thread_test {args} { set A $args set P(testname) [lshift A] set P(seconds) 5 set P(init) "" set P(threads) [list] set P(processes) 0 set P(check) { set ic [db eval "PRAGMA integrity_check"] if {$ic != "ok"} { error $ic } } unset -nocomplain ::done while {[llength $A]>0} { set a [lshift A] switch -glob -- $a { -seconds { set P(seconds) [lshift A] } -init { set P(init) [lshift A] } -processes { set P(processes) [lshift A] } -check { set P(check) [lshift A] } -thread { set name [lshift A] set count [lshift A] set prg [lshift A] lappend P(threads) [list $name $count $prg] } default { error "Unknown option: $a" } } } if {$P(seconds) == 0} { puts "Skipping $P(testname)" return } puts "Running $P(testname) for $P(seconds) seconds..." catch { db close } file delete -force test.db test.db-journal test.db-wal sqlite3 db test.db eval $P(init) catch { db close } foreach T $P(threads) { set name [lindex $T 0] set count [lindex $T 1] set prg [lindex $T 2] for {set i 1} {$i <= $count} {incr i} { set vars " set E(pid) $i set E(nthread) $count set E(seconds) $P(seconds) " set program [string map [list %TEST% $prg %VARS% $vars] { %VARS% proc usleep {ms} { set ::usleep 0 after $ms {set ::usleep 1} vwait ::usleep } proc integrity_check {{db db}} { set ic [$db eval {PRAGMA integrity_check}] if {$ic != "ok"} {error $ic} } proc busyhandler {n} { usleep 10 ; return 0 } sqlite3 db test.db db busy busyhandler db eval { SELECT randomblob($E(pid)*5) } set ::finished 0 after [expr $E(seconds) * 1000] {set ::finished 1} proc tt_continue {} { update ; expr ($::finished==0) } set rc [catch { %TEST% } msg] catch { db close } list $rc $msg }] if {$P(processes)==0} { sqlthread spawn ::done($name,$i) $program } else { testfixture_nb ::done($name,$i) $program } } } set report " Results:" foreach T $P(threads) { set name [lindex $T 0] set count [lindex $T 1] set prg [lindex $T 2] set reslist [list] for {set i 1} {$i <= $count} {incr i} { set res [wait_for_var ::done($name,$i)] lappend reslist [lindex $res 1] do_test $P(testname).$name.$i [list lindex $res 0] 0 } append report " $name $reslist" } puts $report sqlite3 db test.db set res "" if {[catch $P(check) msg]} { set res $msg } do_test $P(testname).check [list set {} $res] "" } # A wrapper around [do_thread_test] which runs the specified test twice. # Once using processes, once using threads. This command takes the same # arguments as [do_thread_test], except specifying the -processes switch # is illegal. # proc do_thread_test2 {args} { set name [lindex $args 0] if {[lsearch $args -processes]>=0} { error "bad option: -processes"} uplevel [lreplace $args 0 0 do_thread_test "$name-threads" -processes 0] uplevel [lreplace $args 0 0 do_thread_test "$name-processes" -processes 1] } #-------------------------------------------------------------------------- # Start 10 threads. Each thread performs both read and write # transactions. Each read transaction consists of: # # 1) Reading the md5sum of all but the last table row, # 2) Running integrity check. # 3) Reading the value stored in the last table row, # 4) Check that the values read in steps 1 and 3 are the same, and that # the md5sum of all but the last table row has not changed. # # Each write transaction consists of: # # 1) Modifying the contents of t1 (inserting, updating, deleting rows). # 2) Appending a new row to the table containing the md5sum() of all # rows in the table. # # Each of the N threads runs N read transactions followed by a single write # transaction in a loop as fast as possible. # # There is also a single checkpointer thread. It runs the following loop: # # 1) Execute "PRAGMA wal_checkpoint" # 2) Sleep for 500 ms. # do_thread_test2 walthread-1 -seconds $seconds(walthread-1) -init { execsql { PRAGMA journal_mode = WAL; CREATE TABLE t1(x PRIMARY KEY); PRAGMA lock_status; INSERT INTO t1 VALUES(randomblob(100)); INSERT INTO t1 VALUES(randomblob(100)); INSERT INTO t1 SELECT md5sum(x) FROM t1; } } -thread main 10 { proc read_transaction {} { set results [db eval { BEGIN; PRAGMA integrity_check; SELECT md5sum(x) FROM t1 WHERE rowid != (SELECT max(rowid) FROM t1); SELECT x FROM t1 WHERE rowid = (SELECT max(rowid) FROM t1); SELECT md5sum(x) FROM t1 WHERE rowid != (SELECT max(rowid) FROM t1); COMMIT; }] if {[llength $results]!=4 || [lindex $results 0] != "ok" || [lindex $results 1] != [lindex $results 2] || [lindex $results 2] != [lindex $results 3] } { error "Failed read transaction: $results" } } proc write_transaction {} { db eval { BEGIN; INSERT INTO t1 VALUES(randomblob(100)); INSERT INTO t1 VALUES(randomblob(100)); INSERT INTO t1 SELECT md5sum(x) FROM t1; COMMIT; } } # Turn off auto-checkpoint. Otherwise, an auto-checkpoint run by a # writer may cause the dedicated checkpoint thread to return an # SQLITE_BUSY error. # db eval { PRAGMA wal_autocheckpoint = 0 } set nRun 0 while {[tt_continue]} { read_transaction write_transaction incr nRun } set nRun } -thread ckpt 1 { set nRun 0 while {[tt_continue]} { db eval "PRAGMA wal_checkpoint" usleep 500 incr nRun } set nRun } #-------------------------------------------------------------------------- # This test has clients run the following procedure as fast as possible # in a loop: # # 1. Open a database handle. # 2. Execute a read-only transaction on the db. # 3. Do "PRAGMA journal_mode = XXX", where XXX is one of WAL or DELETE. # Ignore any SQLITE_BUSY error. # 4. Execute a write transaction to insert a row into the db. # 5. Run "PRAGMA integrity_check" # # At present, there are 4 clients in total. 2 do "journal_mode = WAL", and # two do "journal_mode = DELETE". # # Each client returns a string of the form "W w, R r", where W is the # number of write-transactions performed using a WAL journal, and D is # the number of write-transactions performed using a rollback journal. # For example, "192 w, 185 r". # do_thread_test2 walthread-2 -seconds $seconds(walthread-2) -init { execsql { CREATE TABLE t1(x INTEGER PRIMARY KEY, y UNIQUE) } } -thread RB 2 { db close set nRun 0 set nDel 0 while {[tt_continue]} { sqlite3 db test.db db busy busyhandler db eval { SELECT * FROM sqlite_master } catch { db eval { PRAGMA journal_mode = DELETE } } db eval { BEGIN; INSERT INTO t1 VALUES(NULL, randomblob(100+$E(pid))); } incr nRun 1 incr nDel [file exists test.db-journal] if {[file exists test.db-journal] + [file exists test.db-wal] != 1} { error "File-system looks bad..." } db eval COMMIT integrity_check db close } list $nRun $nDel set {} "[expr $nRun-$nDel] w, $nDel r" } -thread WAL 2 { db close set nRun 0 set nDel 0 while {[tt_continue]} { sqlite3 db test.db db busy busyhandler db eval { SELECT * FROM sqlite_master } catch { db eval { PRAGMA journal_mode = WAL } } db eval { BEGIN; INSERT INTO t1 VALUES(NULL, randomblob(110+$E(pid))); } incr nRun 1 incr nDel [file exists test.db-journal] if {[file exists test.db-journal] + [file exists test.db-wal] != 1} { error "File-system looks bad..." } db eval COMMIT integrity_check db close } set {} "[expr $nRun-$nDel] w, $nDel r" } do_thread_test walthread-3 -seconds $seconds(walthread-3) -init { execsql { PRAGMA journal_mode = WAL; CREATE TABLE t1(cnt PRIMARY KEY, sum1, sum2); CREATE INDEX i1 ON t1(sum1); CREATE INDEX i2 ON t1(sum2); INSERT INTO t1 VALUES(0, 0, 0); } } -thread t 10 { set nextwrite $E(pid) proc wal_hook {zDb nEntry} { if {$nEntry>10} { set rc [catch { db eval {PRAGMA wal_checkpoint} } msg] if {$rc && $msg != "database is locked"} { error $msg } } return 0 } db wal_hook wal_hook while {[tt_continue]} { set max 0 while { $max != ($nextwrite-1) && [tt_continue] } { set max [db eval { SELECT max(cnt) FROM t1 }] } if {[tt_continue]} { set sum1 [db eval { SELECT sum(cnt) FROM t1 }] set sum2 [db eval { SELECT sum(sum1) FROM t1 }] db eval { INSERT INTO t1 VALUES($nextwrite, $sum1, $sum2) } incr nextwrite $E(nthread) integrity_check } } set {} ok } -check { puts " Final db contains [db eval {SELECT count(*) FROM t1}] rows" puts " Final integrity-check says: [db eval {PRAGMA integrity_check}]" # Check that the contents of the database are Ok. set c 0 set s1 0 set s2 0 db eval { SELECT cnt, sum1, sum2 FROM t1 ORDER BY cnt } { if {$c != $cnt || $s1 != $sum1 || $s2 != $sum2} { error "database content is invalid" } incr s2 $s1 incr s1 $c incr c 1 } } do_thread_test2 walthread-4 -seconds $seconds(walthread-4) -init { execsql { PRAGMA journal_mode = WAL; CREATE TABLE t1(a INTEGER PRIMARY KEY, b UNIQUE); } } -thread r 1 { # This connection only ever reads the database. Therefore the # busy-handler is not required. Disable it to check that this is true. # # UPDATE: That is no longer entirely true - as we don't use a blocking # lock to enter RECOVER state. Which means there is a small chance a # reader can see an SQLITE_BUSY. # while {[tt_continue]} { integrity_check } set {} ok } -thread w 1 { proc wal_hook {zDb nEntry} { if {$nEntry>15} {db eval {PRAGMA wal_checkpoint}} return 0 } db wal_hook wal_hook set row 1 while {[tt_continue]} { db eval { REPLACE INTO t1 VALUES($row, randomblob(300)) } incr row if {$row == 10} { set row 1 } } set {} ok } # This test case attempts to provoke a deadlock condition that existed in # the unix VFS at one point. The problem occurred only while recovering a # very large wal file (one that requires a wal-index larger than the # initial default allocation of 64KB). # do_thread_test walthread-5 -seconds $seconds(walthread-5) -init { proc log_file_size {nFrame pgsz} { expr {12 + ($pgsz+16)*$nFrame} } execsql { PRAGMA page_size = 1024; PRAGMA journal_mode = WAL; CREATE TABLE t1(x); BEGIN; INSERT INTO t1 VALUES(randomblob(900)); INSERT INTO t1 SELECT randomblob(900) FROM t1; /* 2 */ INSERT INTO t1 SELECT randomblob(900) FROM t1; /* 4 */ INSERT INTO t1 SELECT randomblob(900) FROM t1; /* 8 */ INSERT INTO t1 SELECT randomblob(900) FROM t1; /* 16 */ INSERT INTO t1 SELECT randomblob(900) FROM t1; /* 32 */ INSERT INTO t1 SELECT randomblob(900) FROM t1; /* 64 */ INSERT INTO t1 SELECT randomblob(900) FROM t1; /* 128 */ INSERT INTO t1 SELECT randomblob(900) FROM t1; /* 256 */ INSERT INTO t1 SELECT randomblob(900) FROM t1; /* 512 */ INSERT INTO t1 SELECT randomblob(900) FROM t1; /* 1024 */ INSERT INTO t1 SELECT randomblob(900) FROM t1; /* 2048 */ INSERT INTO t1 SELECT randomblob(900) FROM t1; /* 4096 */ INSERT INTO t1 SELECT randomblob(900) FROM t1; /* 8192 */ INSERT INTO t1 SELECT randomblob(900) FROM t1; /* 16384 */ INSERT INTO t1 SELECT randomblob(900) FROM t1; /* 32768 */ INSERT INTO t1 SELECT randomblob(900) FROM t1; /* 65536 */ COMMIT; } file copy -force test.db-wal bak.db-wal file copy -force test.db bak.db db close file copy -force bak.db-wal test.db-wal file copy -force bak.db test.db if {[file size test.db-wal] < [log_file_size [expr 64*1024] 1024]} { error "Somehow failed to create a large log file" } puts "Database with large log file recovered. Now running clients..." } -thread T 5 { db eval { SELECT count(*) FROM t1 } } unset -nocomplain seconds finish_test |
Changes to test/where2.test.
︙ | ︙ | |||
615 616 617 618 619 620 621 622 623 | do_test where2-9.2 { count { SELECT * FROM t10 WHERE a=1 AND (b=2 OR b=3) } } {1 2 2 1 3 3 7} } } finish_test | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | do_test where2-9.2 { count { SELECT * FROM t10 WHERE a=1 AND (b=2 OR b=3) } } {1 2 2 1 3 3 7} } } # Indices with redundant columns # do_test where2-11.1 { execsql { CREATE TABLE t11(a,b,c,d); CREATE INDEX i11aba ON t11(a,b,a,c); -- column A occurs twice. INSERT INTO t11 VALUES(1,2,3,4); INSERT INTO t11 VALUES(5,6,7,8); INSERT INTO t11 VALUES(1,2,9,10); INSERT INTO t11 VALUES(5,11,12,13); SELECT c FROM t11 WHERE a=1 AND b=2 ORDER BY c; } } {3 9} do_test where2-11.2 { execsql { CREATE INDEX i11cccccccc ON t11(c,c,c,c,c,c,c,c); -- repeated column SELECT d FROM t11 WHERE c=9; } } {10} do_test where2-11.3 { execsql { SELECT d FROM t11 WHERE c IN (1,2,3,4,5); } } {4} do_test where2-11.4 { execsql { SELECT d FROM t11 WHERE c=7 OR (a=1 AND b=2) ORDER BY d; } } {4 8 10} finish_test |
Changes to test/where3.test.
︙ | ︙ | |||
195 196 197 198 199 200 201 | } {tC {} tA * tB * tD *} do_test where3-2.5 { queryplan { SELECT * FROM tA, tB, tC LEFT JOIN tD ON dpk=cx WHERE cpk=ax AND bpk=cx } } {tA {} tC * tB * tD *} | | | | 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 | } {tC {} tA * tB * tD *} do_test where3-2.5 { queryplan { SELECT * FROM tA, tB, tC LEFT JOIN tD ON dpk=cx WHERE cpk=ax AND bpk=cx } } {tA {} tC * tB * tD *} do_test where3-2.6 { queryplan { SELECT * FROM tA, tB, tC LEFT JOIN tD ON dpk=cx WHERE bpk=cx AND apk=bx } } {tC {} tB * tA * tD *} do_test where3-2.7 { queryplan { SELECT * FROM tA, tB, tC LEFT JOIN tD ON dpk=cx WHERE cpk=bx AND apk=cx } } {tB {} tC * tA * tD *} |
︙ | ︙ |
Changes to test/where7.test.
︙ | ︙ | |||
86 87 88 89 90 91 92 | } {2 4 5 scan 0 sort 0} do_test where7-1.9 { count_steps { SELECT a FROM t1 WHERE (b=3 OR c>=10 OR c=4) } } {2 4 5 scan 0 sort 0} do_test where7-1.10 { | < < | < < < < < < | 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 | } {2 4 5 scan 0 sort 0} do_test where7-1.9 { count_steps { SELECT a FROM t1 WHERE (b=3 OR c>=10 OR c=4) } } {2 4 5 scan 0 sort 0} do_test where7-1.10 { count_steps { SELECT a FROM t1 WHERE (b=3 OR c>=10 OR c=4 OR b>10) } } {2 4 5 scan 0 sort 0} do_test where7-1.11 { count_steps { SELECT a FROM t1 WHERE (d=5 AND b=3) OR c==100 ORDER BY a; } } {2 5 scan 0 sort 1} do_test where7-1.12 { count_steps { SELECT a FROM t1 WHERE (b BETWEEN 2 AND 4) OR c=100 ORDER BY a } } {1 2 3 5 scan 0 sort 1} do_test where7-1.13 { count_steps { SELECT a FROM t1 WHERE (b BETWEEN 0 AND 2) OR (c BETWEEN 9 AND 999) ORDER BY +a DESC } } {5 4 1 scan 0 sort 1} do_test where7-1.14 { |
︙ | ︙ |
Changes to test/where8.test.
︙ | ︙ | |||
282 283 284 285 286 287 288 289 | } {IV V 9 0} do_test where8-3.15 { execsql_status { SELECT c FROM t1, t2 WHERE a BETWEEN 1 AND 2 OR a = ( SELECT sum(e IS NULL) FROM t2 AS inner WHERE t2.d>inner.d ) } | > < > | 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 | } {IV V 9 0} do_test where8-3.15 { execsql_status { SELECT c FROM t1, t2 WHERE a BETWEEN 1 AND 2 OR a = ( SELECT sum(e IS NULL) FROM t2 AS inner WHERE t2.d>inner.d ) ORDER BY c } } {I I I I I I I I I I II II II II II II II II II II III III III III III 9 1} #----------------------------------------------------------------------- # The following tests - where8-4.* - verify that adding or removing # indexes does not change the results returned by various queries. # do_test where8-4.1 { execsql { |
︙ | ︙ |
Changes to tool/lemon.c.
︙ | ︙ | |||
30 31 32 33 34 35 36 37 38 39 40 41 42 43 | #ifdef TEST #define MAXRHS 5 /* Set low to exercise exception code */ #else #define MAXRHS 1000 #endif static const char **made_files = NULL; static int made_files_count = 0; static int successful_exit = 0; static void LemonAtExit(void) { /* if we failed, delete (most) files we made, to unconfuse build tools. */ int i; | > | 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 | #ifdef TEST #define MAXRHS 5 /* Set low to exercise exception code */ #else #define MAXRHS 1000 #endif static int showPrecedenceConflict = 0; static const char **made_files = NULL; static int made_files_count = 0; static int successful_exit = 0; static void LemonAtExit(void) { /* if we failed, delete (most) files we made, to unconfuse build tools. */ int i; |
︙ | ︙ | |||
1080 1081 1082 1083 1084 1085 1086 | if( apx->type==SHIFT && apy->type==REDUCE ){ spx = apx->sp; spy = apy->x.rp->precsym; if( spy==0 || spx->prec<0 || spy->prec<0 ){ /* Not enough precedence information. */ apy->type = SRCONFLICT; errcnt++; | | | 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 | if( apx->type==SHIFT && apy->type==REDUCE ){ spx = apx->sp; spy = apy->x.rp->precsym; if( spy==0 || spx->prec<0 || spy->prec<0 ){ /* Not enough precedence information. */ apy->type = SRCONFLICT; errcnt++; }else if( spx->prec>spy->prec ){ /* higher precedence wins */ apy->type = RD_RESOLVED; }else if( spx->prec<spy->prec ){ apx->type = SH_RESOLVED; }else if( spx->prec==spy->prec && spx->assoc==RIGHT ){ /* Use operator */ apy->type = RD_RESOLVED; /* associativity */ }else if( spx->prec==spy->prec && spx->assoc==LEFT ){ /* to break tie */ apx->type = SH_RESOLVED; |
︙ | ︙ | |||
1400 1401 1402 1403 1404 1405 1406 1407 1408 1409 1410 1411 1412 1413 1414 1415 1416 1417 1418 1419 1420 1421 1422 | static int rpflag = 0; static int basisflag = 0; static int compress = 0; static int quiet = 0; static int statistics = 0; static int mhflag = 0; static int nolinenosflag = 0; static struct s_options options[] = { {OPT_FLAG, "b", (char*)&basisflag, "Print only the basis in report."}, {OPT_FLAG, "c", (char*)&compress, "Don't compress the action table."}, {OPT_FSTR, "D", (char*)handle_D_option, "Define an %ifdef macro."}, {OPT_FSTR, "T", (char*)handle_T_option, "Specify a template file."}, {OPT_FLAG, "g", (char*)&rpflag, "Print grammar without actions."}, {OPT_FLAG, "m", (char*)&mhflag, "Output a makeheaders compatible file."}, {OPT_FLAG, "l", (char*)&nolinenosflag, "Do not print #line statements."}, {OPT_FLAG, "q", (char*)&quiet, "(Quiet) Don't print the report file."}, {OPT_FLAG, "s", (char*)&statistics, "Print parser stats to standard output."}, {OPT_FLAG, "x", (char*)&version, "Print the version number."}, {OPT_FLAG,0,0,0} }; int i; int exitcode; | > > > > | 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 | static int rpflag = 0; static int basisflag = 0; static int compress = 0; static int quiet = 0; static int statistics = 0; static int mhflag = 0; static int nolinenosflag = 0; static int noResort = 0; static struct s_options options[] = { {OPT_FLAG, "b", (char*)&basisflag, "Print only the basis in report."}, {OPT_FLAG, "c", (char*)&compress, "Don't compress the action table."}, {OPT_FSTR, "D", (char*)handle_D_option, "Define an %ifdef macro."}, {OPT_FSTR, "T", (char*)handle_T_option, "Specify a template file."}, {OPT_FLAG, "g", (char*)&rpflag, "Print grammar without actions."}, {OPT_FLAG, "m", (char*)&mhflag, "Output a makeheaders compatible file."}, {OPT_FLAG, "l", (char*)&nolinenosflag, "Do not print #line statements."}, {OPT_FLAG, "p", (char*)&showPrecedenceConflict, "Show conflicts resolved by precedence rules"}, {OPT_FLAG, "q", (char*)&quiet, "(Quiet) Don't print the report file."}, {OPT_FLAG, "r", (char*)&noResort, "Do not sort or renumber states"}, {OPT_FLAG, "s", (char*)&statistics, "Print parser stats to standard output."}, {OPT_FLAG, "x", (char*)&version, "Print the version number."}, {OPT_FLAG,0,0,0} }; int i; int exitcode; |
︙ | ︙ | |||
1495 1496 1497 1498 1499 1500 1501 | /* Compute the action tables */ FindActions(&lem); /* Compress the action tables */ if( compress==0 ) CompressTables(&lem); /* Reorder and renumber the states so that states with fewer choices | | > | | 1500 1501 1502 1503 1504 1505 1506 1507 1508 1509 1510 1511 1512 1513 1514 1515 1516 | /* Compute the action tables */ FindActions(&lem); /* Compress the action tables */ if( compress==0 ) CompressTables(&lem); /* Reorder and renumber the states so that states with fewer choices ** occur at the end. This is an optimization that helps make the ** generated parser tables smaller. */ if( noResort==0 ) ResortStates(&lem); /* Generate a report of the parser generated. (the "y.output" file) */ if( !quiet ) ReportOutput(&lem); /* Generate the source code for the parser */ ReportTable(&lem, mhflag); |
︙ | ︙ | |||
2892 2893 2894 2895 2896 2897 2898 | break; case SRCONFLICT: case RRCONFLICT: fprintf(fp,"%*s reduce %-3d ** Parsing conflict **", indent,ap->sp->name,ap->x.rp->index); break; case SSCONFLICT: | | > > > > > > > > > > > > > > | 2898 2899 2900 2901 2902 2903 2904 2905 2906 2907 2908 2909 2910 2911 2912 2913 2914 2915 2916 2917 2918 2919 2920 2921 2922 2923 2924 2925 2926 2927 2928 2929 2930 | break; case SRCONFLICT: case RRCONFLICT: fprintf(fp,"%*s reduce %-3d ** Parsing conflict **", indent,ap->sp->name,ap->x.rp->index); break; case SSCONFLICT: fprintf(fp,"%*s shift %-3d ** Parsing conflict **", indent,ap->sp->name,ap->x.stp->statenum); break; case SH_RESOLVED: if( showPrecedenceConflict ){ fprintf(fp,"%*s shift %-3d -- dropped by precedence", indent,ap->sp->name,ap->x.stp->statenum); }else{ result = 0; } break; case RD_RESOLVED: if( showPrecedenceConflict ){ fprintf(fp,"%*s reduce %-3d -- dropped by precedence", indent,ap->sp->name,ap->x.rp->index); }else{ result = 0; } break; case NOT_USED: result = 0; break; } return result; } |
︙ | ︙ |
Changes to tool/mksqlite3c.tcl.
︙ | ︙ | |||
106 107 108 109 110 111 112 113 114 115 116 117 118 119 | sqlite3.h sqliteicu.h sqliteInt.h sqliteLimit.h sqlrr.h vdbe.h vdbeInt.h } { set available_hdr($hdr) 1 } set available_hdr(sqliteInt.h) 0 # 78 stars used for comment formatting. set s78 \ | > | 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 | sqlite3.h sqliteicu.h sqliteInt.h sqliteLimit.h sqlrr.h vdbe.h vdbeInt.h wal.h } { set available_hdr($hdr) 1 } set available_hdr(sqliteInt.h) 0 # 78 stars used for comment formatting. set s78 \ |
︙ | ︙ | |||
241 242 243 244 245 246 247 248 249 250 251 252 253 254 | os_win.c bitvec.c pcache.c pcache1.c rowset.c pager.c btmutex.c btree.c backup.c vdbemem.c vdbeaux.c | > | 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 | os_win.c bitvec.c pcache.c pcache1.c rowset.c pager.c wal.c btmutex.c btree.c backup.c vdbemem.c vdbeaux.c |
︙ | ︙ |
Changes to tool/shell1.test.
︙ | ︙ | |||
191 192 193 194 195 196 197 | list $rc \ [regexp {Error: missing argument for option: -nullvalue} $res] } {1 1} # -version show SQLite version do_test shell1-1.16.1 { catchcmd "-version test.db" "" | | | 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 | list $rc \ [regexp {Error: missing argument for option: -nullvalue} $res] } {1 1} # -version show SQLite version do_test shell1-1.16.1 { catchcmd "-version test.db" "" } {0 3.7.0} #---------------------------------------------------------------------------- # Test cases shell1-2.*: Basic "dot" command token parsing. # # check first token handling do_test shell1-2.1.1 { |
︙ | ︙ | |||
365 366 367 368 369 370 371 | catchcmd "test.db" ".explain OFF" } {0 {}} do_test shell1-3.7.4 { # too many arguments catchcmd "test.db" ".explain OFF BAD" } {1 {Error: unknown command or invalid arguments: "explain". Enter ".help" for help}} | < < < < < < < < < < < < | 365 366 367 368 369 370 371 372 373 374 375 376 377 378 | catchcmd "test.db" ".explain OFF" } {0 {}} do_test shell1-3.7.4 { # too many arguments catchcmd "test.db" ".explain OFF BAD" } {1 {Error: unknown command or invalid arguments: "explain". Enter ".help" for help}} # .header(s) ON|OFF Turn display of headers on or off do_test shell1-3.9.1 { catchcmd "test.db" ".header" } {1 {Error: unknown command or invalid arguments: "header". Enter ".help" for help}} do_test shell1-3.9.2 { catchcmd "test.db" ".header ON" |
︙ | ︙ | |||
698 699 700 701 702 703 704 | catchcmd "test.db" ".timer OFF" } {0 {}} do_test shell1-3.27.4 { # too many arguments catchcmd "test.db" ".timer OFF BAD" } {1 {Error: unknown command or invalid arguments: "timer". Enter ".help" for help}} | | < | 686 687 688 689 690 691 692 693 | catchcmd "test.db" ".timer OFF" } {0 {}} do_test shell1-3.27.4 { # too many arguments catchcmd "test.db" ".timer OFF BAD" } {1 {Error: unknown command or invalid arguments: "timer". Enter ".help" for help}} puts "CLI tests completed successfully" |
Changes to tool/shell2.test.
︙ | ︙ | |||
214 215 216 217 218 219 220 | a 1 2 SELECT * FROM foo2; b 1 2}} | > > | 214 215 216 217 218 219 220 221 222 | a 1 2 SELECT * FROM foo2; b 1 2}} puts "CLI tests completed successfully" |
Changes to tool/shell3.test.
︙ | ︙ | |||
117 118 119 120 121 122 123 | catchcmd "foo.db" ".tables" } {0 {}} do_test shell3-2.7 { catchcmd "foo.db" "CREATE TABLE" } {1 {Error: incomplete SQL: CREATE TABLE}} | > | 117 118 119 120 121 122 123 124 | catchcmd "foo.db" ".tables" } {0 {}} do_test shell3-2.7 { catchcmd "foo.db" "CREATE TABLE" } {1 {Error: incomplete SQL: CREATE TABLE}} puts "CLI tests completed successfully" |
Changes to tool/showdb.c.
1 2 3 4 5 6 7 8 9 10 11 12 | /* ** A utility for printing all or part of an SQLite database file. */ #include <stdio.h> #include <ctype.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <unistd.h> #include <stdlib.h> | > | | | | > > > > > > > > > > > > > > > > > > > > > > > > > > > | < | | | > > | > > > > > > > > > > > > > > > > > > > > > > > > > | | > > > | | > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 | /* ** A utility for printing all or part of an SQLite database file. */ #include <stdio.h> #include <ctype.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <unistd.h> #include <stdlib.h> #include <string.h> static int pagesize = 1024; /* Size of a database page */ static int db = -1; /* File descriptor for reading the DB */ static int mxPage = 0; /* Last page number */ static int perLine = 16; /* HEX elements to print per line */ typedef long long int i64; /* Datatype for 64-bit integers */ /* ** Convert the var-int format into i64. Return the number of bytes ** in the var-int. Write the var-int value into *pVal. */ static int decodeVarint(const unsigned char *z, i64 *pVal){ i64 v = 0; int i; for(i=0; i<8; i++){ v = (v<<7) + (z[i]&0x7f); if( (z[i]&0x80)==0 ){ *pVal = v; return i+1; } } v = (v<<8) + (z[i]&0xff); *pVal = v; return 9; } /* Report an out-of-memory error and die. */ static void out_of_memory(void){ fprintf(stderr,"Out of memory...\n"); exit(1); } /* ** Read content from the file. ** ** Space to hold the content is obtained from malloc() and needs to be ** freed by the caller. */ static unsigned char *getContent(int ofst, int nByte){ unsigned char *aData; aData = malloc(nByte); if( aData==0 ) out_of_memory(); lseek(db, ofst, SEEK_SET); read(db, aData, nByte); return aData; } /* ** Print a range of bytes as hex and as ascii. */ static unsigned char *print_byte_range( int ofst, /* First byte in the range of bytes to print */ int nByte, /* Number of bytes to print */ int printOfst /* Add this amount to the index on the left column */ ){ unsigned char *aData; int i, j; const char *zOfstFmt; if( ((printOfst+nByte)&~0xfff)==0 ){ zOfstFmt = " %03x: "; }else if( ((printOfst+nByte)&~0xffff)==0 ){ zOfstFmt = " %04x: "; }else if( ((printOfst+nByte)&~0xfffff)==0 ){ zOfstFmt = " %05x: "; }else if( ((printOfst+nByte)&~0xffffff)==0 ){ zOfstFmt = " %06x: "; }else{ zOfstFmt = " %08x: "; } aData = getContent(ofst, nByte); for(i=0; i<nByte; i += perLine){ fprintf(stdout, zOfstFmt, i+printOfst); for(j=0; j<perLine; j++){ if( i+j>nByte ){ fprintf(stdout, " "); }else{ fprintf(stdout,"%02x ", aData[i+j]); } } for(j=0; j<perLine; j++){ if( i+j>nByte ){ fprintf(stdout, " "); }else{ fprintf(stdout,"%c", isprint(aData[i+j]) ? aData[i+j] : '.'); } } fprintf(stdout,"\n"); } return aData; } /* ** Print an entire page of content as hex */ static print_page(int iPg){ int iStart; unsigned char *aData; iStart = (iPg-1)*pagesize; fprintf(stdout, "Page %d: (offsets 0x%x..0x%x)\n", iPg, iStart, iStart+pagesize-1); aData = print_byte_range(iStart, pagesize, 0); free(aData); } /* Print a line of decode output showing a 4-byte integer. */ static print_decode_line( unsigned char *aData, /* Content being decoded */ int ofst, int nByte, /* Start and size of decode */ const char *zMsg /* Message to append */ ){ int i, j; int val = aData[ofst]; char zBuf[100]; sprintf(zBuf, " %03x: %02x", ofst, aData[ofst]); i = strlen(zBuf); for(j=1; j<4; j++){ if( j>=nByte ){ sprintf(&zBuf[i], " "); }else{ sprintf(&zBuf[i], " %02x", aData[ofst+j]); val = val*256 + aData[ofst+j]; } i += strlen(&zBuf[i]); } sprintf(&zBuf[i], " %9d", val); printf("%s %s\n", zBuf, zMsg); } /* ** Decode the database header. */ static void print_db_header(void){ unsigned char *aData; aData = print_byte_range(0, 100, 0); printf("Decoded:\n"); print_decode_line(aData, 16, 2, "Database page size"); print_decode_line(aData, 18, 1, "File format write version"); print_decode_line(aData, 19, 1, "File format read version"); print_decode_line(aData, 20, 1, "Reserved space at end of page"); print_decode_line(aData, 24, 4, "File change counter"); print_decode_line(aData, 28, 4, "Size of database in pages"); print_decode_line(aData, 32, 4, "Page number of first freelist page"); print_decode_line(aData, 36, 4, "Number of freelist pages"); print_decode_line(aData, 40, 4, "Schema cookie"); print_decode_line(aData, 44, 4, "Schema format version"); print_decode_line(aData, 48, 4, "Default page cache size"); print_decode_line(aData, 52, 4, "Largest auto-vac root page"); print_decode_line(aData, 56, 4, "Text encoding"); print_decode_line(aData, 60, 4, "User version"); print_decode_line(aData, 64, 4, "Incremental-vacuum mode"); print_decode_line(aData, 68, 4, "meta[7]"); print_decode_line(aData, 72, 4, "meta[8]"); print_decode_line(aData, 76, 4, "meta[9]"); print_decode_line(aData, 80, 4, "meta[10]"); print_decode_line(aData, 84, 4, "meta[11]"); print_decode_line(aData, 88, 4, "meta[12]"); print_decode_line(aData, 92, 4, "Change counter for version number"); print_decode_line(aData, 96, 4, "SQLite version number"); } /* ** Create a description for a single cell. */ static int describeCell(unsigned char cType, unsigned char *a, char **pzDesc){ int i; int nDesc = 0; int n = 0; int leftChild; i64 nPayload; i64 rowid; static char zDesc[100]; i = 0; if( cType<=5 ){ leftChild = ((a[0]*256 + a[1])*256 + a[2])*256 + a[3]; a += 4; n += 4; sprintf(zDesc, "left-child: %d ", leftChild); nDesc = strlen(zDesc); } if( cType!=5 ){ i = decodeVarint(a, &nPayload); a += i; n += i; sprintf(&zDesc[nDesc], "sz: %lld ", nPayload); nDesc += strlen(&zDesc[nDesc]); } if( cType==5 || cType==13 ){ i = decodeVarint(a, &rowid); a += i; n += i; sprintf(&zDesc[nDesc], "rowid: %lld ", rowid); nDesc += strlen(&zDesc[nDesc]); } *pzDesc = zDesc; return n; } /* ** Decode a btree page */ static void decode_btree_page(unsigned char *a, int pgno, int hdrSize){ const char *zType = "unknown"; int nCell; int i; int iCellPtr; switch( a[0] ){ case 2: zType = "index interior node"; break; case 5: zType = "table interior node"; break; case 10: zType = "index leaf"; break; case 13: zType = "table leaf"; break; } printf("Decode of btree page %d:\n", pgno); print_decode_line(a, 0, 1, zType); print_decode_line(a, 1, 2, "Offset to first freeblock"); print_decode_line(a, 3, 2, "Number of cells on this page"); nCell = a[3]*256 + a[4]; print_decode_line(a, 5, 2, "Offset to cell content area"); print_decode_line(a, 7, 1, "Fragmented byte count"); if( a[0]==2 || a[0]==5 ){ print_decode_line(a, 8, 4, "Right child"); iCellPtr = 12; }else{ iCellPtr = 8; } for(i=0; i<nCell; i++){ int cofst = iCellPtr + i*2; char *zDesc; cofst = a[cofst]*256 + a[cofst+1]; describeCell(a[0], &a[cofst-hdrSize], &zDesc); printf(" %03x: cell[%d] %s\n", cofst, i, zDesc); } } int main(int argc, char **argv){ struct stat sbuf; unsigned char zPgSz[2]; if( argc<2 ){ fprintf(stderr,"Usage: %s FILENAME ?PAGE? ...\n", argv[0]); exit(1); } db = open(argv[1], O_RDONLY); if( db<0 ){ fprintf(stderr,"%s: can't open %s\n", argv[0], argv[1]); exit(1); } zPgSz[0] = 0; zPgSz[1] = 0; lseek(db, 16, SEEK_SET); read(db, zPgSz, 2); pagesize = zPgSz[0]*256 + zPgSz[1]; if( pagesize==0 ) pagesize = 1024; printf("Pagesize: %d\n", pagesize); fstat(db, &sbuf); mxPage = sbuf.st_size/pagesize; printf("Available pages: 1..%d\n", mxPage); if( argc==2 ){ int i; for(i=1; i<=mxPage; i++) print_page(i); }else{ int i; for(i=2; i<argc; i++){ int iStart, iEnd; char *zLeft; if( strcmp(argv[i], "dbheader")==0 ){ print_db_header(); continue; } if( !isdigit(argv[i][0]) ){ fprintf(stderr, "%s: unknown option: [%s]\n", argv[0], argv[i]); continue; } iStart = strtol(argv[i], &zLeft, 0); if( zLeft && strcmp(zLeft,"..end")==0 ){ iEnd = mxPage; }else if( zLeft && zLeft[0]=='.' && zLeft[1]=='.' ){ iEnd = strtol(&zLeft[2], 0, 0); }else if( zLeft && zLeft[0]=='b' ){ int ofst, nByte, hdrSize; unsigned char *a; if( iStart==1 ){ ofst = hdrSize = 100; nByte = pagesize-100; }else{ hdrSize = 0; ofst = (iStart-1)*pagesize; nByte = pagesize; } a = getContent(ofst, nByte); decode_btree_page(a, iStart, hdrSize); free(a); continue; }else{ iEnd = iStart; } if( iStart<1 || iEnd<iStart || iEnd>mxPage ){ fprintf(stderr, "Page argument should be LOWER?..UPPER?. Range 1 to %d\n", mxPage); |
︙ | ︙ |
Added tool/showwal.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 | /* ** A utility for printing content from a write-ahead log file. */ #include <stdio.h> #include <ctype.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <unistd.h> #include <stdlib.h> #include <string.h> static int pagesize = 1024; /* Size of a database page */ static int fd = -1; /* File descriptor for reading the WAL file */ static int mxFrame = 0; /* Last frame */ static int perLine = 16; /* HEX elements to print per line */ typedef long long int i64; /* Datatype for 64-bit integers */ /* ** Convert the var-int format into i64. Return the number of bytes ** in the var-int. Write the var-int value into *pVal. */ static int decodeVarint(const unsigned char *z, i64 *pVal){ i64 v = 0; int i; for(i=0; i<8; i++){ v = (v<<7) + (z[i]&0x7f); if( (z[i]&0x80)==0 ){ *pVal = v; return i+1; } } v = (v<<8) + (z[i]&0xff); *pVal = v; return 9; } /* Report an out-of-memory error and die. */ static void out_of_memory(void){ fprintf(stderr,"Out of memory...\n"); exit(1); } /* ** Read content from the file. ** ** Space to hold the content is obtained from malloc() and needs to be ** freed by the caller. */ static unsigned char *getContent(int ofst, int nByte){ unsigned char *aData; aData = malloc(nByte); if( aData==0 ) out_of_memory(); lseek(fd, ofst, SEEK_SET); read(fd, aData, nByte); return aData; } /* ** Print a range of bytes as hex and as ascii. */ static void print_byte_range( int ofst, /* First byte in the range of bytes to print */ int nByte, /* Number of bytes to print */ unsigned char *aData, /* Content to print */ int printOfst /* Add this amount to the index on the left column */ ){ int i, j; const char *zOfstFmt; if( ((printOfst+nByte)&~0xfff)==0 ){ zOfstFmt = " %03x: "; }else if( ((printOfst+nByte)&~0xffff)==0 ){ zOfstFmt = " %04x: "; }else if( ((printOfst+nByte)&~0xfffff)==0 ){ zOfstFmt = " %05x: "; }else if( ((printOfst+nByte)&~0xffffff)==0 ){ zOfstFmt = " %06x: "; }else{ zOfstFmt = " %08x: "; } for(i=0; i<nByte; i += perLine){ fprintf(stdout, zOfstFmt, i+printOfst); for(j=0; j<perLine; j++){ if( i+j>nByte ){ fprintf(stdout, " "); }else{ fprintf(stdout,"%02x ", aData[i+j]); } } for(j=0; j<perLine; j++){ if( i+j>nByte ){ fprintf(stdout, " "); }else{ fprintf(stdout,"%c", isprint(aData[i+j]) ? aData[i+j] : '.'); } } fprintf(stdout,"\n"); } } /* Print a line of decode output showing a 4-byte integer. */ static void print_decode_line( unsigned char *aData, /* Content being decoded */ int ofst, int nByte, /* Start and size of decode */ int asHex, /* If true, output value as hex */ const char *zMsg /* Message to append */ ){ int i, j; int val = aData[ofst]; char zBuf[100]; sprintf(zBuf, " %03x: %02x", ofst, aData[ofst]); i = strlen(zBuf); for(j=1; j<4; j++){ if( j>=nByte ){ sprintf(&zBuf[i], " "); }else{ sprintf(&zBuf[i], " %02x", aData[ofst+j]); val = val*256 + aData[ofst+j]; } i += strlen(&zBuf[i]); } if( asHex ){ sprintf(&zBuf[i], " 0x%08x", val); }else{ sprintf(&zBuf[i], " %9d", val); } printf("%s %s\n", zBuf, zMsg); } /* ** Print an entire page of content as hex */ static void print_frame(int iFrame){ int iStart; unsigned char *aData; iStart = 32 + (iFrame-1)*(pagesize+24); fprintf(stdout, "Frame %d: (offsets 0x%x..0x%x)\n", iFrame, iStart, iStart+pagesize+24); aData = getContent(iStart, pagesize+24); print_decode_line(aData, 0, 4, 0, "Page number"); print_decode_line(aData, 4, 4, 0, "DB size, or 0 for non-commit"); print_decode_line(aData, 8, 4, 1, "Salt-1"); print_decode_line(aData,12, 4, 1, "Salt-2"); print_decode_line(aData,16, 4, 1, "Checksum-1"); print_decode_line(aData,20, 4, 1, "Checksum-2"); print_byte_range(iStart+24, pagesize, aData+24, 0); free(aData); } /* ** extract a 32-bit big-endian integer */ static unsigned int getInt32(const unsigned char *a){ unsigned int x = (a[0]<<24) + (a[1]<<16) + (a[2]<<8) + a[3]; return x; } /* ** Print an entire page of content as hex */ static void print_oneline_frame(int iFrame){ int iStart; unsigned char *aData; iStart = 32 + (iFrame-1)*(pagesize+24); aData = getContent(iStart, 24); fprintf(stdout, "Frame %4d: %6d %6d 0x%08x 0x%08x 0x%08x 0x%08x\n", iFrame, getInt32(aData), getInt32(aData+4), getInt32(aData+8), getInt32(aData+12), getInt32(aData+16), getInt32(aData+20) ); free(aData); } /* ** Decode the WAL header. */ static void print_wal_header(void){ unsigned char *aData; aData = getContent(0, 32); printf("WAL Header:\n"); print_decode_line(aData, 0, 4,1,"Magic. 0x377f0682 (le) or 0x377f0683 (be)"); print_decode_line(aData, 4, 4, 0, "File format"); print_decode_line(aData, 8, 4, 0, "Database page size"); print_decode_line(aData, 12,4, 0, "Checkpoint sequence number"); print_decode_line(aData, 16,4, 1, "Salt-1"); print_decode_line(aData, 20,4, 1, "Salt-2"); print_decode_line(aData, 24,4, 1, "Checksum-1"); print_decode_line(aData, 28,4, 1, "Checksum-2"); free(aData); } /* ** Create a description for a single cell. */ static int describeCell(unsigned char cType, unsigned char *a, char **pzDesc){ int i; int nDesc = 0; int n = 0; int leftChild; i64 nPayload; i64 rowid; static char zDesc[100]; i = 0; if( cType<=5 ){ leftChild = ((a[0]*256 + a[1])*256 + a[2])*256 + a[3]; a += 4; n += 4; sprintf(zDesc, "left-child: %d ", leftChild); nDesc = strlen(zDesc); } if( cType!=5 ){ i = decodeVarint(a, &nPayload); a += i; n += i; sprintf(&zDesc[nDesc], "sz: %lld ", nPayload); nDesc += strlen(&zDesc[nDesc]); } if( cType==5 || cType==13 ){ i = decodeVarint(a, &rowid); a += i; n += i; sprintf(&zDesc[nDesc], "rowid: %lld ", rowid); nDesc += strlen(&zDesc[nDesc]); } *pzDesc = zDesc; return n; } /* ** Decode a btree page */ static void decode_btree_page(unsigned char *a, int pgno, int hdrSize){ const char *zType = "unknown"; int nCell; int i; int iCellPtr; switch( a[0] ){ case 2: zType = "index interior node"; break; case 5: zType = "table interior node"; break; case 10: zType = "index leaf"; break; case 13: zType = "table leaf"; break; } printf("Decode of btree page %d:\n", pgno); print_decode_line(a, 0, 1, 0, zType); print_decode_line(a, 1, 2, 0, "Offset to first freeblock"); print_decode_line(a, 3, 2, 0, "Number of cells on this page"); nCell = a[3]*256 + a[4]; print_decode_line(a, 5, 2, 0, "Offset to cell content area"); print_decode_line(a, 7, 1, 0, "Fragmented byte count"); if( a[0]==2 || a[0]==5 ){ print_decode_line(a, 8, 4, 0, "Right child"); iCellPtr = 12; }else{ iCellPtr = 8; } for(i=0; i<nCell; i++){ int cofst = iCellPtr + i*2; char *zDesc; cofst = a[cofst]*256 + a[cofst+1]; describeCell(a[0], &a[cofst-hdrSize], &zDesc); printf(" %03x: cell[%d] %s\n", cofst, i, zDesc); } } int main(int argc, char **argv){ struct stat sbuf; unsigned char zPgSz[2]; if( argc<2 ){ fprintf(stderr,"Usage: %s FILENAME ?PAGE? ...\n", argv[0]); exit(1); } fd = open(argv[1], O_RDONLY); if( fd<0 ){ fprintf(stderr,"%s: can't open %s\n", argv[0], argv[1]); exit(1); } zPgSz[0] = 0; zPgSz[1] = 0; lseek(fd, 10, SEEK_SET); read(fd, zPgSz, 2); pagesize = zPgSz[0]*256 + zPgSz[1]; if( pagesize==0 ) pagesize = 1024; printf("Pagesize: %d\n", pagesize); fstat(fd, &sbuf); if( sbuf.st_size<32 ){ printf("file too small to be a WAL\n"); return 0; } mxFrame = (sbuf.st_size - 32)/(pagesize + 24); printf("Available pages: 1..%d\n", mxFrame); if( argc==2 ){ int i; print_wal_header(); for(i=1; i<=mxFrame; i++) print_oneline_frame(i); }else{ int i; for(i=2; i<argc; i++){ int iStart, iEnd; char *zLeft; if( strcmp(argv[i], "header")==0 ){ print_wal_header(); continue; } if( !isdigit(argv[i][0]) ){ fprintf(stderr, "%s: unknown option: [%s]\n", argv[0], argv[i]); continue; } iStart = strtol(argv[i], &zLeft, 0); if( zLeft && strcmp(zLeft,"..end")==0 ){ iEnd = mxFrame; }else if( zLeft && zLeft[0]=='.' && zLeft[1]=='.' ){ iEnd = strtol(&zLeft[2], 0, 0); #if 0 }else if( zLeft && zLeft[0]=='b' ){ int ofst, nByte, hdrSize; unsigned char *a; if( iStart==1 ){ ofst = hdrSize = 100; nByte = pagesize-100; }else{ hdrSize = 0; ofst = (iStart-1)*pagesize; nByte = pagesize; } a = getContent(ofst, nByte); decode_btree_page(a, iStart, hdrSize); free(a); continue; #endif }else{ iEnd = iStart; } if( iStart<1 || iEnd<iStart || iEnd>mxFrame ){ fprintf(stderr, "Page argument should be LOWER?..UPPER?. Range 1 to %d\n", mxFrame); exit(1); } while( iStart<=iEnd ){ print_frame(iStart); iStart++; } } } close(fd); return 0; } |
Changes to tool/spaceanal.tcl.
︙ | ︙ | |||
22 23 24 25 26 27 28 | exit 1 } if {[file size $file_to_analyze]<512} { puts stderr "Empty or malformed database: $file_to_analyze" exit 1 } | < < < < > > > > | | 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 | exit 1 } if {[file size $file_to_analyze]<512} { puts stderr "Empty or malformed database: $file_to_analyze" exit 1 } # Open the database # sqlite3 db [lindex $argv 0] register_dbstat_vtab db set pageSize [db one {PRAGMA page_size}] #set DB [btree_open [lindex $argv 0] 1000 0] # In-memory database for collecting statistics. This script loops through # the tables and indices in the database being analyzed, adding a row for each # to an in-memory database (for which the schema is shown below). It then # queries the in-memory db to produce the space-analysis report. # sqlite3 mem :memory: |
︙ | ︙ | |||
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 | ovfl_pages int, -- Number of overflow pages used int_unused int, -- Number of unused bytes on interior pages leaf_unused int, -- Number of unused bytes on primary pages ovfl_unused int, -- Number of unused bytes on overflow pages gap_cnt int -- Number of gaps in the page layout );} mem eval $tabledef proc integerify {real} { if {[string is double -strict $real]} { return [expr {int($real)}] } else { return 0 } } mem function int integerify # Quote a string for use in an SQL query. Examples: # # [quote {hello world}] == {'hello world'} # [quote {hello world's}] == {'hello world''s'} # proc quote {txt} { regsub -all ' $txt '' q return '$q' } | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | 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 | ovfl_pages int, -- Number of overflow pages used int_unused int, -- Number of unused bytes on interior pages leaf_unused int, -- Number of unused bytes on primary pages ovfl_unused int, -- Number of unused bytes on overflow pages gap_cnt int -- Number of gaps in the page layout );} mem eval $tabledef # Create a temporary "dbstat" virtual table. # db eval { CREATE VIRTUAL TABLE temp.stat USING dbstat; CREATE TEMP TABLE dbstat AS SELECT * FROM temp.stat ORDER BY name, path; DROP TABLE temp.stat; } proc isleaf {pagetype is_index} { return [expr {$pagetype == "leaf" || ($pagetype == "internal" && $is_index)}] } proc isoverflow {pagetype is_index} { return [expr {$pagetype == "overflow"}] } proc isinternal {pagetype is_index} { return [expr {$pagetype == "internal" && $is_index==0}] } db func isleaf isleaf db func isinternal isinternal db func isoverflow isoverflow set sql { SELECT name, tbl_name FROM sqlite_master WHERE rootpage>0 } foreach {name tblname} [concat sqlite_master sqlite_master [db eval $sql]] { set is_index [expr {$name!=$tblname}] db eval { SELECT sum(ncell) AS nentry, sum(isleaf(pagetype, $is_index) * ncell) AS leaf_entries, sum(payload) AS payload, sum(isoverflow(pagetype, $is_index) * payload) AS ovfl_payload, sum(path LIKE '%+000000') AS ovfl_cnt, max(mx_payload) AS mx_payload, sum(isinternal(pagetype, $is_index)) AS int_pages, sum(isleaf(pagetype, $is_index)) AS leaf_pages, sum(isoverflow(pagetype, $is_index)) AS ovfl_pages, sum(isinternal(pagetype, $is_index) * unused) AS int_unused, sum(isleaf(pagetype, $is_index) * unused) AS leaf_unused, sum(isoverflow(pagetype, $is_index) * unused) AS ovfl_unused FROM temp.dbstat WHERE name = $name } break # Column 'gap_cnt' is set to the number of non-contiguous entries in the # list of pages visited if the b-tree structure is traversed in a top-down # fashion (each node visited before its child-tree is passed). Any overflow # chains present are traversed from start to finish before any child-tree # is. # set gap_cnt 0 set pglist [db eval { SELECT pageno FROM temp.dbstat WHERE name = $name ORDER BY rowid }] set prev [lindex $pglist 0] foreach pgno [lrange $pglist 1 end] { if {$pgno != $prev+1} {incr gap_cnt} set prev $pgno } mem eval { INSERT INTO space_used VALUES( $name, $tblname, $is_index, $nentry, $leaf_entries, $payload, $ovfl_payload, $ovfl_cnt, $mx_payload, $int_pages, $leaf_pages, $ovfl_pages, $int_unused, $leaf_unused, $ovfl_unused, $gap_cnt ); } } proc integerify {real} { if {[string is double -strict $real]} { return [expr {int($real)}] } else { return 0 } } mem function int integerify # Quote a string for use in an SQL query. Examples: # # [quote {hello world}] == {'hello world'} # [quote {hello world's}] == {'hello world''s'} # proc quote {txt} { regsub -all ' $txt '' q return '$q' } # Generate a single line of output in the statistics section of the # report. # proc statline {title value {extra {}}} { set len [string length $title] set dots [string range {......................................} $len end] set len [string length $value] |
︙ | ︙ | |||
544 545 546 547 548 549 550 | # # This procedure calculates and returns the number of pages used by the # auto-vacuum 'pointer-map'. If the database does not support auto-vacuum, # then 0 is returned. The two arguments are the size of the database file in # pages and the page size used by the database (in bytes). proc autovacuum_overhead {filePages pageSize} { | | | < < < | | 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 | # # This procedure calculates and returns the number of pages used by the # auto-vacuum 'pointer-map'. If the database does not support auto-vacuum, # then 0 is returned. The two arguments are the size of the database file in # pages and the page size used by the database (in bytes). proc autovacuum_overhead {filePages pageSize} { # Set $autovacuum to non-zero for databases that support auto-vacuum. set autovacuum [db one {PRAGMA auto_vacuum}] # If the database is not an auto-vacuum database or the file consists # of one page only then there is no overhead for auto-vacuum. Return zero. if {0==$autovacuum || $filePages==1} { return 0 } # The number of entries on each pointer map page. The layout of the # database file is one pointer-map page, followed by $ptrsPerPage other # pages, followed by a pointer-map page etc. The first pointer-map page # is the second page of the file overall. |
︙ | ︙ | |||
602 603 604 605 606 607 608 | set sql {SELECT sum(leaf_pages+int_pages+ovfl_pages) FROM space_used} set inuse_pgcnt [expr int([mem eval $sql])] set inuse_percent [percent $inuse_pgcnt $file_pgcnt] set free_pgcnt [expr $file_pgcnt-$inuse_pgcnt-$av_pgcnt] set free_percent [percent $free_pgcnt $file_pgcnt] | | | 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 | set sql {SELECT sum(leaf_pages+int_pages+ovfl_pages) FROM space_used} set inuse_pgcnt [expr int([mem eval $sql])] set inuse_percent [percent $inuse_pgcnt $file_pgcnt] set free_pgcnt [expr $file_pgcnt-$inuse_pgcnt-$av_pgcnt] set free_percent [percent $free_pgcnt $file_pgcnt] set free_pgcnt2 [db one {PRAGMA freelist_count}] set free_percent2 [percent $free_pgcnt2 $file_pgcnt] set file_pgcnt2 [expr {$inuse_pgcnt+$free_pgcnt2+$av_pgcnt}] set ntable [db eval {SELECT count(*)+1 FROM sqlite_master WHERE type='table'}] set nindex [db eval {SELECT count(*) FROM sqlite_master WHERE type='index'}] set sql {SELECT count(*) FROM sqlite_master WHERE name LIKE 'sqlite_autoindex%'} |
︙ | ︙ |