Compare commits
574 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 0ba9725489 | |||
| abfd35ea03 | |||
| 2b519ab015 | |||
| 634133183c | |||
| e6ad1717da | |||
| 25ef7f55d7 | |||
| 6d4e9c3d92 | |||
| 861a56821b | |||
| 7a40707b1f | |||
| edaeba5e64 | |||
| 92ddb3bd02 | |||
| 807b90f679 | |||
| 698a01ca2f | |||
| 1a4a73764c | |||
| c9c81dc6d7 | |||
| 77320ea48d | |||
| ab5be2e651 | |||
| b5489b88b0 | |||
| 64777ba834 | |||
| d76a902c87 | |||
| 3edb157e23 | |||
| bc60368d6f | |||
| 158e4e56d7 | |||
| dd5fa17d3c | |||
| 6412a4c8a5 | |||
| 9057fc7c7f | |||
| cb3cabd01b | |||
| 06a09a828a | |||
| e9bb566096 | |||
| 043e586e29 | |||
| 36fd41e52b | |||
| 36f49d0565 | |||
| 4b72524072 | |||
| 30642cf4d2 | |||
| 4b4ffadb43 | |||
| bc88711d08 | |||
| e74cc5c9bc | |||
| 50da663516 | |||
| 65f38d960d | |||
| 60ffc80756 | |||
| 89500dcd0b | |||
| c10b9dac84 | |||
| 0f85b2ff09 | |||
| 93f5113290 | |||
| 4d4757fcc8 | |||
| 20ba3d7fcb | |||
| 6a9e7f16eb | |||
| 5b4009f637 | |||
| 1158498dce | |||
| edace5d4f1 | |||
| 93006cdd85 | |||
| 25accbca72 | |||
| 9c8753c0a8 | |||
| 223b4bb3f7 | |||
| 54cda4edbe | |||
| d2d69cc1fa | |||
| ba6eb8769f | |||
| d6ef5afe3f | |||
| c9b9deaee2 | |||
| 5413ba4b17 | |||
| 588429a87d | |||
| 362b66f051 | |||
| 55377b4717 | |||
| 2adb878b1f | |||
| b3e845ba33 | |||
| 48c286fc61 | |||
| 66adb0a848 | |||
| 4b727f17d6 | |||
| 04cd7aa373 | |||
| d95ef5cb5a | |||
| 9af90b7231 | |||
| 79297d1d59 | |||
| 4a5bad572a | |||
| ea432ba2df | |||
| 3bafdede8a | |||
| 4e8142c726 | |||
| b47ebe6128 | |||
| 2f9a613ced | |||
| c5b86be7db | |||
| 4360fcea6d | |||
| 7a1c28defe | |||
| 257c13fa00 | |||
| c6c9e158c5 | |||
| bf2f5739c9 | |||
| 2c4dfc3095 | |||
| eaf6ae2992 | |||
| 7405fa745e | |||
| 58282f68d8 | |||
| 4a8b013ef6 | |||
| fb8c56f21b | |||
| 088c086b14 | |||
| 7723371eb7 | |||
| 9b4eaebc68 | |||
| 3b908d41a0 | |||
| 9d06ff2cb7 | |||
| 0a22c865e4 | |||
| 662389b265 | |||
| 6850a63393 | |||
| 27b2d1b88b | |||
| ad875e7416 | |||
| a2bfa046ea | |||
| b1c9633493 | |||
| 54fc5cc2ae | |||
| 792557ac67 | |||
| a622d90040 | |||
| af58068550 | |||
| 7df9b8e6e4 | |||
| 59ce2c434c | |||
| 16eb59484f | |||
| 6027561178 | |||
| 506a1400a3 | |||
| 75ba676790 | |||
| e77caa191b | |||
| 1ad93a003d | |||
| d7265378fd | |||
| 298af02308 | |||
| dadafa881f | |||
| c52496f57f | |||
| 9904298bb3 | |||
| 392ddeb12d | |||
| d15f430111 | |||
| b40897ab5e | |||
| d240f091c6 | |||
| 635f83c062 | |||
| 5c35e90377 | |||
| 01e697b4ec | |||
| 7ad3eb63cf | |||
| 2df45e02f4 | |||
| e43635aaa7 | |||
| 41f5f6ec2c | |||
| e24452edef | |||
| ceb97c1107 | |||
| 86873baa58 | |||
| c3191d236a | |||
| ba69061ebc | |||
| f8a89ca5dd | |||
| 47d00872fc | |||
| 5f0b8a0e30 | |||
| 1480095ff5 | |||
| cee11adaaa | |||
| f013e20c66 | |||
| 1cb3c7c415 | |||
| 8705f868e0 | |||
| 9e31fe02ba | |||
| 813646c182 | |||
| 989412a1cf | |||
| c436a03d02 | |||
| 432697467f | |||
| 013ae68b25 | |||
| 83465a662f | |||
| ef74652ceb | |||
| 90b2fe6b1e | |||
| 48590fcb37 | |||
| ea5e5f0b89 | |||
| e5ec01c481 | |||
| dfe4e6bb33 | |||
| 320d4c304d | |||
| e288992e19 | |||
| 642f0146d7 | |||
| e3c6b61cee | |||
| 4e59344a4c | |||
| da5c62400f | |||
| e0251d44fa | |||
| 4eda15e715 | |||
| 44728d7e01 | |||
| a71c743aae | |||
| c9b39288ae | |||
| de25a88c50 | |||
| 87c05f0c58 | |||
| cca8a4ad6f | |||
| 1dd518cfcd | |||
| cb43a937e5 | |||
| 1a7df58c1a | |||
| 445f3d564c | |||
| 216b70ff82 | |||
| 230368c30d | |||
| d8e4b13256 | |||
| 8dfef1175e | |||
| 76336d5b9d | |||
| ab3182f7c4 | |||
| cd9af608e1 | |||
| 94881d732b | |||
| 9b40d13f43 | |||
| e05950d878 | |||
| 1d96cc60d0 | |||
| 2a1df937ab | |||
| dd3bfe8648 | |||
| 2831c4d123 | |||
| 42735c7d30 | |||
| 22fa36dc04 | |||
| 5f33eaa6a4 | |||
| 5e769a50ad | |||
| ed204d1f44 | |||
| 96fdcb40af | |||
| 2e34326555 | |||
| 4c5ebee0b5 | |||
| 0cd874bd57 | |||
| 410c301fea | |||
| 87f0e9863d | |||
| 209bc522b0 | |||
| e1c03b6233 | |||
| 8dd099bbaa | |||
| 28f17017ee | |||
| 511f9e8d29 | |||
| 5a0da94302 | |||
| 98ef26b0d4 | |||
| 49377a8fb7 | |||
| 30f8c23657 | |||
| fb04a36c20 | |||
| b6ea12cc05 | |||
| b390681622 | |||
| 48a72ebfa3 | |||
| 09952c6490 | |||
| d8992cef36 | |||
| e0736da15a | |||
| ff535a2499 | |||
| 8aaf7bcc8d | |||
| fadd2b1972 | |||
| 9a2e5169a4 | |||
| c332cc30d9 | |||
| d43075bcaf | |||
| 9e5eb9c8cc | |||
| 598f7c59e1 | |||
| 85f071b850 | |||
| fd7459e0aa | |||
| 8145fc6eda | |||
| 7152cc0641 | |||
| d145915a59 | |||
| 0882da8001 | |||
| edc3537c61 | |||
| 72d5003ed8 | |||
| 4313f045c2 | |||
| a0daa751f8 | |||
| 760c8162ff | |||
| cd0509e256 | |||
| 2db8211526 | |||
| 98365be030 | |||
| 21e19b44f0 | |||
| 95489c58f7 | |||
| 72ceae01e5 | |||
| 5f6eb1a0dc | |||
| 481fd50cb4 | |||
| 12c56aa452 | |||
| ff02e200c0 | |||
| d562ec277c | |||
| 2eb22af03d | |||
| 9871664ed6 | |||
| 35cd8b1eb5 | |||
| 27fe1c3fcc | |||
| 000f95b1ba | |||
| 64caee4086 | |||
| b80dbdc2f7 | |||
| fe3765828a | |||
| 2c33183d11 | |||
| eedeb92caa | |||
| 0e4125a311 | |||
| 1fbd008cf4 | |||
| f78dcd1b96 | |||
| 26c8d0ca21 | |||
| d609bdb9d3 | |||
| c097e122b9 | |||
| af6f65fb26 | |||
| fc8b40f2f6 | |||
| 57a8c61501 | |||
| 63cecc4173 | |||
| 1431807a0b | |||
| 773d3afaa3 | |||
| c7a77ae1c6 | |||
| aaf8a0643e | |||
| 1c12657f9a | |||
| 099a0f5f54 | |||
| 83c434e68d | |||
| ed24da4b16 | |||
| fad0e70c63 | |||
| 9f24b53dcc | |||
| cc0164b5df | |||
| c55c8dbf7f | |||
| c1bcd9cc3e | |||
| 3fffbf9327 | |||
| 083aefc732 | |||
| b29e60c448 | |||
| 5c3340bd47 | |||
| 80aa545337 | |||
| 8f9d1ad8d5 | |||
| 9f6dd025ba | |||
| 2718960316 | |||
| 4910a76d45 | |||
| 55a6f6dc72 | |||
| c6952addf7 | |||
| 76729ed4be | |||
| 033564cca9 | |||
| e5a0cfa161 | |||
| d0d49b9ca3 | |||
| a87070a271 | |||
| 2e5c5052fd | |||
| 6650190891 | |||
| 57b60432ba | |||
| 888e1faf95 | |||
| 1ad78c5791 | |||
| 88e665fd0b | |||
| 106526e1bc | |||
| a84a283d18 | |||
| 363fb95bc3 | |||
| 4b4f51148a | |||
| 6fc8f36490 | |||
| 0c36fca004 | |||
| b7ca2177d7 | |||
| 0dfa4f6fcc | |||
| 61441c3493 | |||
| 8e16b2d3e6 | |||
| 64bcb8cfbb | |||
| 975c800dd8 | |||
| 0a9428d0a7 | |||
| 7dd7d98bbf | |||
| a28f85b076 | |||
| f10122d535 | |||
| 3cc19132da | |||
| fb82820a38 | |||
| e347d3e813 | |||
| 0a1082aec2 | |||
| 1373c3a8c5 | |||
| ecb87ac88d | |||
| ee0f22fd3b | |||
| 539f2fef03 | |||
| 18016ad2bb | |||
| 72e26dec86 | |||
| 4ef916e233 | |||
| ff8e42e2ca | |||
| 62659b2a80 | |||
| 37ff4d4c77 | |||
| 2a1d1e452e | |||
| 24b6b81cb0 | |||
| 321e828d03 | |||
| d0b67a8654 | |||
| 7887d7f24d | |||
| ac6b47d164 | |||
| 894241c847 | |||
| e835bc1285 | |||
| a8dee8df64 | |||
| 66860af3dd | |||
| 7d931b985a | |||
| abb9d5f189 | |||
| 8bbf544747 | |||
| 6ae3ab0036 | |||
| a7d16a5cd1 | |||
| c18d304066 | |||
| cbfe1101b3 | |||
| 12abf408ff | |||
| db45bd5e82 | |||
| 84b19a3da1 | |||
| d832da7f40 | |||
| 76dbe7a8d0 | |||
| 8bd0d58e1c | |||
| 783e778f38 | |||
| 9854260bca | |||
| f358009a93 | |||
| 9e730f0bb8 | |||
| 8762ec1935 | |||
| a1251bc40d | |||
| fc7f27b9d2 | |||
| 8d25cb90cd | |||
| 39a11819f3 | |||
| a15a8bc822 | |||
| c9e75fb23a | |||
| 4602b8e89d | |||
| b684fd5c4a | |||
| 05a41ee9ec | |||
| fcc31545bc | |||
| 6a754dc767 | |||
| 7ca1ea16ff | |||
| 9ef5e7708a | |||
| da84dcae54 | |||
| 61ea610cd0 | |||
| e59be01072 | |||
| 03181c8c27 | |||
| 48cb3a7600 | |||
| db97e56217 | |||
| c8d0b0b9d4 | |||
| 5ea494425f | |||
| a79e10b665 | |||
| b39a5ac229 | |||
| 69c338263a | |||
| e7036202bc | |||
| 8c2b1fde58 | |||
| 0f832ddc06 | |||
| 0f5a186c4c | |||
| 0f825a7e26 | |||
| af69eda63e | |||
| a2f629251f | |||
| 460d38f15e | |||
| 79752b6e63 | |||
| 3b96d1e2e4 | |||
| 7fb8990688 | |||
| 471b4b92bd | |||
| a48d7e7749 | |||
| 1bf4ca7c42 | |||
| 088645b090 | |||
| 17994e3bca | |||
| 6ff4627d5d | |||
| c73bd0a5cc | |||
| 93ca3933d1 | |||
| e104dd3cc3 | |||
| 922802c44e | |||
| f5dbe7f8d8 | |||
| 167fbbe195 | |||
| 765fecf010 | |||
| 701b68879c | |||
| d9fabbcc5b | |||
| c83db9e4e1 | |||
| 6bada27205 | |||
| 8d57d7af23 | |||
| ccf03677a3 | |||
| 882ef0b8c0 | |||
| 9cd4933ec1 | |||
| 6256c1c242 | |||
| 1d9bc9b7a0 | |||
| adeb970da2 | |||
| e22c375659 | |||
| 6da466e4f4 | |||
| e099b67c20 | |||
| 43c1ce390f | |||
| 5360b55c6c | |||
| 0aafa9c89c | |||
| 70ae0e93ec | |||
| cc15313cc9 | |||
| dd545d3bf2 | |||
| 3d1fb1dd75 | |||
| 2c628ea9d9 | |||
| d66e5794d1 | |||
| c0d269e96c | |||
| a36e01a772 | |||
| 78f9bb6c71 | |||
| c330887291 | |||
| 3dffcf911f | |||
| 51d82d1d24 | |||
| f9b2e05c75 | |||
| d05a7144cd | |||
| 080508a186 | |||
| 95a08c065a | |||
| b7203cde29 | |||
| 914b7e4238 | |||
| 38b802dfd7 | |||
| 19eb77bbe0 | |||
| 553168c706 | |||
| b0df540d7a | |||
| 4df049faec | |||
| d7d19b7137 | |||
| 870a0705fe | |||
| 8ef24b8deb | |||
| 32c83c8b9e | |||
| 5c288b929a | |||
| 19e76b2a7c | |||
| cb17ce9300 | |||
| 625015e0c9 | |||
| 19ff12dd76 | |||
| 145b4ea519 | |||
| 11f69b879c | |||
| be56ad31b7 | |||
| d62c07d42e | |||
| 50972b71e1 | |||
| e363d50db8 | |||
| df27352e37 | |||
| fefe82876c | |||
| 99bbcc8287 | |||
| c08556844e | |||
| 2d45d7bfab | |||
| afe18262b4 | |||
| 85bd982ba0 | |||
| 78d5e02f99 | |||
| 177d0f0b47 | |||
| 44e95d4f02 | |||
| 7b35a77b1a | |||
| e37f7397c9 | |||
| 3a22fdab9a | |||
| c0bebc1dcf | |||
| a8914faaaa | |||
| 7c60b26691 | |||
| a121cc7c60 | |||
| aa0325872f | |||
| 7617e4a8a4 | |||
| f27a80cf18 | |||
| fe0c0d2088 | |||
| 4194ff6598 | |||
| 52b1dbb5fc | |||
| 34c125747d | |||
| 4aff119f3a | |||
| 69def7ff2f | |||
| d49fd4e89a | |||
| 5f26e00761 | |||
| e604ec0be0 | |||
| 2409f8a112 | |||
| d93ba6271b | |||
| d9ed09d47b | |||
| 8da209b169 | |||
| 2900a6222f | |||
| 9d60875901 | |||
| 426f4ab009 | |||
| 2b62b5def8 | |||
| 07d9a03187 | |||
| 422dded5a9 | |||
| a522473c65 | |||
| 017256802b | |||
| 2aed979592 | |||
| 26fe413be6 | |||
| 5d513ba08c | |||
| 6fe4c184fb | |||
| 26197d1b62 | |||
| c530b9c65b | |||
| 5fe63bfb93 | |||
| bd441f7700 | |||
| 087ec072ef | |||
| 4b363a5130 | |||
| ba00e30a3a | |||
| 2ce26ff10a | |||
| eb09f6d788 | |||
| 49dcd79e7d | |||
| 194c21de34 | |||
| 8afffe7244 | |||
| 557341e8fa | |||
| cf1e395acb | |||
| a4c07346b7 | |||
| 80f2b33a12 | |||
| 9a8b9ec51c | |||
| dec8bc0eb4 | |||
| 4097d0ca79 | |||
| 12ca5ac3d2 | |||
| 033ac6af7e | |||
| 89441bf185 | |||
| 8414e81359 | |||
| 8d85364261 | |||
| 0e15d921d9 | |||
| eb5a549ecf | |||
| de6fde6af6 | |||
| 283a85ca21 | |||
| 170e998236 | |||
| a8f286a38a | |||
| e2f84b403c | |||
| b52dcd8989 | |||
| b56660f5a4 | |||
| 0b4c042b86 | |||
| 86396219a3 | |||
| 0129a54a16 | |||
| c0e2203adb | |||
| fca760c841 | |||
| 1637a5171f | |||
| 3d2a529df6 | |||
| ed916ba025 | |||
| cfbb5e82db | |||
| a0620acc4a | |||
| ce0442edf3 | |||
| dd703e22a5 | |||
| 709947a315 | |||
| e234cfd11f | |||
| 71c57db099 | |||
| e39cd91aea | |||
| a912348a19 | |||
| 11499f0ab4 | |||
| 43f58d6a17 | |||
| c2024c4ef2 | |||
| 1e75483cf0 | |||
| d3d4b3c698 | |||
| 5225449e5b | |||
| c8dcdff37e | |||
| 9c10608a26 | |||
| dc7b7385c9 | |||
| 272989b44d | |||
| 50687436f7 | |||
| e0a6e0fda0 | |||
| 240c7faf8b | |||
| 2e993288e6 | |||
| 9697308f44 | |||
| e336b8914c | |||
| 2ad96f5880 | |||
| 1b361ff381 |
+14
-4
@@ -382,6 +382,7 @@ TESTSRC = \
|
||||
$(TOP)/src/test_blob.c \
|
||||
$(TOP)/src/test_btree.c \
|
||||
$(TOP)/src/test_config.c \
|
||||
$(TOP)/src/test_delete.c \
|
||||
$(TOP)/src/test_demovfs.c \
|
||||
$(TOP)/src/test_devsym.c \
|
||||
$(TOP)/src/test_fs.c \
|
||||
@@ -429,6 +430,7 @@ TESTSRC += \
|
||||
$(TOP)/ext/misc/nextchar.c \
|
||||
$(TOP)/ext/misc/percentile.c \
|
||||
$(TOP)/ext/misc/regexp.c \
|
||||
$(TOP)/ext/misc/remember.c \
|
||||
$(TOP)/ext/misc/series.c \
|
||||
$(TOP)/ext/misc/spellfix.c \
|
||||
$(TOP)/ext/misc/totype.c \
|
||||
@@ -549,7 +551,8 @@ FUZZDATA = \
|
||||
$(TOP)/test/fuzzdata1.db \
|
||||
$(TOP)/test/fuzzdata2.db \
|
||||
$(TOP)/test/fuzzdata3.db \
|
||||
$(TOP)/test/fuzzdata4.db
|
||||
$(TOP)/test/fuzzdata4.db \
|
||||
$(TOP)/test/fuzzdata5.db
|
||||
|
||||
# Standard options to testfixture
|
||||
#
|
||||
@@ -558,9 +561,12 @@ TESTOPTS = --verbose=file --output=test-out.txt
|
||||
# Extra compiler options for various shell tools
|
||||
#
|
||||
SHELL_OPT = -DSQLITE_ENABLE_JSON1 -DSQLITE_ENABLE_FTS4
|
||||
# SHELL_OPT += -DSQLITE_ENABLE_FTS5
|
||||
SHELL_OPT += -DSQLITE_ENABLE_EXPLAIN_COMMENTS
|
||||
SHELL_OPT += -DSQLITE_ENABLE_UNKNOWN_SQL_FUNCTION
|
||||
FUZZERSHELL_OPT = -DSQLITE_ENABLE_JSON1
|
||||
FUZZCHECK_OPT = -DSQLITE_ENABLE_JSON1 -DSQLITE_ENABLE_MEMSYS5
|
||||
FUZZCHECK_OPT = -DSQLITE_ENABLE_JSON1 -DSQLITE_ENABLE_MEMSYS5 -DSQLITE_OSS_FUZZ
|
||||
FUZZCHECK_SRC = $(TOP)/test/fuzzcheck.c $(TOP)/test/ossfuzz.c
|
||||
|
||||
# This is the default Makefile target. The objects listed here
|
||||
# are what get build when you type just "make" with no arguments.
|
||||
@@ -609,8 +615,11 @@ fuzzershell$(TEXE): $(TOP)/tool/fuzzershell.c sqlite3.c sqlite3.h
|
||||
$(LTLINK) -o $@ $(FUZZERSHELL_OPT) \
|
||||
$(TOP)/tool/fuzzershell.c sqlite3.c $(TLIBS)
|
||||
|
||||
fuzzcheck$(TEXE): $(TOP)/test/fuzzcheck.c sqlite3.c sqlite3.h
|
||||
$(LTLINK) -o $@ $(FUZZCHECK_OPT) $(TOP)/test/fuzzcheck.c sqlite3.c $(TLIBS)
|
||||
fuzzcheck$(TEXE): $(FUZZCHECK_SRC) sqlite3.c sqlite3.h
|
||||
$(LTLINK) -o $@ $(FUZZCHECK_OPT) $(FUZZCHECK_SRC) sqlite3.c $(TLIBS)
|
||||
|
||||
ossshell$(TEXE): $(TOP)/test/ossfuzz.c $(TOP)/test/ossshell.c sqlite3.c sqlite3.h
|
||||
$(LTLINK) -o $@ $(FUZZCHECK_OPT) $(TOP)/test/ossshell.c sqlite3.c sqlite3.h $(TLIBS)
|
||||
|
||||
mptester$(TEXE): sqlite3.lo $(TOP)/mptest/mptest.c
|
||||
$(LTLINK) -o $@ -I. $(TOP)/mptest/mptest.c sqlite3.lo \
|
||||
@@ -1100,6 +1109,7 @@ fastfuzztest: fuzzcheck$(TEXE) $(FUZZDATA)
|
||||
valgrindfuzz: fuzzcheck$(TEXT) $(FUZZDATA)
|
||||
valgrind ./fuzzcheck$(TEXE) --cell-size-check --limit-mem 10M --timeout 600 $(FUZZDATA)
|
||||
|
||||
|
||||
# Minimal testing that runs in less than 3 minutes
|
||||
#
|
||||
quicktest: ./testfixture$(TEXE)
|
||||
|
||||
+148
-22
@@ -24,6 +24,20 @@ USE_AMALGAMATION = 1
|
||||
USE_FULLWARN = 0
|
||||
!ENDIF
|
||||
|
||||
# Set this non-0 to enable full runtime error checks (-RTC1, etc). This
|
||||
# has no effect if (any) optimizations are enabled.
|
||||
#
|
||||
!IFNDEF USE_RUNTIME_CHECKS
|
||||
USE_RUNTIME_CHECKS = 0
|
||||
!ENDIF
|
||||
|
||||
# Set this non-0 to create a SQLite amalgamation file that excludes the
|
||||
# various built-in extensions.
|
||||
#
|
||||
!IFNDEF MINIMAL_AMALGAMATION
|
||||
MINIMAL_AMALGAMATION = 0
|
||||
!ENDIF
|
||||
|
||||
# Set this non-0 to use "stdcall" calling convention for the core library
|
||||
# and shell executable.
|
||||
#
|
||||
@@ -263,12 +277,39 @@ SQLITE3EXEPDB = /pdb:sqlite3sh.pdb
|
||||
!ENDIF
|
||||
!ENDIF
|
||||
|
||||
# <<mark>>
|
||||
# These are the names of the customized Tcl header files used by various parts
|
||||
# of this makefile when the stdcall calling convention is in use. It is not
|
||||
# used for any other purpose.
|
||||
#
|
||||
!IFNDEF SQLITETCLH
|
||||
SQLITETCLH = sqlite_tcl.h
|
||||
!ENDIF
|
||||
|
||||
!IFNDEF SQLITETCLDECLSH
|
||||
SQLITETCLDECLSH = sqlite_tclDecls.h
|
||||
!ENDIF
|
||||
|
||||
# These are the additional targets that the targets that integrate with the
|
||||
# Tcl library should depend on when compiling, etc.
|
||||
#
|
||||
!IFNDEF SQLITE_TCL_DEP
|
||||
!IF $(USE_STDCALL)!=0 || $(FOR_WIN10)!=0
|
||||
SQLITE_TCL_DEP = $(SQLITETCLDECLSH) $(SQLITETCLH)
|
||||
!ELSE
|
||||
SQLITE_TCL_DEP =
|
||||
!ENDIF
|
||||
!ENDIF
|
||||
# <</mark>>
|
||||
|
||||
# These are the "standard" SQLite compilation options used when compiling for
|
||||
# the Windows platform.
|
||||
#
|
||||
!IFNDEF OPT_FEATURE_FLAGS
|
||||
!IF $(MINIMAL_AMALGAMATION)==0
|
||||
OPT_FEATURE_FLAGS = $(OPT_FEATURE_FLAGS) -DSQLITE_ENABLE_FTS3=1
|
||||
OPT_FEATURE_FLAGS = $(OPT_FEATURE_FLAGS) -DSQLITE_ENABLE_RTREE=1
|
||||
!ENDIF
|
||||
OPT_FEATURE_FLAGS = $(OPT_FEATURE_FLAGS) -DSQLITE_ENABLE_COLUMN_METADATA=1
|
||||
!ENDIF
|
||||
|
||||
@@ -464,20 +505,32 @@ RCC = $(RC) -DSQLITE_OS_WIN=1 -I. -I$(TOP) -I$(TOP)\src $(RCOPTS) $(RCCOPTS)
|
||||
#
|
||||
!IF $(USE_STDCALL)!=0 || $(FOR_WIN10)!=0
|
||||
!IF "$(PLATFORM)"=="x86"
|
||||
CORE_CCONV_OPTS = -Gz -DSQLITE_CDECL=__cdecl -DSQLITE_STDCALL=__stdcall
|
||||
SHELL_CCONV_OPTS = -Gz -DSQLITE_CDECL=__cdecl -DSQLITE_STDCALL=__stdcall
|
||||
CORE_CCONV_OPTS = -Gz -DSQLITE_CDECL=__cdecl -DSQLITE_APICALL=__stdcall -DSQLITE_CALLBACK=__stdcall -DSQLITE_SYSAPI=__stdcall
|
||||
SHELL_CCONV_OPTS = -Gz -DSQLITE_CDECL=__cdecl -DSQLITE_APICALL=__stdcall -DSQLITE_CALLBACK=__stdcall -DSQLITE_SYSAPI=__stdcall
|
||||
# <<mark>>
|
||||
TEST_CCONV_OPTS = -Gz -DSQLITE_CDECL=__cdecl -DSQLITE_APICALL=__stdcall -DSQLITE_CALLBACK=__stdcall -DSQLITE_SYSAPI=__stdcall -DINCLUDE_SQLITE_TCL_H=1 -DSQLITE_TCLAPI=__cdecl
|
||||
# <</mark>>
|
||||
!ELSE
|
||||
!IFNDEF PLATFORM
|
||||
CORE_CCONV_OPTS = -Gz -DSQLITE_CDECL=__cdecl -DSQLITE_STDCALL=__stdcall
|
||||
SHELL_CCONV_OPTS = -Gz -DSQLITE_CDECL=__cdecl -DSQLITE_STDCALL=__stdcall
|
||||
CORE_CCONV_OPTS = -Gz -DSQLITE_CDECL=__cdecl -DSQLITE_APICALL=__stdcall -DSQLITE_CALLBACK=__stdcall -DSQLITE_SYSAPI=__stdcall
|
||||
SHELL_CCONV_OPTS = -Gz -DSQLITE_CDECL=__cdecl -DSQLITE_APICALL=__stdcall -DSQLITE_CALLBACK=__stdcall -DSQLITE_SYSAPI=__stdcall
|
||||
# <<mark>>
|
||||
TEST_CCONV_OPTS = -Gz -DSQLITE_CDECL=__cdecl -DSQLITE_APICALL=__stdcall -DSQLITE_CALLBACK=__stdcall -DSQLITE_SYSAPI=__stdcall -DINCLUDE_SQLITE_TCL_H=1 -DSQLITE_TCLAPI=__cdecl
|
||||
# <</mark>>
|
||||
!ELSE
|
||||
CORE_CCONV_OPTS =
|
||||
SHELL_CCONV_OPTS =
|
||||
# <<mark>>
|
||||
TEST_CCONV_OPTS =
|
||||
# <</mark>>
|
||||
!ENDIF
|
||||
!ENDIF
|
||||
!ELSE
|
||||
CORE_CCONV_OPTS =
|
||||
SHELL_CCONV_OPTS =
|
||||
# <<mark>>
|
||||
TEST_CCONV_OPTS =
|
||||
# <</mark>>
|
||||
!ENDIF
|
||||
|
||||
# These are additional compiler options used for the core library.
|
||||
@@ -630,12 +683,35 @@ RCC = $(RCC) -I$(TOP)\ext\session
|
||||
# options are necessary in order to allow debugging symbols to
|
||||
# work correctly with Visual Studio when using the amalgamation.
|
||||
#
|
||||
!IFNDEF MKSQLITE3C_TOOL
|
||||
!IF $(MINIMAL_AMALGAMATION)!=0
|
||||
MKSQLITE3C_TOOL = $(TOP)\tool\mksqlite3c-noext.tcl
|
||||
!ELSE
|
||||
MKSQLITE3C_TOOL = $(TOP)\tool\mksqlite3c.tcl
|
||||
!ENDIF
|
||||
!ENDIF
|
||||
|
||||
!IFNDEF MKSQLITE3C_ARGS
|
||||
!IF $(DEBUG)>1
|
||||
MKSQLITE3C_ARGS = --linemacros
|
||||
!ELSE
|
||||
MKSQLITE3C_ARGS =
|
||||
!ENDIF
|
||||
!IF $(USE_STDCALL)!=0 || $(FOR_WIN10)!=0
|
||||
MKSQLITE3C_ARGS = $(MKSQLITE3C_ARGS) --useapicall
|
||||
!ENDIF
|
||||
!ENDIF
|
||||
|
||||
# The mksqlite3h.tcl script accepts some options on the command line.
|
||||
# When compiling with stdcall support, some of these options are
|
||||
# necessary.
|
||||
#
|
||||
!IFNDEF MKSQLITE3H_ARGS
|
||||
!IF $(USE_STDCALL)!=0 || $(FOR_WIN10)!=0
|
||||
MKSQLITE3H_ARGS = --useapicall
|
||||
!ELSE
|
||||
MKSQLITE3H_ARGS =
|
||||
!ENDIF
|
||||
!ENDIF
|
||||
# <</mark>>
|
||||
|
||||
@@ -718,11 +794,11 @@ TCLLIBDIR = c:\tcl\lib
|
||||
!ENDIF
|
||||
|
||||
!IFNDEF LIBTCL
|
||||
LIBTCL = tcl85.lib
|
||||
LIBTCL = tcl86.lib
|
||||
!ENDIF
|
||||
|
||||
!IFNDEF LIBTCLSTUB
|
||||
LIBTCLSTUB = tclstub85.lib
|
||||
LIBTCLSTUB = tclstub86.lib
|
||||
!ENDIF
|
||||
|
||||
!IFNDEF LIBTCLPATH
|
||||
@@ -752,7 +828,7 @@ LIBICU = icuuc.lib icuin.lib
|
||||
# specific Tcl shell to use.
|
||||
#
|
||||
!IFNDEF TCLSH_CMD
|
||||
TCLSH_CMD = tclsh85
|
||||
TCLSH_CMD = tclsh
|
||||
!ENDIF
|
||||
# <</mark>>
|
||||
|
||||
@@ -835,6 +911,10 @@ RCC = $(RCC) -D_DEBUG
|
||||
!IF $(DEBUG)>1 || $(OPTIMIZATIONS)==0
|
||||
TCC = $(TCC) -Od
|
||||
BCC = $(BCC) -Od
|
||||
!IF $(USE_RUNTIME_CHECKS)!=0
|
||||
TCC = $(TCC) -RTC1
|
||||
BCC = $(BCC) -RTC1
|
||||
!ENDIF
|
||||
!ELSEIF $(OPTIMIZATIONS)>=3
|
||||
TCC = $(TCC) -Ox
|
||||
BCC = $(BCC) -Ox
|
||||
@@ -1235,6 +1315,16 @@ SRC11 = \
|
||||
parse.h \
|
||||
$(SQLITE3H)
|
||||
|
||||
# Generated Tcl header files
|
||||
#
|
||||
!IF $(USE_STDCALL)!=0 || $(FOR_WIN10)!=0
|
||||
SRC12 = \
|
||||
$(SQLITETCLH) \
|
||||
$(SQLITETCLDECLSH)
|
||||
!ELSE
|
||||
SRC12 =
|
||||
!ENDIF
|
||||
|
||||
# All source code files.
|
||||
#
|
||||
SRC = $(SRC00) $(SRC01) $(SRC02) $(SRC03) $(SRC04) $(SRC05) $(SRC06) $(SRC07) $(SRC08) $(SRC09) $(SRC10) $(SRC11)
|
||||
@@ -1258,6 +1348,7 @@ TESTSRC = \
|
||||
$(TOP)\src\test_blob.c \
|
||||
$(TOP)\src\test_btree.c \
|
||||
$(TOP)\src\test_config.c \
|
||||
$(TOP)\src\test_delete.c \
|
||||
$(TOP)\src\test_demovfs.c \
|
||||
$(TOP)\src\test_devsym.c \
|
||||
$(TOP)\src\test_fs.c \
|
||||
@@ -1305,6 +1396,7 @@ TESTEXT = \
|
||||
$(TOP)\ext\misc\nextchar.c \
|
||||
$(TOP)\ext\misc\percentile.c \
|
||||
$(TOP)\ext\misc\regexp.c \
|
||||
$(TOP)\ext\misc\remember.c \
|
||||
$(TOP)\ext\misc\series.c \
|
||||
$(TOP)\ext\misc\spellfix.c \
|
||||
$(TOP)\ext\misc\totype.c \
|
||||
@@ -1341,7 +1433,7 @@ HDR = \
|
||||
parse.h \
|
||||
$(TOP)\src\pragma.h \
|
||||
$(SQLITE3H) \
|
||||
$(TOP)\src\sqlite3ext.h \
|
||||
sqlite3ext.h \
|
||||
$(TOP)\src\sqliteInt.h \
|
||||
$(TOP)\src\sqliteLimit.h \
|
||||
$(TOP)\src\vdbe.h \
|
||||
@@ -1388,7 +1480,8 @@ FUZZDATA = \
|
||||
$(TOP)\test\fuzzdata1.db \
|
||||
$(TOP)\test\fuzzdata2.db \
|
||||
$(TOP)\test\fuzzdata3.db \
|
||||
$(TOP)\test\fuzzdata4.db
|
||||
$(TOP)\test\fuzzdata4.db \
|
||||
$(TOP)\test\fuzzdata5.db
|
||||
# <</mark>>
|
||||
|
||||
# Additional compiler options for the shell. These are only effective
|
||||
@@ -1403,7 +1496,9 @@ SHELL_COMPILE_OPTS = $(SHELL_COMPILE_OPTS) -DSQLITE_SHELL_JSON1 -DSQLITE_ENABLE_
|
||||
#
|
||||
MPTESTER_COMPILE_OPTS = -DSQLITE_SHELL_JSON1 -DSQLITE_ENABLE_FTS5
|
||||
FUZZERSHELL_COMPILE_OPTS = -DSQLITE_ENABLE_JSON1
|
||||
FUZZCHECK_COMPILE_OPTS = -DSQLITE_ENABLE_JSON1 -DSQLITE_ENABLE_MEMSYS5
|
||||
FUZZCHECK_COMPILE_OPTS = -DSQLITE_ENABLE_JSON1 -DSQLITE_ENABLE_MEMSYS5 -DSQLITE_OSS_FUZZ
|
||||
FUZZCHECK_SRC = $(TOP)\test\fuzzcheck.c $(TOP)\test\ossfuzz.c
|
||||
OSSSHELL_SRC = $(TOP)\test\ossshell.c $(TOP)\test\ossfuzz.c
|
||||
|
||||
# Standard options to testfixture.
|
||||
#
|
||||
@@ -1473,8 +1568,11 @@ sourcetest: srcck1.exe sqlite3.c
|
||||
fuzzershell.exe: $(TOP)\tool\fuzzershell.c $(SQLITE3C) $(SQLITE3H)
|
||||
$(LTLINK) $(NO_WARN) $(FUZZERSHELL_COMPILE_OPTS) $(TOP)\tool\fuzzershell.c $(SQLITE3C) /link $(LDFLAGS) $(LTLINKOPTS)
|
||||
|
||||
fuzzcheck.exe: $(TOP)\test\fuzzcheck.c $(SQLITE3C) $(SQLITE3H)
|
||||
$(LTLINK) $(NO_WARN) $(FUZZCHECK_COMPILE_OPTS) $(TOP)\test\fuzzcheck.c $(SQLITE3C) /link $(LDFLAGS) $(LTLINKOPTS)
|
||||
fuzzcheck.exe: $(FUZZCHECK_SRC) $(SQLITE3C) $(SQLITE3H)
|
||||
$(LTLINK) $(NO_WARN) $(FUZZCHECK_COMPILE_OPTS) $(FUZZCHECK_SRC) $(SQLITE3C) /link $(LDFLAGS) $(LTLINKOPTS)
|
||||
|
||||
ossshell.exe: $(OSSSHELL_SRC) $(SQLITE3C) $(SQLITE3H)
|
||||
$(LTLINK) $(NO_WARN) $(FUZZCHECK_COMPILE_OPTS) $(OSSSHELL_SRC) $(SQLITE3C) /link $(LDFLAGS) $(LTLINKOPTS)
|
||||
|
||||
mptester.exe: $(TOP)\mptest\mptest.c $(SQLITE3C) $(SQLITE3H)
|
||||
$(LTLINK) $(NO_WARN) $(MPTESTER_COMPILE_OPTS) $(TOP)\mptest\mptest.c $(SQLITE3C) /link $(LDFLAGS) $(LTLINKOPTS)
|
||||
@@ -1499,7 +1597,7 @@ mptest: mptester.exe
|
||||
# files are automatically generated. This target takes care of
|
||||
# all that automatic generation.
|
||||
#
|
||||
.target_source: $(SRC) $(TOP)\tool\vdbe-compress.tcl fts5.c
|
||||
.target_source: $(SRC) $(TOP)\tool\vdbe-compress.tcl fts5.c $(SQLITE_TCL_DEP)
|
||||
-rmdir /Q/S tsrc 2>NUL
|
||||
-mkdir tsrc
|
||||
for %i in ($(SRC00)) do copy /Y %i tsrc
|
||||
@@ -1514,6 +1612,7 @@ mptest: mptester.exe
|
||||
for %i in ($(SRC09)) do copy /Y %i tsrc
|
||||
for %i in ($(SRC10)) do copy /Y %i tsrc
|
||||
for %i in ($(SRC11)) do copy /Y %i tsrc
|
||||
for %i in ($(SRC12)) do copy /Y %i tsrc
|
||||
copy /Y fts5.c tsrc
|
||||
copy /Y fts5.h tsrc
|
||||
del /Q tsrc\sqlite.h.in tsrc\parse.y 2>NUL
|
||||
@@ -1521,8 +1620,8 @@ mptest: mptester.exe
|
||||
move vdbe.new tsrc\vdbe.c
|
||||
echo > .target_source
|
||||
|
||||
sqlite3.c: .target_source sqlite3ext.h $(TOP)\tool\mksqlite3c.tcl
|
||||
$(TCLSH_CMD) $(TOP)\tool\mksqlite3c.tcl $(MKSQLITE3C_ARGS)
|
||||
sqlite3.c: .target_source sqlite3ext.h $(MKSQLITE3C_TOOL)
|
||||
$(TCLSH_CMD) $(MKSQLITE3C_TOOL) $(MKSQLITE3C_ARGS)
|
||||
copy tsrc\shell.c .
|
||||
copy $(TOP)\ext\session\sqlite3session.h .
|
||||
|
||||
@@ -1795,10 +1894,10 @@ wherecode.lo: $(TOP)\src\wherecode.c $(HDR)
|
||||
whereexpr.lo: $(TOP)\src\whereexpr.c $(HDR)
|
||||
$(LTCOMPILE) $(CORE_COMPILE_OPTS) -c $(TOP)\src\whereexpr.c
|
||||
|
||||
tclsqlite.lo: $(TOP)\src\tclsqlite.c $(HDR)
|
||||
tclsqlite.lo: $(TOP)\src\tclsqlite.c $(HDR) $(SQLITE_TCL_DEP)
|
||||
$(LTCOMPILE) $(NO_WARN) -DUSE_TCL_STUBS=1 -DBUILD_sqlite -I$(TCLINCDIR) -c $(TOP)\src\tclsqlite.c
|
||||
|
||||
tclsqlite-shell.lo: $(TOP)\src\tclsqlite.c $(HDR)
|
||||
tclsqlite-shell.lo: $(TOP)\src\tclsqlite.c $(HDR) $(SQLITE_TCL_DEP)
|
||||
$(LTCOMPILE) $(NO_WARN) -DTCLSH=1 -DBUILD_sqlite -I$(TCLINCDIR) -c $(TOP)\src\tclsqlite.c
|
||||
|
||||
tclsqlite3.exe: tclsqlite-shell.lo $(SQLITE3C) $(SQLITE3H) $(LIBRESOBJS)
|
||||
@@ -1824,10 +1923,16 @@ parse.c: $(TOP)\src\parse.y lemon.exe $(TOP)\tool\addopcodes.tcl
|
||||
$(TCLSH_CMD) $(TOP)\tool\addopcodes.tcl parse.h.temp > parse.h
|
||||
|
||||
$(SQLITE3H): $(TOP)\src\sqlite.h.in $(TOP)\manifest.uuid $(TOP)\VERSION
|
||||
$(TCLSH_CMD) $(TOP)\tool\mksqlite3h.tcl $(TOP:\=/) > $(SQLITE3H)
|
||||
$(TCLSH_CMD) $(TOP)\tool\mksqlite3h.tcl $(TOP:\=/) > $(SQLITE3H) $(MKSQLITE3H_ARGS)
|
||||
|
||||
sqlite3ext.h: .target_source
|
||||
copy tsrc\sqlite3ext.h .
|
||||
!IF $(USE_STDCALL)!=0 || $(FOR_WIN10)!=0
|
||||
type tsrc\sqlite3ext.h | $(TCLSH_CMD) $(TOP)\tool\replace.tcl regsub "\(\*\)" "(SQLITE_CALLBACK *)" \
|
||||
| $(TCLSH_CMD) $(TOP)\tool\replace.tcl regsub "\(\*" "(SQLITE_APICALL *" > sqlite3ext.h
|
||||
copy /Y sqlite3ext.h tsrc\sqlite3ext.h
|
||||
!ELSE
|
||||
copy /Y tsrc\sqlite3ext.h sqlite3ext.h
|
||||
!ENDIF
|
||||
|
||||
mkkeywordhash.exe: $(TOP)\tool\mkkeywordhash.c
|
||||
$(BCC) $(NO_WARN) -Fe$@ $(REQ_FEATURE_FLAGS) $(OPT_FEATURE_FLAGS) $(EXT_FEATURE_FLAGS) $(OPTS) \
|
||||
@@ -1960,6 +2065,7 @@ TESTFIXTURE_FLAGS = $(TESTFIXTURE_FLAGS) -DSQLITE_SERVER=1 -DSQLITE_PRIVATE=""
|
||||
TESTFIXTURE_FLAGS = $(TESTFIXTURE_FLAGS) -DSQLITE_CORE $(NO_WARN)
|
||||
TESTFIXTURE_FLAGS = $(TESTFIXTURE_FLAGS) -DSQLITE_SERIES_CONSTRAINT_VERIFY=1
|
||||
TESTFIXTURE_FLAGS = $(TESTFIXTURE_FLAGS) -DSQLITE_DEFAULT_PAGE_SIZE=1024
|
||||
TESTFIXTURE_FLAGS = $(TESTFIXTURE_FLAGS) $(TEST_CCONV_OPTS)
|
||||
|
||||
TESTFIXTURE_SRC0 = $(TESTEXT) $(TESTSRC2)
|
||||
TESTFIXTURE_SRC1 = $(TESTEXT) $(SQLITE3C)
|
||||
@@ -1969,7 +2075,27 @@ TESTFIXTURE_SRC = $(TESTSRC) $(TOP)\src\tclsqlite.c $(TESTFIXTURE_SRC0)
|
||||
TESTFIXTURE_SRC = $(TESTSRC) $(TOP)\src\tclsqlite.c $(TESTFIXTURE_SRC1)
|
||||
!ENDIF
|
||||
|
||||
testfixture.exe: $(TESTFIXTURE_SRC) $(SQLITE3H) $(LIBRESOBJS) $(HDR)
|
||||
!IF $(USE_STDCALL)!=0 || $(FOR_WIN10)!=0
|
||||
sqlite_tclDecls.h:
|
||||
echo #ifndef SQLITE_TCLAPI > $(SQLITETCLDECLSH)
|
||||
echo # define SQLITE_TCLAPI >> $(SQLITETCLDECLSH)
|
||||
echo #endif >> $(SQLITETCLDECLSH)
|
||||
type "$(TCLINCDIR)\tclDecls.h" \
|
||||
| $(TCLSH_CMD) $(TOP)\tool\replace.tcl regsub "^(EXTERN(?: CONST\d+?)?\s+?[^\(]*?\s+?)Tcl_" "\1 SQLITE_TCLAPI Tcl_" \
|
||||
| $(TCLSH_CMD) $(TOP)\tool\replace.tcl regsub "^(EXTERN\s+?(?:void|VOID)\s+?)TclFreeObj" "\1 SQLITE_TCLAPI TclFreeObj" \
|
||||
| $(TCLSH_CMD) $(TOP)\tool\replace.tcl regsub "\(\*tcl_" "(SQLITE_TCLAPI *tcl_" \
|
||||
| $(TCLSH_CMD) $(TOP)\tool\replace.tcl regsub "\(\*tclFreeObj" "(SQLITE_TCLAPI *tclFreeObj" \
|
||||
| $(TCLSH_CMD) $(TOP)\tool\replace.tcl regsub "\(\*" "(SQLITE_TCLAPI *" >> $(SQLITETCLDECLSH)
|
||||
|
||||
sqlite_tcl.h:
|
||||
type "$(TCLINCDIR)\tcl.h" | $(TCLSH_CMD) $(TOP)\tool\replace.tcl exact tclDecls.h sqlite_tclDecls.h \
|
||||
| $(TCLSH_CMD) $(TOP)\tool\replace.tcl regsub "typedef (.*?)\(Tcl_" "typedef \1 (SQLITE_TCLAPI Tcl_" \
|
||||
| $(TCLSH_CMD) $(TOP)\tool\replace.tcl exact "void (*freeProc)" "void (SQLITE_TCLAPI *freeProc)" \
|
||||
| $(TCLSH_CMD) $(TOP)\tool\replace.tcl exact "Tcl_HashEntry *(*findProc)" "Tcl_HashEntry *(SQLITE_TCLAPI *findProc)" \
|
||||
| $(TCLSH_CMD) $(TOP)\tool\replace.tcl exact "Tcl_HashEntry *(*createProc)" "Tcl_HashEntry *(SQLITE_TCLAPI *createProc)" >> $(SQLITETCLH)
|
||||
!ENDIF
|
||||
|
||||
testfixture.exe: $(TESTFIXTURE_SRC) $(SQLITE3H) $(LIBRESOBJS) $(HDR) $(SQLITE_TCL_DEP)
|
||||
$(LTLINK) -DSQLITE_NO_SYNC=1 $(TESTFIXTURE_FLAGS) \
|
||||
-DBUILD_sqlite -I$(TCLINCDIR) \
|
||||
$(TESTFIXTURE_SRC) \
|
||||
@@ -2018,7 +2144,7 @@ smoketest: $(TESTPROGS)
|
||||
@set PATH=$(LIBTCLPATH);$(PATH)
|
||||
.\testfixture.exe $(TOP)\test\main.test $(TESTOPTS)
|
||||
|
||||
sqlite3_analyzer.c: $(SQLITE3C) $(SQLITE3H) $(TOP)\src\tclsqlite.c $(TOP)\tool\spaceanal.tcl
|
||||
sqlite3_analyzer.c: $(SQLITE3C) $(SQLITE3H) $(TOP)\src\tclsqlite.c $(TOP)\tool\spaceanal.tcl $(SQLITE_TCL_DEP)
|
||||
echo #define TCLSH 2 > $@
|
||||
echo #define SQLITE_ENABLE_DBSTAT_VTAB 1 >> $@
|
||||
copy $@ + $(SQLITE3C) + $(TOP)\src\tclsqlite.c $@
|
||||
@@ -2098,7 +2224,7 @@ clean:
|
||||
-rmdir /Q/S .libs 2>NUL
|
||||
-rmdir /Q/S tsrc 2>NUL
|
||||
del /Q .target_source 2>NUL
|
||||
del /Q tclsqlite3.exe 2>NUL
|
||||
del /Q tclsqlite3.exe $(SQLITETCLH) $(SQLITETCLDECLSH) 2>NUL
|
||||
del /Q testloadext.dll 2>NUL
|
||||
del /Q testfixture.exe test.db 2>NUL
|
||||
del /Q LogEst.exe fts3view.exe rollback-test.exe showdb.exe 2>NUL
|
||||
|
||||
+25
-4
@@ -24,6 +24,20 @@ TOP = .
|
||||
USE_FULLWARN = 0
|
||||
!ENDIF
|
||||
|
||||
# Set this non-0 to enable full runtime error checks (-RTC1, etc). This
|
||||
# has no effect if (any) optimizations are enabled.
|
||||
#
|
||||
!IFNDEF USE_RUNTIME_CHECKS
|
||||
USE_RUNTIME_CHECKS = 0
|
||||
!ENDIF
|
||||
|
||||
# Set this non-0 to create a SQLite amalgamation file that excludes the
|
||||
# various built-in extensions.
|
||||
#
|
||||
!IFNDEF MINIMAL_AMALGAMATION
|
||||
MINIMAL_AMALGAMATION = 0
|
||||
!ENDIF
|
||||
|
||||
# Set this non-0 to use "stdcall" calling convention for the core library
|
||||
# and shell executable.
|
||||
#
|
||||
@@ -248,12 +262,15 @@ SQLITE3EXEPDB = /pdb:sqlite3sh.pdb
|
||||
!ENDIF
|
||||
!ENDIF
|
||||
|
||||
|
||||
# These are the "standard" SQLite compilation options used when compiling for
|
||||
# the Windows platform.
|
||||
#
|
||||
!IFNDEF OPT_FEATURE_FLAGS
|
||||
!IF $(MINIMAL_AMALGAMATION)==0
|
||||
OPT_FEATURE_FLAGS = $(OPT_FEATURE_FLAGS) -DSQLITE_ENABLE_FTS3=1
|
||||
OPT_FEATURE_FLAGS = $(OPT_FEATURE_FLAGS) -DSQLITE_ENABLE_RTREE=1
|
||||
!ENDIF
|
||||
OPT_FEATURE_FLAGS = $(OPT_FEATURE_FLAGS) -DSQLITE_ENABLE_COLUMN_METADATA=1
|
||||
!ENDIF
|
||||
|
||||
@@ -449,12 +466,12 @@ RCC = $(RC) -DSQLITE_OS_WIN=1 -I. -I$(TOP) $(RCOPTS) $(RCCOPTS)
|
||||
#
|
||||
!IF $(USE_STDCALL)!=0 || $(FOR_WIN10)!=0
|
||||
!IF "$(PLATFORM)"=="x86"
|
||||
CORE_CCONV_OPTS = -Gz -DSQLITE_CDECL=__cdecl -DSQLITE_STDCALL=__stdcall
|
||||
SHELL_CCONV_OPTS = -Gz -DSQLITE_CDECL=__cdecl -DSQLITE_STDCALL=__stdcall
|
||||
CORE_CCONV_OPTS = -Gz -DSQLITE_CDECL=__cdecl -DSQLITE_APICALL=__stdcall -DSQLITE_CALLBACK=__stdcall -DSQLITE_SYSAPI=__stdcall
|
||||
SHELL_CCONV_OPTS = -Gz -DSQLITE_CDECL=__cdecl -DSQLITE_APICALL=__stdcall -DSQLITE_CALLBACK=__stdcall -DSQLITE_SYSAPI=__stdcall
|
||||
!ELSE
|
||||
!IFNDEF PLATFORM
|
||||
CORE_CCONV_OPTS = -Gz -DSQLITE_CDECL=__cdecl -DSQLITE_STDCALL=__stdcall
|
||||
SHELL_CCONV_OPTS = -Gz -DSQLITE_CDECL=__cdecl -DSQLITE_STDCALL=__stdcall
|
||||
CORE_CCONV_OPTS = -Gz -DSQLITE_CDECL=__cdecl -DSQLITE_APICALL=__stdcall -DSQLITE_CALLBACK=__stdcall -DSQLITE_SYSAPI=__stdcall
|
||||
SHELL_CCONV_OPTS = -Gz -DSQLITE_CDECL=__cdecl -DSQLITE_APICALL=__stdcall -DSQLITE_CALLBACK=__stdcall -DSQLITE_SYSAPI=__stdcall
|
||||
!ELSE
|
||||
CORE_CCONV_OPTS =
|
||||
SHELL_CCONV_OPTS =
|
||||
@@ -740,6 +757,10 @@ RCC = $(RCC) -D_DEBUG
|
||||
!IF $(DEBUG)>1 || $(OPTIMIZATIONS)==0
|
||||
TCC = $(TCC) -Od
|
||||
BCC = $(BCC) -Od
|
||||
!IF $(USE_RUNTIME_CHECKS)!=0
|
||||
TCC = $(TCC) -RTC1
|
||||
BCC = $(BCC) -RTC1
|
||||
!ENDIF
|
||||
!ELSEIF $(OPTIMIZATIONS)>=3
|
||||
TCC = $(TCC) -Ox
|
||||
BCC = $(BCC) -Ox
|
||||
|
||||
@@ -78,7 +78,6 @@ TEA_ADD_LIBS([])
|
||||
TEA_ADD_CFLAGS([-DSQLITE_ENABLE_FTS3=1])
|
||||
TEA_ADD_CFLAGS([-DSQLITE_3_SUFFIX_ONLY=1])
|
||||
TEA_ADD_CFLAGS([-DSQLITE_ENABLE_RTREE=1])
|
||||
TEA_ADD_CFLAGS([-DSQLITE_OMIT_DEPRECATED=1])
|
||||
TEA_ADD_STUB_SOURCES([])
|
||||
TEA_ADD_TCL_SOURCES([])
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#! /bin/sh
|
||||
# Guess values for system-dependent variables and create Makefiles.
|
||||
# Generated by GNU Autoconf 2.69 for sqlite 3.14.0.
|
||||
# Generated by GNU Autoconf 2.69 for sqlite 3.16.0.
|
||||
#
|
||||
#
|
||||
# Copyright (C) 1992-1996, 1998-2012 Free Software Foundation, Inc.
|
||||
@@ -726,8 +726,8 @@ MAKEFLAGS=
|
||||
# Identity of this package.
|
||||
PACKAGE_NAME='sqlite'
|
||||
PACKAGE_TARNAME='sqlite'
|
||||
PACKAGE_VERSION='3.14.0'
|
||||
PACKAGE_STRING='sqlite 3.14.0'
|
||||
PACKAGE_VERSION='3.16.0'
|
||||
PACKAGE_STRING='sqlite 3.16.0'
|
||||
PACKAGE_BUGREPORT=''
|
||||
PACKAGE_URL=''
|
||||
|
||||
@@ -903,6 +903,8 @@ with_readline_inc
|
||||
enable_debug
|
||||
enable_amalgamation
|
||||
enable_load_extension
|
||||
enable_memsys5
|
||||
enable_memsys3
|
||||
enable_fts3
|
||||
enable_fts4
|
||||
enable_fts5
|
||||
@@ -1461,7 +1463,7 @@ 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.14.0 to adapt to many kinds of systems.
|
||||
\`configure' configures sqlite 3.16.0 to adapt to many kinds of systems.
|
||||
|
||||
Usage: $0 [OPTION]... [VAR=VALUE]...
|
||||
|
||||
@@ -1526,7 +1528,7 @@ fi
|
||||
|
||||
if test -n "$ac_init_help"; then
|
||||
case $ac_init_help in
|
||||
short | recursive ) echo "Configuration of sqlite 3.14.0:";;
|
||||
short | recursive ) echo "Configuration of sqlite 3.16.0:";;
|
||||
esac
|
||||
cat <<\_ACEOF
|
||||
|
||||
@@ -1552,6 +1554,8 @@ Optional Features:
|
||||
separately
|
||||
--disable-load-extension
|
||||
Disable loading of external extensions
|
||||
--enable-memsys5 Enable MEMSYS5
|
||||
--enable-memsys3 Enable MEMSYS3
|
||||
--enable-fts3 Enable the FTS3 extension
|
||||
--enable-fts4 Enable the FTS4 extension
|
||||
--enable-fts5 Enable the FTS5 extension
|
||||
@@ -1648,7 +1652,7 @@ fi
|
||||
test -n "$ac_init_help" && exit $ac_status
|
||||
if $ac_init_version; then
|
||||
cat <<\_ACEOF
|
||||
sqlite configure 3.14.0
|
||||
sqlite configure 3.16.0
|
||||
generated by GNU Autoconf 2.69
|
||||
|
||||
Copyright (C) 2012 Free Software Foundation, Inc.
|
||||
@@ -2067,7 +2071,7 @@ 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.14.0, which was
|
||||
It was created by sqlite $as_me 3.16.0, which was
|
||||
generated by GNU Autoconf 2.69. Invocation command line was
|
||||
|
||||
$ $0 $@
|
||||
@@ -3925,13 +3929,13 @@ if ${lt_cv_nm_interface+:} false; then :
|
||||
else
|
||||
lt_cv_nm_interface="BSD nm"
|
||||
echo "int some_variable = 0;" > conftest.$ac_ext
|
||||
(eval echo "\"\$as_me:3928: $ac_compile\"" >&5)
|
||||
(eval echo "\"\$as_me:3932: $ac_compile\"" >&5)
|
||||
(eval "$ac_compile" 2>conftest.err)
|
||||
cat conftest.err >&5
|
||||
(eval echo "\"\$as_me:3931: $NM \\\"conftest.$ac_objext\\\"\"" >&5)
|
||||
(eval echo "\"\$as_me:3935: $NM \\\"conftest.$ac_objext\\\"\"" >&5)
|
||||
(eval "$NM \"conftest.$ac_objext\"" 2>conftest.err > conftest.out)
|
||||
cat conftest.err >&5
|
||||
(eval echo "\"\$as_me:3934: output\"" >&5)
|
||||
(eval echo "\"\$as_me:3938: output\"" >&5)
|
||||
cat conftest.out >&5
|
||||
if $GREP 'External.*some_variable' conftest.out > /dev/null; then
|
||||
lt_cv_nm_interface="MS dumpbin"
|
||||
@@ -5137,7 +5141,7 @@ ia64-*-hpux*)
|
||||
;;
|
||||
*-*-irix6*)
|
||||
# Find out which ABI we are using.
|
||||
echo '#line 5140 "configure"' > conftest.$ac_ext
|
||||
echo '#line 5144 "configure"' > conftest.$ac_ext
|
||||
if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5
|
||||
(eval $ac_compile) 2>&5
|
||||
ac_status=$?
|
||||
@@ -6662,11 +6666,11 @@ else
|
||||
-e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \
|
||||
-e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \
|
||||
-e 's:$: $lt_compiler_flag:'`
|
||||
(eval echo "\"\$as_me:6665: $lt_compile\"" >&5)
|
||||
(eval echo "\"\$as_me:6669: $lt_compile\"" >&5)
|
||||
(eval "$lt_compile" 2>conftest.err)
|
||||
ac_status=$?
|
||||
cat conftest.err >&5
|
||||
echo "$as_me:6669: \$? = $ac_status" >&5
|
||||
echo "$as_me:6673: \$? = $ac_status" >&5
|
||||
if (exit $ac_status) && test -s "$ac_outfile"; then
|
||||
# The compiler can only warn and ignore the option if not recognized
|
||||
# So say no if there are warnings other than the usual output.
|
||||
@@ -7001,11 +7005,11 @@ else
|
||||
-e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \
|
||||
-e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \
|
||||
-e 's:$: $lt_compiler_flag:'`
|
||||
(eval echo "\"\$as_me:7004: $lt_compile\"" >&5)
|
||||
(eval echo "\"\$as_me:7008: $lt_compile\"" >&5)
|
||||
(eval "$lt_compile" 2>conftest.err)
|
||||
ac_status=$?
|
||||
cat conftest.err >&5
|
||||
echo "$as_me:7008: \$? = $ac_status" >&5
|
||||
echo "$as_me:7012: \$? = $ac_status" >&5
|
||||
if (exit $ac_status) && test -s "$ac_outfile"; then
|
||||
# The compiler can only warn and ignore the option if not recognized
|
||||
# So say no if there are warnings other than the usual output.
|
||||
@@ -7106,11 +7110,11 @@ else
|
||||
-e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \
|
||||
-e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \
|
||||
-e 's:$: $lt_compiler_flag:'`
|
||||
(eval echo "\"\$as_me:7109: $lt_compile\"" >&5)
|
||||
(eval echo "\"\$as_me:7113: $lt_compile\"" >&5)
|
||||
(eval "$lt_compile" 2>out/conftest.err)
|
||||
ac_status=$?
|
||||
cat out/conftest.err >&5
|
||||
echo "$as_me:7113: \$? = $ac_status" >&5
|
||||
echo "$as_me:7117: \$? = $ac_status" >&5
|
||||
if (exit $ac_status) && test -s out/conftest2.$ac_objext
|
||||
then
|
||||
# The compiler can only warn and ignore the option if not recognized
|
||||
@@ -7161,11 +7165,11 @@ else
|
||||
-e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \
|
||||
-e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \
|
||||
-e 's:$: $lt_compiler_flag:'`
|
||||
(eval echo "\"\$as_me:7164: $lt_compile\"" >&5)
|
||||
(eval echo "\"\$as_me:7168: $lt_compile\"" >&5)
|
||||
(eval "$lt_compile" 2>out/conftest.err)
|
||||
ac_status=$?
|
||||
cat out/conftest.err >&5
|
||||
echo "$as_me:7168: \$? = $ac_status" >&5
|
||||
echo "$as_me:7172: \$? = $ac_status" >&5
|
||||
if (exit $ac_status) && test -s out/conftest2.$ac_objext
|
||||
then
|
||||
# The compiler can only warn and ignore the option if not recognized
|
||||
@@ -9541,7 +9545,7 @@ else
|
||||
lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2
|
||||
lt_status=$lt_dlunknown
|
||||
cat > conftest.$ac_ext <<_LT_EOF
|
||||
#line 9544 "configure"
|
||||
#line 9548 "configure"
|
||||
#include "confdefs.h"
|
||||
|
||||
#if HAVE_DLFCN_H
|
||||
@@ -9637,7 +9641,7 @@ else
|
||||
lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2
|
||||
lt_status=$lt_dlunknown
|
||||
cat > conftest.$ac_ext <<_LT_EOF
|
||||
#line 9640 "configure"
|
||||
#line 9644 "configure"
|
||||
#include "confdefs.h"
|
||||
|
||||
#if HAVE_DLFCN_H
|
||||
@@ -11248,7 +11252,7 @@ else
|
||||
fi
|
||||
|
||||
if test "${use_debug}" = "yes" ; then
|
||||
TARGET_DEBUG="-DSQLITE_DEBUG=1"
|
||||
TARGET_DEBUG="-DSQLITE_DEBUG=1 -DSQLITE_ENABLE_SELECTTRACE -DSQLITE_ENABLE_WHERETRACE"
|
||||
else
|
||||
TARGET_DEBUG="-DNDEBUG"
|
||||
fi
|
||||
@@ -11339,6 +11343,44 @@ else
|
||||
OPT_FEATURE_FLAGS="-DSQLITE_OMIT_LOAD_EXTENSION=1"
|
||||
fi
|
||||
|
||||
##########
|
||||
# Do we want to support memsys3 and/or memsys5
|
||||
#
|
||||
# Check whether --enable-memsys5 was given.
|
||||
if test "${enable_memsys5+set}" = set; then :
|
||||
enableval=$enable_memsys5; enable_memsys5=yes
|
||||
else
|
||||
enable_memsys5=no
|
||||
fi
|
||||
|
||||
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to support MEMSYS5" >&5
|
||||
$as_echo_n "checking whether to support MEMSYS5... " >&6; }
|
||||
if test "${enable_memsys5}" = "yes"; then
|
||||
OPT_FEATURE_FLAGS+=" -DSQLITE_ENABLE_MEMSYS5"
|
||||
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
|
||||
$as_echo "yes" >&6; }
|
||||
else
|
||||
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
|
||||
$as_echo "no" >&6; }
|
||||
fi
|
||||
# Check whether --enable-memsys3 was given.
|
||||
if test "${enable_memsys3+set}" = set; then :
|
||||
enableval=$enable_memsys3; enable_memsys3=yes
|
||||
else
|
||||
enable_memsys3=no
|
||||
fi
|
||||
|
||||
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to support MEMSYS3" >&5
|
||||
$as_echo_n "checking whether to support MEMSYS3... " >&6; }
|
||||
if test "${enable_memsys3}" = "yes" -a "${enable_memsys5}" = "no"; then
|
||||
OPT_FEATURE_FLAGS+=" -DSQLITE_ENABLE_MEMSYS3"
|
||||
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
|
||||
$as_echo "yes" >&6; }
|
||||
else
|
||||
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
|
||||
$as_echo "no" >&6; }
|
||||
fi
|
||||
|
||||
#########
|
||||
# See whether we should enable Full Text Search extensions
|
||||
# Check whether --enable-fts3 was given.
|
||||
@@ -12109,7 +12151,7 @@ cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
|
||||
# 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.14.0, which was
|
||||
This file was extended by sqlite $as_me 3.16.0, which was
|
||||
generated by GNU Autoconf 2.69. Invocation command line was
|
||||
|
||||
CONFIG_FILES = $CONFIG_FILES
|
||||
@@ -12175,7 +12217,7 @@ _ACEOF
|
||||
cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
|
||||
ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`"
|
||||
ac_cs_version="\\
|
||||
sqlite config.status 3.14.0
|
||||
sqlite config.status 3.16.0
|
||||
configured by $0, generated by GNU Autoconf 2.69,
|
||||
with options \\"\$ac_cs_config\\"
|
||||
|
||||
@@ -13787,4 +13829,3 @@ if test -n "$ac_unrecognized_opts" && test "$enable_option_checking" != no; then
|
||||
{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: unrecognized options: $ac_unrecognized_opts" >&5
|
||||
$as_echo "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2;}
|
||||
fi
|
||||
|
||||
|
||||
+25
-1
@@ -560,7 +560,7 @@ AC_SEARCH_LIBS(fdatasync, [rt])
|
||||
AC_ARG_ENABLE(debug, AC_HELP_STRING([--enable-debug],[enable debugging & verbose explain]),
|
||||
[use_debug=$enableval],[use_debug=no])
|
||||
if test "${use_debug}" = "yes" ; then
|
||||
TARGET_DEBUG="-DSQLITE_DEBUG=1"
|
||||
TARGET_DEBUG="-DSQLITE_DEBUG=1 -DSQLITE_ENABLE_SELECTTRACE -DSQLITE_ENABLE_WHERETRACE"
|
||||
else
|
||||
TARGET_DEBUG="-DNDEBUG"
|
||||
fi
|
||||
@@ -588,6 +588,30 @@ else
|
||||
OPT_FEATURE_FLAGS="-DSQLITE_OMIT_LOAD_EXTENSION=1"
|
||||
fi
|
||||
|
||||
##########
|
||||
# Do we want to support memsys3 and/or memsys5
|
||||
#
|
||||
AC_ARG_ENABLE(memsys5,
|
||||
AC_HELP_STRING([--enable-memsys5],[Enable MEMSYS5]),
|
||||
[enable_memsys5=yes],[enable_memsys5=no])
|
||||
AC_MSG_CHECKING([whether to support MEMSYS5])
|
||||
if test "${enable_memsys5}" = "yes"; then
|
||||
OPT_FEATURE_FLAGS+=" -DSQLITE_ENABLE_MEMSYS5"
|
||||
AC_MSG_RESULT([yes])
|
||||
else
|
||||
AC_MSG_RESULT([no])
|
||||
fi
|
||||
AC_ARG_ENABLE(memsys3,
|
||||
AC_HELP_STRING([--enable-memsys3],[Enable MEMSYS3]),
|
||||
[enable_memsys3=yes],[enable_memsys3=no])
|
||||
AC_MSG_CHECKING([whether to support MEMSYS3])
|
||||
if test "${enable_memsys3}" = "yes" -a "${enable_memsys5}" = "no"; then
|
||||
OPT_FEATURE_FLAGS+=" -DSQLITE_ENABLE_MEMSYS3"
|
||||
AC_MSG_RESULT([yes])
|
||||
else
|
||||
AC_MSG_RESULT([no])
|
||||
fi
|
||||
|
||||
#########
|
||||
# See whether we should enable Full Text Search extensions
|
||||
AC_ARG_ENABLE(fts3, AC_HELP_STRING([--enable-fts3],
|
||||
|
||||
+2
-2
@@ -85,7 +85,7 @@ Define C preprocessor macro <i>name</i>. This macro is useable by
|
||||
Do not generate a parser. Instead write the input grammar to standard
|
||||
output with all comments, actions, and other extraneous text removed.
|
||||
<li><b>-l</b>
|
||||
Omit "#line" directives int the generated parser C code.
|
||||
Omit "#line" directives in the generated parser C code.
|
||||
<li><b>-m</b>
|
||||
Cause the output C source code to be compatible with the "makeheaders"
|
||||
program.
|
||||
@@ -929,7 +929,7 @@ token structure. Like this:</p>
|
||||
</pre></p>
|
||||
|
||||
<p>If the data type of terminals is not specified, the default value
|
||||
is "int".</p>
|
||||
is "void*".</p>
|
||||
|
||||
<p>Non-terminal symbols can each have their own data types. Typically
|
||||
the data type of a non-terminal is a pointer to the root of a parse-tree
|
||||
|
||||
@@ -99,7 +99,11 @@ static void scalarFunc(
|
||||
|
||||
#ifdef SQLITE_TEST
|
||||
|
||||
#include <tcl.h>
|
||||
#if defined(INCLUDE_SQLITE_TCL_H)
|
||||
# include "sqlite_tcl.h"
|
||||
#else
|
||||
# include "tcl.h"
|
||||
#endif
|
||||
#include <string.h>
|
||||
|
||||
/*
|
||||
|
||||
+12
-5
@@ -18,7 +18,14 @@
|
||||
** that the sqlite3_tokenizer_module.xLanguage() method is invoked correctly.
|
||||
*/
|
||||
|
||||
#include <tcl.h>
|
||||
#if defined(INCLUDE_SQLITE_TCL_H)
|
||||
# include "sqlite_tcl.h"
|
||||
#else
|
||||
# include "tcl.h"
|
||||
# ifndef SQLITE_TCLAPI
|
||||
# define SQLITE_TCLAPI
|
||||
# endif
|
||||
#endif
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
|
||||
@@ -143,7 +150,7 @@ static int nm_match_count(
|
||||
/*
|
||||
** Tclcmd: fts3_near_match DOCUMENT EXPR ?OPTIONS?
|
||||
*/
|
||||
static int fts3_near_match_cmd(
|
||||
static int SQLITE_TCLAPI fts3_near_match_cmd(
|
||||
ClientData clientData,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
@@ -278,7 +285,7 @@ static int fts3_near_match_cmd(
|
||||
** # Restore initial incr-load settings:
|
||||
** eval fts3_configure_incr_load $cfg
|
||||
*/
|
||||
static int fts3_configure_incr_load_cmd(
|
||||
static int SQLITE_TCLAPI fts3_configure_incr_load_cmd(
|
||||
ClientData clientData,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
@@ -488,7 +495,7 @@ static int testTokenizerLanguage(
|
||||
}
|
||||
#endif
|
||||
|
||||
static int fts3_test_tokenizer_cmd(
|
||||
static int SQLITE_TCLAPI fts3_test_tokenizer_cmd(
|
||||
ClientData clientData,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
@@ -517,7 +524,7 @@ static int fts3_test_tokenizer_cmd(
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
static int fts3_test_varint_cmd(
|
||||
static int SQLITE_TCLAPI fts3_test_varint_cmd(
|
||||
ClientData clientData,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
|
||||
@@ -224,7 +224,11 @@ int sqlite3Fts3InitTokenizer(
|
||||
|
||||
#ifdef SQLITE_TEST
|
||||
|
||||
#include <tcl.h>
|
||||
#if defined(INCLUDE_SQLITE_TCL_H)
|
||||
# include "sqlite_tcl.h"
|
||||
#else
|
||||
# include "tcl.h"
|
||||
#endif
|
||||
#include <string.h>
|
||||
|
||||
/*
|
||||
|
||||
+1
-2
@@ -318,7 +318,7 @@ struct Fts5ExtensionApi {
|
||||
** behaviour. The structure methods are expected to function as follows:
|
||||
**
|
||||
** xCreate:
|
||||
** This function is used to allocate and inititalize a tokenizer instance.
|
||||
** This function is used to allocate and initialize a tokenizer instance.
|
||||
** A tokenizer instance is required to actually tokenize text.
|
||||
**
|
||||
** The first argument passed to this function is a copy of the (void*)
|
||||
@@ -577,4 +577,3 @@ struct fts5_api {
|
||||
#endif
|
||||
|
||||
#endif /* _FTS5_H */
|
||||
|
||||
|
||||
@@ -737,6 +737,7 @@ void sqlite3Fts5ParseNodeFree(Fts5ExprNode*);
|
||||
|
||||
void sqlite3Fts5ParseSetDistance(Fts5Parse*, Fts5ExprNearset*, Fts5Token*);
|
||||
void sqlite3Fts5ParseSetColset(Fts5Parse*, Fts5ExprNearset*, Fts5Colset*);
|
||||
Fts5Colset *sqlite3Fts5ParseColsetInvert(Fts5Parse*, Fts5Colset*);
|
||||
void sqlite3Fts5ParseFinished(Fts5Parse *pParse, Fts5ExprNode *p);
|
||||
void sqlite3Fts5ParseNear(Fts5Parse *pParse, Fts5Token*);
|
||||
|
||||
|
||||
+187
-45
@@ -189,7 +189,7 @@ static int fts5HighlightCb(
|
||||
if( p->iRangeEnd>0 && iPos==p->iRangeEnd ){
|
||||
fts5HighlightAppend(&rc, p, &p->zIn[p->iOff], iEndOff - p->iOff);
|
||||
p->iOff = iEndOff;
|
||||
if( iPos<p->iter.iEnd ){
|
||||
if( iPos>=p->iter.iStart && iPos<p->iter.iEnd ){
|
||||
fts5HighlightAppend(&rc, p, p->zClose, -1);
|
||||
}
|
||||
}
|
||||
@@ -246,6 +246,118 @@ static void fts5HighlightFunction(
|
||||
** End of highlight() implementation.
|
||||
**************************************************************************/
|
||||
|
||||
/*
|
||||
** Context object passed to the fts5SentenceFinderCb() function.
|
||||
*/
|
||||
typedef struct Fts5SFinder Fts5SFinder;
|
||||
struct Fts5SFinder {
|
||||
int iPos; /* Current token position */
|
||||
int nFirstAlloc; /* Allocated size of aFirst[] */
|
||||
int nFirst; /* Number of entries in aFirst[] */
|
||||
int *aFirst; /* Array of first token in each sentence */
|
||||
const char *zDoc; /* Document being tokenized */
|
||||
};
|
||||
|
||||
/*
|
||||
** Add an entry to the Fts5SFinder.aFirst[] array. Grow the array if
|
||||
** necessary. Return SQLITE_OK if successful, or SQLITE_NOMEM if an
|
||||
** error occurs.
|
||||
*/
|
||||
static int fts5SentenceFinderAdd(Fts5SFinder *p, int iAdd){
|
||||
if( p->nFirstAlloc==p->nFirst ){
|
||||
int nNew = p->nFirstAlloc ? p->nFirstAlloc*2 : 64;
|
||||
int *aNew;
|
||||
|
||||
aNew = (int*)sqlite3_realloc(p->aFirst, nNew*sizeof(int));
|
||||
if( aNew==0 ) return SQLITE_NOMEM;
|
||||
p->aFirst = aNew;
|
||||
p->nFirstAlloc = nNew;
|
||||
}
|
||||
p->aFirst[p->nFirst++] = iAdd;
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** This function is an xTokenize() callback used by the auxiliary snippet()
|
||||
** function. Its job is to identify tokens that are the first in a sentence.
|
||||
** For each such token, an entry is added to the SFinder.aFirst[] array.
|
||||
*/
|
||||
static int fts5SentenceFinderCb(
|
||||
void *pContext, /* Pointer to HighlightContext object */
|
||||
int tflags, /* Mask of FTS5_TOKEN_* flags */
|
||||
const char *pToken, /* Buffer containing token */
|
||||
int nToken, /* Size of token in bytes */
|
||||
int iStartOff, /* Start offset of token */
|
||||
int iEndOff /* End offset of token */
|
||||
){
|
||||
int rc = SQLITE_OK;
|
||||
|
||||
UNUSED_PARAM2(pToken, nToken);
|
||||
UNUSED_PARAM(iEndOff);
|
||||
|
||||
if( (tflags & FTS5_TOKEN_COLOCATED)==0 ){
|
||||
Fts5SFinder *p = (Fts5SFinder*)pContext;
|
||||
if( p->iPos>0 ){
|
||||
int i;
|
||||
char c = 0;
|
||||
for(i=iStartOff-1; i>=0; i--){
|
||||
c = p->zDoc[i];
|
||||
if( c!=' ' && c!='\t' && c!='\n' && c!='\r' ) break;
|
||||
}
|
||||
if( i!=iStartOff-1 && (c=='.' || c==':') ){
|
||||
rc = fts5SentenceFinderAdd(p, p->iPos);
|
||||
}
|
||||
}else{
|
||||
rc = fts5SentenceFinderAdd(p, 0);
|
||||
}
|
||||
p->iPos++;
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int fts5SnippetScore(
|
||||
const Fts5ExtensionApi *pApi, /* API offered by current FTS version */
|
||||
Fts5Context *pFts, /* First arg to pass to pApi functions */
|
||||
int nDocsize, /* Size of column in tokens */
|
||||
unsigned char *aSeen, /* Array with one element per query phrase */
|
||||
int iCol, /* Column to score */
|
||||
int iPos, /* Starting offset to score */
|
||||
int nToken, /* Max tokens per snippet */
|
||||
int *pnScore, /* OUT: Score */
|
||||
int *piPos /* OUT: Adjusted offset */
|
||||
){
|
||||
int rc;
|
||||
int i;
|
||||
int ip = 0;
|
||||
int ic = 0;
|
||||
int iOff = 0;
|
||||
int iFirst = -1;
|
||||
int nInst;
|
||||
int nScore = 0;
|
||||
int iLast = 0;
|
||||
|
||||
rc = pApi->xInstCount(pFts, &nInst);
|
||||
for(i=0; i<nInst && rc==SQLITE_OK; i++){
|
||||
rc = pApi->xInst(pFts, i, &ip, &ic, &iOff);
|
||||
if( rc==SQLITE_OK && ic==iCol && iOff>=iPos && iOff<(iPos+nToken) ){
|
||||
nScore += (aSeen[ip] ? 1 : 1000);
|
||||
aSeen[ip] = 1;
|
||||
if( iFirst<0 ) iFirst = iOff;
|
||||
iLast = iOff + pApi->xPhraseSize(pFts, ip);
|
||||
}
|
||||
}
|
||||
|
||||
*pnScore = nScore;
|
||||
if( piPos ){
|
||||
int iAdj = iFirst - (nToken - (iLast-iFirst)) / 2;
|
||||
if( (iAdj+nToken)>nDocsize ) iAdj = nDocsize - nToken;
|
||||
if( iAdj<0 ) iAdj = 0;
|
||||
*piPos = iAdj;
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** Implementation of snippet() function.
|
||||
*/
|
||||
@@ -267,9 +379,10 @@ static void fts5SnippetFunction(
|
||||
unsigned char *aSeen; /* Array of "seen instance" flags */
|
||||
int iBestCol; /* Column containing best snippet */
|
||||
int iBestStart = 0; /* First token of best snippet */
|
||||
int iBestLast; /* Last token of best snippet */
|
||||
int nBestScore = 0; /* Score of best snippet */
|
||||
int nColSize = 0; /* Total size of iBestCol in tokens */
|
||||
Fts5SFinder sFinder; /* Used to find the beginnings of sentences */
|
||||
int nCol;
|
||||
|
||||
if( nVal!=5 ){
|
||||
const char *zErr = "wrong number of arguments to function snippet()";
|
||||
@@ -277,13 +390,13 @@ static void fts5SnippetFunction(
|
||||
return;
|
||||
}
|
||||
|
||||
nCol = pApi->xColumnCount(pFts);
|
||||
memset(&ctx, 0, sizeof(HighlightContext));
|
||||
iCol = sqlite3_value_int(apVal[0]);
|
||||
ctx.zOpen = (const char*)sqlite3_value_text(apVal[1]);
|
||||
ctx.zClose = (const char*)sqlite3_value_text(apVal[2]);
|
||||
zEllips = (const char*)sqlite3_value_text(apVal[3]);
|
||||
nToken = sqlite3_value_int(apVal[4]);
|
||||
iBestLast = nToken-1;
|
||||
|
||||
iBestCol = (iCol>=0 ? iCol : 0);
|
||||
nPhrase = pApi->xPhraseCount(pFts);
|
||||
@@ -291,65 +404,94 @@ static void fts5SnippetFunction(
|
||||
if( aSeen==0 ){
|
||||
rc = SQLITE_NOMEM;
|
||||
}
|
||||
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = pApi->xInstCount(pFts, &nInst);
|
||||
}
|
||||
for(i=0; rc==SQLITE_OK && i<nInst; i++){
|
||||
int ip, iSnippetCol, iStart;
|
||||
memset(aSeen, 0, nPhrase);
|
||||
rc = pApi->xInst(pFts, i, &ip, &iSnippetCol, &iStart);
|
||||
if( rc==SQLITE_OK && (iCol<0 || iSnippetCol==iCol) ){
|
||||
int nScore = 1000;
|
||||
int iLast = iStart - 1 + pApi->xPhraseSize(pFts, ip);
|
||||
int j;
|
||||
aSeen[ip] = 1;
|
||||
|
||||
for(j=i+1; rc==SQLITE_OK && j<nInst; j++){
|
||||
int ic; int io; int iFinal;
|
||||
rc = pApi->xInst(pFts, j, &ip, &ic, &io);
|
||||
iFinal = io + pApi->xPhraseSize(pFts, ip) - 1;
|
||||
if( rc==SQLITE_OK && ic==iSnippetCol && iLast<iStart+nToken ){
|
||||
nScore += aSeen[ip] ? 1000 : 1;
|
||||
aSeen[ip] = 1;
|
||||
if( iFinal>iLast ) iLast = iFinal;
|
||||
memset(&sFinder, 0, sizeof(Fts5SFinder));
|
||||
for(i=0; i<nCol; i++){
|
||||
if( iCol<0 || iCol==i ){
|
||||
int nDoc;
|
||||
int nDocsize;
|
||||
int ii;
|
||||
sFinder.iPos = 0;
|
||||
sFinder.nFirst = 0;
|
||||
rc = pApi->xColumnText(pFts, i, &sFinder.zDoc, &nDoc);
|
||||
if( rc!=SQLITE_OK ) break;
|
||||
rc = pApi->xTokenize(pFts,
|
||||
sFinder.zDoc, nDoc, (void*)&sFinder,fts5SentenceFinderCb
|
||||
);
|
||||
if( rc!=SQLITE_OK ) break;
|
||||
rc = pApi->xColumnSize(pFts, i, &nDocsize);
|
||||
if( rc!=SQLITE_OK ) break;
|
||||
|
||||
for(ii=0; rc==SQLITE_OK && ii<nInst; ii++){
|
||||
int ip, ic, io;
|
||||
int iAdj;
|
||||
int nScore;
|
||||
int jj;
|
||||
|
||||
rc = pApi->xInst(pFts, ii, &ip, &ic, &io);
|
||||
if( ic!=i || rc!=SQLITE_OK ) continue;
|
||||
memset(aSeen, 0, nPhrase);
|
||||
rc = fts5SnippetScore(pApi, pFts, nDocsize, aSeen, i,
|
||||
io, nToken, &nScore, &iAdj
|
||||
);
|
||||
if( rc==SQLITE_OK && nScore>nBestScore ){
|
||||
nBestScore = nScore;
|
||||
iBestCol = i;
|
||||
iBestStart = iAdj;
|
||||
nColSize = nDocsize;
|
||||
}
|
||||
}
|
||||
|
||||
if( rc==SQLITE_OK && nScore>nBestScore ){
|
||||
iBestCol = iSnippetCol;
|
||||
iBestStart = iStart;
|
||||
iBestLast = iLast;
|
||||
nBestScore = nScore;
|
||||
if( rc==SQLITE_OK && sFinder.nFirst && nDocsize>nToken ){
|
||||
for(jj=0; jj<(sFinder.nFirst-1); jj++){
|
||||
if( sFinder.aFirst[jj+1]>io ) break;
|
||||
}
|
||||
|
||||
if( sFinder.aFirst[jj]<io ){
|
||||
memset(aSeen, 0, nPhrase);
|
||||
rc = fts5SnippetScore(pApi, pFts, nDocsize, aSeen, i,
|
||||
sFinder.aFirst[jj], nToken, &nScore, 0
|
||||
);
|
||||
|
||||
nScore += (sFinder.aFirst[jj]==0 ? 120 : 100);
|
||||
if( rc==SQLITE_OK && nScore>nBestScore ){
|
||||
nBestScore = nScore;
|
||||
iBestCol = i;
|
||||
iBestStart = sFinder.aFirst[jj];
|
||||
nColSize = nDocsize;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = pApi->xColumnSize(pFts, iBestCol, &nColSize);
|
||||
}
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = pApi->xColumnText(pFts, iBestCol, &ctx.zIn, &ctx.nIn);
|
||||
}
|
||||
if( rc==SQLITE_OK && nColSize==0 ){
|
||||
rc = pApi->xColumnSize(pFts, iBestCol, &nColSize);
|
||||
}
|
||||
if( ctx.zIn ){
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = fts5CInstIterInit(pApi, pFts, iBestCol, &ctx.iter);
|
||||
}
|
||||
|
||||
if( (iBestStart+nToken-1)>iBestLast ){
|
||||
iBestStart -= (iBestStart+nToken-1-iBestLast) / 2;
|
||||
}
|
||||
if( iBestStart+nToken>nColSize ){
|
||||
iBestStart = nColSize - nToken;
|
||||
}
|
||||
if( iBestStart<0 ) iBestStart = 0;
|
||||
|
||||
ctx.iRangeStart = iBestStart;
|
||||
ctx.iRangeEnd = iBestStart + nToken - 1;
|
||||
|
||||
if( iBestStart>0 ){
|
||||
fts5HighlightAppend(&rc, &ctx, zEllips, -1);
|
||||
}
|
||||
|
||||
/* Advance iterator ctx.iter so that it points to the first coalesced
|
||||
** phrase instance at or following position iBestStart. */
|
||||
while( ctx.iter.iStart>=0 && ctx.iter.iStart<iBestStart && rc==SQLITE_OK ){
|
||||
rc = fts5CInstIterNext(&ctx.iter);
|
||||
}
|
||||
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = pApi->xTokenize(pFts, ctx.zIn, ctx.nIn, (void*)&ctx,fts5HighlightCb);
|
||||
}
|
||||
@@ -358,15 +500,15 @@ static void fts5SnippetFunction(
|
||||
}else{
|
||||
fts5HighlightAppend(&rc, &ctx, zEllips, -1);
|
||||
}
|
||||
|
||||
if( rc==SQLITE_OK ){
|
||||
sqlite3_result_text(pCtx, (const char*)ctx.zOut, -1, SQLITE_TRANSIENT);
|
||||
}else{
|
||||
sqlite3_result_error_code(pCtx, rc);
|
||||
}
|
||||
sqlite3_free(ctx.zOut);
|
||||
}
|
||||
if( rc==SQLITE_OK ){
|
||||
sqlite3_result_text(pCtx, (const char*)ctx.zOut, -1, SQLITE_TRANSIENT);
|
||||
}else{
|
||||
sqlite3_result_error_code(pCtx, rc);
|
||||
}
|
||||
sqlite3_free(ctx.zOut);
|
||||
sqlite3_free(aSeen);
|
||||
sqlite3_free(sFinder.aFirst);
|
||||
}
|
||||
|
||||
/************************************************************************/
|
||||
|
||||
+88
-41
@@ -167,6 +167,7 @@ static int fts5ExprGetToken(
|
||||
case ',': tok = FTS5_COMMA; break;
|
||||
case '+': tok = FTS5_PLUS; break;
|
||||
case '*': tok = FTS5_STAR; break;
|
||||
case '-': tok = FTS5_MINUS; break;
|
||||
case '\0': tok = FTS5_EOF; break;
|
||||
|
||||
case '"': {
|
||||
@@ -745,49 +746,61 @@ static int fts5ExprNearTest(
|
||||
** Initialize all term iterators in the pNear object. If any term is found
|
||||
** to match no documents at all, return immediately without initializing any
|
||||
** further iterators.
|
||||
**
|
||||
** If an error occurs, return an SQLite error code. Otherwise, return
|
||||
** SQLITE_OK. It is not considered an error if some term matches zero
|
||||
** documents.
|
||||
*/
|
||||
static int fts5ExprNearInitAll(
|
||||
Fts5Expr *pExpr,
|
||||
Fts5ExprNode *pNode
|
||||
){
|
||||
Fts5ExprNearset *pNear = pNode->pNear;
|
||||
int i, j;
|
||||
int rc = SQLITE_OK;
|
||||
int i;
|
||||
|
||||
assert( pNode->bNomatch==0 );
|
||||
for(i=0; rc==SQLITE_OK && i<pNear->nPhrase; i++){
|
||||
for(i=0; i<pNear->nPhrase; i++){
|
||||
Fts5ExprPhrase *pPhrase = pNear->apPhrase[i];
|
||||
for(j=0; j<pPhrase->nTerm; j++){
|
||||
Fts5ExprTerm *pTerm = &pPhrase->aTerm[j];
|
||||
Fts5ExprTerm *p;
|
||||
int bEof = 1;
|
||||
if( pPhrase->nTerm==0 ){
|
||||
pNode->bEof = 1;
|
||||
return SQLITE_OK;
|
||||
}else{
|
||||
int j;
|
||||
for(j=0; j<pPhrase->nTerm; j++){
|
||||
Fts5ExprTerm *pTerm = &pPhrase->aTerm[j];
|
||||
Fts5ExprTerm *p;
|
||||
int bHit = 0;
|
||||
|
||||
for(p=pTerm; p && rc==SQLITE_OK; p=p->pSynonym){
|
||||
if( p->pIter ){
|
||||
sqlite3Fts5IterClose(p->pIter);
|
||||
p->pIter = 0;
|
||||
for(p=pTerm; p; p=p->pSynonym){
|
||||
int rc;
|
||||
if( p->pIter ){
|
||||
sqlite3Fts5IterClose(p->pIter);
|
||||
p->pIter = 0;
|
||||
}
|
||||
rc = sqlite3Fts5IndexQuery(
|
||||
pExpr->pIndex, p->zTerm, (int)strlen(p->zTerm),
|
||||
(pTerm->bPrefix ? FTS5INDEX_QUERY_PREFIX : 0) |
|
||||
(pExpr->bDesc ? FTS5INDEX_QUERY_DESC : 0),
|
||||
pNear->pColset,
|
||||
&p->pIter
|
||||
);
|
||||
assert( (rc==SQLITE_OK)==(p->pIter!=0) );
|
||||
if( rc!=SQLITE_OK ) return rc;
|
||||
if( 0==sqlite3Fts5IterEof(p->pIter) ){
|
||||
bHit = 1;
|
||||
}
|
||||
}
|
||||
rc = sqlite3Fts5IndexQuery(
|
||||
pExpr->pIndex, p->zTerm, (int)strlen(p->zTerm),
|
||||
(pTerm->bPrefix ? FTS5INDEX_QUERY_PREFIX : 0) |
|
||||
(pExpr->bDesc ? FTS5INDEX_QUERY_DESC : 0),
|
||||
pNear->pColset,
|
||||
&p->pIter
|
||||
);
|
||||
assert( rc==SQLITE_OK || p->pIter==0 );
|
||||
if( p->pIter && 0==sqlite3Fts5IterEof(p->pIter) ){
|
||||
bEof = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if( bEof ){
|
||||
pNode->bEof = 1;
|
||||
return rc;
|
||||
if( bHit==0 ){
|
||||
pNode->bEof = 1;
|
||||
return SQLITE_OK;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return rc;
|
||||
pNode->bEof = 0;
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -920,7 +933,7 @@ static int fts5ExprNodeTest_STRING(
|
||||
}
|
||||
}else{
|
||||
Fts5IndexIter *pIter = pPhrase->aTerm[j].pIter;
|
||||
if( pIter->iRowid==iLast ) continue;
|
||||
if( pIter->iRowid==iLast || pIter->bEof ) continue;
|
||||
bMatch = 0;
|
||||
if( fts5ExprAdvanceto(pIter, bDesc, &iLast, &rc, &pNode->bEof) ){
|
||||
return rc;
|
||||
@@ -1637,7 +1650,6 @@ int sqlite3Fts5ExprClonePhrase(
|
||||
){
|
||||
int rc = SQLITE_OK; /* Return code */
|
||||
Fts5ExprPhrase *pOrig; /* The phrase extracted from pExpr */
|
||||
int i; /* Used to iterate through phrase terms */
|
||||
Fts5Expr *pNew = 0; /* Expression to return via *ppNew */
|
||||
TokenCtx sCtx = {0,0}; /* Context object for fts5ParseTokenize */
|
||||
|
||||
@@ -1658,7 +1670,7 @@ int sqlite3Fts5ExprClonePhrase(
|
||||
if( rc==SQLITE_OK ){
|
||||
Fts5Colset *pColsetOrig = pOrig->pNode->pNear->pColset;
|
||||
if( pColsetOrig ){
|
||||
int nByte = sizeof(Fts5Colset) + pColsetOrig->nCol * sizeof(int);
|
||||
int nByte = sizeof(Fts5Colset) + (pColsetOrig->nCol-1) * sizeof(int);
|
||||
Fts5Colset *pColset = (Fts5Colset*)sqlite3Fts5MallocZero(&rc, nByte);
|
||||
if( pColset ){
|
||||
memcpy(pColset, pColsetOrig, nByte);
|
||||
@@ -1667,18 +1679,25 @@ int sqlite3Fts5ExprClonePhrase(
|
||||
}
|
||||
}
|
||||
|
||||
for(i=0; rc==SQLITE_OK && i<pOrig->nTerm; i++){
|
||||
int tflags = 0;
|
||||
Fts5ExprTerm *p;
|
||||
for(p=&pOrig->aTerm[i]; p && rc==SQLITE_OK; p=p->pSynonym){
|
||||
const char *zTerm = p->zTerm;
|
||||
rc = fts5ParseTokenize((void*)&sCtx, tflags, zTerm, (int)strlen(zTerm),
|
||||
0, 0);
|
||||
tflags = FTS5_TOKEN_COLOCATED;
|
||||
}
|
||||
if( rc==SQLITE_OK ){
|
||||
sCtx.pPhrase->aTerm[i].bPrefix = pOrig->aTerm[i].bPrefix;
|
||||
if( pOrig->nTerm ){
|
||||
int i; /* Used to iterate through phrase terms */
|
||||
for(i=0; rc==SQLITE_OK && i<pOrig->nTerm; i++){
|
||||
int tflags = 0;
|
||||
Fts5ExprTerm *p;
|
||||
for(p=&pOrig->aTerm[i]; p && rc==SQLITE_OK; p=p->pSynonym){
|
||||
const char *zTerm = p->zTerm;
|
||||
rc = fts5ParseTokenize((void*)&sCtx, tflags, zTerm, (int)strlen(zTerm),
|
||||
0, 0);
|
||||
tflags = FTS5_TOKEN_COLOCATED;
|
||||
}
|
||||
if( rc==SQLITE_OK ){
|
||||
sCtx.pPhrase->aTerm[i].bPrefix = pOrig->aTerm[i].bPrefix;
|
||||
}
|
||||
}
|
||||
}else{
|
||||
/* This happens when parsing a token or quoted phrase that contains
|
||||
** no token characters at all. (e.g ... MATCH '""'). */
|
||||
sCtx.pPhrase = sqlite3Fts5MallocZero(&rc, sizeof(Fts5ExprPhrase));
|
||||
}
|
||||
|
||||
if( rc==SQLITE_OK ){
|
||||
@@ -1793,6 +1812,34 @@ static Fts5Colset *fts5ParseColset(
|
||||
return pNew;
|
||||
}
|
||||
|
||||
/*
|
||||
** Allocate and return an Fts5Colset object specifying the inverse of
|
||||
** the colset passed as the second argument. Free the colset passed
|
||||
** as the second argument before returning.
|
||||
*/
|
||||
Fts5Colset *sqlite3Fts5ParseColsetInvert(Fts5Parse *pParse, Fts5Colset *p){
|
||||
Fts5Colset *pRet;
|
||||
int nCol = pParse->pConfig->nCol;
|
||||
|
||||
pRet = (Fts5Colset*)sqlite3Fts5MallocZero(&pParse->rc,
|
||||
sizeof(Fts5Colset) + sizeof(int)*nCol
|
||||
);
|
||||
if( pRet ){
|
||||
int i;
|
||||
int iOld = 0;
|
||||
for(i=0; i<nCol; i++){
|
||||
if( iOld>=p->nCol || p->aiCol[iOld]!=i ){
|
||||
pRet->aiCol[pRet->nCol++] = i;
|
||||
}else{
|
||||
iOld++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sqlite3_free(p);
|
||||
return pRet;
|
||||
}
|
||||
|
||||
Fts5Colset *sqlite3Fts5ParseColset(
|
||||
Fts5Parse *pParse, /* Store SQLITE_NOMEM here if required */
|
||||
Fts5Colset *pColset, /* Existing colset object */
|
||||
|
||||
+33
-4
@@ -702,7 +702,6 @@ static Fts5Data *fts5DataRead(Fts5Index *p, i64 iRowid){
|
||||
return pRet;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Release a reference to data record returned by an earlier call to
|
||||
** fts5DataRead().
|
||||
@@ -711,6 +710,18 @@ static void fts5DataRelease(Fts5Data *pData){
|
||||
sqlite3_free(pData);
|
||||
}
|
||||
|
||||
static Fts5Data *fts5LeafRead(Fts5Index *p, i64 iRowid){
|
||||
Fts5Data *pRet = fts5DataRead(p, iRowid);
|
||||
if( pRet ){
|
||||
if( pRet->szLeaf>pRet->nn ){
|
||||
p->rc = FTS5_CORRUPT;
|
||||
fts5DataRelease(pRet);
|
||||
pRet = 0;
|
||||
}
|
||||
}
|
||||
return pRet;
|
||||
}
|
||||
|
||||
static int fts5IndexPrepareStmt(
|
||||
Fts5Index *p,
|
||||
sqlite3_stmt **ppStmt,
|
||||
@@ -1519,7 +1530,7 @@ static void fts5SegIterNextPage(
|
||||
pIter->pLeaf = pIter->pNextLeaf;
|
||||
pIter->pNextLeaf = 0;
|
||||
}else if( pIter->iLeafPgno<=pSeg->pgnoLast ){
|
||||
pIter->pLeaf = fts5DataRead(p,
|
||||
pIter->pLeaf = fts5LeafRead(p,
|
||||
FTS5_SEGMENT_ROWID(pSeg->iSegid, pIter->iLeafPgno)
|
||||
);
|
||||
}else{
|
||||
@@ -2022,9 +2033,8 @@ static void fts5SegIterNext(
|
||||
if( pLeaf->nn>pLeaf->szLeaf ){
|
||||
pIter->iPgidxOff = pLeaf->szLeaf + fts5GetVarint32(
|
||||
&pLeaf->p[pLeaf->szLeaf], pIter->iEndofDoclist
|
||||
);
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
else if( pLeaf->nn>pLeaf->szLeaf ){
|
||||
pIter->iPgidxOff = pLeaf->szLeaf + fts5GetVarint32(
|
||||
@@ -2269,6 +2279,11 @@ static void fts5LeafSeek(
|
||||
iTermOff += nKeep;
|
||||
iOff = iTermOff;
|
||||
|
||||
if( iOff>=n ){
|
||||
p->rc = FTS5_CORRUPT;
|
||||
return;
|
||||
}
|
||||
|
||||
/* Read the nKeep field of the next term. */
|
||||
fts5FastGetVarint32(a, iOff, nKeep);
|
||||
}
|
||||
@@ -2825,6 +2840,7 @@ static void fts5MultiIterNext(
|
||||
i64 iFrom /* Advance at least as far as this */
|
||||
){
|
||||
int bUseFrom = bFrom;
|
||||
assert( pIter->base.bEof==0 );
|
||||
while( p->rc==SQLITE_OK ){
|
||||
int iFirst = pIter->aFirst[1].iFirst;
|
||||
int bNewTerm = 0;
|
||||
@@ -3195,6 +3211,15 @@ static void fts5IterSetOutputs_Nocolset(Fts5Iter *pIter, Fts5SegIter *pSeg){
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** xSetOutputs callback used when the Fts5Colset object has nCol==0 (match
|
||||
** against no columns at all).
|
||||
*/
|
||||
static void fts5IterSetOutputs_ZeroColset(Fts5Iter *pIter, Fts5SegIter *pSeg){
|
||||
UNUSED_PARAM(pSeg);
|
||||
pIter->base.nData = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
** xSetOutputs callback used by detail=col when there is a column filter
|
||||
** and there are 100 or more columns. Also called as a fallback from
|
||||
@@ -3300,6 +3325,10 @@ static void fts5IterSetOutputCb(int *pRc, Fts5Iter *pIter){
|
||||
pIter->xSetOutputs = fts5IterSetOutputs_Nocolset;
|
||||
}
|
||||
|
||||
else if( pIter->pColset->nCol==0 ){
|
||||
pIter->xSetOutputs = fts5IterSetOutputs_ZeroColset;
|
||||
}
|
||||
|
||||
else if( pConfig->eDetail==FTS5_DETAIL_FULL ){
|
||||
pIter->xSetOutputs = fts5IterSetOutputs_Full;
|
||||
}
|
||||
|
||||
@@ -247,7 +247,11 @@ int sqlite3Fts5CreateTable(
|
||||
char *zErr = 0;
|
||||
|
||||
rc = fts5ExecPrintf(pConfig->db, &zErr, "CREATE TABLE %Q.'%q_%q'(%s)%s",
|
||||
pConfig->zDb, pConfig->zName, zPost, zDefn, bWithout?" WITHOUT ROWID":""
|
||||
pConfig->zDb, pConfig->zName, zPost, zDefn,
|
||||
#ifndef SQLITE_FTS5_NO_WITHOUT_ROWID
|
||||
bWithout?" WITHOUT ROWID":
|
||||
#endif
|
||||
""
|
||||
);
|
||||
if( zErr ){
|
||||
*pzErr = sqlite3_mprintf(
|
||||
|
||||
+20
-13
@@ -14,7 +14,14 @@
|
||||
|
||||
|
||||
#ifdef SQLITE_TEST
|
||||
#include <tcl.h>
|
||||
#if defined(INCLUDE_SQLITE_TCL_H)
|
||||
# include "sqlite_tcl.h"
|
||||
#else
|
||||
# include "tcl.h"
|
||||
# ifndef SQLITE_TCLAPI
|
||||
# define SQLITE_TCLAPI
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#ifdef SQLITE_ENABLE_FTS5
|
||||
|
||||
@@ -78,7 +85,7 @@ static int f5tResultToErrorCode(const char *zRes){
|
||||
return SQLITE_ERROR;
|
||||
}
|
||||
|
||||
static int f5tDbAndApi(
|
||||
static int SQLITE_TCLAPI f5tDbAndApi(
|
||||
Tcl_Interp *interp,
|
||||
Tcl_Obj *pObj,
|
||||
sqlite3 **ppDb,
|
||||
@@ -164,7 +171,7 @@ static int xTokenizeCb(
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int xF5tApi(void*, Tcl_Interp*, int, Tcl_Obj *CONST []);
|
||||
static int SQLITE_TCLAPI xF5tApi(void*, Tcl_Interp*, int, Tcl_Obj *CONST []);
|
||||
|
||||
static int xQueryPhraseCb(
|
||||
const Fts5ExtensionApi *pApi,
|
||||
@@ -209,7 +216,7 @@ static void xSetAuxdataDestructor(void *p){
|
||||
**
|
||||
** Description...
|
||||
*/
|
||||
static int xF5tApi(
|
||||
static int SQLITE_TCLAPI xF5tApi(
|
||||
void * clientData,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
@@ -602,7 +609,7 @@ static void xF5tDestroy(void *pCtx){
|
||||
**
|
||||
** Description...
|
||||
*/
|
||||
static int f5tCreateFunction(
|
||||
static int SQLITE_TCLAPI f5tCreateFunction(
|
||||
void * clientData,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
@@ -672,7 +679,7 @@ static int xTokenizeCb2(
|
||||
**
|
||||
** Description...
|
||||
*/
|
||||
static int f5tTokenize(
|
||||
static int SQLITE_TCLAPI f5tTokenize(
|
||||
void * clientData,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
@@ -878,7 +885,7 @@ static int f5tTokenizerTokenize(
|
||||
/*
|
||||
** sqlite3_fts5_token ?-colocated? TEXT START END
|
||||
*/
|
||||
static int f5tTokenizerReturn(
|
||||
static int SQLITE_TCLAPI f5tTokenizerReturn(
|
||||
void * clientData,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
@@ -949,7 +956,7 @@ static void f5tDelTokenizer(void *pCtx){
|
||||
** SCRIPT2 should invoke the [sqlite3_fts5_token] command once for each
|
||||
** token within the tokenized text.
|
||||
*/
|
||||
static int f5tCreateTokenizer(
|
||||
static int SQLITE_TCLAPI f5tCreateTokenizer(
|
||||
ClientData clientData,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
@@ -992,7 +999,7 @@ static int f5tCreateTokenizer(
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
static void xF5tFree(ClientData clientData){
|
||||
static void SQLITE_TCLAPI xF5tFree(ClientData clientData){
|
||||
ckfree(clientData);
|
||||
}
|
||||
|
||||
@@ -1001,7 +1008,7 @@ static void xF5tFree(ClientData clientData){
|
||||
**
|
||||
** Set or clear the global "may-be-corrupt" flag. Return the old value.
|
||||
*/
|
||||
static int f5tMayBeCorrupt(
|
||||
static int SQLITE_TCLAPI f5tMayBeCorrupt(
|
||||
void * clientData,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
@@ -1033,7 +1040,7 @@ static unsigned int f5t_fts5HashKey(int nSlot, const char *p, int n){
|
||||
return (h % nSlot);
|
||||
}
|
||||
|
||||
static int f5tTokenHash(
|
||||
static int SQLITE_TCLAPI f5tTokenHash(
|
||||
void * clientData,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
@@ -1058,7 +1065,7 @@ static int f5tTokenHash(
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
static int f5tRegisterMatchinfo(
|
||||
static int SQLITE_TCLAPI f5tRegisterMatchinfo(
|
||||
void * clientData,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
@@ -1083,7 +1090,7 @@ static int f5tRegisterMatchinfo(
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
static int f5tRegisterTok(
|
||||
static int SQLITE_TCLAPI f5tRegisterTok(
|
||||
void * clientData,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
|
||||
+12
-1
@@ -279,8 +279,19 @@ static int fts5VocabBestIndexMethod(
|
||||
}
|
||||
}
|
||||
|
||||
pInfo->idxNum = idxNum;
|
||||
/* This virtual table always delivers results in ascending order of
|
||||
** the "term" column (column 0). So if the user has requested this
|
||||
** specifically - "ORDER BY term" or "ORDER BY term ASC" - set the
|
||||
** sqlite3_index_info.orderByConsumed flag to tell the core the results
|
||||
** are already in sorted order. */
|
||||
if( pInfo->nOrderBy==1
|
||||
&& pInfo->aOrderBy[0].iColumn==0
|
||||
&& pInfo->aOrderBy[0].desc==0
|
||||
){
|
||||
pInfo->orderByConsumed = 1;
|
||||
}
|
||||
|
||||
pInfo->idxNum = idxNum;
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
|
||||
@@ -120,10 +120,17 @@ cnearset(A) ::= colset(X) COLON nearset(Y). {
|
||||
%type colsetlist {Fts5Colset*}
|
||||
%destructor colsetlist { sqlite3_free($$); }
|
||||
|
||||
colset(A) ::= MINUS LCP colsetlist(X) RCP. {
|
||||
A = sqlite3Fts5ParseColsetInvert(pParse, X);
|
||||
}
|
||||
colset(A) ::= LCP colsetlist(X) RCP. { A = X; }
|
||||
colset(A) ::= STRING(X). {
|
||||
A = sqlite3Fts5ParseColset(pParse, 0, &X);
|
||||
}
|
||||
colset(A) ::= MINUS STRING(X). {
|
||||
A = sqlite3Fts5ParseColset(pParse, 0, &X);
|
||||
A = sqlite3Fts5ParseColsetInvert(pParse, A);
|
||||
}
|
||||
|
||||
colsetlist(A) ::= colsetlist(Y) STRING(X). {
|
||||
A = sqlite3Fts5ParseColset(pParse, Y, &X); }
|
||||
@@ -131,7 +138,6 @@ colsetlist(A) ::= STRING(X). {
|
||||
A = sqlite3Fts5ParseColset(pParse, 0, &X);
|
||||
}
|
||||
|
||||
|
||||
%type nearset {Fts5ExprNearset*}
|
||||
%type nearphrases {Fts5ExprNearset*}
|
||||
%destructor nearset { sqlite3Fts5ParseNearsetFree($$); }
|
||||
|
||||
+62
-29
@@ -72,42 +72,56 @@ foreach {tn doc res} {
|
||||
2.2 {o X o o o o o o} {o [X] o o o o o...}
|
||||
2.3 {o o X o o o o o} {o o [X] o o o o...}
|
||||
2.4 {o o o X o o o o} {o o o [X] o o o...}
|
||||
2.5 {o o o o X o o o} {...o o o [X] o o o}
|
||||
2.6 {o o o o o X o o} {...o o o o [X] o o}
|
||||
2.7 {o o o o o o X o} {...o o o o o [X] o}
|
||||
2.5 {o o o o X o o o} {o o o o [X] o o...}
|
||||
2.6 {o o o o o X o o} {o o o o o [X] o...}
|
||||
2.7 {o o o o o o X o} {o o o o o o [X]...}
|
||||
2.8 {o o o o o o o X} {...o o o o o o [X]}
|
||||
|
||||
2.9 {o o o o o o o X o} {...o o o o o [X] o}
|
||||
2.10 {o o o o o o o X o o} {...o o o o [X] o o}
|
||||
2.11 {o o o o o o o X o o o} {...o o o [X] o o o}
|
||||
2.12 {o o o o o o o X o o o o} {...o o o [X] o o o...}
|
||||
|
||||
|
||||
3.1 {X o o o o o o o o} {[X] o o o o o o...}
|
||||
3.2 {o X o o o o o o o} {o [X] o o o o o...}
|
||||
3.3 {o o X o o o o o o} {o o [X] o o o o...}
|
||||
3.4 {o o o X o o o o o} {o o o [X] o o o...}
|
||||
3.5 {o o o o X o o o o} {...o o o [X] o o o...}
|
||||
3.6 {o o o o o X o o o} {...o o o [X] o o o}
|
||||
3.7 {o o o o o o X o o} {...o o o o [X] o o}
|
||||
3.8 {o o o o o o o X o} {...o o o o o [X] o}
|
||||
3.9 {o o o o o o o o X} {...o o o o o o [X]}
|
||||
|
||||
3.5 {o o o o o o o X o o o o} {...o o o [X] o o o...}
|
||||
3.6 {o o o o o o o o X o o o} {...o o o [X] o o o}
|
||||
3.7 {o o o o o o o o o X o o} {...o o o o [X] o o}
|
||||
3.8 {o o o o o o o o o o X o} {...o o o o o [X] o}
|
||||
3.9 {o o o o o o o o o o o X} {...o o o o o o [X]}
|
||||
|
||||
4.1 {X o o o o o X o o} {[X] o o o o o [X]...}
|
||||
4.2 {o X o o o o o X o} {...[X] o o o o o [X]...}
|
||||
4.3 {o o X o o o o o X} {...[X] o o o o o [X]}
|
||||
4.2 {o o o o o o o X o o o o o X o} {...[X] o o o o o [X]...}
|
||||
4.3 {o o o o o o o o X o o o o o X} {...[X] o o o o o [X]}
|
||||
|
||||
5.1 {X o o o o X o o o} {[X] o o o o [X] o...}
|
||||
5.2 {o X o o o o X o o} {...[X] o o o o [X] o...}
|
||||
5.3 {o o X o o o o X o} {...[X] o o o o [X] o}
|
||||
5.4 {o o o X o o o o X} {...o [X] o o o o [X]}
|
||||
5.2 {o o o o o o o X o o o o X o o} {...[X] o o o o [X] o...}
|
||||
5.3 {o o o o o o o o X o o o o X o} {...[X] o o o o [X] o}
|
||||
5.4 {o o o o o o o o o X o o o o X} {...o [X] o o o o [X]}
|
||||
|
||||
6.1 {X o o o X o o o} {[X] o o o [X] o o...}
|
||||
6.2 {o X o o o X o o o} {o [X] o o o [X] o...}
|
||||
6.3 {o o X o o o X o o} {...o [X] o o o [X] o...}
|
||||
6.4 {o o o X o o o X o} {...o [X] o o o [X] o}
|
||||
6.5 {o o o o X o o o X} {...o o [X] o o o [X]}
|
||||
6.3 {o o o o o o o X o o o X o o} {...o [X] o o o [X] o...}
|
||||
6.4 {o o o o o o o o X o o o X o} {...o [X] o o o [X] o}
|
||||
6.5 {o o o o o o o o o X o o o X} {...o o [X] o o o [X]}
|
||||
|
||||
7.1 {X o o X o o o o o} {[X] o o [X] o o o...}
|
||||
7.2 {o X o o X o o o o} {o [X] o o [X] o o...}
|
||||
7.3 {o o X o o X o o o} {...o [X] o o [X] o o...}
|
||||
7.4 {o o o X o o X o o} {...o [X] o o [X] o o}
|
||||
7.5 {o o o o X o o X o} {...o o [X] o o [X] o}
|
||||
7.6 {o o o o o X o o X} {...o o o [X] o o [X]}
|
||||
7.3 {o o o o o o o X o o X o o o} {...o [X] o o [X] o o...}
|
||||
7.4 {o o o o o o o o X o o X o o} {...o [X] o o [X] o o}
|
||||
7.5 {o o o o o o o o o X o o X o} {...o o [X] o o [X] o}
|
||||
7.6 {o o o o o o o o o o X o o X} {...o o o [X] o o [X]}
|
||||
|
||||
8.1 {o o o o o o o o o X o o o o o o o o o o o o o o o o X X X o o o}
|
||||
{...o o [X] [X] [X] o o...}
|
||||
8.2 {o o o o o o o. o o X o o o o o o o o o o o o o o o o X X X o o o}
|
||||
{...o o [X] o o o o...}
|
||||
8.3 {o o o o X o o o o o o o o o o o o o o o o o o o o o X X X o o o}
|
||||
{o o o o [X] o o...}
|
||||
} {
|
||||
do_snippet_test 1.$tn $doc X $res
|
||||
}
|
||||
@@ -124,24 +138,43 @@ if {[detail_is_full]} {
|
||||
2.1 {X Y o o o o o o} {[X Y] o o o o o...}
|
||||
2.2 {o X Y o o o o o} {o [X Y] o o o o...}
|
||||
2.3 {o o X Y o o o o} {o o [X Y] o o o...}
|
||||
2.4 {o o o X Y o o o} {...o o [X Y] o o o}
|
||||
2.5 {o o o o X Y o o} {...o o o [X Y] o o}
|
||||
2.6 {o o o o o X Y o} {...o o o o [X Y] o}
|
||||
2.7 {o o o o o o X Y} {...o o o o o [X Y]}
|
||||
2.4 {o o o o o o o X Y o o o} {...o o [X Y] o o o}
|
||||
2.5 {o o o o o o o o X Y o o} {...o o o [X Y] o o}
|
||||
2.6 {o o o o o o o o o X Y o} {...o o o o [X Y] o}
|
||||
2.7 {o o o o o o o o o o X Y} {...o o o o o [X Y]}
|
||||
|
||||
3.1 {X Y o o o o o o o} {[X Y] o o o o o...}
|
||||
3.2 {o X Y o o o o o o} {o [X Y] o o o o...}
|
||||
3.3 {o o X Y o o o o o} {o o [X Y] o o o...}
|
||||
3.4 {o o o X Y o o o o} {...o o [X Y] o o o...}
|
||||
3.5 {o o o o X Y o o o} {...o o [X Y] o o o}
|
||||
3.6 {o o o o o X Y o o} {...o o o [X Y] o o}
|
||||
3.7 {o o o o o o X Y o} {...o o o o [X Y] o}
|
||||
3.8 {o o o o o o o X Y} {...o o o o o [X Y]}
|
||||
3.4 {o o o o o o o X Y o o o o} {...o o [X Y] o o o...}
|
||||
3.5 {o o o o o o o o X Y o o o} {...o o [X Y] o o o}
|
||||
3.6 {o o o o o o o o o X Y o o} {...o o o [X Y] o o}
|
||||
3.7 {o o o o o o o o o o X Y o} {...o o o o [X Y] o}
|
||||
3.8 {o o o o o o o o o o o X Y} {...o o o o o [X Y]}
|
||||
} {
|
||||
do_snippet_test 2.$tn $doc "X + Y" $res
|
||||
}
|
||||
}
|
||||
|
||||
do_execsql_test 4.0 {
|
||||
CREATE VIRTUAL TABLE x1 USING fts5(a, b);
|
||||
INSERT INTO x1 VALUES('xyz', '1 2 3 4 5 6 7 8 9 10 11 12 13');
|
||||
SELECT snippet(x1, 1, '[', ']', '...', 5) FROM x1('xyz');
|
||||
} {
|
||||
{1 2 3 4 5...}
|
||||
}
|
||||
|
||||
do_execsql_test 5.0 {
|
||||
CREATE VIRTUAL TABLE p1 USING fts5(a, b);
|
||||
INSERT INTO p1 VALUES(
|
||||
'x a a a a a a a a a a',
|
||||
'a a a a a a a a a a a a a a a a a a a x'
|
||||
);
|
||||
}
|
||||
do_execsql_test 5.1 {
|
||||
SELECT snippet(p1, 0, '[', ']', '...', 6) FROM p1('x');
|
||||
} {{[x] a a a a a...}}
|
||||
|
||||
} ;# foreach_detail_mode
|
||||
|
||||
finish_test
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
# 2016 August 10
|
||||
#
|
||||
# The author disclaims copyright to this source code. In place of
|
||||
# a legal notice, here is a blessing:
|
||||
#
|
||||
# May you do good and not evil.
|
||||
# May you find forgiveness for yourself and forgive others.
|
||||
# May you share freely, never taking more than you give.
|
||||
#
|
||||
#*************************************************************************
|
||||
# This file implements regression tests for SQLite library. The
|
||||
# focus of this script is testing the FTS5 module.
|
||||
#
|
||||
|
||||
source [file join [file dirname [info script]] fts5_common.tcl]
|
||||
set testprefix fts5colset
|
||||
|
||||
# If SQLITE_ENABLE_FTS5 is not defined, omit this file.
|
||||
ifcapable !fts5 {
|
||||
finish_test
|
||||
return
|
||||
}
|
||||
|
||||
foreach_detail_mode $::testprefix {
|
||||
if {[detail_is_none]} continue
|
||||
|
||||
do_execsql_test 1.0 {
|
||||
CREATE VIRTUAL TABLE t1 USING fts5(a, b, c, d, detail=%DETAIL%);
|
||||
INSERT INTO t1 VALUES('a', 'b', 'c', 'd'); -- 1
|
||||
INSERT INTO t1 VALUES('d', 'a', 'b', 'c'); -- 2
|
||||
INSERT INTO t1 VALUES('c', 'd', 'a', 'b'); -- 3
|
||||
INSERT INTO t1 VALUES('b', 'c', 'd', 'a'); -- 4
|
||||
}
|
||||
|
||||
foreach {tn q res} {
|
||||
1 "a" {1 2 3 4}
|
||||
2 "{a} : a" {1}
|
||||
3 "-{a} : a" {2 3 4}
|
||||
4 "- {a c} : a" {2 4}
|
||||
5 " - {d d c} : a" {1 2}
|
||||
6 "- {d c b a} : a" {}
|
||||
7 "-{\"a\"} : b" {1 2 3}
|
||||
8 "- c : a" {1 2 4}
|
||||
9 "-c : a" {1 2 4}
|
||||
10 "-\"c\" : a" {1 2 4}
|
||||
} {
|
||||
breakpoint
|
||||
do_execsql_test 1.$tn {
|
||||
SELECT rowid FROM t1($q)
|
||||
} $res
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
finish_test
|
||||
|
||||
|
||||
@@ -37,7 +37,7 @@ do_execsql_test 1.0 {
|
||||
}
|
||||
set mask [expr 31 << 31]
|
||||
|
||||
if 1 {
|
||||
if 0 {
|
||||
|
||||
# Test 1:
|
||||
#
|
||||
@@ -84,6 +84,8 @@ foreach {tno stmt} {
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
# Using the same database as the 1.* tests.
|
||||
#
|
||||
# Run N-1 tests, where N is the number of bytes in the rightmost leaf page
|
||||
@@ -212,8 +214,6 @@ foreach {tn nCut} {
|
||||
# do_test 4.$tn.x { expr $nCorrupt>0 } 1
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
set doc [string repeat "A B C " 1000]
|
||||
do_execsql_test 5.0 {
|
||||
CREATE VIRTUAL TABLE x5 USING fts5(tt);
|
||||
|
||||
@@ -63,6 +63,23 @@ do_catchsql_test 2.1 {
|
||||
SELECT fts5_expr_tcl()
|
||||
} {1 {wrong number of arguments to function fts5_expr_tcl}}
|
||||
|
||||
|
||||
do_execsql_test 3.0 {
|
||||
CREATE VIRTUAL TABLE e1 USING fts5(text, tokenize = 'porter unicode61');
|
||||
INSERT INTO e1 VALUES ("just a few words with a / inside");
|
||||
}
|
||||
do_execsql_test 3.1 {
|
||||
SELECT rowid, bm25(e1) FROM e1 WHERE e1 MATCH '"just"' ORDER BY rank;
|
||||
} {1 -1e-06}
|
||||
do_execsql_test 3.2 {
|
||||
SELECT rowid FROM e1 WHERE e1 MATCH '"/" OR "just"'
|
||||
} 1
|
||||
do_execsql_test 3.3 {
|
||||
SELECT rowid, bm25(e1) FROM e1 WHERE e1 MATCH '"/" OR "just"' ORDER BY rank;
|
||||
} {1 -1e-06}
|
||||
|
||||
|
||||
|
||||
finish_test
|
||||
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
#
|
||||
#***********************************************************************
|
||||
#
|
||||
# This file containst tests focused on prefix indexes.
|
||||
# This file contains tests focused on prefix indexes.
|
||||
#
|
||||
|
||||
source [file join [file dirname [info script]] fts5_common.tcl]
|
||||
|
||||
@@ -100,30 +100,55 @@ do_test 2.7 {
|
||||
# following tests verify that that problem has been addressed.
|
||||
#
|
||||
foreach_detail_mode $::testprefix {
|
||||
do_execsql_test 3.0 {
|
||||
do_execsql_test 3.1.0 {
|
||||
CREATE VIRTUAL TABLE y1 USING fts5(z, detail=%DETAIL%);
|
||||
INSERT INTO y1 VALUES('test xyz');
|
||||
INSERT INTO y1 VALUES('test test xyz test');
|
||||
INSERT INTO y1 VALUES('test test xyz');
|
||||
}
|
||||
|
||||
do_execsql_test 3.1 {
|
||||
do_execsql_test 3.1.1 {
|
||||
SELECT rowid FROM y1('test OR tset');
|
||||
} {1 2 3}
|
||||
|
||||
do_execsql_test 3.2 {
|
||||
do_execsql_test 3.1.2 {
|
||||
SELECT rowid FROM y1('test OR tset') ORDER BY bm25(y1)
|
||||
} {2 3 1}
|
||||
|
||||
do_execsql_test 3.3 {
|
||||
do_execsql_test 3.1.3 {
|
||||
SELECT rowid FROM y1('test OR tset') ORDER BY +rank
|
||||
} {2 3 1}
|
||||
|
||||
do_execsql_test 3.4 {
|
||||
do_execsql_test 3.1.4 {
|
||||
SELECT rowid FROM y1('test OR tset') ORDER BY rank
|
||||
} {2 3 1}
|
||||
|
||||
do_execsql_test 3.1.5 {
|
||||
SELECT rowid FROM y1('test OR xyz') ORDER BY rank
|
||||
} {3 2 1}
|
||||
|
||||
|
||||
do_execsql_test 3.2.1 {
|
||||
CREATE VIRTUAL TABLE z1 USING fts5(a, detail=%DETAIL%);
|
||||
INSERT INTO z1 VALUES('wrinkle in time');
|
||||
SELECT * FROM z1 WHERE z1 MATCH 'wrinkle in time OR a wrinkle in time';
|
||||
} {{wrinkle in time}}
|
||||
}
|
||||
|
||||
do_execsql_test 4.1 {
|
||||
DROP TABLE IF EXISTS VTest;
|
||||
CREATE virtual TABLE VTest USING FTS5(
|
||||
Title, AUthor, tokenize ='porter unicode61 remove_diacritics 1',
|
||||
columnsize='1', detail=full
|
||||
);
|
||||
INSERT INTO VTest (Title, Author) VALUES ('wrinkle in time', 'Bill Smith');
|
||||
|
||||
SELECT * FROM VTest WHERE
|
||||
VTest MATCH 'wrinkle in time OR a wrinkle in time' ORDER BY rank;
|
||||
} {{wrinkle in time} {Bill Smith}}
|
||||
|
||||
|
||||
|
||||
|
||||
finish_test
|
||||
|
||||
|
||||
@@ -265,7 +265,7 @@ do_test 11.0 {
|
||||
execsql "
|
||||
INSERT INTO t4 VALUES('a b c \x1A');
|
||||
INSERT INTO t4 VALUES('a b c d\x1A');
|
||||
INSERT INTO t4 VALUES('a b c \x1Ad');
|
||||
INSERT INTO t4 VALUES('a b c \x1Ag');
|
||||
INSERT INTO t4 VALUES('a b c d');
|
||||
"
|
||||
} {}
|
||||
|
||||
@@ -332,6 +332,41 @@ do_execsql_test 16.0 {
|
||||
DELETE FROM t2;
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
reset_db
|
||||
do_execsql_test 17.0 {
|
||||
CREATE VIRTUAL TABLE t2 USING fts5(x, y);
|
||||
BEGIN;
|
||||
INSERT INTO t2 VALUES('a aa aaa', 'b bb bbb');
|
||||
INSERT INTO t2 VALUES('a aa aaa', 'b bb bbb');
|
||||
INSERT INTO t2 VALUES('a aa aaa', 'b bb bbb');
|
||||
COMMIT;
|
||||
}
|
||||
do_execsql_test 17.1 { SELECT * FROM t2('y:a*') WHERE rowid BETWEEN 10 AND 20 }
|
||||
do_execsql_test 17.2 {
|
||||
BEGIN;
|
||||
INSERT INTO t2 VALUES('a aa aaa', 'b bb bbb');
|
||||
SELECT * FROM t2('y:a*') WHERE rowid BETWEEN 10 AND 20 ;
|
||||
}
|
||||
do_execsql_test 17.3 {
|
||||
COMMIT
|
||||
}
|
||||
|
||||
reset_db
|
||||
do_execsql_test 17.4 {
|
||||
CREATE VIRTUAL TABLE t2 USING fts5(x, y);
|
||||
BEGIN;
|
||||
INSERT INTO t2 VALUES('a aa aaa', 'b bb bbb');
|
||||
INSERT INTO t2 VALUES('a aa aaa', 'b bb bbb');
|
||||
SELECT * FROM t2('y:a*') WHERE rowid>66;
|
||||
}
|
||||
do_execsql_test 17.5 { SELECT * FROM t2('x:b* OR y:a*') }
|
||||
do_execsql_test 17.5 { COMMIT ; SELECT * FROM t2('x:b* OR y:a*') }
|
||||
do_execsql_test 17.6 {
|
||||
SELECT * FROM t2('x:b* OR y:a*') WHERE rowid>55
|
||||
}
|
||||
|
||||
#db eval {SELECT rowid, fts5_decode_none(rowid, block) aS r FROM t2_data} {puts $r}
|
||||
|
||||
finish_test
|
||||
|
||||
@@ -80,6 +80,40 @@ do_execsql_test 3.0 {
|
||||
SELECT * FROM x3('x OR y OR z');
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# Test that a crash occuring when the second or subsequent tokens in a
|
||||
# phrase matched zero rows has been fixed.
|
||||
#
|
||||
do_execsql_test 4.0 {
|
||||
CREATE VIRTUAL TABLE t1 USING fts5(x);
|
||||
INSERT INTO t1 VALUES('ab');
|
||||
INSERT INTO t1 VALUES('cd');
|
||||
INSERT INTO t1 VALUES('ab cd');
|
||||
INSERT INTO t1 VALUES('ab cdXXX');
|
||||
INSERT INTO t1 VALUES('abXXX cd');
|
||||
}
|
||||
do_execsql_test 4.1 {
|
||||
SELECT * FROM t1('"ab cd" OR "ab cd" *');
|
||||
} {{ab cd} {ab cdXXX}}
|
||||
do_execsql_test 4.2 {
|
||||
SELECT * FROM t1('"xy zz" OR "ab cd" *');
|
||||
} {{ab cd} {ab cdXXX}}
|
||||
do_execsql_test 4.3 {
|
||||
SELECT * FROM t1('"xy zz" OR "xy zz" *');
|
||||
}
|
||||
do_execsql_test 4.4 {
|
||||
SELECT * FROM t1('"ab cd" OR "xy zz" *');
|
||||
} {{ab cd}}
|
||||
do_execsql_test 4.5 {
|
||||
CREATE VIRTUAL TABLE t2 USING fts5(x);
|
||||
INSERT INTO t2 VALUES('ab');
|
||||
INSERT INTO t2 VALUES('cd');
|
||||
INSERT INTO t2 VALUES('ef');
|
||||
}
|
||||
do_execsql_test 4.6 {
|
||||
SELECT * FROM t2('ab + xyz');
|
||||
}
|
||||
|
||||
|
||||
finish_test
|
||||
|
||||
|
||||
@@ -160,12 +160,12 @@ foreach {tn query snippet} {
|
||||
the maximum x value.
|
||||
}
|
||||
4 "rollback" {
|
||||
...[ROLLBACK]. Instead, the pending statement
|
||||
will return SQLITE_ABORT upon next access after the [ROLLBACK].
|
||||
Pending statements no longer block [ROLLBACK]. Instead, the pending
|
||||
statement will return SQLITE_ABORT upon...
|
||||
}
|
||||
5 "rOllback" {
|
||||
...[ROLLBACK]. Instead, the pending statement
|
||||
will return SQLITE_ABORT upon next access after the [ROLLBACK].
|
||||
Pending statements no longer block [ROLLBACK]. Instead, the pending
|
||||
statement will return SQLITE_ABORT upon...
|
||||
}
|
||||
6 "lang*" {
|
||||
Added support for the FTS4 [languageid] option.
|
||||
|
||||
@@ -442,8 +442,44 @@ if {[detail_is_none]} {
|
||||
}
|
||||
|
||||
sqlite3_fts5_may_be_corrupt 0
|
||||
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# Test that both "ORDER BY term" and "ORDER BY term DESC" work.
|
||||
#
|
||||
reset_db
|
||||
do_execsql_test 9.1 {
|
||||
CREATE VIRTUAL TABLE x1 USING fts5(x);
|
||||
INSERT INTO x1 VALUES('def ABC ghi');
|
||||
INSERT INTO x1 VALUES('DEF abc GHI');
|
||||
}
|
||||
|
||||
do_execsql_test 9.2 {
|
||||
CREATE VIRTUAL TABLE rrr USING fts5vocab(x1, row);
|
||||
SELECT * FROM rrr
|
||||
} {
|
||||
abc 2 2 def 2 2 ghi 2 2
|
||||
}
|
||||
do_execsql_test 9.3 {
|
||||
SELECT * FROM rrr ORDER BY term ASC
|
||||
} {
|
||||
abc 2 2 def 2 2 ghi 2 2
|
||||
}
|
||||
do_execsql_test 9.4 {
|
||||
SELECT * FROM rrr ORDER BY term DESC
|
||||
} {
|
||||
ghi 2 2 def 2 2 abc 2 2
|
||||
}
|
||||
do_test 9.5 {
|
||||
set e2 [db eval { EXPLAIN SELECT * FROM rrr ORDER BY term ASC }]
|
||||
expr [lsearch $e2 SorterSort]<0
|
||||
} 1
|
||||
do_test 9.6 {
|
||||
set e2 [db eval { EXPLAIN SELECT * FROM rrr ORDER BY term DESC }]
|
||||
expr [lsearch $e2 SorterSort]<0
|
||||
} 0
|
||||
|
||||
|
||||
|
||||
finish_test
|
||||
|
||||
|
||||
+12
-12
@@ -349,7 +349,7 @@ static void icuRegexpFunc(sqlite3_context *p, int nArg, sqlite3_value **apArg){
|
||||
** of upper() or lower().
|
||||
**
|
||||
** lower('I', 'en_us') -> 'i'
|
||||
** lower('I', 'tr_tr') -> 'ı' (small dotless i)
|
||||
** lower('I', 'tr_tr') -> '\u131' (small dotless i)
|
||||
**
|
||||
** http://www.icu-project.org/userguide/posix.html#case_mappings
|
||||
*/
|
||||
@@ -500,20 +500,20 @@ int sqlite3IcuInit(sqlite3 *db){
|
||||
void *pContext; /* sqlite3_user_data() context */
|
||||
void (*xFunc)(sqlite3_context*,int,sqlite3_value**);
|
||||
} scalars[] = {
|
||||
{"regexp", 2, SQLITE_ANY, 0, icuRegexpFunc},
|
||||
{"regexp", 2, SQLITE_ANY|SQLITE_DETERMINISTIC, 0, icuRegexpFunc},
|
||||
|
||||
{"lower", 1, SQLITE_UTF16, 0, icuCaseFunc16},
|
||||
{"lower", 2, SQLITE_UTF16, 0, icuCaseFunc16},
|
||||
{"upper", 1, SQLITE_UTF16, (void*)1, icuCaseFunc16},
|
||||
{"upper", 2, SQLITE_UTF16, (void*)1, icuCaseFunc16},
|
||||
{"lower", 1, SQLITE_UTF16|SQLITE_DETERMINISTIC, 0, icuCaseFunc16},
|
||||
{"lower", 2, SQLITE_UTF16|SQLITE_DETERMINISTIC, 0, icuCaseFunc16},
|
||||
{"upper", 1, SQLITE_UTF16|SQLITE_DETERMINISTIC, (void*)1, icuCaseFunc16},
|
||||
{"upper", 2, SQLITE_UTF16|SQLITE_DETERMINISTIC, (void*)1, icuCaseFunc16},
|
||||
|
||||
{"lower", 1, SQLITE_UTF8, 0, icuCaseFunc16},
|
||||
{"lower", 2, SQLITE_UTF8, 0, icuCaseFunc16},
|
||||
{"upper", 1, SQLITE_UTF8, (void*)1, icuCaseFunc16},
|
||||
{"upper", 2, SQLITE_UTF8, (void*)1, icuCaseFunc16},
|
||||
{"lower", 1, SQLITE_UTF8|SQLITE_DETERMINISTIC, 0, icuCaseFunc16},
|
||||
{"lower", 2, SQLITE_UTF8|SQLITE_DETERMINISTIC, 0, icuCaseFunc16},
|
||||
{"upper", 1, SQLITE_UTF8|SQLITE_DETERMINISTIC, (void*)1, icuCaseFunc16},
|
||||
{"upper", 2, SQLITE_UTF8|SQLITE_DETERMINISTIC, (void*)1, icuCaseFunc16},
|
||||
|
||||
{"like", 2, SQLITE_UTF8, 0, icuLikeFunc},
|
||||
{"like", 3, SQLITE_UTF8, 0, icuLikeFunc},
|
||||
{"like", 2, SQLITE_UTF8|SQLITE_DETERMINISTIC, 0, icuLikeFunc},
|
||||
{"like", 3, SQLITE_UTF8|SQLITE_DETERMINISTIC, 0, icuLikeFunc},
|
||||
|
||||
{"icu_load_collation", 2, SQLITE_UTF8, (void*)db, icuLoadCollation},
|
||||
};
|
||||
|
||||
+5
-10
@@ -14,7 +14,7 @@
|
||||
** returns the values in a C-language array.
|
||||
** Examples:
|
||||
**
|
||||
** SELECT * FROM array($ptr,5)
|
||||
** SELECT * FROM carray($ptr,5)
|
||||
**
|
||||
** The query above returns 5 integers contained in a C-language array
|
||||
** at the address $ptr. $ptr is a pointer to the array of integers that
|
||||
@@ -24,7 +24,7 @@
|
||||
** the C-language array. Allowed values of the third parameter are
|
||||
** 'int32', 'int64', 'double', 'char*'. Example:
|
||||
**
|
||||
** SELECT * FROM array($ptr,10,'char*');
|
||||
** SELECT * FROM carray($ptr,10,'char*');
|
||||
**
|
||||
** HOW IT WORKS
|
||||
**
|
||||
@@ -237,7 +237,7 @@ static int carrayFilter(
|
||||
if( idxNum<3 ){
|
||||
pCur->eType = CARRAY_INT32;
|
||||
}else{
|
||||
int i;
|
||||
unsigned char i;
|
||||
const char *zType = (const char*)sqlite3_value_text(argv[2]);
|
||||
for(i=0; i<sizeof(azType)/sizeof(azType[0]); i++){
|
||||
if( sqlite3_stricmp(zType, azType[i])==0 ) break;
|
||||
@@ -303,7 +303,7 @@ static int carrayBestIndex(
|
||||
pIdxInfo->aConstraintUsage[cntIdx].argvIndex = 2;
|
||||
pIdxInfo->aConstraintUsage[cntIdx].omit = 1;
|
||||
pIdxInfo->estimatedCost = (double)1;
|
||||
pIdxInfo->estimatedRows = (double)100;
|
||||
pIdxInfo->estimatedRows = 100;
|
||||
pIdxInfo->idxNum = 2;
|
||||
if( ctypeIdx>=0 ){
|
||||
pIdxInfo->aConstraintUsage[ctypeIdx].argvIndex = 3;
|
||||
@@ -312,7 +312,7 @@ static int carrayBestIndex(
|
||||
}
|
||||
}else{
|
||||
pIdxInfo->estimatedCost = (double)2147483647;
|
||||
pIdxInfo->estimatedRows = (double)2147483647;
|
||||
pIdxInfo->estimatedRows = 2147483647;
|
||||
pIdxInfo->idxNum = 0;
|
||||
}
|
||||
return SQLITE_OK;
|
||||
@@ -358,11 +358,6 @@ int sqlite3_carray_init(
|
||||
int rc = SQLITE_OK;
|
||||
SQLITE_EXTENSION_INIT2(pApi);
|
||||
#ifndef SQLITE_OMIT_VIRTUALTABLE
|
||||
if( sqlite3_libversion_number()<3008012 ){
|
||||
*pzErrMsg = sqlite3_mprintf(
|
||||
"carray() requires SQLite 3.8.12 or later");
|
||||
return SQLITE_ERROR;
|
||||
}
|
||||
rc = sqlite3_create_module(db, "carray", &carrayModule, 0);
|
||||
#endif
|
||||
return rc;
|
||||
|
||||
+18
-10
@@ -48,6 +48,8 @@ SQLITE_EXTENSION_INIT1
|
||||
#include <ctype.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#ifndef SQLITE_OMIT_VIRTUALTABLE
|
||||
|
||||
/*
|
||||
** A macro to hint to the compiler that a function should not be
|
||||
** inlined.
|
||||
@@ -230,7 +232,7 @@ static char *csv_read_one_field(CsvReader *p){
|
||||
|| (c==EOF && pc=='"')
|
||||
){
|
||||
do{ p->n--; }while( p->z[p->n]!='"' );
|
||||
p->cTerm = c;
|
||||
p->cTerm = (char)c;
|
||||
break;
|
||||
}
|
||||
if( pc=='"' && c!='\r' ){
|
||||
@@ -240,7 +242,7 @@ static char *csv_read_one_field(CsvReader *p){
|
||||
if( c==EOF ){
|
||||
csv_errmsg(p, "line %d: unterminated %c-quoted field\n",
|
||||
startLine, '"');
|
||||
p->cTerm = c;
|
||||
p->cTerm = (char)c;
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -257,7 +259,7 @@ static char *csv_read_one_field(CsvReader *p){
|
||||
p->nLine++;
|
||||
if( p->n>0 && p->z[p->n-1]=='\r' ) p->n--;
|
||||
}
|
||||
p->cTerm = c;
|
||||
p->cTerm = (char)c;
|
||||
}
|
||||
if( p->z ) p->z[p->n] = 0;
|
||||
return p->z;
|
||||
@@ -336,9 +338,9 @@ static void csv_trim_whitespace(char *z){
|
||||
|
||||
/* Dequote the string */
|
||||
static void csv_dequote(char *z){
|
||||
int i, j;
|
||||
int j;
|
||||
char cQuote = z[0];
|
||||
size_t n;
|
||||
size_t i, n;
|
||||
|
||||
if( cQuote!='\'' && cQuote!='"' ) return;
|
||||
n = strlen(z);
|
||||
@@ -375,7 +377,7 @@ static int csv_string_parameter(
|
||||
char **pzVal /* Write the dequoted string value here */
|
||||
){
|
||||
const char *zValue;
|
||||
zValue = csv_parameter(zParam,strlen(zParam),zArg);
|
||||
zValue = csv_parameter(zParam,(int)strlen(zParam),zArg);
|
||||
if( zValue==0 ) return 0;
|
||||
p->zErr[0] = 0;
|
||||
if( *pzVal ){
|
||||
@@ -400,7 +402,7 @@ static int csv_boolean(const char *z){
|
||||
if( sqlite3_stricmp("yes",z)==0
|
||||
|| sqlite3_stricmp("on",z)==0
|
||||
|| sqlite3_stricmp("true",z)==0
|
||||
|| (z[0]=='1' && z[0]==0)
|
||||
|| (z[0]=='1' && z[1]==0)
|
||||
){
|
||||
return 1;
|
||||
}
|
||||
@@ -616,7 +618,7 @@ static int csvtabOpen(sqlite3_vtab *p, sqlite3_vtab_cursor **ppCursor){
|
||||
CsvCursor *pCur;
|
||||
size_t nByte;
|
||||
nByte = sizeof(*pCur) + (sizeof(char*)+sizeof(int))*pTab->nCol;
|
||||
pCur = sqlite3_malloc( nByte );
|
||||
pCur = sqlite3_malloc64( nByte );
|
||||
if( pCur==0 ) return SQLITE_NOMEM;
|
||||
memset(pCur, 0, nByte);
|
||||
pCur->azVal = (char**)&pCur[1];
|
||||
@@ -647,7 +649,7 @@ static int csvtabNext(sqlite3_vtab_cursor *cur){
|
||||
}
|
||||
if( i<pTab->nCol ){
|
||||
if( pCur->aLen[i] < pCur->rdr.n+1 ){
|
||||
char *zNew = sqlite3_realloc(pCur->azVal[i], pCur->rdr.n+1);
|
||||
char *zNew = sqlite3_realloc64(pCur->azVal[i], pCur->rdr.n+1);
|
||||
if( zNew==0 ){
|
||||
csv_errmsg(&pCur->rdr, "out of memory");
|
||||
csv_xfer_error(pTab, &pCur->rdr);
|
||||
@@ -723,7 +725,8 @@ static int csvtabFilter(
|
||||
pCur->iRowid = 0;
|
||||
if( pCur->rdr.in==0 ){
|
||||
assert( pCur->rdr.zIn==pTab->zData );
|
||||
assert( pTab->iStart<=pCur->rdr.nIn );
|
||||
assert( pTab->iStart>=0 );
|
||||
assert( (size_t)pTab->iStart<=pCur->rdr.nIn );
|
||||
pCur->rdr.iIn = pTab->iStart;
|
||||
}else{
|
||||
fseek(pCur->rdr.in, pTab->iStart, SEEK_SET);
|
||||
@@ -834,6 +837,7 @@ static sqlite3_module CsvModuleFauxWrite = {
|
||||
};
|
||||
#endif /* SQLITE_TEST */
|
||||
|
||||
#endif /* !defined(SQLITE_OMIT_VIRTUALTABLE) */
|
||||
|
||||
|
||||
#ifdef _WIN32
|
||||
@@ -849,6 +853,7 @@ int sqlite3_csv_init(
|
||||
char **pzErrMsg,
|
||||
const sqlite3_api_routines *pApi
|
||||
){
|
||||
#ifndef SQLITE_OMIT_VIRTUALTABLE
|
||||
int rc;
|
||||
SQLITE_EXTENSION_INIT2(pApi);
|
||||
rc = sqlite3_create_module(db, "csv", &CsvModule, 0);
|
||||
@@ -858,4 +863,7 @@ int sqlite3_csv_init(
|
||||
}
|
||||
#endif
|
||||
return rc;
|
||||
#else
|
||||
return SQLITE_OK;
|
||||
#endif
|
||||
}
|
||||
|
||||
+52
-14
@@ -49,13 +49,15 @@ SQLITE_EXTENSION_INIT1
|
||||
#ifdef sqlite3Isdigit
|
||||
/* Use the SQLite core versions if this routine is part of the
|
||||
** SQLite amalgamation */
|
||||
# define safe_isdigit(x) sqlite3Isdigit(x)
|
||||
# define safe_isalnum(x) sqlite3Isalnum(x)
|
||||
# define safe_isdigit(x) sqlite3Isdigit(x)
|
||||
# define safe_isalnum(x) sqlite3Isalnum(x)
|
||||
# define safe_isxdigit(x) sqlite3Isxdigit(x)
|
||||
#else
|
||||
/* Use the standard library for separate compilation */
|
||||
#include <ctype.h> /* amalgamator: keep */
|
||||
# define safe_isdigit(x) isdigit((unsigned char)(x))
|
||||
# define safe_isalnum(x) isalnum((unsigned char)(x))
|
||||
# define safe_isdigit(x) isdigit((unsigned char)(x))
|
||||
# define safe_isalnum(x) isalnum((unsigned char)(x))
|
||||
# define safe_isxdigit(x) isxdigit((unsigned char)(x))
|
||||
#endif
|
||||
|
||||
/*
|
||||
@@ -593,12 +595,13 @@ static void jsonReturn(
|
||||
c = z[++i];
|
||||
if( c=='u' ){
|
||||
u32 v = 0, k;
|
||||
for(k=0; k<4 && i<n-2; i++, k++){
|
||||
for(k=0; k<4; i++, k++){
|
||||
assert( i<n-2 );
|
||||
c = z[i+1];
|
||||
if( c>='0' && c<='9' ) v = v*16 + c - '0';
|
||||
else if( c>='A' && c<='F' ) v = v*16 + c - 'A' + 10;
|
||||
else if( c>='a' && c<='f' ) v = v*16 + c - 'a' + 10;
|
||||
else break;
|
||||
assert( safe_isxdigit(c) );
|
||||
if( c<='9' ) v = v*16 + c - '0';
|
||||
else if( c<='F' ) v = v*16 + c - 'A' + 10;
|
||||
else v = v*16 + c - 'a' + 10;
|
||||
}
|
||||
if( v==0 ) break;
|
||||
if( v<=0x7f ){
|
||||
@@ -702,6 +705,15 @@ static int jsonParseAddNode(
|
||||
return pParse->nNode++;
|
||||
}
|
||||
|
||||
/*
|
||||
** Return true if z[] begins with 4 (or more) hexadecimal digits
|
||||
*/
|
||||
static int jsonIs4Hex(const char *z){
|
||||
int i;
|
||||
for(i=0; i<4; i++) if( !safe_isxdigit(z[i]) ) return 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
** Parse a single JSON value which begins at pParse->zJson[i]. Return the
|
||||
** index of the first character past the end of the value parsed.
|
||||
@@ -776,8 +788,13 @@ static int jsonParseValue(JsonParse *pParse, u32 i){
|
||||
if( c==0 ) return -1;
|
||||
if( c=='\\' ){
|
||||
c = pParse->zJson[++j];
|
||||
if( c==0 ) return -1;
|
||||
jnFlags = JNODE_ESCAPE;
|
||||
if( c=='"' || c=='\\' || c=='/' || c=='b' || c=='f'
|
||||
|| c=='n' || c=='r' || c=='t'
|
||||
|| (c=='u' && jsonIs4Hex(pParse->zJson+j+1)) ){
|
||||
jnFlags = JNODE_ESCAPE;
|
||||
}else{
|
||||
return -1;
|
||||
}
|
||||
}else if( c=='"' ){
|
||||
break;
|
||||
}
|
||||
@@ -1211,6 +1228,26 @@ static void jsonTest1Func(
|
||||
** Scalar SQL function implementations
|
||||
****************************************************************************/
|
||||
|
||||
/*
|
||||
** Implementation of the json_QUOTE(VALUE) function. Return a JSON value
|
||||
** corresponding to the SQL value input. Mostly this means putting
|
||||
** double-quotes around strings and returning the unquoted string "null"
|
||||
** when given a NULL input.
|
||||
*/
|
||||
static void jsonQuoteFunc(
|
||||
sqlite3_context *ctx,
|
||||
int argc,
|
||||
sqlite3_value **argv
|
||||
){
|
||||
JsonString jx;
|
||||
UNUSED_PARAM(argc);
|
||||
|
||||
jsonInit(&jx, ctx);
|
||||
jsonAppendValue(&jx, argv[0]);
|
||||
jsonResult(&jx);
|
||||
sqlite3_result_subtype(ctx, JSON_SUBTYPE);
|
||||
}
|
||||
|
||||
/*
|
||||
** Implementation of the json_array(VALUE,...) function. Return a JSON
|
||||
** array that contains all values given in arguments. Or if any argument
|
||||
@@ -1625,7 +1662,7 @@ static void jsonObjectFinal(sqlite3_context *ctx){
|
||||
if( pStr ){
|
||||
jsonAppendChar(pStr, '}');
|
||||
if( pStr->bErr ){
|
||||
if( pStr->bErr==0 ) sqlite3_result_error_nomem(ctx);
|
||||
if( pStr->bErr==1 ) sqlite3_result_error_nomem(ctx);
|
||||
assert( pStr->bStatic );
|
||||
}else{
|
||||
sqlite3_result_text(ctx, pStr->zBuf, pStr->nUsed,
|
||||
@@ -1903,9 +1940,9 @@ static int jsonEachColumn(
|
||||
/* For json_each() path and root are the same so fall through
|
||||
** into the root case */
|
||||
}
|
||||
case JEACH_ROOT: {
|
||||
default: {
|
||||
const char *zRoot = p->zRoot;
|
||||
if( zRoot==0 ) zRoot = "$";
|
||||
if( zRoot==0 ) zRoot = "$";
|
||||
sqlite3_result_text(ctx, zRoot, -1, SQLITE_STATIC);
|
||||
break;
|
||||
}
|
||||
@@ -2124,6 +2161,7 @@ int sqlite3Json1Init(sqlite3 *db){
|
||||
{ "json_extract", -1, 0, jsonExtractFunc },
|
||||
{ "json_insert", -1, 0, jsonSetFunc },
|
||||
{ "json_object", -1, 0, jsonObjectFunc },
|
||||
{ "json_quote", 1, 0, jsonQuoteFunc },
|
||||
{ "json_remove", -1, 0, jsonRemoveFunc },
|
||||
{ "json_replace", -1, 0, jsonReplaceFunc },
|
||||
{ "json_set", -1, 1, jsonSetFunc },
|
||||
|
||||
@@ -0,0 +1,491 @@
|
||||
/*
|
||||
** 2016-09-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 is an in-memory read-only VFS implementation. The application
|
||||
** supplies a block of memory which is the database file, and this VFS
|
||||
** uses that block of memory.
|
||||
**
|
||||
** Because there is no place to store journals and no good way to lock
|
||||
** the "file", this VFS is read-only.
|
||||
**
|
||||
** USAGE:
|
||||
**
|
||||
** sqlite3_open_v2("file:/whatever?ptr=0xf05538&sz=14336", &db,
|
||||
** SQLITE_OPEN_READONLY | SQLITE_OPEN_URI,
|
||||
** "memvfs");
|
||||
**
|
||||
** The ptr= and sz= query parameters are required or the open will fail.
|
||||
** The ptr= parameter gives the memory address of the buffer holding the
|
||||
** read-only database and sz= gives the size of the database. The parameter
|
||||
** values may be in hexadecimal or decimal. The filename is ignored.
|
||||
*/
|
||||
#include <sqlite3ext.h>
|
||||
SQLITE_EXTENSION_INIT1
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
|
||||
|
||||
/*
|
||||
** Forward declaration of objects used by this utility
|
||||
*/
|
||||
typedef struct sqlite3_vfs MemVfs;
|
||||
typedef struct MemFile MemFile;
|
||||
|
||||
/* Access to a lower-level VFS that (might) implement dynamic loading,
|
||||
** access to randomness, etc.
|
||||
*/
|
||||
#define ORIGVFS(p) ((sqlite3_vfs*)((p)->pAppData))
|
||||
|
||||
/* An open file */
|
||||
struct MemFile {
|
||||
sqlite3_file base; /* IO methods */
|
||||
sqlite3_int64 sz; /* Size of the file */
|
||||
unsigned char *aData; /* content of the file */
|
||||
};
|
||||
|
||||
/*
|
||||
** Methods for MemFile
|
||||
*/
|
||||
static int memClose(sqlite3_file*);
|
||||
static int memRead(sqlite3_file*, void*, int iAmt, sqlite3_int64 iOfst);
|
||||
static int memWrite(sqlite3_file*,const void*,int iAmt, sqlite3_int64 iOfst);
|
||||
static int memTruncate(sqlite3_file*, sqlite3_int64 size);
|
||||
static int memSync(sqlite3_file*, int flags);
|
||||
static int memFileSize(sqlite3_file*, sqlite3_int64 *pSize);
|
||||
static int memLock(sqlite3_file*, int);
|
||||
static int memUnlock(sqlite3_file*, int);
|
||||
static int memCheckReservedLock(sqlite3_file*, int *pResOut);
|
||||
static int memFileControl(sqlite3_file*, int op, void *pArg);
|
||||
static int memSectorSize(sqlite3_file*);
|
||||
static int memDeviceCharacteristics(sqlite3_file*);
|
||||
static int memShmMap(sqlite3_file*, int iPg, int pgsz, int, void volatile**);
|
||||
static int memShmLock(sqlite3_file*, int offset, int n, int flags);
|
||||
static void memShmBarrier(sqlite3_file*);
|
||||
static int memShmUnmap(sqlite3_file*, int deleteFlag);
|
||||
static int memFetch(sqlite3_file*, sqlite3_int64 iOfst, int iAmt, void **pp);
|
||||
static int memUnfetch(sqlite3_file*, sqlite3_int64 iOfst, void *p);
|
||||
|
||||
/*
|
||||
** Methods for MemVfs
|
||||
*/
|
||||
static int memOpen(sqlite3_vfs*, const char *, sqlite3_file*, int , int *);
|
||||
static int memDelete(sqlite3_vfs*, const char *zName, int syncDir);
|
||||
static int memAccess(sqlite3_vfs*, const char *zName, int flags, int *);
|
||||
static int memFullPathname(sqlite3_vfs*, const char *zName, int, char *zOut);
|
||||
static void *memDlOpen(sqlite3_vfs*, const char *zFilename);
|
||||
static void memDlError(sqlite3_vfs*, int nByte, char *zErrMsg);
|
||||
static void (*memDlSym(sqlite3_vfs *pVfs, void *p, const char*zSym))(void);
|
||||
static void memDlClose(sqlite3_vfs*, void*);
|
||||
static int memRandomness(sqlite3_vfs*, int nByte, char *zOut);
|
||||
static int memSleep(sqlite3_vfs*, int microseconds);
|
||||
static int memCurrentTime(sqlite3_vfs*, double*);
|
||||
static int memGetLastError(sqlite3_vfs*, int, char *);
|
||||
static int memCurrentTimeInt64(sqlite3_vfs*, sqlite3_int64*);
|
||||
|
||||
static sqlite3_vfs mem_vfs = {
|
||||
2, /* iVersion */
|
||||
0, /* szOsFile (set when registered) */
|
||||
1024, /* mxPathname */
|
||||
0, /* pNext */
|
||||
"memvfs", /* zName */
|
||||
0, /* pAppData (set when registered) */
|
||||
memOpen, /* xOpen */
|
||||
memDelete, /* xDelete */
|
||||
memAccess, /* xAccess */
|
||||
memFullPathname, /* xFullPathname */
|
||||
memDlOpen, /* xDlOpen */
|
||||
memDlError, /* xDlError */
|
||||
memDlSym, /* xDlSym */
|
||||
memDlClose, /* xDlClose */
|
||||
memRandomness, /* xRandomness */
|
||||
memSleep, /* xSleep */
|
||||
memCurrentTime, /* xCurrentTime */
|
||||
memGetLastError, /* xGetLastError */
|
||||
memCurrentTimeInt64 /* xCurrentTimeInt64 */
|
||||
};
|
||||
|
||||
static const sqlite3_io_methods mem_io_methods = {
|
||||
3, /* iVersion */
|
||||
memClose, /* xClose */
|
||||
memRead, /* xRead */
|
||||
memWrite, /* xWrite */
|
||||
memTruncate, /* xTruncate */
|
||||
memSync, /* xSync */
|
||||
memFileSize, /* xFileSize */
|
||||
memLock, /* xLock */
|
||||
memUnlock, /* xUnlock */
|
||||
memCheckReservedLock, /* xCheckReservedLock */
|
||||
memFileControl, /* xFileControl */
|
||||
memSectorSize, /* xSectorSize */
|
||||
memDeviceCharacteristics, /* xDeviceCharacteristics */
|
||||
memShmMap, /* xShmMap */
|
||||
memShmLock, /* xShmLock */
|
||||
memShmBarrier, /* xShmBarrier */
|
||||
memShmUnmap, /* xShmUnmap */
|
||||
memFetch, /* xFetch */
|
||||
memUnfetch /* xUnfetch */
|
||||
};
|
||||
|
||||
|
||||
|
||||
/*
|
||||
** Close an mem-file.
|
||||
**
|
||||
** The pData pointer is owned by the application, so there is nothing
|
||||
** to free.
|
||||
*/
|
||||
static int memClose(sqlite3_file *pFile){
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Read data from an mem-file.
|
||||
*/
|
||||
static int memRead(
|
||||
sqlite3_file *pFile,
|
||||
void *zBuf,
|
||||
int iAmt,
|
||||
sqlite_int64 iOfst
|
||||
){
|
||||
MemFile *p = (MemFile *)pFile;
|
||||
memcpy(zBuf, p->aData+iOfst, iAmt);
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Write data to an mem-file.
|
||||
*/
|
||||
static int memWrite(
|
||||
sqlite3_file *pFile,
|
||||
const void *z,
|
||||
int iAmt,
|
||||
sqlite_int64 iOfst
|
||||
){
|
||||
return SQLITE_READONLY;
|
||||
}
|
||||
|
||||
/*
|
||||
** Truncate an mem-file.
|
||||
*/
|
||||
static int memTruncate(sqlite3_file *pFile, sqlite_int64 size){
|
||||
return SQLITE_READONLY;
|
||||
}
|
||||
|
||||
/*
|
||||
** Sync an mem-file.
|
||||
*/
|
||||
static int memSync(sqlite3_file *pFile, int flags){
|
||||
return SQLITE_READONLY;
|
||||
}
|
||||
|
||||
/*
|
||||
** Return the current file-size of an mem-file.
|
||||
*/
|
||||
static int memFileSize(sqlite3_file *pFile, sqlite_int64 *pSize){
|
||||
MemFile *p = (MemFile *)pFile;
|
||||
*pSize = p->sz;
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Lock an mem-file.
|
||||
*/
|
||||
static int memLock(sqlite3_file *pFile, int eLock){
|
||||
return SQLITE_READONLY;
|
||||
}
|
||||
|
||||
/*
|
||||
** Unlock an mem-file.
|
||||
*/
|
||||
static int memUnlock(sqlite3_file *pFile, int eLock){
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Check if another file-handle holds a RESERVED lock on an mem-file.
|
||||
*/
|
||||
static int memCheckReservedLock(sqlite3_file *pFile, int *pResOut){
|
||||
*pResOut = 0;
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** File control method. For custom operations on an mem-file.
|
||||
*/
|
||||
static int memFileControl(sqlite3_file *pFile, int op, void *pArg){
|
||||
MemFile *p = (MemFile *)pFile;
|
||||
int rc = SQLITE_NOTFOUND;
|
||||
if( op==SQLITE_FCNTL_VFSNAME ){
|
||||
*(char**)pArg = sqlite3_mprintf("mem(%p,%lld)", p->aData, p->sz);
|
||||
rc = SQLITE_OK;
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** Return the sector-size in bytes for an mem-file.
|
||||
*/
|
||||
static int memSectorSize(sqlite3_file *pFile){
|
||||
return 1024;
|
||||
}
|
||||
|
||||
/*
|
||||
** Return the device characteristic flags supported by an mem-file.
|
||||
*/
|
||||
static int memDeviceCharacteristics(sqlite3_file *pFile){
|
||||
return SQLITE_IOCAP_IMMUTABLE;
|
||||
}
|
||||
|
||||
/* Create a shared memory file mapping */
|
||||
static int memShmMap(
|
||||
sqlite3_file *pFile,
|
||||
int iPg,
|
||||
int pgsz,
|
||||
int bExtend,
|
||||
void volatile **pp
|
||||
){
|
||||
return SQLITE_READONLY;
|
||||
}
|
||||
|
||||
/* Perform locking on a shared-memory segment */
|
||||
static int memShmLock(sqlite3_file *pFile, int offset, int n, int flags){
|
||||
return SQLITE_READONLY;
|
||||
}
|
||||
|
||||
/* Memory barrier operation on shared memory */
|
||||
static void memShmBarrier(sqlite3_file *pFile){
|
||||
return;
|
||||
}
|
||||
|
||||
/* Unmap a shared memory segment */
|
||||
static int memShmUnmap(sqlite3_file *pFile, int deleteFlag){
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/* Fetch a page of a memory-mapped file */
|
||||
static int memFetch(
|
||||
sqlite3_file *pFile,
|
||||
sqlite3_int64 iOfst,
|
||||
int iAmt,
|
||||
void **pp
|
||||
){
|
||||
MemFile *p = (MemFile *)pFile;
|
||||
*pp = (void*)(p->aData + iOfst);
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/* Release a memory-mapped page */
|
||||
static int memUnfetch(sqlite3_file *pFile, sqlite3_int64 iOfst, void *pPage){
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Open an mem file handle.
|
||||
*/
|
||||
static int memOpen(
|
||||
sqlite3_vfs *pVfs,
|
||||
const char *zName,
|
||||
sqlite3_file *pFile,
|
||||
int flags,
|
||||
int *pOutFlags
|
||||
){
|
||||
MemFile *p = (MemFile*)pFile;
|
||||
memset(p, 0, sizeof(*p));
|
||||
if( (flags & SQLITE_OPEN_MAIN_DB)==0 ) return SQLITE_CANTOPEN;
|
||||
p->aData = (unsigned char*)sqlite3_uri_int64(zName,"ptr",0);
|
||||
if( p->aData==0 ) return SQLITE_CANTOPEN;
|
||||
p->sz = sqlite3_uri_int64(zName,"sz",0);
|
||||
if( p->sz<0 ) return SQLITE_CANTOPEN;
|
||||
pFile->pMethods = &mem_io_methods;
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** 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 memDelete(sqlite3_vfs *pVfs, const char *zPath, int dirSync){
|
||||
return SQLITE_READONLY;
|
||||
}
|
||||
|
||||
/*
|
||||
** Test for access permissions. Return true if the requested permission
|
||||
** is available, or false otherwise.
|
||||
*/
|
||||
static int memAccess(
|
||||
sqlite3_vfs *pVfs,
|
||||
const char *zPath,
|
||||
int flags,
|
||||
int *pResOut
|
||||
){
|
||||
/* The spec says there are three possible values for flags. But only
|
||||
** two of them are actually used */
|
||||
assert( flags==SQLITE_ACCESS_EXISTS || flags==SQLITE_ACCESS_READWRITE );
|
||||
if( flags==SQLITE_ACCESS_READWRITE ){
|
||||
*pResOut = 0;
|
||||
}else{
|
||||
*pResOut = 1;
|
||||
}
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** 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 memFullPathname(
|
||||
sqlite3_vfs *pVfs,
|
||||
const char *zPath,
|
||||
int nOut,
|
||||
char *zOut
|
||||
){
|
||||
sqlite3_snprintf(nOut, zOut, "%s", zPath);
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Open the dynamic library located at zPath and return a handle.
|
||||
*/
|
||||
static void *memDlOpen(sqlite3_vfs *pVfs, const char *zPath){
|
||||
return ORIGVFS(pVfs)->xDlOpen(ORIGVFS(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 memDlError(sqlite3_vfs *pVfs, int nByte, char *zErrMsg){
|
||||
ORIGVFS(pVfs)->xDlError(ORIGVFS(pVfs), nByte, zErrMsg);
|
||||
}
|
||||
|
||||
/*
|
||||
** Return a pointer to the symbol zSymbol in the dynamic library pHandle.
|
||||
*/
|
||||
static void (*memDlSym(sqlite3_vfs *pVfs, void *p, const char *zSym))(void){
|
||||
return ORIGVFS(pVfs)->xDlSym(ORIGVFS(pVfs), p, zSym);
|
||||
}
|
||||
|
||||
/*
|
||||
** Close the dynamic library handle pHandle.
|
||||
*/
|
||||
static void memDlClose(sqlite3_vfs *pVfs, void *pHandle){
|
||||
ORIGVFS(pVfs)->xDlClose(ORIGVFS(pVfs), pHandle);
|
||||
}
|
||||
|
||||
/*
|
||||
** Populate the buffer pointed to by zBufOut with nByte bytes of
|
||||
** random data.
|
||||
*/
|
||||
static int memRandomness(sqlite3_vfs *pVfs, int nByte, char *zBufOut){
|
||||
return ORIGVFS(pVfs)->xRandomness(ORIGVFS(pVfs), nByte, zBufOut);
|
||||
}
|
||||
|
||||
/*
|
||||
** Sleep for nMicro microseconds. Return the number of microseconds
|
||||
** actually slept.
|
||||
*/
|
||||
static int memSleep(sqlite3_vfs *pVfs, int nMicro){
|
||||
return ORIGVFS(pVfs)->xSleep(ORIGVFS(pVfs), nMicro);
|
||||
}
|
||||
|
||||
/*
|
||||
** Return the current time as a Julian Day number in *pTimeOut.
|
||||
*/
|
||||
static int memCurrentTime(sqlite3_vfs *pVfs, double *pTimeOut){
|
||||
return ORIGVFS(pVfs)->xCurrentTime(ORIGVFS(pVfs), pTimeOut);
|
||||
}
|
||||
|
||||
static int memGetLastError(sqlite3_vfs *pVfs, int a, char *b){
|
||||
return ORIGVFS(pVfs)->xGetLastError(ORIGVFS(pVfs), a, b);
|
||||
}
|
||||
static int memCurrentTimeInt64(sqlite3_vfs *pVfs, sqlite3_int64 *p){
|
||||
return ORIGVFS(pVfs)->xCurrentTimeInt64(ORIGVFS(pVfs), p);
|
||||
}
|
||||
|
||||
#ifdef MEMVFS_TEST
|
||||
/*
|
||||
** memload(FILENAME)
|
||||
**
|
||||
** This an SQL function used to help in testing the memvfs VFS. The
|
||||
** function reads the content of a file into memory and then returns
|
||||
** a string that gives the locate and size of the in-memory buffer.
|
||||
*/
|
||||
#include <stdio.h>
|
||||
static void memvfsMemloadFunc(
|
||||
sqlite3_context *context,
|
||||
int argc,
|
||||
sqlite3_value **argv
|
||||
){
|
||||
unsigned char *p;
|
||||
sqlite3_int64 sz;
|
||||
FILE *in;
|
||||
const char *zFilename = (const char*)sqlite3_value_text(argv[0]);
|
||||
char zReturn[100];
|
||||
|
||||
if( zFilename==0 ) return;
|
||||
in = fopen(zFilename, "rb");
|
||||
if( in==0 ) return;
|
||||
fseek(in, 0, SEEK_END);
|
||||
sz = ftell(in);
|
||||
rewind(in);
|
||||
p = sqlite3_malloc( sz );
|
||||
if( p==0 ){
|
||||
fclose(in);
|
||||
sqlite3_result_error_nomem(context);
|
||||
return;
|
||||
}
|
||||
fread(p, sz, 1, in);
|
||||
fclose(in);
|
||||
sqlite3_snprintf(sizeof(zReturn),zReturn,"ptr=%lld&sz=%lld",
|
||||
(sqlite3_int64)p, sz);
|
||||
sqlite3_result_text(context, zReturn, -1, SQLITE_TRANSIENT);
|
||||
}
|
||||
/* Called for each new database connection */
|
||||
static int memvfsRegister(
|
||||
sqlite3 *db,
|
||||
const char **pzErrMsg,
|
||||
const struct sqlite3_api_routines *pThunk
|
||||
){
|
||||
return sqlite3_create_function(db, "memload", 1, SQLITE_UTF8, 0,
|
||||
memvfsMemloadFunc, 0, 0);
|
||||
}
|
||||
#endif /* MEMVFS_TEST */
|
||||
|
||||
|
||||
#ifdef _WIN32
|
||||
__declspec(dllexport)
|
||||
#endif
|
||||
/*
|
||||
** This routine is called when the extension is loaded.
|
||||
** Register the new VFS.
|
||||
*/
|
||||
int sqlite3_memvfs_init(
|
||||
sqlite3 *db,
|
||||
char **pzErrMsg,
|
||||
const sqlite3_api_routines *pApi
|
||||
){
|
||||
int rc = SQLITE_OK;
|
||||
SQLITE_EXTENSION_INIT2(pApi);
|
||||
mem_vfs.pAppData = sqlite3_vfs_find(0);
|
||||
mem_vfs.szOsFile = sizeof(MemFile);
|
||||
rc = sqlite3_vfs_register(&mem_vfs, 1);
|
||||
#ifdef MEMVFS_TEST
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = sqlite3_auto_extension((void(*)(void))memvfsRegister);
|
||||
}
|
||||
#endif
|
||||
if( rc==SQLITE_OK ) rc = SQLITE_OK_LOAD_PERMANENTLY;
|
||||
return rc;
|
||||
}
|
||||
@@ -167,7 +167,7 @@ static void percentStep(sqlite3_context *pCtx, int argc, sqlite3_value **argv){
|
||||
/*
|
||||
** Compare to doubles for sorting using qsort()
|
||||
*/
|
||||
static int doubleCmp(const void *pA, const void *pB){
|
||||
static int SQLITE_CDECL doubleCmp(const void *pA, const void *pB){
|
||||
double a = *(double*)pA;
|
||||
double b = *(double*)pB;
|
||||
if( a==b ) return 0;
|
||||
|
||||
@@ -0,0 +1,68 @@
|
||||
/*
|
||||
** 2016-08-09
|
||||
**
|
||||
** The author disclaims copyright to this source code. In place of
|
||||
** a legal notice, here is a blessing:
|
||||
**
|
||||
** May you do good and not evil.
|
||||
** May you find forgiveness for yourself and forgive others.
|
||||
** May you share freely, never taking more than you give.
|
||||
**
|
||||
*************************************************************************
|
||||
**
|
||||
** This file demonstrates how to create an SQL function that is a pass-through
|
||||
** for integer values (it returns a copy of its argument) but also saves the
|
||||
** value that is passed through into a C-language variable. The address of
|
||||
** the C-language variable is supplied as the second argument.
|
||||
**
|
||||
** This allows, for example, a counter to incremented and the original
|
||||
** value retrieved, atomically, using a single statement:
|
||||
**
|
||||
** UPDATE counterTab SET cnt=remember(cnt,$PTR)+1 WHERE id=$ID
|
||||
**
|
||||
** Prepare the above statement once. Then to use it, bind the address
|
||||
** of the output variable to $PTR and the id of the counter to $ID and
|
||||
** run the prepared statement.
|
||||
**
|
||||
** One can imagine doing similar things with floating-point values and
|
||||
** strings, but this demonstration extension will stick to using just
|
||||
** integers.
|
||||
*/
|
||||
#include "sqlite3ext.h"
|
||||
SQLITE_EXTENSION_INIT1
|
||||
#include <assert.h>
|
||||
|
||||
/*
|
||||
** remember(V,PTR)
|
||||
**
|
||||
** Return the integer value V. Also save the value of V in a
|
||||
** C-language variable whose address is PTR.
|
||||
*/
|
||||
static void rememberFunc(
|
||||
sqlite3_context *pCtx,
|
||||
int argc,
|
||||
sqlite3_value **argv
|
||||
){
|
||||
sqlite3_int64 v;
|
||||
sqlite3_int64 ptr;
|
||||
assert( argc==2 );
|
||||
v = sqlite3_value_int64(argv[0]);
|
||||
ptr = sqlite3_value_int64(argv[1]);
|
||||
*((sqlite3_int64*)ptr) = v;
|
||||
sqlite3_result_int64(pCtx, v);
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
__declspec(dllexport)
|
||||
#endif
|
||||
int sqlite3_remember_init(
|
||||
sqlite3 *db,
|
||||
char **pzErrMsg,
|
||||
const sqlite3_api_routines *pApi
|
||||
){
|
||||
int rc = SQLITE_OK;
|
||||
SQLITE_EXTENSION_INIT2(pApi);
|
||||
rc = sqlite3_create_function(db, "remember", 2, SQLITE_UTF8, 0,
|
||||
rememberFunc, 0, 0);
|
||||
return rc;
|
||||
}
|
||||
@@ -74,6 +74,7 @@ struct ScrubState {
|
||||
u32 szPage; /* Page size */
|
||||
u32 szUsable; /* Usable bytes on each page */
|
||||
u32 nPage; /* Number of pages */
|
||||
u32 iLastPage; /* Page number of last page written so far*/
|
||||
u8 *page1; /* Content of page 1 */
|
||||
};
|
||||
|
||||
@@ -130,6 +131,7 @@ static void scrubBackupWrite(ScrubState *p, int pgno, const u8 *pData){
|
||||
scrubBackupErr(p, "write failed for page %d", pgno);
|
||||
p->rcErr = SQLITE_IOERR;
|
||||
}
|
||||
if( pgno>p->iLastPage ) p->iLastPage = pgno;
|
||||
}
|
||||
|
||||
/* Prepare a statement against the "db" database. */
|
||||
@@ -541,6 +543,20 @@ int sqlite3_scrub_backup(
|
||||
}
|
||||
sqlite3_finalize(pStmt);
|
||||
|
||||
/* If the last page of the input db file is a free-list leaf, then the
|
||||
** backup file on disk is still smaller than the size indicated within
|
||||
** the database header. In this case, write a page of zeroes to the
|
||||
** last page of the backup database so that SQLite does not mistakenly
|
||||
** think the db is corrupt. */
|
||||
if( s.iLastPage<s.nPage ){
|
||||
u8 *aZero = scrubBackupAllocPage(&s);
|
||||
if( aZero ){
|
||||
memset(aZero, 0, s.szPage);
|
||||
scrubBackupWrite(&s, s.nPage, aZero);
|
||||
sqlite3_free(aZero);
|
||||
}
|
||||
}
|
||||
|
||||
scrub_abort:
|
||||
/* Close the destination database without closing the transaction. If we
|
||||
** commit, page zero will be overwritten. */
|
||||
|
||||
+1
-1
@@ -2231,7 +2231,7 @@ static int spellfix1Score(int iDistance, int iRank){
|
||||
** Compare two spellfix1_row objects for sorting purposes in qsort() such
|
||||
** that they sort in order of increasing distance.
|
||||
*/
|
||||
static int spellfix1RowCompare(const void *A, const void *B){
|
||||
static int SQLITE_CDECL spellfix1RowCompare(const void *A, const void *B){
|
||||
const struct spellfix1_row *a = (const struct spellfix1_row*)A;
|
||||
const struct spellfix1_row *b = (const struct spellfix1_row*)B;
|
||||
return a->iScore - b->iScore;
|
||||
|
||||
+1
-1
@@ -809,7 +809,7 @@ int sqlite3_vfsstat_init(
|
||||
vstat_vfs.base.szOsFile = sizeof(VStatFile) + vstat_vfs.pVfs->szOsFile;
|
||||
rc = sqlite3_vfs_register(&vstat_vfs.base, 1);
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = sqlite3_auto_extension((void(*)(void))vstatRegister);
|
||||
rc = sqlite3_auto_extension(vstatRegister);
|
||||
}
|
||||
if( rc==SQLITE_OK ) rc = SQLITE_OK_LOAD_PERMANENTLY;
|
||||
return rc;
|
||||
|
||||
+18
-26
@@ -10,10 +10,7 @@
|
||||
#***********************************************************************
|
||||
#
|
||||
|
||||
if {![info exists testdir]} {
|
||||
set testdir [file join [file dirname [info script]] .. .. test]
|
||||
}
|
||||
source $testdir/tester.tcl
|
||||
source [file join [file dirname [info script]] rbu_common.tcl]
|
||||
set ::testprefix rbu1
|
||||
|
||||
db close
|
||||
@@ -96,26 +93,6 @@ proc create_rbu5 {filename} {
|
||||
return $filename
|
||||
}
|
||||
|
||||
# Run the RBU in file $rbu on target database $target until completion.
|
||||
#
|
||||
proc run_rbu {target rbu} {
|
||||
sqlite3rbu rbu $target $rbu
|
||||
while 1 {
|
||||
set rc [rbu step]
|
||||
if {$rc!="SQLITE_OK"} break
|
||||
}
|
||||
rbu close
|
||||
}
|
||||
|
||||
proc step_rbu {target rbu} {
|
||||
while 1 {
|
||||
sqlite3rbu rbu $target $rbu
|
||||
set rc [rbu step]
|
||||
rbu close
|
||||
if {$rc != "SQLITE_OK"} break
|
||||
}
|
||||
set rc
|
||||
}
|
||||
|
||||
# Same as [step_rbu], except using a URI to open the target db.
|
||||
#
|
||||
@@ -641,10 +618,25 @@ foreach {tn3 create_vfs destroy_vfs} {
|
||||
# correctly.
|
||||
reset_db
|
||||
forcedelete rbu.db
|
||||
do_test $tn3.8 {
|
||||
do_test $tn3.8.1 {
|
||||
list [catch { run_rbu test.db rbu.db } msg] $msg
|
||||
} {0 SQLITE_DONE}
|
||||
|
||||
|
||||
# Test that an RBU database containing only empty data_xxx tables is
|
||||
# also handled correctly.
|
||||
reset_db
|
||||
forcedelete rbu.db
|
||||
do_execsql_test $tn3.8.2.1 {
|
||||
CREATE TABLE t1(a PRIMARY KEY, b);
|
||||
INSERT INTO t1 VALUES(1, 2);
|
||||
ATTACH 'rbu.db' AS rbu;
|
||||
CREATE TABLE data_t1(a, b, rbu_control);
|
||||
DETACH rbu;
|
||||
}
|
||||
do_test $tn3.8.2.1 {
|
||||
list [catch { run_rbu test.db rbu.db } msg] $msg
|
||||
} {0 SQLITE_DONE}
|
||||
|
||||
# Test that RBU can update indexes containing NULL values.
|
||||
#
|
||||
reset_db
|
||||
|
||||
+1
-26
@@ -12,35 +12,10 @@
|
||||
# Test some properties of the pager_rbu_mode and rbu_mode pragmas.
|
||||
#
|
||||
|
||||
if {![info exists testdir]} {
|
||||
set testdir [file join [file dirname [info script]] .. .. test]
|
||||
}
|
||||
source $testdir/tester.tcl
|
||||
source [file join [file dirname [info script]] rbu_common.tcl]
|
||||
set ::testprefix rbu5
|
||||
|
||||
|
||||
# Run the RBU in file $rbu on target database $target until completion.
|
||||
#
|
||||
proc run_rbu {target rbu} {
|
||||
sqlite3rbu rbu $target $rbu
|
||||
while { [rbu step]=="SQLITE_OK" } {}
|
||||
rbu close
|
||||
}
|
||||
|
||||
|
||||
# Run the RBU in file $rbu on target database $target one step at a
|
||||
# time until completion.
|
||||
#
|
||||
proc step_rbu {target rbu} {
|
||||
while 1 {
|
||||
sqlite3rbu rbu $target $rbu
|
||||
set rc [rbu step]
|
||||
rbu close
|
||||
if {$rc != "SQLITE_OK"} break
|
||||
}
|
||||
set rc
|
||||
}
|
||||
|
||||
# Return a list of the primary key columns for table $tbl in the database
|
||||
# opened by database handle $db.
|
||||
#
|
||||
|
||||
@@ -15,12 +15,43 @@ if {![info exists testdir]} {
|
||||
}
|
||||
source $testdir/tester.tcl
|
||||
|
||||
proc check_prestep_state {target state} {
|
||||
set oal_exists [file exists $target-oal]
|
||||
set wal_exists [file exists $target-wal]
|
||||
set progress [rbu progress]
|
||||
|
||||
if {($progress==0 && $state!="oal" && $state!="done")
|
||||
|| ($oal_exists && $wal_exists)
|
||||
|| ($progress>0 && $state=="oal" && (!$oal_exists || $wal_exists))
|
||||
|| ($state=="move" && (!$oal_exists || $wal_exists))
|
||||
|| ($state=="checkpoint" && ($oal_exists || !$wal_exists))
|
||||
|| ($state=="done" && ($oal_exists && $progress!=0))
|
||||
} {
|
||||
error "B: state=$state progress=$progress oal=$oal_exists wal=$wal_exists"
|
||||
}
|
||||
}
|
||||
|
||||
proc check_poststep_state {rc target state} {
|
||||
if {$rc=="SQLITE_OK" || $rc=="SQLITE_DONE"} {
|
||||
set oal_exists [file exists $target-oal]
|
||||
set wal_exists [file exists $target-wal]
|
||||
if {$state=="move" && ($oal_exists || !$wal_exists)} {
|
||||
error "A: state=$state progress=$progress oal=$oal_exists wal=$wal_exists"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# Run the RBU in file $rbu on target database $target until completion.
|
||||
#
|
||||
proc run_rbu {target rbu} {
|
||||
sqlite3rbu rbu $target $rbu
|
||||
while 1 {
|
||||
set state [rbu state]
|
||||
|
||||
check_prestep_state $target $state
|
||||
set rc [rbu step]
|
||||
check_poststep_state $rc $target $state
|
||||
|
||||
if {$rc!="SQLITE_OK"} break
|
||||
}
|
||||
rbu close
|
||||
@@ -29,7 +60,10 @@ proc run_rbu {target rbu} {
|
||||
proc step_rbu {target rbu} {
|
||||
while 1 {
|
||||
sqlite3rbu rbu $target $rbu
|
||||
set state [rbu state]
|
||||
check_prestep_state $target $state
|
||||
set rc [rbu step]
|
||||
check_poststep_state $rc $target $state
|
||||
rbu close
|
||||
if {$rc != "SQLITE_OK"} break
|
||||
}
|
||||
@@ -41,7 +75,10 @@ proc do_rbu_vacuum_test {tn step} {
|
||||
if {$step==0} { sqlite3rbu_vacuum rbu test.db state.db }
|
||||
while 1 {
|
||||
if {$step==1} { sqlite3rbu_vacuum rbu test.db state.db }
|
||||
set state [rbu state]
|
||||
check_prestep_state test.db $state
|
||||
set rc [rbu step]
|
||||
check_poststep_state $rc test.db $state
|
||||
if {$rc!="SQLITE_OK"} break
|
||||
if {$step==1} { rbu close }
|
||||
}
|
||||
|
||||
+78
-1
@@ -140,6 +140,24 @@ foreach {tn init mod} {
|
||||
);
|
||||
}
|
||||
|
||||
4 {
|
||||
CREATE TABLE x1(a, b, c, PRIMARY KEY(a, b, c));
|
||||
INSERT INTO x1 VALUES('u', 'v', NULL);
|
||||
INSERT INTO x1 VALUES('x', 'y', 'z');
|
||||
INSERT INTO x1 VALUES('a', NULL, 'b');
|
||||
} {
|
||||
INSERT INTO x1 VALUES('a', 'b', 'c');
|
||||
}
|
||||
|
||||
5 {
|
||||
CREATE TABLE t1(a PRIMARY KEY, b);
|
||||
INSERT INTO t1 VALUES(1, NULL);
|
||||
INSERT INTO t1 VALUES(2, X'');
|
||||
} {
|
||||
UPDATE t1 SET b = X'' WHERE a=1;
|
||||
UPDATE t1 SET b = NULL WHERE a=2;
|
||||
}
|
||||
|
||||
} {
|
||||
catch { db close }
|
||||
|
||||
@@ -203,7 +221,6 @@ foreach {tn init mod} {
|
||||
DELETE FROM 'x''y' WHERE rowid = 1;
|
||||
INSERT INTO 'x''y' VALUES('one two three');
|
||||
}
|
||||
|
||||
} {
|
||||
|
||||
forcedelete test.db test.db2
|
||||
@@ -222,5 +239,65 @@ foreach {tn init mod} {
|
||||
|
||||
}
|
||||
|
||||
ifcapable fts5 {
|
||||
foreach {tn init mod} {
|
||||
1 {
|
||||
CREATE VIRTUAL TABLE t1 USING fts5(c);
|
||||
INSERT INTO t1 VALUES('a b c');
|
||||
INSERT INTO t1 VALUES('a b c');
|
||||
} {
|
||||
DELETE FROM t1 WHERE rowid = 1;
|
||||
INSERT INTO t1 VALUES('a b c');
|
||||
}
|
||||
|
||||
2 {
|
||||
CREATE VIRTUAL TABLE t1 USING FTs5(c);
|
||||
INSERT INTO t1 VALUES('a b c');
|
||||
INSERT INTO t1 VALUES('a b c');
|
||||
} {
|
||||
DELETE FROM t1 WHERE rowid = 1;
|
||||
INSERT INTO t1 VALUES('a b c');
|
||||
}
|
||||
|
||||
3 {
|
||||
creAte virTUal
|
||||
tablE t1 USING FTs5(c);
|
||||
INSERT INTO t1 VALUES('a b c');
|
||||
INSERT INTO t1 VALUES('a b c');
|
||||
} {
|
||||
DELETE FROM t1 WHERE rowid = 1;
|
||||
INSERT INTO t1 VALUES('a b c');
|
||||
}
|
||||
|
||||
} {
|
||||
forcedelete test.db test.db2
|
||||
sqlite3 db test.db
|
||||
db eval "$init"
|
||||
sqlite3 db test.db2
|
||||
db eval "$init ; $mod"
|
||||
db eval { INSERT INTO t1(t1) VALUES('optimize') }
|
||||
db close
|
||||
|
||||
do_test 3.$tn.1 {
|
||||
set sql [get_vtab_rbudiff_sql test.db test.db2]
|
||||
apply_rbudiff $sql test.db
|
||||
} {SQLITE_DONE}
|
||||
|
||||
sqlite3 db test.db
|
||||
sqlite3 db2 test.db2
|
||||
do_test 3.$tn.2 {
|
||||
db2 eval { SELECT * FROM t1 ORDER BY rowid }
|
||||
} [db eval { SELECT * FROM t1 ORDER BY rowid }]
|
||||
|
||||
do_test 3.$tn.3 {
|
||||
db2 eval { INSERT INTO t1(t1) VALUES('integrity-check') }
|
||||
} {}
|
||||
|
||||
db close
|
||||
db2 close
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
finish_test
|
||||
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
# 2016 October 21
|
||||
#
|
||||
# The author disclaims copyright to this source code. In place of
|
||||
# a legal notice, here is a blessing:
|
||||
#
|
||||
# May you do good and not evil.
|
||||
# May you find forgiveness for yourself and forgive others.
|
||||
# May you share freely, never taking more than you give.
|
||||
#
|
||||
#***********************************************************************
|
||||
#
|
||||
# This test file focuses on interactions between RBU and the feature
|
||||
# enabled by SQLITE_DIRECT_OVERFLOW_READ - Direct Overflow Read.
|
||||
#
|
||||
|
||||
if {![info exists testdir]} {
|
||||
set testdir [file join [file dirname [info script]] .. .. test]
|
||||
}
|
||||
source $testdir/tester.tcl
|
||||
set ::testprefix rbudor
|
||||
|
||||
set bigA [string repeat a 5000]
|
||||
set bigB [string repeat b 5000]
|
||||
do_execsql_test 1.0 {
|
||||
PRAGMA page_size = 1024;
|
||||
CREATE TABLE t1(a INTEGER PRIMARY KEY, b BLOB);
|
||||
INSERT INTO t1 VALUES(1, $bigA);
|
||||
} {}
|
||||
|
||||
do_test 1.1 {
|
||||
forcedelete rbu.db
|
||||
sqlite3 rbu rbu.db
|
||||
rbu eval {
|
||||
CREATE TABLE data_t1(a, b, rbu_control);
|
||||
INSERT INTO data_t1 VALUES(2, $bigB, 0);
|
||||
}
|
||||
rbu close
|
||||
} {}
|
||||
|
||||
do_test 1.2 {
|
||||
sqlite3rbu rbu test.db rbu.db
|
||||
while {[rbu state]!="checkpoint"} {
|
||||
rbu step
|
||||
}
|
||||
rbu step
|
||||
db eval { SELECT * FROM t1 }
|
||||
} [list 1 $bigA 2 $bigB]
|
||||
|
||||
do_test 1.3 {
|
||||
while {[rbu step]=="SQLITE_OK"} {}
|
||||
rbu close
|
||||
} {SQLITE_DONE}
|
||||
|
||||
do_execsql_test 1.4 {
|
||||
SELECT * FROM t1
|
||||
} [list 1 $bigA 2 $bigB]
|
||||
|
||||
finish_test
|
||||
|
||||
@@ -361,6 +361,8 @@ foreach {bReopen} { 0 1 } {
|
||||
}
|
||||
} {
|
||||
|
||||
if {$tn=="vtab"} { ifcapable !fts5 break }
|
||||
|
||||
foreach {tn2 rbusql r1 r2} {
|
||||
1 {
|
||||
CREATE TABLE data0_t1(a, b, c, rbu_rowid, rbu_control);
|
||||
|
||||
@@ -156,7 +156,49 @@ foreach step {0 1} {
|
||||
trigger tr1 t1 0 {CREATE TRIGGER tr1 AFTER INSERT ON t1 BEGIN SELECT 1; END}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# Test that passing a NULL value as the second argument to
|
||||
# sqlite3rbu_vacuum() causes it to:
|
||||
#
|
||||
# * Use <database>-vacuum as the state db, and
|
||||
# * Set the state db permissions to the same as those on the db file.
|
||||
#
|
||||
db close
|
||||
if {$::tcl_platform(platform)=="unix"} {
|
||||
forcedelete test.db
|
||||
|
||||
sqlite3 db test.db
|
||||
do_execsql_test 5.0 {
|
||||
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);
|
||||
}
|
||||
db close
|
||||
|
||||
foreach {tn perm} {
|
||||
1 00755
|
||||
2 00666
|
||||
3 00644
|
||||
4 00444
|
||||
} {
|
||||
forcedelete test.db-vacuum
|
||||
|
||||
do_test 5.$tn.1 {
|
||||
file attributes test.db -permissions $perm
|
||||
sqlite3rbu_vacuum rbu test.db
|
||||
rbu step
|
||||
} {SQLITE_OK}
|
||||
|
||||
do_test 5.$tn.2 { file exists test.db-vacuum } 1
|
||||
do_test 5.$tn.3 { file attributes test.db-vacuum -permissions} $perm
|
||||
rbu close
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
finish_test
|
||||
|
||||
|
||||
+83
-36
@@ -2334,15 +2334,18 @@ static RbuState *rbuLoadState(sqlite3rbu *p){
|
||||
** error occurs, leave an error code and message in the RBU handle.
|
||||
*/
|
||||
static void rbuOpenDatabase(sqlite3rbu *p){
|
||||
assert( p->rc==SQLITE_OK );
|
||||
assert( p->dbMain==0 && p->dbRbu==0 );
|
||||
assert( rbuIsVacuum(p) || p->zTarget!=0 );
|
||||
assert( p->rc || (p->dbMain==0 && p->dbRbu==0) );
|
||||
assert( p->rc || rbuIsVacuum(p) || p->zTarget!=0 );
|
||||
|
||||
/* Open the RBU database */
|
||||
p->dbRbu = rbuOpenDbhandle(p, p->zRbu, 1);
|
||||
|
||||
if( p->rc==SQLITE_OK && rbuIsVacuum(p) ){
|
||||
sqlite3_file_control(p->dbRbu, "main", SQLITE_FCNTL_RBUCNT, (void*)p);
|
||||
if( p->zState==0 ){
|
||||
const char *zFile = sqlite3_db_filename(p->dbRbu, "main");
|
||||
p->zState = rbuMPrintf(p, "file://%s-vacuum?modeof=%s", zFile, zFile);
|
||||
}
|
||||
}
|
||||
|
||||
/* If using separate RBU and state databases, attach the state database to
|
||||
@@ -3477,8 +3480,7 @@ static sqlite3rbu *openRbuHandle(
|
||||
sqlite3rbu *p;
|
||||
size_t nTarget = zTarget ? strlen(zTarget) : 0;
|
||||
size_t nRbu = strlen(zRbu);
|
||||
size_t nState = zState ? strlen(zState) : 0;
|
||||
size_t nByte = sizeof(sqlite3rbu) + nTarget+1 + nRbu+1+ nState+1;
|
||||
size_t nByte = sizeof(sqlite3rbu) + nTarget+1 + nRbu+1;
|
||||
|
||||
p = (sqlite3rbu*)sqlite3_malloc64(nByte);
|
||||
if( p ){
|
||||
@@ -3500,8 +3502,7 @@ static sqlite3rbu *openRbuHandle(
|
||||
memcpy(p->zRbu, zRbu, nRbu+1);
|
||||
pCsr += nRbu+1;
|
||||
if( zState ){
|
||||
p->zState = pCsr;
|
||||
memcpy(p->zState, zState, nState+1);
|
||||
p->zState = rbuMPrintf(p, "%s", zState);
|
||||
}
|
||||
rbuOpenDatabase(p);
|
||||
}
|
||||
@@ -3555,30 +3556,7 @@ static sqlite3rbu *openRbuHandle(
|
||||
if( p->rc==SQLITE_OK ){
|
||||
if( p->eStage==RBU_STAGE_OAL ){
|
||||
sqlite3 *db = p->dbMain;
|
||||
|
||||
if( pState->eStage==0 && rbuIsVacuum(p) ){
|
||||
rbuCopyPragma(p, "page_size");
|
||||
rbuCopyPragma(p, "auto_vacuum");
|
||||
}
|
||||
|
||||
/* Open transactions both databases. The *-oal file is opened or
|
||||
** created at this point. */
|
||||
if( p->rc==SQLITE_OK ){
|
||||
p->rc = sqlite3_exec(db, "BEGIN IMMEDIATE", 0, 0, &p->zErrmsg);
|
||||
}
|
||||
if( p->rc==SQLITE_OK ){
|
||||
p->rc = sqlite3_exec(p->dbRbu, "BEGIN", 0, 0, &p->zErrmsg);
|
||||
}
|
||||
|
||||
/* Check if the main database is a zipvfs db. If it is, set the upper
|
||||
** level pager to use "journal_mode=off". This prevents it from
|
||||
** generating a large journal using a temp file. */
|
||||
if( p->rc==SQLITE_OK ){
|
||||
int frc = sqlite3_file_control(db, "main", SQLITE_FCNTL_ZIPVFS, 0);
|
||||
if( frc==SQLITE_OK ){
|
||||
p->rc = sqlite3_exec(db, "PRAGMA journal_mode=off",0,0,&p->zErrmsg);
|
||||
}
|
||||
}
|
||||
p->rc = sqlite3_exec(p->dbRbu, "BEGIN", 0, 0, &p->zErrmsg);
|
||||
|
||||
/* Point the object iterator at the first object */
|
||||
if( p->rc==SQLITE_OK ){
|
||||
@@ -3589,12 +3567,34 @@ static sqlite3rbu *openRbuHandle(
|
||||
** update finished. */
|
||||
if( p->rc==SQLITE_OK && p->objiter.zTbl==0 ){
|
||||
p->rc = SQLITE_DONE;
|
||||
}
|
||||
p->eStage = RBU_STAGE_DONE;
|
||||
}else{
|
||||
if( p->rc==SQLITE_OK && pState->eStage==0 && rbuIsVacuum(p) ){
|
||||
rbuCopyPragma(p, "page_size");
|
||||
rbuCopyPragma(p, "auto_vacuum");
|
||||
}
|
||||
|
||||
if( p->rc==SQLITE_OK ){
|
||||
rbuSetupOal(p, pState);
|
||||
}
|
||||
/* Open transactions both databases. The *-oal file is opened or
|
||||
** created at this point. */
|
||||
if( p->rc==SQLITE_OK ){
|
||||
p->rc = sqlite3_exec(db, "BEGIN IMMEDIATE", 0, 0, &p->zErrmsg);
|
||||
}
|
||||
|
||||
/* Check if the main database is a zipvfs db. If it is, set the upper
|
||||
** level pager to use "journal_mode=off". This prevents it from
|
||||
** generating a large journal using a temp file. */
|
||||
if( p->rc==SQLITE_OK ){
|
||||
int frc = sqlite3_file_control(db, "main", SQLITE_FCNTL_ZIPVFS, 0);
|
||||
if( frc==SQLITE_OK ){
|
||||
p->rc = sqlite3_exec(
|
||||
db, "PRAGMA journal_mode=off",0,0,&p->zErrmsg);
|
||||
}
|
||||
}
|
||||
|
||||
if( p->rc==SQLITE_OK ){
|
||||
rbuSetupOal(p, pState);
|
||||
}
|
||||
}
|
||||
}else if( p->eStage==RBU_STAGE_MOVE ){
|
||||
/* no-op */
|
||||
}else if( p->eStage==RBU_STAGE_CKPT ){
|
||||
@@ -3612,6 +3612,20 @@ static sqlite3rbu *openRbuHandle(
|
||||
return p;
|
||||
}
|
||||
|
||||
/*
|
||||
** Allocate and return an RBU handle with all fields zeroed except for the
|
||||
** error code, which is set to SQLITE_MISUSE.
|
||||
*/
|
||||
static sqlite3rbu *rbuMisuseError(void){
|
||||
sqlite3rbu *pRet;
|
||||
pRet = sqlite3_malloc64(sizeof(sqlite3rbu));
|
||||
if( pRet ){
|
||||
memset(pRet, 0, sizeof(sqlite3rbu));
|
||||
pRet->rc = SQLITE_MISUSE;
|
||||
}
|
||||
return pRet;
|
||||
}
|
||||
|
||||
/*
|
||||
** Open and return a new RBU handle.
|
||||
*/
|
||||
@@ -3620,6 +3634,7 @@ sqlite3rbu *sqlite3rbu_open(
|
||||
const char *zRbu,
|
||||
const char *zState
|
||||
){
|
||||
if( zTarget==0 || zRbu==0 ){ return rbuMisuseError(); }
|
||||
/* TODO: Check that zTarget and zRbu are non-NULL */
|
||||
return openRbuHandle(zTarget, zRbu, zState);
|
||||
}
|
||||
@@ -3631,6 +3646,7 @@ sqlite3rbu *sqlite3rbu_vacuum(
|
||||
const char *zTarget,
|
||||
const char *zState
|
||||
){
|
||||
if( zTarget==0 ){ return rbuMisuseError(); }
|
||||
/* TODO: Check that both arguments are non-NULL */
|
||||
return openRbuHandle(0, zTarget, zState);
|
||||
}
|
||||
@@ -3708,6 +3724,7 @@ int sqlite3rbu_close(sqlite3rbu *p, char **pzErrmsg){
|
||||
rbuEditErrmsg(p);
|
||||
rc = p->rc;
|
||||
*pzErrmsg = p->zErrmsg;
|
||||
sqlite3_free(p->zState);
|
||||
sqlite3_free(p);
|
||||
}else{
|
||||
rc = SQLITE_NOMEM;
|
||||
@@ -3761,9 +3778,39 @@ void sqlite3rbu_bp_progress(sqlite3rbu *p, int *pnOne, int *pnTwo){
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** Return the current state of the RBU vacuum or update operation.
|
||||
*/
|
||||
int sqlite3rbu_state(sqlite3rbu *p){
|
||||
int aRes[] = {
|
||||
0, SQLITE_RBU_STATE_OAL, SQLITE_RBU_STATE_MOVE,
|
||||
0, SQLITE_RBU_STATE_CHECKPOINT, SQLITE_RBU_STATE_DONE
|
||||
};
|
||||
|
||||
assert( RBU_STAGE_OAL==1 );
|
||||
assert( RBU_STAGE_MOVE==2 );
|
||||
assert( RBU_STAGE_CKPT==4 );
|
||||
assert( RBU_STAGE_DONE==5 );
|
||||
assert( aRes[RBU_STAGE_OAL]==SQLITE_RBU_STATE_OAL );
|
||||
assert( aRes[RBU_STAGE_MOVE]==SQLITE_RBU_STATE_MOVE );
|
||||
assert( aRes[RBU_STAGE_CKPT]==SQLITE_RBU_STATE_CHECKPOINT );
|
||||
assert( aRes[RBU_STAGE_DONE]==SQLITE_RBU_STATE_DONE );
|
||||
|
||||
if( p->rc!=SQLITE_OK && p->rc!=SQLITE_DONE ){
|
||||
return SQLITE_RBU_STATE_ERROR;
|
||||
}else{
|
||||
assert( p->rc!=SQLITE_DONE || p->eStage==RBU_STAGE_DONE );
|
||||
assert( p->eStage==RBU_STAGE_OAL
|
||||
|| p->eStage==RBU_STAGE_MOVE
|
||||
|| p->eStage==RBU_STAGE_CKPT
|
||||
|| p->eStage==RBU_STAGE_DONE
|
||||
);
|
||||
return aRes[p->eStage];
|
||||
}
|
||||
}
|
||||
|
||||
int sqlite3rbu_savestate(sqlite3rbu *p){
|
||||
int rc = p->rc;
|
||||
|
||||
if( rc==SQLITE_DONE ) return SQLITE_OK;
|
||||
|
||||
assert( p->eStage>=RBU_STAGE_OAL && p->eStage<=RBU_STAGE_DONE );
|
||||
|
||||
+50
-6
@@ -104,7 +104,7 @@
|
||||
** may also be named data<integer>_<target>, where <integer> is any sequence
|
||||
** of zero or more numeric characters (0-9). This can be significant because
|
||||
** tables within the RBU database are always processed in order sorted by
|
||||
** name. By judicious selection of the the <integer> portion of the names
|
||||
** name. By judicious selection of the <integer> portion of the names
|
||||
** of the RBU tables the user can therefore control the order in which they
|
||||
** are processed. This can be useful, for example, to ensure that "external
|
||||
** content" FTS4 tables are updated before their underlying content tables.
|
||||
@@ -319,16 +319,22 @@ sqlite3rbu *sqlite3rbu_open(
|
||||
** An RBU vacuum is similar to SQLite's built-in VACUUM command, except
|
||||
** that it can be suspended and resumed like an RBU update.
|
||||
**
|
||||
** The second argument to this function, which may not be NULL, identifies
|
||||
** a database in which to store the state of the RBU vacuum operation if
|
||||
** it is suspended. The first time sqlite3rbu_vacuum() is called, to start
|
||||
** an RBU vacuum operation, the state database should either not exist or
|
||||
** be empty (contain no tables). If an RBU vacuum is suspended by calling
|
||||
** The second argument to this function identifies a database in which
|
||||
** to store the state of the RBU vacuum operation if it is suspended. The
|
||||
** first time sqlite3rbu_vacuum() is called, to start an RBU vacuum
|
||||
** operation, the state database should either not exist or be empty
|
||||
** (contain no tables). If an RBU vacuum is suspended by calling
|
||||
** sqlite3rbu_close() on the RBU handle before sqlite3rbu_step() has
|
||||
** returned SQLITE_DONE, the vacuum state is stored in the state database.
|
||||
** The vacuum can be resumed by calling this function to open a new RBU
|
||||
** handle specifying the same target and state databases.
|
||||
**
|
||||
** If the second argument passed to this function is NULL, then the
|
||||
** name of the state database is "<database>-vacuum", where <database>
|
||||
** is the name of the target database file. In this case, on UNIX, if the
|
||||
** state database is not already present in the file-system, it is created
|
||||
** with the same permissions as the target db is made.
|
||||
**
|
||||
** This function does not delete the state database after an RBU vacuum
|
||||
** is completed, even if it created it. However, if the call to
|
||||
** sqlite3rbu_close() returns any value other than SQLITE_OK, the contents
|
||||
@@ -474,6 +480,44 @@ sqlite3_int64 sqlite3rbu_progress(sqlite3rbu *pRbu);
|
||||
*/
|
||||
void sqlite3rbu_bp_progress(sqlite3rbu *pRbu, int *pnOne, int *pnTwo);
|
||||
|
||||
/*
|
||||
** Obtain an indication as to the current stage of an RBU update or vacuum.
|
||||
** This function always returns one of the SQLITE_RBU_STATE_XXX constants
|
||||
** defined in this file. Return values should be interpreted as follows:
|
||||
**
|
||||
** SQLITE_RBU_STATE_OAL:
|
||||
** RBU is currently building a *-oal file. The next call to sqlite3rbu_step()
|
||||
** may either add further data to the *-oal file, or compute data that will
|
||||
** be added by a subsequent call.
|
||||
**
|
||||
** SQLITE_RBU_STATE_MOVE:
|
||||
** RBU has finished building the *-oal file. The next call to sqlite3rbu_step()
|
||||
** will move the *-oal file to the equivalent *-wal path. If the current
|
||||
** operation is an RBU update, then the updated version of the database
|
||||
** file will become visible to ordinary SQLite clients following the next
|
||||
** call to sqlite3rbu_step().
|
||||
**
|
||||
** SQLITE_RBU_STATE_CHECKPOINT:
|
||||
** RBU is currently performing an incremental checkpoint. The next call to
|
||||
** sqlite3rbu_step() will copy a page of data from the *-wal file into
|
||||
** the target database file.
|
||||
**
|
||||
** SQLITE_RBU_STATE_DONE:
|
||||
** The RBU operation has finished. Any subsequent calls to sqlite3rbu_step()
|
||||
** will immediately return SQLITE_DONE.
|
||||
**
|
||||
** SQLITE_RBU_STATE_ERROR:
|
||||
** An error has occurred. Any subsequent calls to sqlite3rbu_step() will
|
||||
** immediately return the SQLite error code associated with the error.
|
||||
*/
|
||||
#define SQLITE_RBU_STATE_OAL 1
|
||||
#define SQLITE_RBU_STATE_MOVE 2
|
||||
#define SQLITE_RBU_STATE_CHECKPOINT 3
|
||||
#define SQLITE_RBU_STATE_DONE 4
|
||||
#define SQLITE_RBU_STATE_ERROR 5
|
||||
|
||||
int sqlite3rbu_state(sqlite3rbu *pRbu);
|
||||
|
||||
/*
|
||||
** Create an RBU VFS named zName that accesses the underlying file-system
|
||||
** via existing VFS zParent. Or, if the zParent parameter is passed NULL,
|
||||
|
||||
+36
-11
@@ -17,7 +17,14 @@
|
||||
#if !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_RBU)
|
||||
|
||||
#include "sqlite3rbu.h"
|
||||
#include <tcl.h>
|
||||
#if defined(INCLUDE_SQLITE_TCL_H)
|
||||
# include "sqlite_tcl.h"
|
||||
#else
|
||||
# include "tcl.h"
|
||||
# ifndef SQLITE_TCLAPI
|
||||
# define SQLITE_TCLAPI
|
||||
# endif
|
||||
#endif
|
||||
#include <assert.h>
|
||||
|
||||
/* From main.c */
|
||||
@@ -49,7 +56,7 @@ void test_rbu_delta(sqlite3_context *pCtx, int nArg, sqlite3_value **apVal){
|
||||
}
|
||||
|
||||
|
||||
static int test_sqlite3rbu_cmd(
|
||||
static int SQLITE_TCLAPI test_sqlite3rbu_cmd(
|
||||
ClientData clientData,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
@@ -69,6 +76,8 @@ static int test_sqlite3rbu_cmd(
|
||||
{"dbMain_eval", 3, "SQL"}, /* 4 */
|
||||
{"bp_progress", 2, ""}, /* 5 */
|
||||
{"db", 3, "RBU"}, /* 6 */
|
||||
{"state", 2, ""}, /* 7 */
|
||||
{"progress", 2, ""}, /* 8 */
|
||||
{0,0,0}
|
||||
};
|
||||
int iCmd;
|
||||
@@ -166,6 +175,18 @@ static int test_sqlite3rbu_cmd(
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 7: /* state */ {
|
||||
const char *aRes[] = { 0, "oal", "move", "checkpoint", "done", "error" };
|
||||
int eState = sqlite3rbu_state(pRbu);
|
||||
assert( eState>0 && eState<=5 );
|
||||
Tcl_SetResult(interp, (char*)aRes[eState], TCL_STATIC);
|
||||
break;
|
||||
}
|
||||
case 8: /* progress */ {
|
||||
sqlite3_int64 nStep = sqlite3rbu_progress(pRbu);
|
||||
Tcl_SetObjResult(interp, Tcl_NewWideIntObj(nStep));
|
||||
break;
|
||||
}
|
||||
|
||||
default: /* seems unlikely */
|
||||
assert( !"cannot happen" );
|
||||
@@ -178,7 +199,7 @@ static int test_sqlite3rbu_cmd(
|
||||
/*
|
||||
** Tclcmd: sqlite3rbu CMD <target-db> <rbu-db> ?<state-db>?
|
||||
*/
|
||||
static int test_sqlite3rbu(
|
||||
static int SQLITE_TCLAPI test_sqlite3rbu(
|
||||
ClientData clientData,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
@@ -208,7 +229,7 @@ static int test_sqlite3rbu(
|
||||
/*
|
||||
** Tclcmd: sqlite3rbu_vacuum CMD <target-db> <state-db>
|
||||
*/
|
||||
static int test_sqlite3rbu_vacuum(
|
||||
static int SQLITE_TCLAPI test_sqlite3rbu_vacuum(
|
||||
ClientData clientData,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
@@ -219,13 +240,13 @@ static int test_sqlite3rbu_vacuum(
|
||||
const char *zTarget;
|
||||
const char *zStateDb = 0;
|
||||
|
||||
if( objc!=4 ){
|
||||
Tcl_WrongNumArgs(interp, 1, objv, "NAME TARGET-DB STATE-DB");
|
||||
if( objc!=3 && objc!=4 ){
|
||||
Tcl_WrongNumArgs(interp, 1, objv, "NAME TARGET-DB ?STATE-DB?");
|
||||
return TCL_ERROR;
|
||||
}
|
||||
zCmd = Tcl_GetString(objv[1]);
|
||||
zTarget = Tcl_GetString(objv[2]);
|
||||
zStateDb = Tcl_GetString(objv[3]);
|
||||
if( objc==4 ) zStateDb = Tcl_GetString(objv[3]);
|
||||
|
||||
pRbu = sqlite3rbu_vacuum(zTarget, zStateDb);
|
||||
Tcl_CreateObjCommand(interp, zCmd, test_sqlite3rbu_cmd, (ClientData)pRbu, 0);
|
||||
@@ -236,7 +257,7 @@ static int test_sqlite3rbu_vacuum(
|
||||
/*
|
||||
** Tclcmd: sqlite3rbu_create_vfs ?-default? NAME PARENT
|
||||
*/
|
||||
static int test_sqlite3rbu_create_vfs(
|
||||
static int SQLITE_TCLAPI test_sqlite3rbu_create_vfs(
|
||||
ClientData clientData,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
@@ -271,7 +292,7 @@ static int test_sqlite3rbu_create_vfs(
|
||||
/*
|
||||
** Tclcmd: sqlite3rbu_destroy_vfs NAME
|
||||
*/
|
||||
static int test_sqlite3rbu_destroy_vfs(
|
||||
static int SQLITE_TCLAPI test_sqlite3rbu_destroy_vfs(
|
||||
ClientData clientData,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
@@ -292,7 +313,7 @@ static int test_sqlite3rbu_destroy_vfs(
|
||||
/*
|
||||
** Tclcmd: sqlite3rbu_internal_test
|
||||
*/
|
||||
static int test_sqlite3rbu_internal_test(
|
||||
static int SQLITE_TCLAPI test_sqlite3rbu_internal_test(
|
||||
ClientData clientData,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
@@ -333,7 +354,11 @@ int SqliteRbu_Init(Tcl_Interp *interp){
|
||||
}
|
||||
|
||||
#else
|
||||
#include <tcl.h>
|
||||
#if defined(INCLUDE_SQLITE_TCL_H)
|
||||
# include "sqlite_tcl.h"
|
||||
#else
|
||||
# include "tcl.h"
|
||||
#endif
|
||||
int SqliteRbu_Init(Tcl_Interp *interp){ return TCL_OK; }
|
||||
#endif /* !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_RBU) */
|
||||
#endif /* defined(SQLITE_TEST) */
|
||||
|
||||
+6
-4
@@ -1542,7 +1542,7 @@ static int rtreeFilter(
|
||||
if( idxNum==1 ){
|
||||
/* Special case - lookup by rowid. */
|
||||
RtreeNode *pLeaf; /* Leaf on which the required cell resides */
|
||||
RtreeSearchPoint *p; /* Search point for the the leaf */
|
||||
RtreeSearchPoint *p; /* Search point for the leaf */
|
||||
i64 iRowid = sqlite3_value_int64(argv[0]);
|
||||
i64 iNode = 0;
|
||||
rc = findLeafNode(pRtree, iRowid, &pLeaf, &iNode);
|
||||
@@ -3012,10 +3012,12 @@ static int rtreeQueryStat1(sqlite3 *db, Rtree *pRtree){
|
||||
int rc;
|
||||
i64 nRow = 0;
|
||||
|
||||
if( sqlite3_table_column_metadata(db,pRtree->zDb,"sqlite_stat1",
|
||||
0,0,0,0,0,0)==SQLITE_ERROR ){
|
||||
rc = sqlite3_table_column_metadata(
|
||||
db, pRtree->zDb, "sqlite_stat1",0,0,0,0,0,0
|
||||
);
|
||||
if( rc!=SQLITE_OK ){
|
||||
pRtree->nRowEst = RTREE_DEFAULT_ROWEST;
|
||||
return SQLITE_OK;
|
||||
return rc==SQLITE_ERROR ? SQLITE_OK : rc;
|
||||
}
|
||||
zSql = sqlite3_mprintf(zFmt, pRtree->zDb, pRtree->zName);
|
||||
if( zSql==0 ){
|
||||
|
||||
@@ -19,7 +19,7 @@ if {![info exists testdir]} {
|
||||
source [file join [file dirname [info script]] rtree_util.tcl]
|
||||
source $testdir/tester.tcl
|
||||
source $testdir/lock_common.tcl
|
||||
ifcapable !rtree {
|
||||
ifcapable !rtree||!builtin_test {
|
||||
finish_test
|
||||
return
|
||||
}
|
||||
|
||||
@@ -0,0 +1,222 @@
|
||||
# 2016 August 23
|
||||
#
|
||||
# The author disclaims copyright to this source code. In place of
|
||||
# a legal notice, here is a blessing:
|
||||
#
|
||||
# May you do good and not evil.
|
||||
# May you find forgiveness for yourself and forgive others.
|
||||
# May you share freely, never taking more than you give.
|
||||
#
|
||||
#***********************************************************************
|
||||
# This file implements regression tests for SQLite library.
|
||||
#
|
||||
|
||||
if {![info exists testdir]} {
|
||||
set testdir [file join [file dirname [info script]] .. .. test]
|
||||
}
|
||||
source $testdir/tester.tcl
|
||||
ifcapable !session {finish_test; return}
|
||||
|
||||
set testprefix changebatch1
|
||||
|
||||
|
||||
proc sql_to_changeset {method sql} {
|
||||
sqlite3session S db main
|
||||
S attach *
|
||||
execsql $sql
|
||||
set ret [S $method]
|
||||
S delete
|
||||
return $ret
|
||||
}
|
||||
|
||||
proc do_changebatch_test {tn method args} {
|
||||
set C [list]
|
||||
foreach a $args {
|
||||
lappend C [sql_to_changeset $method $a]
|
||||
}
|
||||
|
||||
sqlite3changebatch cb db
|
||||
set i 1
|
||||
foreach ::cs [lrange $C 0 end-1] {
|
||||
set rc [cb add $::cs]
|
||||
if {$rc!="SQLITE_OK"} { error "expected SQLITE_OK, got $rc (i=$i)" }
|
||||
incr i
|
||||
}
|
||||
|
||||
set ::cs [lindex $C end]
|
||||
do_test $tn { cb add [set ::cs] } SQLITE_CONSTRAINT
|
||||
cb delete
|
||||
}
|
||||
|
||||
proc do_changebatch_test1 {tn args} {
|
||||
uplevel do_changebatch_test $tn changeset $args
|
||||
}
|
||||
proc do_changebatch_test2 {tn args} {
|
||||
uplevel do_changebatch_test $tn fullchangeset $args
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# The body of the following loop contains tests for database schemas
|
||||
# that do not feature multi-column UNIQUE constraints. In this case
|
||||
# it doesn't matter if the changesets are generated using
|
||||
# sqlite3session_changeset() or sqlite3session_fullchangeset().
|
||||
#
|
||||
foreach {tn testfunction} {
|
||||
1 do_changebatch_test1
|
||||
2 do_changebatch_test2
|
||||
} {
|
||||
reset_db
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
do_execsql_test $tn.1.0 {
|
||||
CREATE TABLE t1(a PRIMARY KEY, b);
|
||||
}
|
||||
|
||||
$testfunction $tn.1.1 {
|
||||
INSERT INTO t1 VALUES(1, 1);
|
||||
} {
|
||||
DELETE FROM t1 WHERE a=1;
|
||||
}
|
||||
|
||||
do_execsql_test $tn.1.2.0 {
|
||||
INSERT INTO t1 VALUES(1, 1);
|
||||
INSERT INTO t1 VALUES(2, 2);
|
||||
INSERT INTO t1 VALUES(3, 3);
|
||||
}
|
||||
$testfunction $tn.1.2.1 {
|
||||
DELETE FROM t1 WHERE a=2;
|
||||
} {
|
||||
INSERT INTO t1 VALUES(2, 2);
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
do_execsql_test $tn.2.0 {
|
||||
CREATE TABLE x1(a, b PRIMARY KEY, c UNIQUE);
|
||||
CREATE TABLE x2(a PRIMARY KEY, b UNIQUE, c UNIQUE);
|
||||
CREATE INDEX x1a ON x1(a);
|
||||
|
||||
INSERT INTO x1 VALUES(1, 1, 'a');
|
||||
INSERT INTO x1 VALUES(1, 2, 'b');
|
||||
INSERT INTO x1 VALUES(1, 3, 'c');
|
||||
}
|
||||
|
||||
$testfunction $tn.2.1 {
|
||||
DELETE FROM x1 WHERE b=2;
|
||||
} {
|
||||
UPDATE x1 SET c='b' WHERE b=3;
|
||||
}
|
||||
|
||||
$testfunction $tn.2.2 {
|
||||
DELETE FROM x1 WHERE b=1;
|
||||
} {
|
||||
INSERT INTO x1 VALUES(1, 5, 'a');
|
||||
}
|
||||
|
||||
set L [list]
|
||||
for {set i 1000} {$i < 10000} {incr i} {
|
||||
lappend L "INSERT INTO x2 VALUES($i, $i, 'x' || $i)"
|
||||
}
|
||||
lappend L "DELETE FROM x2 WHERE b=1005"
|
||||
$testfunction $tn.2.3 {*}$L
|
||||
|
||||
execsql { INSERT INTO x1 VALUES('f', 'f', 'f') }
|
||||
$testfunction $tn.2.4 {
|
||||
INSERT INTO x2 VALUES('f', 'f', 'f');
|
||||
} {
|
||||
INSERT INTO x1 VALUES('g', 'g', 'g');
|
||||
} {
|
||||
DELETE FROM x1 WHERE b='f';
|
||||
} {
|
||||
INSERT INTO x2 VALUES('g', 'g', 'g');
|
||||
} {
|
||||
INSERT INTO x1 VALUES('f', 'f', 'f');
|
||||
}
|
||||
|
||||
execsql {
|
||||
DELETE FROM x1;
|
||||
INSERT INTO x1 VALUES(1.5, 1.5, 1.5);
|
||||
}
|
||||
$testfunction $tn.2.5 {
|
||||
DELETE FROM x1 WHERE b BETWEEN 1 AND 2;
|
||||
} {
|
||||
INSERT INTO x1 VALUES(2.5, 2.5, 2.5);
|
||||
} {
|
||||
INSERT INTO x1 VALUES(1.5, 1.5, 1.5);
|
||||
}
|
||||
|
||||
execsql {
|
||||
DELETE FROM x2;
|
||||
INSERT INTO x2 VALUES(X'abcd', X'1234', X'7890');
|
||||
INSERT INTO x2 VALUES(X'0000', X'0000', X'0000');
|
||||
}
|
||||
breakpoint
|
||||
$testfunction $tn.2.6 {
|
||||
UPDATE x2 SET c = X'1234' WHERE a=X'abcd';
|
||||
INSERT INTO x2 VALUES(X'1234', X'abcd', X'7890');
|
||||
} {
|
||||
DELETE FROM x2 WHERE b=X'0000';
|
||||
} {
|
||||
INSERT INTO x2 VALUES(1, X'0000', NULL);
|
||||
}
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# Test some multi-column UNIQUE constraints. First Using _changeset() to
|
||||
# demonstrate the problem, then using _fullchangeset() to show that it has
|
||||
# been fixed.
|
||||
#
|
||||
reset_db
|
||||
do_execsql_test 3.0 {
|
||||
CREATE TABLE y1(a PRIMARY KEY, b, c, UNIQUE(b, c));
|
||||
INSERT INTO y1 VALUES(1, 1, 1);
|
||||
INSERT INTO y1 VALUES(2, 2, 2);
|
||||
INSERT INTO y1 VALUES(3, 3, 3);
|
||||
INSERT INTO y1 VALUES(4, 3, 4);
|
||||
BEGIN;
|
||||
}
|
||||
|
||||
do_test 3.1.1 {
|
||||
set c1 [sql_to_changeset changeset { DELETE FROM y1 WHERE a=4 }]
|
||||
set c2 [sql_to_changeset changeset { UPDATE y1 SET c=4 WHERE a=3 }]
|
||||
sqlite3changebatch cb db
|
||||
cb add $c1
|
||||
cb add $c2
|
||||
} {SQLITE_OK}
|
||||
do_test 3.1.2 {
|
||||
cb delete
|
||||
execsql ROLLBACK
|
||||
} {}
|
||||
|
||||
do_test 3.1.1 {
|
||||
set c1 [sql_to_changeset fullchangeset { DELETE FROM y1 WHERE a=4 }]
|
||||
set c2 [sql_to_changeset fullchangeset { UPDATE y1 SET c=4 WHERE a=3 }]
|
||||
sqlite3changebatch cb db
|
||||
cb add $c1
|
||||
cb add $c2
|
||||
} {SQLITE_CONSTRAINT}
|
||||
do_test 3.1.2 {
|
||||
cb delete
|
||||
} {}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
reset_db
|
||||
do_execsql_test 4.0 {
|
||||
CREATE TABLE t1(x, y, z, PRIMARY KEY(x, y), UNIQUE(z));
|
||||
}
|
||||
|
||||
do_test 4.1 {
|
||||
set c1 [sql_to_changeset fullchangeset { INSERT INTO t1 VALUES(1, 2, 3) }]
|
||||
execsql {
|
||||
DROP TABLE t1;
|
||||
CREATE TABLE t1(w, x, y, z, PRIMARY KEY(x, y), UNIQUE(z));
|
||||
}
|
||||
sqlite3changebatch cb db
|
||||
list [catch { cb add $c1 } msg] $msg
|
||||
} {1 SQLITE_RANGE}
|
||||
|
||||
|
||||
|
||||
finish_test
|
||||
@@ -0,0 +1,42 @@
|
||||
# 2011 Mar 21
|
||||
#
|
||||
# The author disclaims copyright to this source code. In place of
|
||||
# a legal notice, here is a blessing:
|
||||
#
|
||||
# May you do good and not evil.
|
||||
# May you find forgiveness for yourself and forgive others.
|
||||
# May you share freely, never taking more than you give.
|
||||
#
|
||||
#***********************************************************************
|
||||
#
|
||||
# The focus of this file is testing the session module.
|
||||
#
|
||||
|
||||
if {![info exists testdir]} {
|
||||
set testdir [file join [file dirname [info script]] .. .. test]
|
||||
}
|
||||
source [file join [file dirname [info script]] session_common.tcl]
|
||||
source $testdir/tester.tcl
|
||||
ifcapable !session {finish_test; return}
|
||||
set testprefix changebatchfault
|
||||
|
||||
do_execsql_test 1.0 {
|
||||
CREATE TABLE t1(a, b, c PRIMARY KEY, UNIQUE(a, b));
|
||||
INSERT INTO t1 VALUES('a', 'a', 'a');
|
||||
INSERT INTO t1 VALUES('b', 'b', 'b');
|
||||
}
|
||||
|
||||
set ::c1 [changeset_from_sql { delete from t1 where c='a' }]
|
||||
set ::c2 [changeset_from_sql { insert into t1 values('c', 'c', 'c') }]
|
||||
|
||||
do_faultsim_test 1 -faults oom-* -body {
|
||||
sqlite3changebatch cb db
|
||||
cb add $::c1
|
||||
cb add $::c2
|
||||
} -test {
|
||||
faultsim_test_result {0 SQLITE_OK} {1 SQLITE_NOMEM}
|
||||
catch { cb delete }
|
||||
}
|
||||
|
||||
|
||||
finish_test
|
||||
@@ -569,5 +569,17 @@ do_changeset_test 10.1.2 S "
|
||||
"
|
||||
do_test 10.1.4 { S delete } {}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# Test the effect of updating a column from 0.0 to 0.0.
|
||||
#
|
||||
reset_db
|
||||
do_execsql_test 11.1 {
|
||||
CREATE TABLE t1(a INTEGER PRIMARY KEY, b REAL);
|
||||
INSERT INTO t1 VALUES(1, 0.0);
|
||||
}
|
||||
do_iterator_test 11.2 * {
|
||||
UPDATE t1 SET b = 0.0;
|
||||
} {
|
||||
}
|
||||
|
||||
finish_test
|
||||
|
||||
@@ -30,7 +30,6 @@ do_test 1.0 {
|
||||
INSERT INTO x VALUES(19, 'domains', 'espouse', -94, 'throw');
|
||||
}
|
||||
|
||||
sqlite3session S db main
|
||||
set changeset [changeset_from_sql {
|
||||
DELETE FROM x WHERE e = -66;
|
||||
UPDATE x SET a = 'parameterizable', b = 31.8 WHERE c = 35;
|
||||
|
||||
@@ -75,6 +75,9 @@ proc do_common_sql {sql} {
|
||||
}
|
||||
|
||||
proc changeset_from_sql {sql {dbname main}} {
|
||||
if {$dbname == "main"} {
|
||||
return [sql_exec_changeset db $sql]
|
||||
}
|
||||
set rc [catch {
|
||||
sqlite3session S db $dbname
|
||||
db eval "SELECT name FROM $dbname.sqlite_master WHERE type = 'table'" {
|
||||
|
||||
@@ -0,0 +1,486 @@
|
||||
|
||||
#if !defined(SQLITE_TEST) || (defined(SQLITE_ENABLE_SESSION) && defined(SQLITE_ENABLE_PREUPDATE_HOOK))
|
||||
|
||||
#include "sqlite3session.h"
|
||||
#include "sqlite3changebatch.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
|
||||
typedef struct BatchTable BatchTable;
|
||||
typedef struct BatchIndex BatchIndex;
|
||||
typedef struct BatchIndexEntry BatchIndexEntry;
|
||||
typedef struct BatchHash BatchHash;
|
||||
|
||||
struct sqlite3_changebatch {
|
||||
sqlite3 *db; /* Database handle used to read schema */
|
||||
BatchTable *pTab; /* First in linked list of tables */
|
||||
int iChangesetId; /* Current changeset id */
|
||||
int iNextIdxId; /* Next available index id */
|
||||
int nEntry; /* Number of entries in hash table */
|
||||
int nHash; /* Number of hash buckets */
|
||||
BatchIndexEntry **apHash; /* Array of hash buckets */
|
||||
};
|
||||
|
||||
struct BatchTable {
|
||||
BatchIndex *pIdx; /* First in linked list of UNIQUE indexes */
|
||||
BatchTable *pNext; /* Next table */
|
||||
char zTab[1]; /* Table name */
|
||||
};
|
||||
|
||||
struct BatchIndex {
|
||||
BatchIndex *pNext; /* Next index on same table */
|
||||
int iId; /* Index id (assigned internally) */
|
||||
int bPk; /* True for PK index */
|
||||
int nCol; /* Size of aiCol[] array */
|
||||
int *aiCol; /* Array of columns that make up index */
|
||||
};
|
||||
|
||||
struct BatchIndexEntry {
|
||||
BatchIndexEntry *pNext; /* Next colliding hash table entry */
|
||||
int iChangesetId; /* Id of associated changeset */
|
||||
int iIdxId; /* Id of index this key is from */
|
||||
int szRecord;
|
||||
char aRecord[1];
|
||||
};
|
||||
|
||||
/*
|
||||
** Allocate and zero a block of nByte bytes. Must be freed using cbFree().
|
||||
*/
|
||||
static void *cbMalloc(int *pRc, int nByte){
|
||||
void *pRet;
|
||||
|
||||
if( *pRc ){
|
||||
pRet = 0;
|
||||
}else{
|
||||
pRet = sqlite3_malloc(nByte);
|
||||
if( pRet ){
|
||||
memset(pRet, 0, nByte);
|
||||
}else{
|
||||
*pRc = SQLITE_NOMEM;
|
||||
}
|
||||
}
|
||||
|
||||
return pRet;
|
||||
}
|
||||
|
||||
/*
|
||||
** Free an allocation made by cbMalloc().
|
||||
*/
|
||||
static void cbFree(void *p){
|
||||
sqlite3_free(p);
|
||||
}
|
||||
|
||||
/*
|
||||
** Return the hash bucket that pEntry belongs in.
|
||||
*/
|
||||
static int cbHash(sqlite3_changebatch *p, BatchIndexEntry *pEntry){
|
||||
unsigned int iHash = (unsigned int)pEntry->iIdxId;
|
||||
unsigned char *pEnd = (unsigned char*)&pEntry->aRecord[pEntry->szRecord];
|
||||
unsigned char *pIter;
|
||||
|
||||
for(pIter=(unsigned char*)pEntry->aRecord; pIter<pEnd; pIter++){
|
||||
iHash += (iHash << 7) + *pIter;
|
||||
}
|
||||
|
||||
return (int)(iHash % p->nHash);
|
||||
}
|
||||
|
||||
/*
|
||||
** Resize the hash table.
|
||||
*/
|
||||
static int cbHashResize(sqlite3_changebatch *p){
|
||||
int rc = SQLITE_OK;
|
||||
BatchIndexEntry **apNew;
|
||||
int nNew = (p->nHash ? p->nHash*2 : 512);
|
||||
int i;
|
||||
|
||||
apNew = cbMalloc(&rc, sizeof(BatchIndexEntry*) * nNew);
|
||||
if( rc==SQLITE_OK ){
|
||||
int nHash = p->nHash;
|
||||
p->nHash = nNew;
|
||||
for(i=0; i<nHash; i++){
|
||||
BatchIndexEntry *pEntry;
|
||||
while( (pEntry=p->apHash[i])!=0 ){
|
||||
int iHash = cbHash(p, pEntry);
|
||||
p->apHash[i] = pEntry->pNext;
|
||||
pEntry->pNext = apNew[iHash];
|
||||
apNew[iHash] = pEntry;
|
||||
}
|
||||
}
|
||||
|
||||
cbFree(p->apHash);
|
||||
p->apHash = apNew;
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Allocate a new sqlite3_changebatch object.
|
||||
*/
|
||||
int sqlite3changebatch_new(sqlite3 *db, sqlite3_changebatch **pp){
|
||||
sqlite3_changebatch *pRet;
|
||||
int rc = SQLITE_OK;
|
||||
*pp = pRet = (sqlite3_changebatch*)cbMalloc(&rc, sizeof(sqlite3_changebatch));
|
||||
if( pRet ){
|
||||
pRet->db = db;
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** Add a BatchIndex entry for index zIdx to table pTab.
|
||||
*/
|
||||
static int cbAddIndex(
|
||||
sqlite3_changebatch *p,
|
||||
BatchTable *pTab,
|
||||
const char *zIdx,
|
||||
int bPk
|
||||
){
|
||||
int nCol = 0;
|
||||
sqlite3_stmt *pIndexInfo = 0;
|
||||
BatchIndex *pNew = 0;
|
||||
int rc;
|
||||
char *zIndexInfo;
|
||||
|
||||
zIndexInfo = (char*)sqlite3_mprintf("PRAGMA main.index_info = %Q", zIdx);
|
||||
if( zIndexInfo ){
|
||||
rc = sqlite3_prepare_v2(p->db, zIndexInfo, -1, &pIndexInfo, 0);
|
||||
sqlite3_free(zIndexInfo);
|
||||
}else{
|
||||
rc = SQLITE_NOMEM;
|
||||
}
|
||||
|
||||
if( rc==SQLITE_OK ){
|
||||
while( SQLITE_ROW==sqlite3_step(pIndexInfo) ){ nCol++; }
|
||||
rc = sqlite3_reset(pIndexInfo);
|
||||
}
|
||||
|
||||
pNew = (BatchIndex*)cbMalloc(&rc, sizeof(BatchIndex) + sizeof(int) * nCol);
|
||||
if( rc==SQLITE_OK ){
|
||||
int rc2;
|
||||
pNew->nCol = nCol;
|
||||
pNew->bPk = bPk;
|
||||
pNew->aiCol = (int*)&pNew[1];
|
||||
pNew->iId = p->iNextIdxId++;
|
||||
while( SQLITE_ROW==sqlite3_step(pIndexInfo) ){
|
||||
int i = sqlite3_column_int(pIndexInfo, 0);
|
||||
int j = sqlite3_column_int(pIndexInfo, 1);
|
||||
pNew->aiCol[i] = j;
|
||||
}
|
||||
rc = sqlite3_reset(pIndexInfo);
|
||||
}
|
||||
|
||||
if( rc==SQLITE_OK ){
|
||||
pNew->pNext = pTab->pIdx;
|
||||
pTab->pIdx = pNew;
|
||||
}else{
|
||||
cbFree(pNew);
|
||||
}
|
||||
sqlite3_finalize(pIndexInfo);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** Free the object passed as the first argument.
|
||||
*/
|
||||
static void cbFreeTable(BatchTable *pTab){
|
||||
BatchIndex *pIdx;
|
||||
BatchIndex *pIdxNext;
|
||||
for(pIdx=pTab->pIdx; pIdx; pIdx=pIdxNext){
|
||||
pIdxNext = pIdx->pNext;
|
||||
cbFree(pIdx);
|
||||
}
|
||||
cbFree(pTab);
|
||||
}
|
||||
|
||||
/*
|
||||
** Find or create the BatchTable object named zTab.
|
||||
*/
|
||||
static int cbFindTable(
|
||||
sqlite3_changebatch *p,
|
||||
const char *zTab,
|
||||
BatchTable **ppTab
|
||||
){
|
||||
BatchTable *pRet = 0;
|
||||
int rc = SQLITE_OK;
|
||||
|
||||
for(pRet=p->pTab; pRet; pRet=pRet->pNext){
|
||||
if( 0==sqlite3_stricmp(zTab, pRet->zTab) ) break;
|
||||
}
|
||||
|
||||
if( pRet==0 ){
|
||||
int nTab = strlen(zTab);
|
||||
pRet = (BatchTable*)cbMalloc(&rc, nTab + sizeof(BatchTable));
|
||||
if( pRet ){
|
||||
sqlite3_stmt *pIndexList = 0;
|
||||
char *zIndexList = 0;
|
||||
int rc2;
|
||||
memcpy(pRet->zTab, zTab, nTab);
|
||||
|
||||
zIndexList = sqlite3_mprintf("PRAGMA main.index_list = %Q", zTab);
|
||||
if( zIndexList==0 ){
|
||||
rc = SQLITE_NOMEM;
|
||||
}else{
|
||||
rc = sqlite3_prepare_v2(p->db, zIndexList, -1, &pIndexList, 0);
|
||||
sqlite3_free(zIndexList);
|
||||
}
|
||||
|
||||
while( rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pIndexList) ){
|
||||
if( sqlite3_column_int(pIndexList, 2) ){
|
||||
const char *zIdx = (const char*)sqlite3_column_text(pIndexList, 1);
|
||||
const char *zTyp = (const char*)sqlite3_column_text(pIndexList, 3);
|
||||
rc = cbAddIndex(p, pRet, zIdx, (zTyp[0]=='p'));
|
||||
}
|
||||
}
|
||||
rc2 = sqlite3_finalize(pIndexList);
|
||||
if( rc==SQLITE_OK ) rc = rc2;
|
||||
|
||||
if( rc==SQLITE_OK ){
|
||||
pRet->pNext = p->pTab;
|
||||
p->pTab = pRet;
|
||||
}else{
|
||||
cbFreeTable(pRet);
|
||||
pRet = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
*ppTab = pRet;
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** Extract value iVal from the changeset iterator passed as the first
|
||||
** argument. Set *ppVal to point to the value before returning.
|
||||
**
|
||||
** This function attempts to extract the value using function xVal
|
||||
** (which is always either sqlite3changeset_new or sqlite3changeset_old).
|
||||
** If the call returns SQLITE_OK but does not supply an sqlite3_value*
|
||||
** pointer, an attempt to extract the value is made using the xFallback
|
||||
** function.
|
||||
*/
|
||||
static int cbGetChangesetValue(
|
||||
sqlite3_changeset_iter *pIter,
|
||||
int (*xVal)(sqlite3_changeset_iter*,int,sqlite3_value**),
|
||||
int (*xFallback)(sqlite3_changeset_iter*,int,sqlite3_value**),
|
||||
int iVal,
|
||||
sqlite3_value **ppVal
|
||||
){
|
||||
int rc = xVal(pIter, iVal, ppVal);
|
||||
if( rc==SQLITE_OK && *ppVal==0 && xFallback ){
|
||||
rc = xFallback(pIter, iVal, ppVal);
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int cbAddToHash(
|
||||
sqlite3_changebatch *p,
|
||||
sqlite3_changeset_iter *pIter,
|
||||
BatchIndex *pIdx,
|
||||
int (*xVal)(sqlite3_changeset_iter*,int,sqlite3_value**),
|
||||
int (*xFallback)(sqlite3_changeset_iter*,int,sqlite3_value**),
|
||||
int *pbConf
|
||||
){
|
||||
BatchIndexEntry *pNew;
|
||||
int sz = pIdx->nCol;
|
||||
int i;
|
||||
int iOut = 0;
|
||||
int rc = SQLITE_OK;
|
||||
|
||||
for(i=0; rc==SQLITE_OK && i<pIdx->nCol; i++){
|
||||
sqlite3_value *pVal;
|
||||
rc = cbGetChangesetValue(pIter, xVal, xFallback, pIdx->aiCol[i], &pVal);
|
||||
if( rc==SQLITE_OK ){
|
||||
int eType = 0;
|
||||
if( pVal ) eType = sqlite3_value_type(pVal);
|
||||
switch( eType ){
|
||||
case 0:
|
||||
case SQLITE_NULL:
|
||||
return SQLITE_OK;
|
||||
|
||||
case SQLITE_INTEGER:
|
||||
sz += 8;
|
||||
break;
|
||||
case SQLITE_FLOAT:
|
||||
sz += 8;
|
||||
break;
|
||||
|
||||
default:
|
||||
assert( eType==SQLITE_TEXT || eType==SQLITE_BLOB );
|
||||
sz += sqlite3_value_bytes(pVal);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pNew = cbMalloc(&rc, sizeof(BatchIndexEntry) + sz);
|
||||
if( pNew ){
|
||||
pNew->iChangesetId = p->iChangesetId;
|
||||
pNew->iIdxId = pIdx->iId;
|
||||
pNew->szRecord = sz;
|
||||
|
||||
for(i=0; i<pIdx->nCol; i++){
|
||||
int eType;
|
||||
sqlite3_value *pVal;
|
||||
rc = cbGetChangesetValue(pIter, xVal, xFallback, pIdx->aiCol[i], &pVal);
|
||||
if( rc!=SQLITE_OK ) break; /* coverage: condition is never true */
|
||||
eType = sqlite3_value_type(pVal);
|
||||
pNew->aRecord[iOut++] = eType;
|
||||
switch( eType ){
|
||||
case SQLITE_INTEGER: {
|
||||
sqlite3_int64 i64 = sqlite3_value_int64(pVal);
|
||||
memcpy(&pNew->aRecord[iOut], &i64, 8);
|
||||
iOut += 8;
|
||||
break;
|
||||
}
|
||||
case SQLITE_FLOAT: {
|
||||
double d64 = sqlite3_value_double(pVal);
|
||||
memcpy(&pNew->aRecord[iOut], &d64, sizeof(double));
|
||||
iOut += sizeof(double);
|
||||
break;
|
||||
}
|
||||
|
||||
default: {
|
||||
int nByte = sqlite3_value_bytes(pVal);
|
||||
const char *z = (const char*)sqlite3_value_blob(pVal);
|
||||
memcpy(&pNew->aRecord[iOut], z, nByte);
|
||||
iOut += nByte;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if( rc==SQLITE_OK && p->nEntry>=(p->nHash/2) ){
|
||||
rc = cbHashResize(p);
|
||||
}
|
||||
|
||||
if( rc==SQLITE_OK ){
|
||||
BatchIndexEntry *pIter;
|
||||
int iHash = cbHash(p, pNew);
|
||||
|
||||
assert( iHash>=0 && iHash<p->nHash );
|
||||
for(pIter=p->apHash[iHash]; pIter; pIter=pIter->pNext){
|
||||
if( pNew->szRecord==pIter->szRecord
|
||||
&& 0==memcmp(pNew->aRecord, pIter->aRecord, pNew->szRecord)
|
||||
){
|
||||
if( pNew->iChangesetId!=pIter->iChangesetId ){
|
||||
*pbConf = 1;
|
||||
}
|
||||
cbFree(pNew);
|
||||
pNew = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if( pNew ){
|
||||
pNew->pNext = p->apHash[iHash];
|
||||
p->apHash[iHash] = pNew;
|
||||
p->nEntry++;
|
||||
}
|
||||
}else{
|
||||
cbFree(pNew);
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Add a changeset to the current batch.
|
||||
*/
|
||||
int sqlite3changebatch_add(sqlite3_changebatch *p, void *pBuf, int nBuf){
|
||||
sqlite3_changeset_iter *pIter; /* Iterator opened on pBuf/nBuf */
|
||||
int rc; /* Return code */
|
||||
int bConf = 0; /* Conflict was detected */
|
||||
|
||||
rc = sqlite3changeset_start(&pIter, nBuf, pBuf);
|
||||
if( rc==SQLITE_OK ){
|
||||
int rc2;
|
||||
for(rc2 = sqlite3changeset_next(pIter);
|
||||
rc2==SQLITE_ROW;
|
||||
rc2 = sqlite3changeset_next(pIter)
|
||||
){
|
||||
BatchTable *pTab;
|
||||
BatchIndex *pIdx;
|
||||
const char *zTab; /* Table this change applies to */
|
||||
int nCol; /* Number of columns in table */
|
||||
int op; /* UPDATE, INSERT or DELETE */
|
||||
|
||||
sqlite3changeset_op(pIter, &zTab, &nCol, &op, 0);
|
||||
assert( op==SQLITE_INSERT || op==SQLITE_UPDATE || op==SQLITE_DELETE );
|
||||
|
||||
rc = cbFindTable(p, zTab, &pTab);
|
||||
assert( pTab || rc!=SQLITE_OK );
|
||||
if( pTab ){
|
||||
for(pIdx=pTab->pIdx; pIdx && rc==SQLITE_OK; pIdx=pIdx->pNext){
|
||||
if( op==SQLITE_UPDATE && pIdx->bPk ) continue;
|
||||
if( op==SQLITE_UPDATE || op==SQLITE_DELETE ){
|
||||
rc = cbAddToHash(p, pIter, pIdx, sqlite3changeset_old, 0, &bConf);
|
||||
}
|
||||
if( op==SQLITE_UPDATE || op==SQLITE_INSERT ){
|
||||
rc = cbAddToHash(p, pIter, pIdx,
|
||||
sqlite3changeset_new, sqlite3changeset_old, &bConf
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
if( rc!=SQLITE_OK ) break;
|
||||
}
|
||||
|
||||
rc2 = sqlite3changeset_finalize(pIter);
|
||||
if( rc==SQLITE_OK ) rc = rc2;
|
||||
}
|
||||
|
||||
if( rc==SQLITE_OK && bConf ){
|
||||
rc = SQLITE_CONSTRAINT;
|
||||
}
|
||||
p->iChangesetId++;
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** Zero an existing changebatch object.
|
||||
*/
|
||||
void sqlite3changebatch_zero(sqlite3_changebatch *p){
|
||||
int i;
|
||||
for(i=0; i<p->nHash; i++){
|
||||
BatchIndexEntry *pEntry;
|
||||
BatchIndexEntry *pNext;
|
||||
for(pEntry=p->apHash[i]; pEntry; pEntry=pNext){
|
||||
pNext = pEntry->pNext;
|
||||
cbFree(pEntry);
|
||||
}
|
||||
}
|
||||
cbFree(p->apHash);
|
||||
p->nHash = 0;
|
||||
p->apHash = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
** Delete a changebatch object.
|
||||
*/
|
||||
void sqlite3changebatch_delete(sqlite3_changebatch *p){
|
||||
BatchTable *pTab;
|
||||
BatchTable *pTabNext;
|
||||
|
||||
sqlite3changebatch_zero(p);
|
||||
for(pTab=p->pTab; pTab; pTab=pTabNext){
|
||||
pTabNext = pTab->pNext;
|
||||
cbFreeTable(pTab);
|
||||
}
|
||||
cbFree(p);
|
||||
}
|
||||
|
||||
/*
|
||||
** Return the db handle.
|
||||
*/
|
||||
sqlite3 *sqlite3changebatch_db(sqlite3_changebatch *p){
|
||||
return p->db;
|
||||
}
|
||||
|
||||
#endif /* SQLITE_ENABLE_SESSION && SQLITE_ENABLE_PREUPDATE_HOOK */
|
||||
@@ -0,0 +1,82 @@
|
||||
|
||||
#if !defined(SQLITECHANGEBATCH_H_)
|
||||
#define SQLITECHANGEBATCH_H_ 1
|
||||
|
||||
typedef struct sqlite3_changebatch sqlite3_changebatch;
|
||||
|
||||
/*
|
||||
** Create a new changebatch object for detecting conflicts between
|
||||
** changesets associated with a schema equivalent to that of the "main"
|
||||
** database of the open database handle db passed as the first
|
||||
** parameter. It is the responsibility of the caller to ensure that
|
||||
** the database handle is not closed until after the changebatch
|
||||
** object has been deleted.
|
||||
**
|
||||
** A changebatch object is used to detect batches of non-conflicting
|
||||
** changesets. Changesets that do not conflict may be applied to the
|
||||
** target database in any order without affecting the final state of
|
||||
** the database.
|
||||
**
|
||||
** The changebatch object only works reliably if PRIMARY KEY and UNIQUE
|
||||
** constraints on tables affected by the changesets use collation
|
||||
** sequences that are equivalent to built-in collation sequence
|
||||
** BINARY for the == operation.
|
||||
**
|
||||
** If successful, SQLITE_OK is returned and (*pp) set to point to
|
||||
** the new changebatch object. If an error occurs, an SQLite error
|
||||
** code is returned and the final value of (*pp) is undefined.
|
||||
*/
|
||||
int sqlite3changebatch_new(sqlite3 *db, sqlite3_changebatch **pp);
|
||||
|
||||
/*
|
||||
** Argument p points to a buffer containing a changeset n bytes in
|
||||
** size. Assuming no error occurs, this function returns SQLITE_OK
|
||||
** if the changeset does not conflict with any changeset passed
|
||||
** to an sqlite3changebatch_add() call made on the same
|
||||
** sqlite3_changebatch* handle since the most recent call to
|
||||
** sqlite3changebatch_zero(). If the changeset does conflict with
|
||||
** an earlier such changeset, SQLITE_CONSTRAINT is returned. Or,
|
||||
** if an error occurs, some other SQLite error code may be returned.
|
||||
**
|
||||
** One changeset is said to conflict with another if
|
||||
** either:
|
||||
**
|
||||
** * the two changesets contain operations (INSERT, UPDATE or
|
||||
** DELETE) on the same row, identified by primary key, or
|
||||
**
|
||||
** * the two changesets contain operations (INSERT, UPDATE or
|
||||
** DELETE) on rows with identical values in any combination
|
||||
** of fields constrained by a UNIQUE constraint.
|
||||
**
|
||||
** Even if this function returns SQLITE_CONFLICT, the current
|
||||
** changeset is added to the internal data structures - so future
|
||||
** calls to this function may conflict with it. If this function
|
||||
** returns any result code other than SQLITE_OK or SQLITE_CONFLICT,
|
||||
** the result of any future call to sqlite3changebatch_add() is
|
||||
** undefined.
|
||||
**
|
||||
** Only changesets may be passed to this function. Passing a
|
||||
** patchset to this function results in an SQLITE_MISUSE error.
|
||||
*/
|
||||
int sqlite3changebatch_add(sqlite3_changebatch*, void *p, int n);
|
||||
|
||||
/*
|
||||
** Zero a changebatch object. This causes the records of all earlier
|
||||
** calls to sqlite3changebatch_add() to be discarded.
|
||||
*/
|
||||
void sqlite3changebatch_zero(sqlite3_changebatch*);
|
||||
|
||||
/*
|
||||
** Return a copy of the first argument passed to the sqlite3changebatch_new()
|
||||
** call used to create the changebatch object passed as the only argument
|
||||
** to this function.
|
||||
*/
|
||||
sqlite3 *sqlite3changebatch_db(sqlite3_changebatch*);
|
||||
|
||||
/*
|
||||
** Delete a changebatch object.
|
||||
*/
|
||||
void sqlite3changebatch_delete(sqlite3_changebatch*);
|
||||
|
||||
#endif /* !defined(SQLITECHANGEBATCH_H_) */
|
||||
|
||||
@@ -25,6 +25,13 @@ typedef struct SessionInput SessionInput;
|
||||
# endif
|
||||
#endif
|
||||
|
||||
/*
|
||||
** The three different types of changesets generated.
|
||||
*/
|
||||
#define SESSIONS_PATCHSET 0
|
||||
#define SESSIONS_CHANGESET 1
|
||||
#define SESSIONS_FULLCHANGESET 2
|
||||
|
||||
typedef struct SessionHook SessionHook;
|
||||
struct SessionHook {
|
||||
void *pCtx;
|
||||
@@ -1934,7 +1941,7 @@ static void sessionAppendCol(
|
||||
*/
|
||||
static int sessionAppendUpdate(
|
||||
SessionBuffer *pBuf, /* Buffer to append to */
|
||||
int bPatchset, /* True for "patchset", 0 for "changeset" */
|
||||
int ePatchset, /* True for "patchset", 0 for "changeset" */
|
||||
sqlite3_stmt *pStmt, /* Statement handle pointing at new row */
|
||||
SessionChange *p, /* Object containing old values */
|
||||
u8 *abPK /* Boolean array - true for PK columns */
|
||||
@@ -1997,8 +2004,8 @@ static int sessionAppendUpdate(
|
||||
|
||||
/* Add a field to the old.* record. This is omitted if this modules is
|
||||
** currently generating a patchset. */
|
||||
if( bPatchset==0 ){
|
||||
if( bChanged || abPK[i] ){
|
||||
if( ePatchset!=SESSIONS_PATCHSET ){
|
||||
if( ePatchset==SESSIONS_FULLCHANGESET || bChanged || abPK[i] ){
|
||||
sessionAppendBlob(pBuf, pCsr, nAdvance, &rc);
|
||||
}else{
|
||||
sessionAppendByte(pBuf, 0, &rc);
|
||||
@@ -2007,7 +2014,7 @@ static int sessionAppendUpdate(
|
||||
|
||||
/* Add a field to the new.* record. Or the only record if currently
|
||||
** generating a patchset. */
|
||||
if( bChanged || (bPatchset && abPK[i]) ){
|
||||
if( bChanged || (ePatchset==SESSIONS_PATCHSET && abPK[i]) ){
|
||||
sessionAppendCol(&buf2, pStmt, i, &rc);
|
||||
}else{
|
||||
sessionAppendByte(&buf2, 0, &rc);
|
||||
@@ -2033,7 +2040,7 @@ static int sessionAppendUpdate(
|
||||
*/
|
||||
static int sessionAppendDelete(
|
||||
SessionBuffer *pBuf, /* Buffer to append to */
|
||||
int bPatchset, /* True for "patchset", 0 for "changeset" */
|
||||
int eChangeset, /* One of SESSIONS_CHANGESET etc. */
|
||||
SessionChange *p, /* Object containing old values */
|
||||
int nCol, /* Number of columns in table */
|
||||
u8 *abPK /* Boolean array - true for PK columns */
|
||||
@@ -2043,7 +2050,7 @@ static int sessionAppendDelete(
|
||||
sessionAppendByte(pBuf, SQLITE_DELETE, &rc);
|
||||
sessionAppendByte(pBuf, p->bIndirect, &rc);
|
||||
|
||||
if( bPatchset==0 ){
|
||||
if( eChangeset!=SESSIONS_PATCHSET ){
|
||||
sessionAppendBlob(pBuf, p->aRecord, p->nRecord, &rc);
|
||||
}else{
|
||||
int i;
|
||||
@@ -2202,12 +2209,12 @@ static int sessionSelectBind(
|
||||
*/
|
||||
static void sessionAppendTableHdr(
|
||||
SessionBuffer *pBuf, /* Append header to this buffer */
|
||||
int bPatchset, /* Use the patchset format if true */
|
||||
int ePatchset, /* Use the patchset format if true */
|
||||
SessionTable *pTab, /* Table object to append header for */
|
||||
int *pRc /* IN/OUT: Error code */
|
||||
){
|
||||
/* Write a table header */
|
||||
sessionAppendByte(pBuf, (bPatchset ? 'P' : 'T'), pRc);
|
||||
sessionAppendByte(pBuf, (ePatchset==SESSIONS_PATCHSET) ? 'P' : 'T', pRc);
|
||||
sessionAppendVarint(pBuf, pTab->nCol, pRc);
|
||||
sessionAppendBlob(pBuf, pTab->abPK, pTab->nCol, pRc);
|
||||
sessionAppendBlob(pBuf, (u8 *)pTab->zName, (int)strlen(pTab->zName)+1, pRc);
|
||||
@@ -2225,7 +2232,7 @@ static void sessionAppendTableHdr(
|
||||
*/
|
||||
static int sessionGenerateChangeset(
|
||||
sqlite3_session *pSession, /* Session object */
|
||||
int bPatchset, /* True for patchset, false for changeset */
|
||||
int ePatchset, /* One of SESSIONS_CHANGESET etc. */
|
||||
int (*xOutput)(void *pOut, const void *pData, int nData),
|
||||
void *pOut, /* First argument for xOutput */
|
||||
int *pnChangeset, /* OUT: Size of buffer at *ppChangeset */
|
||||
@@ -2270,7 +2277,7 @@ static int sessionGenerateChangeset(
|
||||
}
|
||||
|
||||
/* Write a table header */
|
||||
sessionAppendTableHdr(&buf, bPatchset, pTab, &rc);
|
||||
sessionAppendTableHdr(&buf, ePatchset, pTab, &rc);
|
||||
|
||||
/* Build and compile a statement to execute: */
|
||||
if( rc==SQLITE_OK ){
|
||||
@@ -2294,10 +2301,10 @@ static int sessionGenerateChangeset(
|
||||
sessionAppendCol(&buf, pSel, iCol, &rc);
|
||||
}
|
||||
}else{
|
||||
rc = sessionAppendUpdate(&buf, bPatchset, pSel, p, abPK);
|
||||
rc = sessionAppendUpdate(&buf, ePatchset, pSel, p, abPK);
|
||||
}
|
||||
}else if( p->op!=SQLITE_INSERT ){
|
||||
rc = sessionAppendDelete(&buf, bPatchset, p, nCol, abPK);
|
||||
rc = sessionAppendDelete(&buf, ePatchset, p, nCol, abPK);
|
||||
}
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = sqlite3_reset(pSel);
|
||||
@@ -2354,7 +2361,8 @@ int sqlite3session_changeset(
|
||||
int *pnChangeset, /* OUT: Size of buffer at *ppChangeset */
|
||||
void **ppChangeset /* OUT: Buffer containing changeset */
|
||||
){
|
||||
return sessionGenerateChangeset(pSession, 0, 0, 0, pnChangeset, ppChangeset);
|
||||
return sessionGenerateChangeset(
|
||||
pSession, SESSIONS_CHANGESET, 0, 0, pnChangeset, ppChangeset);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -2365,7 +2373,8 @@ int sqlite3session_changeset_strm(
|
||||
int (*xOutput)(void *pOut, const void *pData, int nData),
|
||||
void *pOut
|
||||
){
|
||||
return sessionGenerateChangeset(pSession, 0, xOutput, pOut, 0, 0);
|
||||
return sessionGenerateChangeset(
|
||||
pSession, SESSIONS_CHANGESET, xOutput, pOut, 0, 0);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -2376,7 +2385,8 @@ int sqlite3session_patchset_strm(
|
||||
int (*xOutput)(void *pOut, const void *pData, int nData),
|
||||
void *pOut
|
||||
){
|
||||
return sessionGenerateChangeset(pSession, 1, xOutput, pOut, 0, 0);
|
||||
return sessionGenerateChangeset(
|
||||
pSession, SESSIONS_PATCHSET, xOutput, pOut, 0, 0);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -2391,9 +2401,20 @@ int sqlite3session_patchset(
|
||||
int *pnPatchset, /* OUT: Size of buffer at *ppChangeset */
|
||||
void **ppPatchset /* OUT: Buffer containing changeset */
|
||||
){
|
||||
return sessionGenerateChangeset(pSession, 1, 0, 0, pnPatchset, ppPatchset);
|
||||
return sessionGenerateChangeset(
|
||||
pSession, SESSIONS_PATCHSET, 0, 0, pnPatchset, ppPatchset);
|
||||
}
|
||||
|
||||
int sqlite3session_fullchangeset(
|
||||
sqlite3_session *pSession, /* Session object */
|
||||
int *pnChangeset, /* OUT: Size of buffer at *ppChangeset */
|
||||
void **ppChangeset /* OUT: Buffer containing changeset */
|
||||
){
|
||||
return sessionGenerateChangeset(
|
||||
pSession, SESSIONS_FULLCHANGESET, 0, 0, pnChangeset, ppChangeset);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Enable or disable the session object passed as the first argument.
|
||||
*/
|
||||
@@ -4463,10 +4484,11 @@ static int sessionChangegroupOutput(
|
||||
** hash tables attached to the SessionTable objects in list p->pList.
|
||||
*/
|
||||
for(pTab=pGrp->pList; rc==SQLITE_OK && pTab; pTab=pTab->pNext){
|
||||
int eChangeset = pGrp->bPatch ? SESSIONS_PATCHSET : SESSIONS_CHANGESET;
|
||||
int i;
|
||||
if( pTab->nEntry==0 ) continue;
|
||||
|
||||
sessionAppendTableHdr(&buf, pGrp->bPatch, pTab, &rc);
|
||||
sessionAppendTableHdr(&buf, eChangeset, pTab, &rc);
|
||||
for(i=0; i<pTab->nChange; i++){
|
||||
SessionChange *p;
|
||||
for(p=pTab->apChange[i]; p; p=p->pNext){
|
||||
|
||||
@@ -157,7 +157,7 @@ int sqlite3session_attach(
|
||||
** CAPI3REF: Set a table filter on a Session Object.
|
||||
**
|
||||
** The second argument (xFilter) is the "filter callback". For changes to rows
|
||||
** in tables that are not attached to the Session oject, the filter is called
|
||||
** in tables that are not attached to the Session object, the filter is called
|
||||
** to determine whether changes to the table's rows should be tracked or not.
|
||||
** If xFilter returns 0, changes is not tracked. Note that once a table is
|
||||
** attached, xFilter will not be called again.
|
||||
@@ -281,6 +281,19 @@ int sqlite3session_changeset(
|
||||
void **ppChangeset /* OUT: Buffer containing changeset */
|
||||
);
|
||||
|
||||
/*
|
||||
** CAPI3REF: Generate A Full Changeset From A Session Object
|
||||
**
|
||||
** This function is similar to sqlite3session_changeset(), except that for
|
||||
** each row affected by an UPDATE statement, all old.* values are recorded
|
||||
** as part of the changeset, not just those modified.
|
||||
*/
|
||||
int sqlite3session_fullchangeset(
|
||||
sqlite3_session *pSession, /* Session object */
|
||||
int *pnChangeset, /* OUT: Size of buffer at *ppChangeset */
|
||||
void **ppChangeset /* OUT: Buffer containing changeset */
|
||||
);
|
||||
|
||||
/*
|
||||
** CAPI3REF: Load The Difference Between Tables Into A Session
|
||||
**
|
||||
@@ -423,7 +436,7 @@ int sqlite3session_isempty(sqlite3_session *pSession);
|
||||
** [sqlite3changeset_invert()] functions, all changes within the changeset
|
||||
** that apply to a single table are grouped together. This means that when
|
||||
** an application iterates through a changeset using an iterator created by
|
||||
** this function, all changes that relate to a single table are visted
|
||||
** this function, all changes that relate to a single table are visited
|
||||
** consecutively. There is no chance that the iterator will visit a change
|
||||
** the applies to table X, then one for table Y, and then later on visit
|
||||
** another change for table X.
|
||||
@@ -510,7 +523,7 @@ int sqlite3changeset_op(
|
||||
** 0x01 if the corresponding column is part of the tables primary key, or
|
||||
** 0x00 if it is not.
|
||||
**
|
||||
** If argumet pnCol is not NULL, then *pnCol is set to the number of columns
|
||||
** If argument pnCol is not NULL, then *pnCol is set to the number of columns
|
||||
** in the table.
|
||||
**
|
||||
** If this function is called when the iterator does not point to a valid
|
||||
@@ -727,12 +740,12 @@ int sqlite3changeset_concat(
|
||||
|
||||
|
||||
/*
|
||||
** Changegroup handle.
|
||||
** CAPI3REF: Changegroup Handle
|
||||
*/
|
||||
typedef struct sqlite3_changegroup sqlite3_changegroup;
|
||||
|
||||
/*
|
||||
** CAPI3REF: Combine two or more changesets into a single changeset.
|
||||
** CAPI3REF: Create A New Changegroup Object
|
||||
**
|
||||
** An sqlite3_changegroup object is used to combine two or more changesets
|
||||
** (or patchsets) into a single changeset (or patchset). A single changegroup
|
||||
@@ -769,6 +782,8 @@ typedef struct sqlite3_changegroup sqlite3_changegroup;
|
||||
int sqlite3changegroup_new(sqlite3_changegroup **pp);
|
||||
|
||||
/*
|
||||
** CAPI3REF: Add A Changeset To A Changegroup
|
||||
**
|
||||
** Add all changes within the changeset (or patchset) in buffer pData (size
|
||||
** nData bytes) to the changegroup.
|
||||
**
|
||||
@@ -783,7 +798,7 @@ int sqlite3changegroup_new(sqlite3_changegroup **pp);
|
||||
** apply to the same row as a change already present in the changegroup if
|
||||
** the two rows have the same primary key.
|
||||
**
|
||||
** Changes to rows that that do not already appear in the changegroup are
|
||||
** Changes to rows that do not already appear in the changegroup are
|
||||
** simply copied into it. Or, if both the new changeset and the changegroup
|
||||
** contain changes that apply to a single row, the final contents of the
|
||||
** changegroup depends on the type of each change, as follows:
|
||||
@@ -844,6 +859,8 @@ int sqlite3changegroup_new(sqlite3_changegroup **pp);
|
||||
int sqlite3changegroup_add(sqlite3_changegroup*, int nData, void *pData);
|
||||
|
||||
/*
|
||||
** CAPI3REF: Obtain A Composite Changeset From A Changegroup
|
||||
**
|
||||
** Obtain a buffer containing a changeset (or patchset) representing the
|
||||
** current contents of the changegroup. If the inputs to the changegroup
|
||||
** were themselves changesets, the output is a changeset. Or, if the
|
||||
@@ -872,7 +889,7 @@ int sqlite3changegroup_output(
|
||||
);
|
||||
|
||||
/*
|
||||
** Delete a changegroup object.
|
||||
** CAPI3REF: Delete A Changegroup Object
|
||||
*/
|
||||
void sqlite3changegroup_delete(sqlite3_changegroup*);
|
||||
|
||||
|
||||
+276
-35
@@ -5,7 +5,14 @@
|
||||
#include "sqlite3session.h"
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
#include <tcl.h>
|
||||
#if defined(INCLUDE_SQLITE_TCL_H)
|
||||
# include "sqlite_tcl.h"
|
||||
#else
|
||||
# include "tcl.h"
|
||||
# ifndef SQLITE_TCLAPI
|
||||
# define SQLITE_TCLAPI
|
||||
# endif
|
||||
#endif
|
||||
|
||||
typedef struct TestSession TestSession;
|
||||
struct TestSession {
|
||||
@@ -22,6 +29,107 @@ struct TestStreamInput {
|
||||
int iData; /* Bytes of data already read by sessions */
|
||||
};
|
||||
|
||||
/*
|
||||
** Extract an sqlite3* db handle from the object passed as the second
|
||||
** argument. If successful, set *pDb to point to the db handle and return
|
||||
** TCL_OK. Otherwise, return TCL_ERROR.
|
||||
*/
|
||||
static int dbHandleFromObj(Tcl_Interp *interp, Tcl_Obj *pObj, sqlite3 **pDb){
|
||||
Tcl_CmdInfo info;
|
||||
if( 0==Tcl_GetCommandInfo(interp, Tcl_GetString(pObj), &info) ){
|
||||
Tcl_AppendResult(interp, "no such handle: ", Tcl_GetString(pObj), 0);
|
||||
return TCL_ERROR;
|
||||
}
|
||||
|
||||
*pDb = *(sqlite3 **)info.objClientData;
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
/*************************************************************************
|
||||
** The following code is copied byte-for-byte from the sessions module
|
||||
** documentation. It is used by some of the sessions modules tests to
|
||||
** ensure that the example in the documentation does actually work.
|
||||
*/
|
||||
/*
|
||||
** Argument zSql points to a buffer containing an SQL script to execute
|
||||
** against the database handle passed as the first argument. As well as
|
||||
** executing the SQL script, this function collects a changeset recording
|
||||
** all changes made to the "main" database file. Assuming no error occurs,
|
||||
** output variables (*ppChangeset) and (*pnChangeset) are set to point
|
||||
** to a buffer containing the changeset and the size of the changeset in
|
||||
** bytes before returning SQLITE_OK. In this case it is the responsibility
|
||||
** of the caller to eventually free the changeset blob by passing it to
|
||||
** the sqlite3_free function.
|
||||
**
|
||||
** Or, if an error does occur, return an SQLite error code. The final
|
||||
** value of (*pChangeset) and (*pnChangeset) are undefined in this case.
|
||||
*/
|
||||
int sql_exec_changeset(
|
||||
sqlite3 *db, /* Database handle */
|
||||
const char *zSql, /* SQL script to execute */
|
||||
int *pnChangeset, /* OUT: Size of changeset blob in bytes */
|
||||
void **ppChangeset /* OUT: Pointer to changeset blob */
|
||||
){
|
||||
sqlite3_session *pSession = 0;
|
||||
int rc;
|
||||
|
||||
/* Create a new session object */
|
||||
rc = sqlite3session_create(db, "main", &pSession);
|
||||
|
||||
/* Configure the session object to record changes to all tables */
|
||||
if( rc==SQLITE_OK ) rc = sqlite3session_attach(pSession, NULL);
|
||||
|
||||
/* Execute the SQL script */
|
||||
if( rc==SQLITE_OK ) rc = sqlite3_exec(db, zSql, 0, 0, 0);
|
||||
|
||||
/* Collect the changeset */
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = sqlite3session_changeset(pSession, pnChangeset, ppChangeset);
|
||||
}
|
||||
|
||||
/* Delete the session object */
|
||||
sqlite3session_delete(pSession);
|
||||
|
||||
return rc;
|
||||
}
|
||||
/************************************************************************/
|
||||
|
||||
/*
|
||||
** Tclcmd: sql_exec_changeset DB SQL
|
||||
*/
|
||||
static int SQLITE_TCLAPI test_sql_exec_changeset(
|
||||
void * clientData,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
Tcl_Obj *CONST objv[]
|
||||
){
|
||||
const char *zSql;
|
||||
sqlite3 *db;
|
||||
void *pChangeset;
|
||||
int nChangeset;
|
||||
int rc;
|
||||
|
||||
if( objc!=3 ){
|
||||
Tcl_WrongNumArgs(interp, 1, objv, "DB SQL");
|
||||
return TCL_ERROR;
|
||||
}
|
||||
if( dbHandleFromObj(interp, objv[1], &db) ) return TCL_ERROR;
|
||||
zSql = (const char*)Tcl_GetString(objv[2]);
|
||||
|
||||
rc = sql_exec_changeset(db, zSql, &nChangeset, &pChangeset);
|
||||
if( rc!=SQLITE_OK ){
|
||||
Tcl_ResetResult(interp);
|
||||
Tcl_AppendResult(interp, "error in sql_exec_changeset()", 0);
|
||||
return TCL_ERROR;
|
||||
}
|
||||
|
||||
Tcl_SetObjResult(interp, Tcl_NewByteArrayObj(pChangeset, nChangeset));
|
||||
sqlite3_free(pChangeset);
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
|
||||
|
||||
#define SESSION_STREAM_TCL_VAR "sqlite3session_streams"
|
||||
|
||||
/*
|
||||
@@ -106,8 +214,9 @@ static int testStreamOutput(
|
||||
** $session indirect INTEGER
|
||||
** $session patchset
|
||||
** $session table_filter SCRIPT
|
||||
** $session fullchangeset
|
||||
*/
|
||||
static int test_session_cmd(
|
||||
static int SQLITE_TCLAPI test_session_cmd(
|
||||
void *clientData,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
@@ -119,17 +228,17 @@ static int test_session_cmd(
|
||||
const char *zSub;
|
||||
int nArg;
|
||||
const char *zMsg;
|
||||
int iSub;
|
||||
} aSub[] = {
|
||||
{ "attach", 1, "TABLE", }, /* 0 */
|
||||
{ "changeset", 0, "", }, /* 1 */
|
||||
{ "delete", 0, "", }, /* 2 */
|
||||
{ "enable", 1, "BOOL", }, /* 3 */
|
||||
{ "indirect", 1, "BOOL", }, /* 4 */
|
||||
{ "isempty", 0, "", }, /* 5 */
|
||||
{ "table_filter", 1, "SCRIPT", }, /* 6 */
|
||||
{ "attach", 1, "TABLE" }, /* 0 */
|
||||
{ "changeset", 0, "" }, /* 1 */
|
||||
{ "delete", 0, "" }, /* 2 */
|
||||
{ "enable", 1, "BOOL" }, /* 3 */
|
||||
{ "indirect", 1, "BOOL" }, /* 4 */
|
||||
{ "isempty", 0, "" }, /* 5 */
|
||||
{ "table_filter", 1, "SCRIPT" }, /* 6 */
|
||||
{ "patchset", 0, "", }, /* 7 */
|
||||
{ "diff", 2, "FROMDB TBL", }, /* 8 */
|
||||
{ "diff", 2, "FROMDB TBL" }, /* 8 */
|
||||
{ "fullchangeset",0, "" }, /* 9 */
|
||||
{ 0 }
|
||||
};
|
||||
int iSub;
|
||||
@@ -159,10 +268,11 @@ static int test_session_cmd(
|
||||
break;
|
||||
}
|
||||
|
||||
case 9: /* fullchangeset */
|
||||
case 7: /* patchset */
|
||||
case 1: { /* changeset */
|
||||
TestSessionsBlob o = {0, 0};
|
||||
if( test_tcl_integer(interp, SESSION_STREAM_TCL_VAR) ){
|
||||
if( iSub!=9 && test_tcl_integer(interp, SESSION_STREAM_TCL_VAR) ){
|
||||
void *pCtx = (void*)&o;
|
||||
if( iSub==7 ){
|
||||
rc = sqlite3session_patchset_strm(pSession, testStreamOutput, pCtx);
|
||||
@@ -172,6 +282,8 @@ static int test_session_cmd(
|
||||
}else{
|
||||
if( iSub==7 ){
|
||||
rc = sqlite3session_patchset(pSession, &o.n, &o.p);
|
||||
}else if( iSub==9 ){
|
||||
rc = sqlite3session_fullchangeset(pSession, &o.n, &o.p);
|
||||
}else{
|
||||
rc = sqlite3session_changeset(pSession, &o.n, &o.p);
|
||||
}
|
||||
@@ -186,6 +298,7 @@ static int test_session_cmd(
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
case 2: /* delete */
|
||||
Tcl_DeleteCommand(interp, Tcl_GetString(objv[0]));
|
||||
break;
|
||||
@@ -240,7 +353,7 @@ static int test_session_cmd(
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
static void test_session_del(void *clientData){
|
||||
static void SQLITE_TCLAPI test_session_del(void *clientData){
|
||||
TestSession *p = (TestSession*)clientData;
|
||||
if( p->pFilterScript ) Tcl_DecrRefCount(p->pFilterScript);
|
||||
sqlite3session_delete(p->pSession);
|
||||
@@ -250,7 +363,7 @@ static void test_session_del(void *clientData){
|
||||
/*
|
||||
** Tclcmd: sqlite3session CMD DB-HANDLE DB-NAME
|
||||
*/
|
||||
static int test_sqlite3session(
|
||||
static int SQLITE_TCLAPI test_sqlite3session(
|
||||
void * clientData,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
@@ -366,7 +479,7 @@ static int test_filter_handler(
|
||||
|
||||
Tcl_DecrRefCount(pEval);
|
||||
return res;
|
||||
}
|
||||
}
|
||||
|
||||
static int test_conflict_handler(
|
||||
void *pCtx, /* Pointer to TestConflictHandler structure */
|
||||
@@ -606,7 +719,7 @@ static int testStreamInput(
|
||||
/*
|
||||
** sqlite3changeset_apply DB CHANGESET CONFLICT-SCRIPT ?FILTER-SCRIPT?
|
||||
*/
|
||||
static int test_sqlite3changeset_apply(
|
||||
static int SQLITE_TCLAPI test_sqlite3changeset_apply(
|
||||
void * clientData,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
@@ -661,7 +774,7 @@ static int test_sqlite3changeset_apply(
|
||||
/*
|
||||
** sqlite3changeset_apply_replace_all DB CHANGESET
|
||||
*/
|
||||
static int test_sqlite3changeset_apply_replace_all(
|
||||
static int SQLITE_TCLAPI test_sqlite3changeset_apply_replace_all(
|
||||
void * clientData,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
@@ -696,7 +809,7 @@ static int test_sqlite3changeset_apply_replace_all(
|
||||
/*
|
||||
** sqlite3changeset_invert CHANGESET
|
||||
*/
|
||||
static int test_sqlite3changeset_invert(
|
||||
static int SQLITE_TCLAPI test_sqlite3changeset_invert(
|
||||
void * clientData,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
@@ -735,7 +848,7 @@ static int test_sqlite3changeset_invert(
|
||||
/*
|
||||
** sqlite3changeset_concat LEFT RIGHT
|
||||
*/
|
||||
static int test_sqlite3changeset_concat(
|
||||
static int SQLITE_TCLAPI test_sqlite3changeset_concat(
|
||||
void * clientData,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
@@ -783,7 +896,7 @@ static int test_sqlite3changeset_concat(
|
||||
/*
|
||||
** sqlite3session_foreach VARNAME CHANGESET SCRIPT
|
||||
*/
|
||||
static int test_sqlite3session_foreach(
|
||||
static int SQLITE_TCLAPI test_sqlite3session_foreach(
|
||||
void * clientData,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
@@ -911,23 +1024,151 @@ static int test_sqlite3session_foreach(
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
#include "sqlite3changebatch.h"
|
||||
|
||||
typedef struct TestChangebatch TestChangebatch;
|
||||
struct TestChangebatch {
|
||||
sqlite3_changebatch *pChangebatch;
|
||||
};
|
||||
|
||||
/*
|
||||
** Tclcmd: $changebatch add BLOB
|
||||
** $changebatch zero
|
||||
** $changebatch delete
|
||||
*/
|
||||
static int SQLITE_TCLAPI test_changebatch_cmd(
|
||||
void *clientData,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
Tcl_Obj *CONST objv[]
|
||||
){
|
||||
TestChangebatch *p = (TestChangebatch*)clientData;
|
||||
sqlite3_changebatch *pChangebatch = p->pChangebatch;
|
||||
struct SessionSubcmd {
|
||||
const char *zSub;
|
||||
int nArg;
|
||||
const char *zMsg;
|
||||
int iSub;
|
||||
} aSub[] = {
|
||||
{ "add", 1, "CHANGESET", }, /* 0 */
|
||||
{ "zero", 0, "", }, /* 1 */
|
||||
{ "delete", 0, "", }, /* 2 */
|
||||
{ 0 }
|
||||
};
|
||||
int iSub;
|
||||
int rc;
|
||||
|
||||
if( objc<2 ){
|
||||
Tcl_WrongNumArgs(interp, 1, objv, "SUBCOMMAND ...");
|
||||
return TCL_ERROR;
|
||||
}
|
||||
rc = Tcl_GetIndexFromObjStruct(interp,
|
||||
objv[1], aSub, sizeof(aSub[0]), "sub-command", 0, &iSub
|
||||
);
|
||||
if( rc!=TCL_OK ) return rc;
|
||||
if( objc!=2+aSub[iSub].nArg ){
|
||||
Tcl_WrongNumArgs(interp, 2, objv, aSub[iSub].zMsg);
|
||||
return TCL_ERROR;
|
||||
}
|
||||
|
||||
switch( iSub ){
|
||||
case 0: { /* add */
|
||||
int nArg;
|
||||
unsigned char *pArg = Tcl_GetByteArrayFromObj(objv[2], &nArg);
|
||||
rc = sqlite3changebatch_add(pChangebatch, pArg, nArg);
|
||||
if( rc!=SQLITE_OK && rc!=SQLITE_CONSTRAINT ){
|
||||
return test_session_error(interp, rc, 0);
|
||||
}else{
|
||||
extern const char *sqlite3ErrName(int);
|
||||
Tcl_SetObjResult(interp, Tcl_NewStringObj(sqlite3ErrName(rc), -1));
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case 1: { /* zero */
|
||||
sqlite3changebatch_zero(pChangebatch);
|
||||
break;
|
||||
}
|
||||
|
||||
case 2: /* delete */
|
||||
Tcl_DeleteCommand(interp, Tcl_GetString(objv[0]));
|
||||
break;
|
||||
}
|
||||
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
static void SQLITE_TCLAPI test_changebatch_del(void *clientData){
|
||||
TestChangebatch *p = (TestChangebatch*)clientData;
|
||||
sqlite3changebatch_delete(p->pChangebatch);
|
||||
ckfree((char*)p);
|
||||
}
|
||||
|
||||
/*
|
||||
** Tclcmd: sqlite3changebatch CMD DB-HANDLE
|
||||
*/
|
||||
static int SQLITE_TCLAPI test_sqlite3changebatch(
|
||||
void * clientData,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
Tcl_Obj *CONST objv[]
|
||||
){
|
||||
sqlite3 *db;
|
||||
Tcl_CmdInfo info;
|
||||
int rc; /* sqlite3session_create() return code */
|
||||
TestChangebatch *p; /* New wrapper object */
|
||||
|
||||
if( objc!=3 ){
|
||||
Tcl_WrongNumArgs(interp, 1, objv, "CMD DB-HANDLE");
|
||||
return TCL_ERROR;
|
||||
}
|
||||
|
||||
if( 0==Tcl_GetCommandInfo(interp, Tcl_GetString(objv[2]), &info) ){
|
||||
Tcl_AppendResult(interp, "no such handle: ", Tcl_GetString(objv[2]), 0);
|
||||
return TCL_ERROR;
|
||||
}
|
||||
db = *(sqlite3 **)info.objClientData;
|
||||
|
||||
p = (TestChangebatch*)ckalloc(sizeof(TestChangebatch));
|
||||
memset(p, 0, sizeof(TestChangebatch));
|
||||
rc = sqlite3changebatch_new(db, &p->pChangebatch);
|
||||
if( rc!=SQLITE_OK ){
|
||||
ckfree((char*)p);
|
||||
return test_session_error(interp, rc, 0);
|
||||
}
|
||||
|
||||
Tcl_CreateObjCommand(
|
||||
interp, Tcl_GetString(objv[1]), test_changebatch_cmd, (ClientData)p,
|
||||
test_changebatch_del
|
||||
);
|
||||
Tcl_SetObjResult(interp, objv[1]);
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
int TestSession_Init(Tcl_Interp *interp){
|
||||
Tcl_CreateObjCommand(interp, "sqlite3session", test_sqlite3session, 0, 0);
|
||||
struct Cmd {
|
||||
const char *zCmd;
|
||||
Tcl_ObjCmdProc *xProc;
|
||||
} aCmd[] = {
|
||||
{ "sqlite3session", test_sqlite3session },
|
||||
{ "sqlite3session_foreach", test_sqlite3session_foreach },
|
||||
{ "sqlite3changeset_invert", test_sqlite3changeset_invert },
|
||||
{ "sqlite3changeset_concat", test_sqlite3changeset_concat },
|
||||
{ "sqlite3changeset_apply", test_sqlite3changeset_apply },
|
||||
{ "sqlite3changeset_apply_replace_all",
|
||||
test_sqlite3changeset_apply_replace_all },
|
||||
{ "sql_exec_changeset", test_sql_exec_changeset },
|
||||
};
|
||||
int i;
|
||||
|
||||
for(i=0; i<sizeof(aCmd)/sizeof(struct Cmd); i++){
|
||||
struct Cmd *p = &aCmd[i];
|
||||
Tcl_CreateObjCommand(interp, p->zCmd, p->xProc, 0, 0);
|
||||
}
|
||||
|
||||
|
||||
Tcl_CreateObjCommand(
|
||||
interp, "sqlite3session_foreach", test_sqlite3session_foreach, 0, 0
|
||||
);
|
||||
Tcl_CreateObjCommand(
|
||||
interp, "sqlite3changeset_invert", test_sqlite3changeset_invert, 0, 0
|
||||
);
|
||||
Tcl_CreateObjCommand(
|
||||
interp, "sqlite3changeset_concat", test_sqlite3changeset_concat, 0, 0
|
||||
);
|
||||
Tcl_CreateObjCommand(
|
||||
interp, "sqlite3changeset_apply", test_sqlite3changeset_apply, 0, 0
|
||||
);
|
||||
Tcl_CreateObjCommand(
|
||||
interp, "sqlite3changeset_apply_replace_all",
|
||||
test_sqlite3changeset_apply_replace_all, 0, 0
|
||||
interp, "sqlite3changebatch", test_sqlite3changebatch, 0, 0
|
||||
);
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
@@ -293,6 +293,7 @@ TESTSRC = \
|
||||
$(TOP)/src/test_blob.c \
|
||||
$(TOP)/src/test_btree.c \
|
||||
$(TOP)/src/test_config.c \
|
||||
$(TOP)/src/test_delete.c \
|
||||
$(TOP)/src/test_demovfs.c \
|
||||
$(TOP)/src/test_devsym.c \
|
||||
$(TOP)/src/test_fs.c \
|
||||
@@ -334,6 +335,7 @@ TESTSRC += \
|
||||
$(TOP)/ext/misc/nextchar.c \
|
||||
$(TOP)/ext/misc/percentile.c \
|
||||
$(TOP)/ext/misc/regexp.c \
|
||||
$(TOP)/ext/misc/remember.c \
|
||||
$(TOP)/ext/misc/series.c \
|
||||
$(TOP)/ext/misc/spellfix.c \
|
||||
$(TOP)/ext/misc/totype.c \
|
||||
@@ -390,6 +392,7 @@ TESTSRC2 = \
|
||||
$(TOP)/ext/fts3/fts3_write.c \
|
||||
$(TOP)/ext/async/sqlite3async.c \
|
||||
$(TOP)/ext/session/sqlite3session.c \
|
||||
$(TOP)/ext/session/sqlite3changebatch.c \
|
||||
$(TOP)/ext/session/test_session.c
|
||||
|
||||
# Header files used by all library source files.
|
||||
@@ -461,7 +464,8 @@ FUZZDATA = \
|
||||
$(TOP)/test/fuzzdata1.db \
|
||||
$(TOP)/test/fuzzdata2.db \
|
||||
$(TOP)/test/fuzzdata3.db \
|
||||
$(TOP)/test/fuzzdata4.db
|
||||
$(TOP)/test/fuzzdata4.db \
|
||||
$(TOP)/test/fuzzdata5.db
|
||||
|
||||
# Standard options to testfixture
|
||||
#
|
||||
@@ -470,6 +474,8 @@ TESTOPTS = --verbose=file --output=test-out.txt
|
||||
# Extra compiler options for various shell tools
|
||||
#
|
||||
SHELL_OPT = -DSQLITE_ENABLE_JSON1 -DSQLITE_ENABLE_FTS4 -DSQLITE_ENABLE_FTS5
|
||||
SHELL_OPT += -DSQLITE_ENABLE_EXPLAIN_COMMENTS
|
||||
SHELL_OPT += -DSQLITE_ENABLE_UNKNOWN_SQL_FUNCTION
|
||||
FUZZERSHELL_OPT = -DSQLITE_ENABLE_JSON1
|
||||
FUZZCHECK_OPT = -DSQLITE_ENABLE_JSON1 -DSQLITE_ENABLE_MEMSYS5
|
||||
|
||||
@@ -508,10 +514,15 @@ fuzzershell$(EXE): $(TOP)/tool/fuzzershell.c sqlite3.c sqlite3.h
|
||||
$(FUZZERSHELL_OPT) $(TOP)/tool/fuzzershell.c sqlite3.c \
|
||||
$(TLIBS) $(THREADLIB)
|
||||
|
||||
fuzzcheck$(EXE): $(TOP)/test/fuzzcheck.c sqlite3.c sqlite3.h
|
||||
fuzzcheck$(EXE): $(TOP)/test/fuzzcheck.c sqlite3.c sqlite3.h $(TOP)/test/ossfuzz.c
|
||||
$(TCCX) -o fuzzcheck$(EXE) -DSQLITE_THREADSAFE=0 -DSQLITE_OMIT_LOAD_EXTENSION \
|
||||
-DSQLITE_ENABLE_MEMSYS5 $(FUZZCHECK_OPT) -DSQLITE_OSS_FUZZ \
|
||||
$(TOP)/test/fuzzcheck.c $(TOP)/test/ossfuzz.c sqlite3.c $(TLIBS) $(THREADLIB)
|
||||
|
||||
ossshell$(EXE): $(TOP)/test/ossfuzz.c $(TOP)/test/ossshell.c sqlite3.c sqlite3.h
|
||||
$(TCCX) -o ossshell$(EXE) -DSQLITE_THREADSAFE=0 -DSQLITE_OMIT_LOAD_EXTENSION \
|
||||
-DSQLITE_ENABLE_MEMSYS5 $(FUZZCHECK_OPT) \
|
||||
$(TOP)/test/fuzzcheck.c sqlite3.c $(TLIBS) $(THREADLIB)
|
||||
$(TOP)/test/ossfuzz.c $(TOP)/test/ossshell.c sqlite3.c $(TLIBS) $(THREADLIB)
|
||||
|
||||
mptester$(EXE): sqlite3.c $(TOP)/mptest/mptest.c
|
||||
$(TCCX) -o $@ -I. $(TOP)/mptest/mptest.c sqlite3.c \
|
||||
|
||||
+1
-1
@@ -1 +1 @@
|
||||
a204ba99db34b356acb259189158a32d2df25da0
|
||||
391344d88a284f92c59b8d96a315b69292641de0
|
||||
+14
-11
@@ -413,7 +413,7 @@ void sqlite3AlterRenameTable(
|
||||
pTab = sqlite3LocateTableItem(pParse, 0, &pSrc->a[0]);
|
||||
if( !pTab ) goto exit_rename_table;
|
||||
iDb = sqlite3SchemaToIndex(pParse->db, pTab->pSchema);
|
||||
zDb = db->aDb[iDb].zName;
|
||||
zDb = db->aDb[iDb].zDbSName;
|
||||
db->flags |= SQLITE_PreferBuiltin;
|
||||
|
||||
/* Get a NULL terminated version of the new table name. */
|
||||
@@ -601,6 +601,7 @@ void sqlite3AlterFinishAddColumn(Parse *pParse, Token *pColDef){
|
||||
Expr *pDflt; /* Default value for the new column */
|
||||
sqlite3 *db; /* The database connection; */
|
||||
Vdbe *v = pParse->pVdbe; /* The prepared statement under construction */
|
||||
int r1; /* Temporary registers */
|
||||
|
||||
db = pParse->db;
|
||||
if( pParse->nErr || db->mallocFailed ) return;
|
||||
@@ -610,7 +611,7 @@ void sqlite3AlterFinishAddColumn(Parse *pParse, Token *pColDef){
|
||||
|
||||
assert( sqlite3BtreeHoldsAllMutexes(db) );
|
||||
iDb = sqlite3SchemaToIndex(db, pNew->pSchema);
|
||||
zDb = db->aDb[iDb].zName;
|
||||
zDb = db->aDb[iDb].zDbSName;
|
||||
zTab = &pNew->zName[16]; /* Skip the "sqlite_altertab_" prefix on the name */
|
||||
pCol = &pNew->aCol[pNew->nCol-1];
|
||||
pDflt = pCol->pDflt;
|
||||
@@ -695,16 +696,18 @@ void sqlite3AlterFinishAddColumn(Parse *pParse, Token *pColDef){
|
||||
db->flags = savedDbFlags;
|
||||
}
|
||||
|
||||
/* If the default value of the new column is NULL, then the file
|
||||
** format to 2. If the default value of the new column is not NULL,
|
||||
** the file format be 3. Back when this feature was first added
|
||||
** in 2006, we went to the trouble to upgrade the file format to the
|
||||
** minimum support values. But 10-years on, we can assume that all
|
||||
** extent versions of SQLite support file-format 4, so we always and
|
||||
** unconditionally upgrade to 4.
|
||||
/* Make sure the schema version is at least 3. But do not upgrade
|
||||
** from less than 3 to 4, as that will corrupt any preexisting DESC
|
||||
** index.
|
||||
*/
|
||||
sqlite3VdbeAddOp3(v, OP_SetCookie, iDb, BTREE_FILE_FORMAT,
|
||||
SQLITE_MAX_FILE_FORMAT);
|
||||
r1 = sqlite3GetTempReg(pParse);
|
||||
sqlite3VdbeAddOp3(v, OP_ReadCookie, iDb, r1, BTREE_FILE_FORMAT);
|
||||
sqlite3VdbeUsesBtree(v, iDb);
|
||||
sqlite3VdbeAddOp2(v, OP_AddImm, r1, -2);
|
||||
sqlite3VdbeAddOp2(v, OP_IfPos, r1, sqlite3VdbeCurrentAddr(v)+2);
|
||||
VdbeCoverage(v);
|
||||
sqlite3VdbeAddOp3(v, OP_SetCookie, iDb, BTREE_FILE_FORMAT, 3);
|
||||
sqlite3ReleaseTempReg(pParse, r1);
|
||||
|
||||
/* Reload the schema of the modified table. */
|
||||
reloadTableSchema(pParse, pTab, pTab->zName);
|
||||
|
||||
+6
-6
@@ -210,14 +210,14 @@ static void openStatTable(
|
||||
for(i=0; i<ArraySize(aTable); i++){
|
||||
const char *zTab = aTable[i].zName;
|
||||
Table *pStat;
|
||||
if( (pStat = sqlite3FindTable(db, zTab, pDb->zName))==0 ){
|
||||
if( (pStat = sqlite3FindTable(db, zTab, pDb->zDbSName))==0 ){
|
||||
if( aTable[i].zCols ){
|
||||
/* The sqlite_statN table does not exist. Create it. Note that a
|
||||
** side-effect of the CREATE TABLE statement is to leave the rootpage
|
||||
** of the new table in register pParse->regRoot. This is important
|
||||
** because the OpenWrite opcode below will be needing it. */
|
||||
sqlite3NestedParse(pParse,
|
||||
"CREATE TABLE %Q.%s(%s)", pDb->zName, zTab, aTable[i].zCols
|
||||
"CREATE TABLE %Q.%s(%s)", pDb->zDbSName, zTab, aTable[i].zCols
|
||||
);
|
||||
aRoot[i] = pParse->regRoot;
|
||||
aCreateTbl[i] = OPFLAG_P2ISREG;
|
||||
@@ -232,7 +232,7 @@ static void openStatTable(
|
||||
if( zWhere ){
|
||||
sqlite3NestedParse(pParse,
|
||||
"DELETE FROM %Q.%s WHERE %s=%Q",
|
||||
pDb->zName, zTab, zWhereType, zWhere
|
||||
pDb->zDbSName, zTab, zWhereType, zWhere
|
||||
);
|
||||
}else{
|
||||
/* The sqlite_stat[134] table already exists. Delete all rows. */
|
||||
@@ -994,7 +994,7 @@ static void analyzeOneTable(
|
||||
assert( sqlite3SchemaMutexHeld(db, iDb, 0) );
|
||||
#ifndef SQLITE_OMIT_AUTHORIZATION
|
||||
if( sqlite3AuthCheck(pParse, SQLITE_ANALYZE, pTab->zName, 0,
|
||||
db->aDb[iDb].zName ) ){
|
||||
db->aDb[iDb].zDbSName ) ){
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
@@ -1384,7 +1384,7 @@ void sqlite3Analyze(Parse *pParse, Token *pName1, Token *pName2){
|
||||
/* Form 3: Analyze the fully qualified table name */
|
||||
iDb = sqlite3TwoPartName(pParse, pName1, pName2, &pTableName);
|
||||
if( iDb>=0 ){
|
||||
zDb = db->aDb[iDb].zName;
|
||||
zDb = db->aDb[iDb].zDbSName;
|
||||
z = sqlite3NameFromToken(db, pTableName);
|
||||
if( z ){
|
||||
if( (pIdx = sqlite3FindIndex(db, z, zDb))!=0 ){
|
||||
@@ -1844,7 +1844,7 @@ int sqlite3AnalysisLoad(sqlite3 *db, int iDb){
|
||||
|
||||
/* Load new statistics out of the sqlite_stat1 table */
|
||||
sInfo.db = db;
|
||||
sInfo.zDatabase = db->aDb[iDb].zName;
|
||||
sInfo.zDatabase = db->aDb[iDb].zDbSName;
|
||||
if( sqlite3FindTable(db, "sqlite_stat1", sInfo.zDatabase)!=0 ){
|
||||
zSql = sqlite3MPrintf(db,
|
||||
"SELECT tbl,idx,stat FROM %Q.sqlite_stat1", sInfo.zDatabase);
|
||||
|
||||
+8
-7
@@ -97,7 +97,7 @@ static void attachFunc(
|
||||
goto attach_error;
|
||||
}
|
||||
for(i=0; i<db->nDb; i++){
|
||||
char *z = db->aDb[i].zName;
|
||||
char *z = db->aDb[i].zDbSName;
|
||||
assert( z && zName );
|
||||
if( sqlite3StrICmp(z, zName)==0 ){
|
||||
zErrDyn = sqlite3MPrintf(db, "database %s is already in use", zName);
|
||||
@@ -162,8 +162,8 @@ static void attachFunc(
|
||||
sqlite3BtreeLeave(aNew->pBt);
|
||||
}
|
||||
aNew->safety_level = SQLITE_DEFAULT_SYNCHRONOUS+1;
|
||||
aNew->zName = sqlite3DbStrDup(db, zName);
|
||||
if( rc==SQLITE_OK && aNew->zName==0 ){
|
||||
aNew->zDbSName = sqlite3DbStrDup(db, zName);
|
||||
if( rc==SQLITE_OK && aNew->zDbSName==0 ){
|
||||
rc = SQLITE_NOMEM_BKPT;
|
||||
}
|
||||
|
||||
@@ -192,7 +192,7 @@ static void attachFunc(
|
||||
case SQLITE_NULL:
|
||||
/* No key specified. Use the key from the main database */
|
||||
sqlite3CodecGetKey(db, 0, (void**)&zKey, &nKey);
|
||||
if( nKey>0 || sqlite3BtreeGetOptimalReserve(db->aDb[0].pBt)>0 ){
|
||||
if( nKey || sqlite3BtreeGetOptimalReserve(db->aDb[0].pBt)>0 ){
|
||||
rc = sqlite3CodecAttach(db, db->nDb-1, zKey, nKey);
|
||||
}
|
||||
break;
|
||||
@@ -275,7 +275,7 @@ static void detachFunc(
|
||||
for(i=0; i<db->nDb; i++){
|
||||
pDb = &db->aDb[i];
|
||||
if( pDb->pBt==0 ) continue;
|
||||
if( sqlite3StrICmp(pDb->zName, zName)==0 ) break;
|
||||
if( sqlite3StrICmp(pDb->zDbSName, zName)==0 ) break;
|
||||
}
|
||||
|
||||
if( i>=db->nDb ){
|
||||
@@ -325,6 +325,7 @@ static void codeAttach(
|
||||
sqlite3* db = pParse->db;
|
||||
int regArgs;
|
||||
|
||||
if( pParse->nErr ) goto attach_end;
|
||||
memset(&sName, 0, sizeof(NameContext));
|
||||
sName.pParse = pParse;
|
||||
|
||||
@@ -433,7 +434,7 @@ void sqlite3FixInit(
|
||||
db = pParse->db;
|
||||
assert( db->nDb>iDb );
|
||||
pFix->pParse = pParse;
|
||||
pFix->zDb = db->aDb[iDb].zName;
|
||||
pFix->zDb = db->aDb[iDb].zDbSName;
|
||||
pFix->pSchema = db->aDb[iDb].pSchema;
|
||||
pFix->zType = zType;
|
||||
pFix->pName = pName;
|
||||
@@ -530,7 +531,7 @@ int sqlite3FixExpr(
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
if( ExprHasProperty(pExpr, EP_TokenOnly) ) break;
|
||||
if( ExprHasProperty(pExpr, EP_TokenOnly|EP_Leaf) ) break;
|
||||
if( ExprHasProperty(pExpr, EP_xIsSelect) ){
|
||||
if( sqlite3FixSelect(pFix, pExpr->x.pSelect) ) return 1;
|
||||
}else{
|
||||
|
||||
+4
-3
@@ -107,10 +107,11 @@ int sqlite3AuthReadCol(
|
||||
const char *zCol, /* Column name */
|
||||
int iDb /* Index of containing database. */
|
||||
){
|
||||
sqlite3 *db = pParse->db; /* Database handle */
|
||||
char *zDb = db->aDb[iDb].zName; /* Name of attached database */
|
||||
int rc; /* Auth callback return code */
|
||||
sqlite3 *db = pParse->db; /* Database handle */
|
||||
char *zDb = db->aDb[iDb].zDbSName; /* Schema name of attached database */
|
||||
int rc; /* Auth callback return code */
|
||||
|
||||
if( db->init.busy ) return SQLITE_OK;
|
||||
rc = db->xAuth(db->pAuthArg, SQLITE_READ, zTab,zCol,zDb,pParse->zAuthContext
|
||||
#ifdef SQLITE_USER_AUTHENTICATION
|
||||
,db->auth.zAuthUser
|
||||
|
||||
+30
-25
@@ -83,22 +83,16 @@ static Btree *findBtree(sqlite3 *pErrorDb, sqlite3 *pDb, const char *zDb){
|
||||
int i = sqlite3FindDbName(pDb, zDb);
|
||||
|
||||
if( i==1 ){
|
||||
Parse *pParse;
|
||||
Parse sParse;
|
||||
int rc = 0;
|
||||
pParse = sqlite3StackAllocZero(pErrorDb, sizeof(*pParse));
|
||||
if( pParse==0 ){
|
||||
sqlite3ErrorWithMsg(pErrorDb, SQLITE_NOMEM, "out of memory");
|
||||
rc = SQLITE_NOMEM_BKPT;
|
||||
}else{
|
||||
pParse->db = pDb;
|
||||
if( sqlite3OpenTempDatabase(pParse) ){
|
||||
sqlite3ErrorWithMsg(pErrorDb, pParse->rc, "%s", pParse->zErrMsg);
|
||||
rc = SQLITE_ERROR;
|
||||
}
|
||||
sqlite3DbFree(pErrorDb, pParse->zErrMsg);
|
||||
sqlite3ParserReset(pParse);
|
||||
sqlite3StackFree(pErrorDb, pParse);
|
||||
memset(&sParse, 0, sizeof(sParse));
|
||||
sParse.db = pDb;
|
||||
if( sqlite3OpenTempDatabase(&sParse) ){
|
||||
sqlite3ErrorWithMsg(pErrorDb, sParse.rc, "%s", sParse.zErrMsg);
|
||||
rc = SQLITE_ERROR;
|
||||
}
|
||||
sqlite3DbFree(pErrorDb, sParse.zErrMsg);
|
||||
sqlite3ParserReset(&sParse);
|
||||
if( rc ){
|
||||
return 0;
|
||||
}
|
||||
@@ -196,7 +190,6 @@ sqlite3_backup *sqlite3_backup_init(
|
||||
p->isAttached = 0;
|
||||
|
||||
if( 0==p->pSrc || 0==p->pDest
|
||||
|| setDestPgsz(p)==SQLITE_NOMEM
|
||||
|| checkReadTransaction(pDestDb, p->pDest)!=SQLITE_OK
|
||||
){
|
||||
/* One (or both) of the named databases did not exist or an OOM
|
||||
@@ -384,14 +377,6 @@ int sqlite3_backup_step(sqlite3_backup *p, int nPage){
|
||||
rc = SQLITE_OK;
|
||||
}
|
||||
|
||||
/* Lock the destination database, if it is not locked already. */
|
||||
if( SQLITE_OK==rc && p->bDestLocked==0
|
||||
&& SQLITE_OK==(rc = sqlite3BtreeBeginTrans(p->pDest, 2))
|
||||
){
|
||||
p->bDestLocked = 1;
|
||||
sqlite3BtreeGetMeta(p->pDest, BTREE_SCHEMA_VERSION, &p->iDestSchema);
|
||||
}
|
||||
|
||||
/* If there is no open read-transaction on the source database, open
|
||||
** one now. If a transaction is opened here, then it will be closed
|
||||
** before this function exits.
|
||||
@@ -401,6 +386,24 @@ int sqlite3_backup_step(sqlite3_backup *p, int nPage){
|
||||
bCloseTrans = 1;
|
||||
}
|
||||
|
||||
/* If the destination database has not yet been locked (i.e. if this
|
||||
** is the first call to backup_step() for the current backup operation),
|
||||
** try to set its page size to the same as the source database. This
|
||||
** is especially important on ZipVFS systems, as in that case it is
|
||||
** not possible to create a database file that uses one page size by
|
||||
** writing to it with another. */
|
||||
if( p->bDestLocked==0 && rc==SQLITE_OK && setDestPgsz(p)==SQLITE_NOMEM ){
|
||||
rc = SQLITE_NOMEM;
|
||||
}
|
||||
|
||||
/* Lock the destination database, if it is not locked already. */
|
||||
if( SQLITE_OK==rc && p->bDestLocked==0
|
||||
&& SQLITE_OK==(rc = sqlite3BtreeBeginTrans(p->pDest, 2))
|
||||
){
|
||||
p->bDestLocked = 1;
|
||||
sqlite3BtreeGetMeta(p->pDest, BTREE_SCHEMA_VERSION, &p->iDestSchema);
|
||||
}
|
||||
|
||||
/* 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);
|
||||
@@ -777,13 +780,15 @@ int sqlite3BtreeCopyFile(Btree *pTo, Btree *pFrom){
|
||||
** sqlite3_backup_step(), we can guarantee that the copy finishes
|
||||
** within a single call (unless an error occurs). The assert() statement
|
||||
** checks this assumption - (p->rc) should be set to either SQLITE_DONE
|
||||
** or an error code.
|
||||
*/
|
||||
** or an error code. */
|
||||
sqlite3_backup_step(&b, 0x7FFFFFFF);
|
||||
assert( b.rc!=SQLITE_OK );
|
||||
|
||||
rc = sqlite3_backup_finish(&b);
|
||||
if( rc==SQLITE_OK ){
|
||||
pTo->pBt->btsFlags &= ~BTS_PAGESIZE_FIXED;
|
||||
}else{
|
||||
sqlite3PagerClearCache(sqlite3BtreePager(b.pDest));
|
||||
}
|
||||
|
||||
assert( sqlite3BtreeIsInTrans(pTo)==0 );
|
||||
|
||||
+92
-78
@@ -632,7 +632,7 @@ static int saveCursorKey(BtCursor *pCur){
|
||||
pCur->nKey = sqlite3BtreePayloadSize(pCur);
|
||||
pKey = sqlite3Malloc( pCur->nKey );
|
||||
if( pKey ){
|
||||
rc = sqlite3BtreeKey(pCur, 0, (int)pCur->nKey, pKey);
|
||||
rc = sqlite3BtreePayload(pCur, 0, (int)pCur->nKey, pKey);
|
||||
if( rc==SQLITE_OK ){
|
||||
pCur->pKey = pKey;
|
||||
}else{
|
||||
@@ -763,7 +763,7 @@ static int btreeMoveto(
|
||||
){
|
||||
int rc; /* Status code */
|
||||
UnpackedRecord *pIdxKey; /* Unpacked index key */
|
||||
char aSpace[200]; /* Temp space for pIdxKey - to avoid a malloc */
|
||||
char aSpace[384]; /* Temp space for pIdxKey - to avoid a malloc */
|
||||
char *pFree = 0;
|
||||
|
||||
if( pKey ){
|
||||
@@ -1605,8 +1605,11 @@ static int freeSpace(MemPage *pPage, u16 iStart, u16 iSize){
|
||||
if( data[iPtr+1]==0 && data[iPtr]==0 ){
|
||||
iFreeBlk = 0; /* Shortcut for the case when the freelist is empty */
|
||||
}else{
|
||||
while( (iFreeBlk = get2byte(&data[iPtr]))>0 && iFreeBlk<iStart ){
|
||||
if( iFreeBlk<iPtr+4 ) return SQLITE_CORRUPT_BKPT;
|
||||
while( (iFreeBlk = get2byte(&data[iPtr]))<iStart ){
|
||||
if( iFreeBlk<iPtr+4 ){
|
||||
if( iFreeBlk==0 ) break;
|
||||
return SQLITE_CORRUPT_BKPT;
|
||||
}
|
||||
iPtr = iFreeBlk;
|
||||
}
|
||||
if( iFreeBlk>iLast ) return SQLITE_CORRUPT_BKPT;
|
||||
@@ -2337,9 +2340,9 @@ int sqlite3BtreeOpen(
|
||||
#if !defined(SQLITE_OMIT_SHARED_CACHE) && !defined(SQLITE_OMIT_DISKIO)
|
||||
/* Add the new BtShared object to the linked list sharable BtShareds.
|
||||
*/
|
||||
pBt->nRef = 1;
|
||||
if( p->sharable ){
|
||||
MUTEX_LOGIC( sqlite3_mutex *mutexShared; )
|
||||
pBt->nRef = 1;
|
||||
MUTEX_LOGIC( mutexShared = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER);)
|
||||
if( SQLITE_THREADSAFE && sqlite3GlobalConfig.bCoreMutex ){
|
||||
pBt->mutex = sqlite3MutexAlloc(SQLITE_MUTEX_FAST);
|
||||
@@ -2392,12 +2395,14 @@ int sqlite3BtreeOpen(
|
||||
btree_open_out:
|
||||
if( rc!=SQLITE_OK ){
|
||||
if( pBt && pBt->pPager ){
|
||||
sqlite3PagerClose(pBt->pPager);
|
||||
sqlite3PagerClose(pBt->pPager, 0);
|
||||
}
|
||||
sqlite3_free(pBt);
|
||||
sqlite3_free(p);
|
||||
*ppBtree = 0;
|
||||
}else{
|
||||
sqlite3_file *pFile;
|
||||
|
||||
/* If the B-Tree was successfully opened, set the pager-cache size to the
|
||||
** default value. Except, when opening on an existing shared pager-cache,
|
||||
** do not change the pager-cache size.
|
||||
@@ -2405,11 +2410,17 @@ btree_open_out:
|
||||
if( sqlite3BtreeSchema(p, 0, 0)==0 ){
|
||||
sqlite3PagerSetCachesize(p->pBt->pPager, SQLITE_DEFAULT_CACHE_SIZE);
|
||||
}
|
||||
|
||||
pFile = sqlite3PagerFile(pBt->pPager);
|
||||
if( pFile->pMethods ){
|
||||
sqlite3OsFileControlHint(pFile, SQLITE_FCNTL_PDB, (void*)&pBt->db);
|
||||
}
|
||||
}
|
||||
if( mutexOpen ){
|
||||
assert( sqlite3_mutex_held(mutexOpen) );
|
||||
sqlite3_mutex_leave(mutexOpen);
|
||||
}
|
||||
assert( rc!=SQLITE_OK || sqlite3BtreeConnectionCount(*ppBtree)>0 );
|
||||
return rc;
|
||||
}
|
||||
|
||||
@@ -2533,7 +2544,7 @@ int sqlite3BtreeClose(Btree *p){
|
||||
** Clean out and delete the BtShared object.
|
||||
*/
|
||||
assert( !pBt->pCursor );
|
||||
sqlite3PagerClose(pBt->pPager);
|
||||
sqlite3PagerClose(pBt->pPager, p->db);
|
||||
if( pBt->xFreeSchema && pBt->pSchema ){
|
||||
pBt->xFreeSchema(pBt->pSchema);
|
||||
}
|
||||
@@ -4267,6 +4278,10 @@ int sqlite3BtreeCursorIsValid(BtCursor *pCur){
|
||||
return pCur && pCur->eState==CURSOR_VALID;
|
||||
}
|
||||
#endif /* NDEBUG */
|
||||
int sqlite3BtreeCursorIsValidNN(BtCursor *pCur){
|
||||
assert( pCur!=0 );
|
||||
return pCur->eState==CURSOR_VALID;
|
||||
}
|
||||
|
||||
/*
|
||||
** Return the value of the integer key or "rowid" for a table btree.
|
||||
@@ -4597,7 +4612,7 @@ static int accessPayload(
|
||||
&& (bEnd || a==ovflSize) /* (6) */
|
||||
&& pBt->inTransaction==TRANS_READ /* (4) */
|
||||
&& (fd = sqlite3PagerFile(pBt->pPager))->pMethods /* (3) */
|
||||
&& pBt->pPage1->aData[19]==0x01 /* (5) */
|
||||
&& 0==sqlite3PagerUseWal(pBt->pPager) /* (5) */
|
||||
&& &pBuf[-4]>=pBufStart /* (7) */
|
||||
){
|
||||
u8 aSave[4];
|
||||
@@ -4636,43 +4651,35 @@ static int accessPayload(
|
||||
}
|
||||
|
||||
/*
|
||||
** Read part of the key associated with cursor pCur. Exactly
|
||||
** "amt" bytes will be transferred into pBuf[]. The transfer
|
||||
** Read part of the payload for the row at which that cursor pCur is currently
|
||||
** pointing. "amt" bytes will be transferred into pBuf[]. The transfer
|
||||
** begins at "offset".
|
||||
**
|
||||
** The caller must ensure that pCur is pointing to a valid row
|
||||
** in the table.
|
||||
** pCur can be pointing to either a table or an index b-tree.
|
||||
** If pointing to a table btree, then the content section is read. If
|
||||
** pCur is pointing to an index b-tree then the key section is read.
|
||||
**
|
||||
** For sqlite3BtreePayload(), the caller must ensure that pCur is pointing
|
||||
** to a valid row in the table. For sqlite3BtreePayloadChecked(), the
|
||||
** cursor might be invalid or might need to be restored before being read.
|
||||
**
|
||||
** Return SQLITE_OK on success or an error code if anything goes
|
||||
** wrong. An error is returned if "offset+amt" is larger than
|
||||
** the available payload.
|
||||
*/
|
||||
int sqlite3BtreeKey(BtCursor *pCur, u32 offset, u32 amt, void *pBuf){
|
||||
int sqlite3BtreePayload(BtCursor *pCur, u32 offset, u32 amt, void *pBuf){
|
||||
assert( cursorHoldsMutex(pCur) );
|
||||
assert( pCur->eState==CURSOR_VALID );
|
||||
assert( pCur->iPage>=0 && pCur->apPage[pCur->iPage] );
|
||||
assert( pCur->aiIdx[pCur->iPage]<pCur->apPage[pCur->iPage]->nCell );
|
||||
return accessPayload(pCur, offset, amt, (unsigned char*)pBuf, 0);
|
||||
}
|
||||
|
||||
/*
|
||||
** Read part of the data associated with cursor pCur. Exactly
|
||||
** "amt" bytes will be transfered into pBuf[]. The transfer
|
||||
** begins at "offset".
|
||||
**
|
||||
** Return SQLITE_OK on success or an error code if anything goes
|
||||
** wrong. An error is returned if "offset+amt" is larger than
|
||||
** the available payload.
|
||||
*/
|
||||
int sqlite3BtreeData(BtCursor *pCur, u32 offset, u32 amt, void *pBuf){
|
||||
int rc;
|
||||
|
||||
#ifndef SQLITE_OMIT_INCRBLOB
|
||||
int sqlite3BtreePayloadChecked(BtCursor *pCur, u32 offset, u32 amt, void *pBuf){
|
||||
int rc;
|
||||
if ( pCur->eState==CURSOR_INVALID ){
|
||||
return SQLITE_ABORT;
|
||||
}
|
||||
#endif
|
||||
|
||||
assert( cursorOwnsBtShared(pCur) );
|
||||
rc = restoreCursorPosition(pCur);
|
||||
if( rc==SQLITE_OK ){
|
||||
@@ -4683,6 +4690,7 @@ int sqlite3BtreeData(BtCursor *pCur, u32 offset, u32 amt, void *pBuf){
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
#endif /* SQLITE_OMIT_INCRBLOB */
|
||||
|
||||
/*
|
||||
** Return a pointer to payload information from the entry that the
|
||||
@@ -4853,9 +4861,12 @@ static int moveToRoot(BtCursor *pCur){
|
||||
}
|
||||
|
||||
if( pCur->iPage>=0 ){
|
||||
while( pCur->iPage ){
|
||||
assert( pCur->apPage[pCur->iPage]!=0 );
|
||||
releasePageNotNull(pCur->apPage[pCur->iPage--]);
|
||||
if( pCur->iPage ){
|
||||
do{
|
||||
assert( pCur->apPage[pCur->iPage]!=0 );
|
||||
releasePageNotNull(pCur->apPage[pCur->iPage--]);
|
||||
}while( pCur->iPage);
|
||||
goto skip_init;
|
||||
}
|
||||
}else if( pCur->pgnoRoot==0 ){
|
||||
pCur->eState = CURSOR_INVALID;
|
||||
@@ -4866,7 +4877,7 @@ static int moveToRoot(BtCursor *pCur){
|
||||
0, pCur->curPagerFlags);
|
||||
if( rc!=SQLITE_OK ){
|
||||
pCur->eState = CURSOR_INVALID;
|
||||
return rc;
|
||||
return rc;
|
||||
}
|
||||
pCur->iPage = 0;
|
||||
pCur->curIntKey = pCur->apPage[0]->intKey;
|
||||
@@ -4889,10 +4900,12 @@ static int moveToRoot(BtCursor *pCur){
|
||||
return SQLITE_CORRUPT_BKPT;
|
||||
}
|
||||
|
||||
skip_init:
|
||||
pCur->aiIdx[0] = 0;
|
||||
pCur->info.nSize = 0;
|
||||
pCur->curFlags &= ~(BTCF_AtLast|BTCF_ValidNKey|BTCF_ValidOvfl);
|
||||
|
||||
pRoot = pCur->apPage[0];
|
||||
if( pRoot->nCell>0 ){
|
||||
pCur->eState = CURSOR_VALID;
|
||||
}else if( !pRoot->leaf ){
|
||||
@@ -5149,16 +5162,16 @@ int sqlite3BtreeMovetoUnpacked(
|
||||
if( lwr>upr ){ c = +1; break; }
|
||||
}else{
|
||||
assert( nCellKey==intKey );
|
||||
pCur->curFlags |= BTCF_ValidNKey;
|
||||
pCur->info.nKey = nCellKey;
|
||||
pCur->aiIdx[pCur->iPage] = (u16)idx;
|
||||
if( !pPage->leaf ){
|
||||
lwr = idx;
|
||||
goto moveto_next_layer;
|
||||
}else{
|
||||
pCur->curFlags |= BTCF_ValidNKey;
|
||||
pCur->info.nKey = nCellKey;
|
||||
pCur->info.nSize = 0;
|
||||
*pRes = 0;
|
||||
rc = SQLITE_OK;
|
||||
goto moveto_finish;
|
||||
return SQLITE_OK;
|
||||
}
|
||||
}
|
||||
assert( lwr+upr>=0 );
|
||||
@@ -5269,7 +5282,7 @@ moveto_next_layer:
|
||||
}
|
||||
moveto_finish:
|
||||
pCur->info.nSize = 0;
|
||||
pCur->curFlags &= ~(BTCF_ValidNKey|BTCF_ValidOvfl);
|
||||
assert( (pCur->curFlags & BTCF_ValidOvfl)==0 );
|
||||
return rc;
|
||||
}
|
||||
|
||||
@@ -5467,7 +5480,7 @@ static SQLITE_NOINLINE int btreePrevious(BtCursor *pCur, int *pRes){
|
||||
moveToParent(pCur);
|
||||
}
|
||||
assert( pCur->info.nSize==0 );
|
||||
assert( (pCur->curFlags & (BTCF_ValidNKey|BTCF_ValidOvfl))==0 );
|
||||
assert( (pCur->curFlags & (BTCF_ValidOvfl))==0 );
|
||||
|
||||
pCur->aiIdx[pCur->iPage]--;
|
||||
pPage = pCur->apPage[pCur->iPage];
|
||||
@@ -6096,8 +6109,6 @@ static int fillInCell(
|
||||
nHeader += putVarint32(&pCell[nHeader], nPayload);
|
||||
nHeader += putVarint(&pCell[nHeader], *(u64*)&pX->nKey);
|
||||
}else{
|
||||
assert( pX->nData==0 );
|
||||
assert( pX->nZero==0 );
|
||||
assert( pX->nKey<=0x7fffffff && pX->pKey!=0 );
|
||||
nSrc = nPayload = (int)pX->nKey;
|
||||
pSrc = pX->pKey;
|
||||
@@ -6384,7 +6395,7 @@ static void insertCell(
|
||||
|
||||
/*
|
||||
** A CellArray object contains a cache of pointers and sizes for a
|
||||
** consecutive sequence of cells that might be held multiple pages.
|
||||
** consecutive sequence of cells that might be held on multiple pages.
|
||||
*/
|
||||
typedef struct CellArray CellArray;
|
||||
struct CellArray {
|
||||
@@ -7339,7 +7350,7 @@ static int balance_nonroot(
|
||||
assert( r<nMaxCells );
|
||||
(void)cachedCellSize(&b, r);
|
||||
if( szRight!=0
|
||||
&& (bBulk || szRight+b.szCell[d]+2 > szLeft-(b.szCell[r]+2)) ){
|
||||
&& (bBulk || szRight+b.szCell[d]+2 > szLeft-(b.szCell[r]+(i==k-1?0:2)))){
|
||||
break;
|
||||
}
|
||||
szRight += b.szCell[d] + 2;
|
||||
@@ -7926,17 +7937,19 @@ static int balance(BtCursor *pCur){
|
||||
** pX.pData,nData,nZero fields must be zero.
|
||||
**
|
||||
** If the seekResult parameter is non-zero, then a successful call to
|
||||
** MovetoUnpacked() to seek cursor pCur to (pKey, nKey) has already
|
||||
** been performed. seekResult is the search result returned (a negative
|
||||
** number if pCur points at an entry that is smaller than (pKey, nKey), or
|
||||
** a positive value if pCur points at an entry that is larger than
|
||||
** (pKey, nKey)).
|
||||
** MovetoUnpacked() to seek cursor pCur to (pKey,nKey) has already
|
||||
** been performed. In other words, if seekResult!=0 then the cursor
|
||||
** is currently pointing to a cell that will be adjacent to the cell
|
||||
** to be inserted. If seekResult<0 then pCur points to a cell that is
|
||||
** smaller then (pKey,nKey). If seekResult>0 then pCur points to a cell
|
||||
** that is larger than (pKey,nKey).
|
||||
**
|
||||
** If the seekResult parameter is non-zero, then the caller guarantees that
|
||||
** cursor pCur is pointing at the existing copy of a row that is to be
|
||||
** overwritten. If the seekResult parameter is 0, then cursor pCur may
|
||||
** point to any entry or to no entry at all and so this function has to seek
|
||||
** the cursor before the new key can be inserted.
|
||||
** If seekResult==0, that means pCur is pointing at some unknown location.
|
||||
** In that case, this routine must seek the cursor to the correct insertion
|
||||
** point for (pKey,nKey) before doing the insertion. For index btrees,
|
||||
** if pX->nMem is non-zero, then pX->aMem contains pointers to the unpacked
|
||||
** key values and pX->aMem can be used instead of pX->pKey to avoid having
|
||||
** to decode the key.
|
||||
*/
|
||||
int sqlite3BtreeInsert(
|
||||
BtCursor *pCur, /* Insert data into the table of this cursor */
|
||||
@@ -7997,15 +8010,26 @@ int sqlite3BtreeInsert(
|
||||
/* If the cursor is currently on the last row and we are appending a
|
||||
** new row onto the end, set the "loc" to avoid an unnecessary
|
||||
** btreeMoveto() call */
|
||||
if( (pCur->curFlags&BTCF_ValidNKey)!=0 && pX->nKey>0
|
||||
&& pCur->info.nKey==pX->nKey-1 ){
|
||||
loc = -1;
|
||||
if( (pCur->curFlags&BTCF_ValidNKey)!=0 && pX->nKey==pCur->info.nKey ){
|
||||
loc = 0;
|
||||
}else if( (pCur->curFlags&BTCF_ValidNKey)!=0 && pX->nKey>0
|
||||
&& pCur->info.nKey==pX->nKey-1 ){
|
||||
loc = -1;
|
||||
}else if( loc==0 ){
|
||||
rc = sqlite3BtreeMovetoUnpacked(pCur, 0, pX->nKey, appendBias, &loc);
|
||||
if( rc ) return rc;
|
||||
}
|
||||
}else if( loc==0 ){
|
||||
rc = btreeMoveto(pCur, pX->pKey, pX->nKey, appendBias, &loc);
|
||||
if( pX->nMem ){
|
||||
UnpackedRecord r;
|
||||
memset(&r, 0, sizeof(r));
|
||||
r.pKeyInfo = pCur->pKeyInfo;
|
||||
r.aMem = pX->aMem;
|
||||
r.nField = pX->nMem;
|
||||
rc = sqlite3BtreeMovetoUnpacked(pCur, &r, 0, appendBias, &loc);
|
||||
}else{
|
||||
rc = btreeMoveto(pCur, pX->pKey, pX->nKey, appendBias, &loc);
|
||||
}
|
||||
if( rc ) return rc;
|
||||
}
|
||||
assert( pCur->eState==CURSOR_VALID || (pCur->eState==CURSOR_INVALID && loc) );
|
||||
@@ -8547,27 +8571,7 @@ static int btreeDropTable(Btree *p, Pgno iTable, int *piMoved){
|
||||
|
||||
assert( sqlite3BtreeHoldsMutex(p) );
|
||||
assert( p->inTrans==TRANS_WRITE );
|
||||
|
||||
/* It is illegal to drop a table if any cursors are open on the
|
||||
** database. This is because in auto-vacuum mode the backend may
|
||||
** need to move another root-page to fill a gap left by the deleted
|
||||
** root page. If an open cursor was using this page a problem would
|
||||
** occur.
|
||||
**
|
||||
** This error is caught long before control reaches this point.
|
||||
*/
|
||||
if( NEVER(pBt->pCursor) ){
|
||||
sqlite3ConnectionBlocked(p->db, pBt->pCursor->pBtree->db);
|
||||
return SQLITE_LOCKED_SHAREDCACHE;
|
||||
}
|
||||
|
||||
/*
|
||||
** It is illegal to drop the sqlite_master table on page 1. But again,
|
||||
** this error is caught long before reaching this point.
|
||||
*/
|
||||
if( NEVER(iTable<2) ){
|
||||
return SQLITE_CORRUPT_BKPT;
|
||||
}
|
||||
assert( iTable>=2 );
|
||||
|
||||
rc = btreeGetPage(pBt, (Pgno)iTable, &pPage, 0);
|
||||
if( rc ) return rc;
|
||||
@@ -9475,7 +9479,7 @@ int sqlite3BtreeCheckpoint(Btree *p, int eMode, int *pnLog, int *pnCkpt){
|
||||
if( pBt->inTransaction!=TRANS_NONE ){
|
||||
rc = SQLITE_LOCKED;
|
||||
}else{
|
||||
rc = sqlite3PagerCheckpoint(pBt->pPager, eMode, pnLog, pnCkpt);
|
||||
rc = sqlite3PagerCheckpoint(pBt->pPager, p->db, eMode, pnLog, pnCkpt);
|
||||
}
|
||||
sqlite3BtreeLeave(p);
|
||||
}
|
||||
@@ -9698,4 +9702,14 @@ int sqlite3HeaderSizeBtree(void){ return ROUND8(sizeof(MemPage)); }
|
||||
int sqlite3BtreeSharable(Btree *p){
|
||||
return p->sharable;
|
||||
}
|
||||
|
||||
/*
|
||||
** Return the number of connections to the BtShared object accessed by
|
||||
** the Btree handle passed as the only argument. For private caches
|
||||
** this is always 1. For shared caches it may be 1 or greater.
|
||||
*/
|
||||
int sqlite3BtreeConnectionCount(Btree *p){
|
||||
testcase( p->sharable );
|
||||
return p->pBt->nRef;
|
||||
}
|
||||
#endif
|
||||
|
||||
+14
-5
@@ -13,8 +13,8 @@
|
||||
** subsystem. See comments in the source code for a detailed description
|
||||
** of what each interface routine does.
|
||||
*/
|
||||
#ifndef _BTREE_H_
|
||||
#define _BTREE_H_
|
||||
#ifndef SQLITE_BTREE_H
|
||||
#define SQLITE_BTREE_H
|
||||
|
||||
/* TODO: This definition is just included so other modules compile. It
|
||||
** needs to be revisited.
|
||||
@@ -90,7 +90,9 @@ int sqlite3BtreeIsInReadTrans(Btree*);
|
||||
int sqlite3BtreeIsInBackup(Btree*);
|
||||
void *sqlite3BtreeSchema(Btree *, int, void(*)(void *));
|
||||
int sqlite3BtreeSchemaLocked(Btree *pBtree);
|
||||
#ifndef SQLITE_OMIT_SHARED_CACHE
|
||||
int sqlite3BtreeLockTable(Btree *pBtree, int iTab, u8 isWriteLock);
|
||||
#endif
|
||||
int sqlite3BtreeSavepoint(Btree *, int, int);
|
||||
|
||||
const char *sqlite3BtreeGetFilename(Btree *);
|
||||
@@ -273,6 +275,8 @@ struct BtreePayload {
|
||||
const void *pKey; /* Key content for indexes. NULL for tables */
|
||||
sqlite3_int64 nKey; /* Size of pKey for indexes. PRIMARY KEY for tabs */
|
||||
const void *pData; /* Data for tables. NULL for indexes */
|
||||
struct Mem *aMem; /* First of nMem value in the unpacked pKey */
|
||||
u16 nMem; /* Number of aMem[] value. Might be zero */
|
||||
int nData; /* Size of pData. 0 if none. */
|
||||
int nZero; /* Extra zero data appended after pData,nData */
|
||||
};
|
||||
@@ -285,16 +289,18 @@ int sqlite3BtreeNext(BtCursor*, int *pRes);
|
||||
int sqlite3BtreeEof(BtCursor*);
|
||||
int sqlite3BtreePrevious(BtCursor*, int *pRes);
|
||||
i64 sqlite3BtreeIntegerKey(BtCursor*);
|
||||
int sqlite3BtreeKey(BtCursor*, u32 offset, u32 amt, void*);
|
||||
int sqlite3BtreePayload(BtCursor*, u32 offset, u32 amt, void*);
|
||||
const void *sqlite3BtreePayloadFetch(BtCursor*, u32 *pAmt);
|
||||
u32 sqlite3BtreePayloadSize(BtCursor*);
|
||||
int sqlite3BtreeData(BtCursor*, u32 offset, u32 amt, void*);
|
||||
|
||||
char *sqlite3BtreeIntegrityCheck(Btree*, int *aRoot, int nRoot, int, int*);
|
||||
struct Pager *sqlite3BtreePager(Btree*);
|
||||
|
||||
#ifndef SQLITE_OMIT_INCRBLOB
|
||||
int sqlite3BtreePayloadChecked(BtCursor*, u32 offset, u32 amt, void*);
|
||||
int sqlite3BtreePutData(BtCursor*, u32 offset, u32 amt, void*);
|
||||
void sqlite3BtreeIncrblobCursor(BtCursor *);
|
||||
#endif
|
||||
void sqlite3BtreeClearCursor(BtCursor *);
|
||||
int sqlite3BtreeSetVersion(Btree *pBt, int iVersion);
|
||||
int sqlite3BtreeCursorHasHint(BtCursor*, unsigned int mask);
|
||||
@@ -304,6 +310,7 @@ int sqlite3HeaderSizeBtree(void);
|
||||
#ifndef NDEBUG
|
||||
int sqlite3BtreeCursorIsValid(BtCursor*);
|
||||
#endif
|
||||
int sqlite3BtreeCursorIsValidNN(BtCursor*);
|
||||
|
||||
#ifndef SQLITE_OMIT_BTREECOUNT
|
||||
int sqlite3BtreeCount(BtCursor *, i64 *);
|
||||
@@ -328,11 +335,13 @@ void sqlite3BtreeCursorList(Btree*);
|
||||
void sqlite3BtreeEnterAll(sqlite3*);
|
||||
int sqlite3BtreeSharable(Btree*);
|
||||
void sqlite3BtreeEnterCursor(BtCursor*);
|
||||
int sqlite3BtreeConnectionCount(Btree*);
|
||||
#else
|
||||
# define sqlite3BtreeEnter(X)
|
||||
# define sqlite3BtreeEnterAll(X)
|
||||
# define sqlite3BtreeSharable(X) 0
|
||||
# define sqlite3BtreeEnterCursor(X)
|
||||
# define sqlite3BtreeConnectionCount(X) 1
|
||||
#endif
|
||||
|
||||
#if !defined(SQLITE_OMIT_SHARED_CACHE) && SQLITE_THREADSAFE
|
||||
@@ -357,4 +366,4 @@ void sqlite3BtreeCursorList(Btree*);
|
||||
#endif
|
||||
|
||||
|
||||
#endif /* _BTREE_H_ */
|
||||
#endif /* SQLITE_BTREE_H */
|
||||
|
||||
+56
-53
@@ -59,6 +59,8 @@ void sqlite3TableLock(
|
||||
TableLock *p;
|
||||
assert( iDb>=0 );
|
||||
|
||||
if( iDb==1 ) return;
|
||||
if( !sqlite3BtreeSharable(pParse->db->aDb[iDb].pBt) ) return;
|
||||
for(i=0; i<pToplevel->nTableLock; i++){
|
||||
p = &pToplevel->aTableLock[i];
|
||||
if( p->iDb==iDb && p->iTab==iTab ){
|
||||
@@ -146,15 +148,14 @@ void sqlite3FinishCoding(Parse *pParse){
|
||||
assert( !pParse->isMultiWrite
|
||||
|| sqlite3VdbeAssertMayAbort(v, pParse->mayAbort));
|
||||
if( v ){
|
||||
while( sqlite3VdbeDeletePriorOpcode(v, OP_Close) ){}
|
||||
sqlite3VdbeAddOp0(v, OP_Halt);
|
||||
|
||||
#if SQLITE_USER_AUTHENTICATION
|
||||
if( pParse->nTableLock>0 && db->init.busy==0 ){
|
||||
sqlite3UserAuthInit(db);
|
||||
if( db->auth.authLevel<UAUTH_User ){
|
||||
pParse->rc = SQLITE_AUTH_USER;
|
||||
sqlite3ErrorMsg(pParse, "user not authenticated");
|
||||
pParse->rc = SQLITE_AUTH_USER;
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -173,14 +174,16 @@ void sqlite3FinishCoding(Parse *pParse){
|
||||
assert( sqlite3VdbeGetOp(v, 0)->opcode==OP_Init );
|
||||
sqlite3VdbeJumpHere(v, 0);
|
||||
for(iDb=0; iDb<db->nDb; iDb++){
|
||||
Schema *pSchema;
|
||||
if( DbMaskTest(pParse->cookieMask, iDb)==0 ) continue;
|
||||
sqlite3VdbeUsesBtree(v, iDb);
|
||||
pSchema = db->aDb[iDb].pSchema;
|
||||
sqlite3VdbeAddOp4Int(v,
|
||||
OP_Transaction, /* Opcode */
|
||||
iDb, /* P1 */
|
||||
DbMaskTest(pParse->writeMask,iDb), /* P2 */
|
||||
pParse->cookieValue[iDb], /* P3 */
|
||||
db->aDb[iDb].pSchema->iGeneration /* P4 */
|
||||
pSchema->schema_cookie, /* P3 */
|
||||
pSchema->iGeneration /* P4 */
|
||||
);
|
||||
if( db->init.busy==0 ) sqlite3VdbeChangeP5(v, 1);
|
||||
VdbeComment((v,
|
||||
@@ -231,16 +234,6 @@ void sqlite3FinishCoding(Parse *pParse){
|
||||
}else{
|
||||
pParse->rc = SQLITE_ERROR;
|
||||
}
|
||||
|
||||
/* We are done with this Parse object. There is no need to de-initialize it */
|
||||
#if 0
|
||||
pParse->colNamesSet = 0;
|
||||
pParse->nTab = 0;
|
||||
pParse->nMem = 0;
|
||||
pParse->nSet = 0;
|
||||
pParse->nVar = 0;
|
||||
DbMaskZero(pParse->cookieMask);
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -260,8 +253,7 @@ void sqlite3NestedParse(Parse *pParse, const char *zFormat, ...){
|
||||
char *zSql;
|
||||
char *zErrMsg = 0;
|
||||
sqlite3 *db = pParse->db;
|
||||
# define SAVE_SZ (sizeof(Parse) - offsetof(Parse,nVar))
|
||||
char saveBuf[SAVE_SZ];
|
||||
char saveBuf[PARSE_TAIL_SZ];
|
||||
|
||||
if( pParse->nErr ) return;
|
||||
assert( pParse->nested<10 ); /* Nesting should only be of limited depth */
|
||||
@@ -272,12 +264,12 @@ void sqlite3NestedParse(Parse *pParse, const char *zFormat, ...){
|
||||
return; /* A malloc must have failed */
|
||||
}
|
||||
pParse->nested++;
|
||||
memcpy(saveBuf, &pParse->nVar, SAVE_SZ);
|
||||
memset(&pParse->nVar, 0, SAVE_SZ);
|
||||
memcpy(saveBuf, PARSE_TAIL(pParse), PARSE_TAIL_SZ);
|
||||
memset(PARSE_TAIL(pParse), 0, PARSE_TAIL_SZ);
|
||||
sqlite3RunParser(pParse, zSql, &zErrMsg);
|
||||
sqlite3DbFree(db, zErrMsg);
|
||||
sqlite3DbFree(db, zSql);
|
||||
memcpy(&pParse->nVar, saveBuf, SAVE_SZ);
|
||||
memcpy(PARSE_TAIL(pParse), saveBuf, PARSE_TAIL_SZ);
|
||||
pParse->nested--;
|
||||
}
|
||||
|
||||
@@ -318,10 +310,11 @@ Table *sqlite3FindTable(sqlite3 *db, const char *zName, const char *zDatabase){
|
||||
#endif
|
||||
for(i=OMIT_TEMPDB; i<db->nDb; i++){
|
||||
int j = (i<2) ? i^1 : i; /* Search TEMP before MAIN */
|
||||
if( zDatabase!=0 && sqlite3StrICmp(zDatabase, db->aDb[j].zName) ) continue;
|
||||
assert( sqlite3SchemaMutexHeld(db, j, 0) );
|
||||
p = sqlite3HashFind(&db->aDb[j].pSchema->tblHash, zName);
|
||||
if( p ) break;
|
||||
if( zDatabase==0 || sqlite3StrICmp(zDatabase, db->aDb[j].zDbSName)==0 ){
|
||||
assert( sqlite3SchemaMutexHeld(db, j, 0) );
|
||||
p = sqlite3HashFind(&db->aDb[j].pSchema->tblHash, zName);
|
||||
if( p ) break;
|
||||
}
|
||||
}
|
||||
return p;
|
||||
}
|
||||
@@ -395,7 +388,7 @@ Table *sqlite3LocateTableItem(
|
||||
assert( p->pSchema==0 || p->zDatabase==0 );
|
||||
if( p->pSchema ){
|
||||
int iDb = sqlite3SchemaToIndex(pParse->db, p->pSchema);
|
||||
zDb = pParse->db->aDb[iDb].zName;
|
||||
zDb = pParse->db->aDb[iDb].zDbSName;
|
||||
}else{
|
||||
zDb = p->zDatabase;
|
||||
}
|
||||
@@ -423,7 +416,7 @@ Index *sqlite3FindIndex(sqlite3 *db, const char *zName, const char *zDb){
|
||||
int j = (i<2) ? i^1 : i; /* Search TEMP before MAIN */
|
||||
Schema *pSchema = db->aDb[j].pSchema;
|
||||
assert( pSchema );
|
||||
if( zDb && sqlite3StrICmp(zDb, db->aDb[j].zName) ) continue;
|
||||
if( zDb && sqlite3StrICmp(zDb, db->aDb[j].zDbSName) ) continue;
|
||||
assert( sqlite3SchemaMutexHeld(db, j, 0) );
|
||||
p = sqlite3HashFind(&pSchema->idxHash, zName);
|
||||
if( p ) break;
|
||||
@@ -492,8 +485,8 @@ void sqlite3CollapseDatabaseArray(sqlite3 *db){
|
||||
for(i=j=2; i<db->nDb; i++){
|
||||
struct Db *pDb = &db->aDb[i];
|
||||
if( pDb->pBt==0 ){
|
||||
sqlite3DbFree(db, pDb->zName);
|
||||
pDb->zName = 0;
|
||||
sqlite3DbFree(db, pDb->zDbSName);
|
||||
pDb->zDbSName = 0;
|
||||
continue;
|
||||
}
|
||||
if( j<i ){
|
||||
@@ -713,7 +706,7 @@ int sqlite3FindDbName(sqlite3 *db, const char *zName){
|
||||
if( zName ){
|
||||
Db *pDb;
|
||||
for(i=(db->nDb-1), pDb=&db->aDb[i]; i>=0; i--, pDb--){
|
||||
if( 0==sqlite3StrICmp(pDb->zName, zName) ) break;
|
||||
if( 0==sqlite3StrICmp(pDb->zDbSName, zName) ) break;
|
||||
}
|
||||
}
|
||||
return i;
|
||||
@@ -772,7 +765,7 @@ int sqlite3TwoPartName(
|
||||
return -1;
|
||||
}
|
||||
}else{
|
||||
assert( db->init.iDb==0 || db->init.busy );
|
||||
assert( db->init.iDb==0 || db->init.busy || (db->flags & SQLITE_Vacuum)!=0);
|
||||
iDb = db->init.iDb;
|
||||
*pUnqual = pName1;
|
||||
}
|
||||
@@ -883,7 +876,7 @@ void sqlite3StartTable(
|
||||
SQLITE_CREATE_VIEW,
|
||||
SQLITE_CREATE_TEMP_VIEW
|
||||
};
|
||||
char *zDb = db->aDb[iDb].zName;
|
||||
char *zDb = db->aDb[iDb].zDbSName;
|
||||
if( sqlite3AuthCheck(pParse, SQLITE_INSERT, SCHEMA_TABLE(isTemp), 0, zDb) ){
|
||||
goto begin_table_error;
|
||||
}
|
||||
@@ -902,7 +895,7 @@ void sqlite3StartTable(
|
||||
** collisions.
|
||||
*/
|
||||
if( !IN_DECLARE_VTAB ){
|
||||
char *zDb = db->aDb[iDb].zName;
|
||||
char *zDb = db->aDb[iDb].zDbSName;
|
||||
if( SQLITE_OK!=sqlite3ReadSchema(pParse) ){
|
||||
goto begin_table_error;
|
||||
}
|
||||
@@ -1457,6 +1450,9 @@ CollSeq *sqlite3LocateCollSeq(Parse *pParse, const char *zName){
|
||||
** set back to prior value. But schema changes are infrequent
|
||||
** and the probability of hitting the same cookie value is only
|
||||
** 1 chance in 2^32. So we're safe enough.
|
||||
**
|
||||
** IMPLEMENTATION-OF: R-34230-56049 SQLite automatically increments
|
||||
** the schema-version whenever the schema changes.
|
||||
*/
|
||||
void sqlite3ChangeCookie(Parse *pParse, int iDb){
|
||||
sqlite3 *db = pParse->db;
|
||||
@@ -1995,7 +1991,7 @@ void sqlite3EndTable(
|
||||
"UPDATE %Q.%s "
|
||||
"SET type='%s', name=%Q, tbl_name=%Q, rootpage=#%d, sql=%Q "
|
||||
"WHERE rowid=#%d",
|
||||
db->aDb[iDb].zName, SCHEMA_TABLE(iDb),
|
||||
db->aDb[iDb].zDbSName, SCHEMA_TABLE(iDb),
|
||||
zType,
|
||||
p->zName,
|
||||
p->zName,
|
||||
@@ -2010,13 +2006,13 @@ void sqlite3EndTable(
|
||||
/* Check to see if we need to create an sqlite_sequence table for
|
||||
** keeping track of autoincrement keys.
|
||||
*/
|
||||
if( p->tabFlags & TF_Autoincrement ){
|
||||
if( (p->tabFlags & TF_Autoincrement)!=0 ){
|
||||
Db *pDb = &db->aDb[iDb];
|
||||
assert( sqlite3SchemaMutexHeld(db, iDb, 0) );
|
||||
if( pDb->pSchema->pSeqTab==0 ){
|
||||
sqlite3NestedParse(pParse,
|
||||
"CREATE TABLE %Q.sqlite_sequence(name,seq)",
|
||||
pDb->zName
|
||||
pDb->zDbSName
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -2140,7 +2136,9 @@ int sqlite3ViewGetColumnNames(Parse *pParse, Table *pTable){
|
||||
int nErr = 0; /* Number of errors encountered */
|
||||
int n; /* Temporarily holds the number of cursors assigned */
|
||||
sqlite3 *db = pParse->db; /* Database connection for malloc errors */
|
||||
#ifndef SQLITE_OMIT_AUTHORIZATION
|
||||
sqlite3_xauth xAuth; /* Saved xAuth pointer */
|
||||
#endif
|
||||
|
||||
assert( pTable );
|
||||
|
||||
@@ -2330,7 +2328,7 @@ static void destroyRootPage(Parse *pParse, int iTable, int iDb){
|
||||
*/
|
||||
sqlite3NestedParse(pParse,
|
||||
"UPDATE %Q.%s SET rootpage=%d WHERE #%d AND rootpage=#%d",
|
||||
pParse->db->aDb[iDb].zName, SCHEMA_TABLE(iDb), iTable, r1, r1);
|
||||
pParse->db->aDb[iDb].zDbSName, SCHEMA_TABLE(iDb), iTable, r1, r1);
|
||||
#endif
|
||||
sqlite3ReleaseTempReg(pParse, r1);
|
||||
}
|
||||
@@ -2406,7 +2404,7 @@ static void sqlite3ClearStatTables(
|
||||
const char *zName /* Name of index or table */
|
||||
){
|
||||
int i;
|
||||
const char *zDbName = pParse->db->aDb[iDb].zName;
|
||||
const char *zDbName = pParse->db->aDb[iDb].zDbSName;
|
||||
for(i=1; i<=4; i++){
|
||||
char zTab[24];
|
||||
sqlite3_snprintf(sizeof(zTab),zTab,"sqlite_stat%d",i);
|
||||
@@ -2459,7 +2457,7 @@ void sqlite3CodeDropTable(Parse *pParse, Table *pTab, int iDb, int isView){
|
||||
if( pTab->tabFlags & TF_Autoincrement ){
|
||||
sqlite3NestedParse(pParse,
|
||||
"DELETE FROM %Q.sqlite_sequence WHERE name=%Q",
|
||||
pDb->zName, pTab->zName
|
||||
pDb->zDbSName, pTab->zName
|
||||
);
|
||||
}
|
||||
#endif
|
||||
@@ -2473,7 +2471,7 @@ void sqlite3CodeDropTable(Parse *pParse, Table *pTab, int iDb, int isView){
|
||||
*/
|
||||
sqlite3NestedParse(pParse,
|
||||
"DELETE FROM %Q.%s WHERE tbl_name=%Q and type!='trigger'",
|
||||
pDb->zName, SCHEMA_TABLE(iDb), pTab->zName);
|
||||
pDb->zDbSName, SCHEMA_TABLE(iDb), pTab->zName);
|
||||
if( !isView && !IsVirtual(pTab) ){
|
||||
destroyTable(pParse, pTab);
|
||||
}
|
||||
@@ -2527,7 +2525,7 @@ void sqlite3DropTable(Parse *pParse, SrcList *pName, int isView, int noErr){
|
||||
{
|
||||
int code;
|
||||
const char *zTab = SCHEMA_TABLE(iDb);
|
||||
const char *zDb = db->aDb[iDb].zName;
|
||||
const char *zDb = db->aDb[iDb].zDbSName;
|
||||
const char *zArg2 = 0;
|
||||
if( sqlite3AuthCheck(pParse, SQLITE_DELETE, zTab, 0, zDb)){
|
||||
goto exit_drop_table;
|
||||
@@ -2768,7 +2766,7 @@ static void sqlite3RefillIndex(Parse *pParse, Index *pIndex, int memRootPage){
|
||||
|
||||
#ifndef SQLITE_OMIT_AUTHORIZATION
|
||||
if( sqlite3AuthCheck(pParse, SQLITE_REINDEX, pIndex->zName, 0,
|
||||
db->aDb[iDb].zName ) ){
|
||||
db->aDb[iDb].zDbSName ) ){
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
@@ -2820,7 +2818,7 @@ static void sqlite3RefillIndex(Parse *pParse, Index *pIndex, int memRootPage){
|
||||
}
|
||||
sqlite3VdbeAddOp3(v, OP_SorterData, iSorter, regRecord, iIdx);
|
||||
sqlite3VdbeAddOp3(v, OP_Last, iIdx, 0, -1);
|
||||
sqlite3VdbeAddOp3(v, OP_IdxInsert, iIdx, regRecord, 0);
|
||||
sqlite3VdbeAddOp2(v, OP_IdxInsert, iIdx, regRecord);
|
||||
sqlite3VdbeChangeP5(v, OPFLAG_USESEEKRESULT);
|
||||
sqlite3ReleaseTempReg(pParse, regRecord);
|
||||
sqlite3VdbeAddOp2(v, OP_SorterNext, iSorter, addr2); VdbeCoverage(v);
|
||||
@@ -3020,7 +3018,7 @@ void sqlite3CreateIndex(
|
||||
goto exit_create_index;
|
||||
}
|
||||
}
|
||||
if( sqlite3FindIndex(db, zName, pDb->zName)!=0 ){
|
||||
if( sqlite3FindIndex(db, zName, pDb->zDbSName)!=0 ){
|
||||
if( !ifNotExist ){
|
||||
sqlite3ErrorMsg(pParse, "index %s already exists", zName);
|
||||
}else{
|
||||
@@ -3037,13 +3035,20 @@ void sqlite3CreateIndex(
|
||||
if( zName==0 ){
|
||||
goto exit_create_index;
|
||||
}
|
||||
|
||||
/* Automatic index names generated from within sqlite3_declare_vtab()
|
||||
** must have names that are distinct from normal automatic index names.
|
||||
** The following statement converts "sqlite3_autoindex..." into
|
||||
** "sqlite3_butoindex..." in order to make the names distinct.
|
||||
** The "vtab_err.test" test demonstrates the need of this statement. */
|
||||
if( IN_DECLARE_VTAB ) zName[7]++;
|
||||
}
|
||||
|
||||
/* Check for authorization to create an index.
|
||||
*/
|
||||
#ifndef SQLITE_OMIT_AUTHORIZATION
|
||||
{
|
||||
const char *zDb = pDb->zName;
|
||||
const char *zDb = pDb->zDbSName;
|
||||
if( sqlite3AuthCheck(pParse, SQLITE_INSERT, SCHEMA_TABLE(iDb), 0, zDb) ){
|
||||
goto exit_create_index;
|
||||
}
|
||||
@@ -3358,7 +3363,7 @@ void sqlite3CreateIndex(
|
||||
*/
|
||||
sqlite3NestedParse(pParse,
|
||||
"INSERT INTO %Q.%s VALUES('index',%Q,%Q,#%d,%Q);",
|
||||
db->aDb[iDb].zName, SCHEMA_TABLE(iDb),
|
||||
db->aDb[iDb].zDbSName, SCHEMA_TABLE(iDb),
|
||||
pIndex->zName,
|
||||
pTab->zName,
|
||||
iMem,
|
||||
@@ -3492,7 +3497,7 @@ void sqlite3DropIndex(Parse *pParse, SrcList *pName, int ifExists){
|
||||
{
|
||||
int code = SQLITE_DROP_INDEX;
|
||||
Table *pTab = pIndex->pTable;
|
||||
const char *zDb = db->aDb[iDb].zName;
|
||||
const char *zDb = db->aDb[iDb].zDbSName;
|
||||
const char *zTab = SCHEMA_TABLE(iDb);
|
||||
if( sqlite3AuthCheck(pParse, SQLITE_DELETE, zTab, 0, zDb) ){
|
||||
goto exit_drop_index;
|
||||
@@ -3510,7 +3515,7 @@ void sqlite3DropIndex(Parse *pParse, SrcList *pName, int ifExists){
|
||||
sqlite3BeginWriteOperation(pParse, 1, iDb);
|
||||
sqlite3NestedParse(pParse,
|
||||
"DELETE FROM %Q.%s WHERE name=%Q AND type='index'",
|
||||
db->aDb[iDb].zName, SCHEMA_TABLE(iDb), pIndex->zName
|
||||
db->aDb[iDb].zDbSName, SCHEMA_TABLE(iDb), pIndex->zName
|
||||
);
|
||||
sqlite3ClearStatTables(pParse, iDb, "idx", pIndex->zName);
|
||||
sqlite3ChangeCookie(pParse, iDb);
|
||||
@@ -4031,15 +4036,13 @@ int sqlite3OpenTempDatabase(Parse *pParse){
|
||||
*/
|
||||
void sqlite3CodeVerifySchema(Parse *pParse, int iDb){
|
||||
Parse *pToplevel = sqlite3ParseToplevel(pParse);
|
||||
sqlite3 *db = pToplevel->db;
|
||||
|
||||
assert( iDb>=0 && iDb<db->nDb );
|
||||
assert( db->aDb[iDb].pBt!=0 || iDb==1 );
|
||||
assert( iDb>=0 && iDb<pParse->db->nDb );
|
||||
assert( pParse->db->aDb[iDb].pBt!=0 || iDb==1 );
|
||||
assert( iDb<SQLITE_MAX_ATTACHED+2 );
|
||||
assert( sqlite3SchemaMutexHeld(db, iDb, 0) );
|
||||
assert( sqlite3SchemaMutexHeld(pParse->db, iDb, 0) );
|
||||
if( DbMaskTest(pToplevel->cookieMask, iDb)==0 ){
|
||||
DbMaskSet(pToplevel->cookieMask, iDb);
|
||||
pToplevel->cookieValue[iDb] = db->aDb[iDb].pSchema->schema_cookie;
|
||||
if( !OMIT_TEMPDB && iDb==1 ){
|
||||
sqlite3OpenTempDatabase(pToplevel);
|
||||
}
|
||||
@@ -4055,7 +4058,7 @@ void sqlite3CodeVerifyNamedSchema(Parse *pParse, const char *zDb){
|
||||
int i;
|
||||
for(i=0; i<db->nDb; i++){
|
||||
Db *pDb = &db->aDb[i];
|
||||
if( pDb->pBt && (!zDb || 0==sqlite3StrICmp(zDb, pDb->zName)) ){
|
||||
if( pDb->pBt && (!zDb || 0==sqlite3StrICmp(zDb, pDb->zDbSName)) ){
|
||||
sqlite3CodeVerifySchema(pParse, i);
|
||||
}
|
||||
}
|
||||
@@ -4302,7 +4305,7 @@ void sqlite3Reindex(Parse *pParse, Token *pName1, Token *pName2){
|
||||
if( iDb<0 ) return;
|
||||
z = sqlite3NameFromToken(db, pObjName);
|
||||
if( z==0 ) return;
|
||||
zDb = db->aDb[iDb].zName;
|
||||
zDb = db->aDb[iDb].zDbSName;
|
||||
pTab = sqlite3FindTable(db, z, zDb);
|
||||
if( pTab ){
|
||||
reindexTable(pParse, pTab, 0);
|
||||
|
||||
+10
-2
@@ -45,8 +45,10 @@ static const char * const azCompileOpt[] = {
|
||||
#if SQLITE_CHECK_PAGES
|
||||
"CHECK_PAGES",
|
||||
#endif
|
||||
#if defined(__clang__) && defined(__clang_version__)
|
||||
"COMPILER=clang-" __clang_version__,
|
||||
#if defined(__clang__) && defined(__clang_major__)
|
||||
"COMPILER=clang-" CTIMEOPT_VAL(__clang_major__) "."
|
||||
CTIMEOPT_VAL(__clang_minor__) "."
|
||||
CTIMEOPT_VAL(__clang_patchlevel__),
|
||||
#elif defined(_MSC_VER)
|
||||
"COMPILER=msvc-" CTIMEOPT_VAL(_MSC_VER),
|
||||
#elif defined(__GNUC__) && defined(__VERSION__)
|
||||
@@ -64,6 +66,9 @@ static const char * const azCompileOpt[] = {
|
||||
#if defined(SQLITE_DEFAULT_MMAP_SIZE) && !defined(SQLITE_DEFAULT_MMAP_SIZE_xc)
|
||||
"DEFAULT_MMAP_SIZE=" CTIMEOPT_VAL(SQLITE_DEFAULT_MMAP_SIZE),
|
||||
#endif
|
||||
#if SQLITE_DIRECT_OVERFLOW_READ
|
||||
"DIRECT_OVERFLOW_READ",
|
||||
#endif
|
||||
#if SQLITE_DISABLE_DIRSYNC
|
||||
"DISABLE_DIRSYNC",
|
||||
#endif
|
||||
@@ -150,6 +155,9 @@ static const char * const azCompileOpt[] = {
|
||||
#if SQLITE_ENABLE_UPDATE_DELETE_LIMIT
|
||||
"ENABLE_UPDATE_DELETE_LIMIT",
|
||||
#endif
|
||||
#if defined(SQLITE_ENABLE_URI_00_ERROR)
|
||||
"ENABLE_URI_00_ERROR",
|
||||
#endif
|
||||
#if SQLITE_HAS_CODEC
|
||||
"HAS_CODEC",
|
||||
#endif
|
||||
|
||||
+143
-71
@@ -65,16 +65,18 @@ struct tm *__cdecl localtime(const time_t *);
|
||||
*/
|
||||
typedef struct DateTime DateTime;
|
||||
struct DateTime {
|
||||
sqlite3_int64 iJD; /* The julian day number times 86400000 */
|
||||
int Y, M, D; /* Year, month, and day */
|
||||
int h, m; /* Hour and minutes */
|
||||
int tz; /* Timezone offset in minutes */
|
||||
double s; /* Seconds */
|
||||
char validYMD; /* True (1) if Y,M,D are valid */
|
||||
char validHMS; /* True (1) if h,m,s are valid */
|
||||
char validJD; /* True (1) if iJD is valid */
|
||||
char validTZ; /* True (1) if tz is valid */
|
||||
char tzSet; /* Timezone was set explicitly */
|
||||
sqlite3_int64 iJD; /* The julian day number times 86400000 */
|
||||
int Y, M, D; /* Year, month, and day */
|
||||
int h, m; /* Hour and minutes */
|
||||
int tz; /* Timezone offset in minutes */
|
||||
double s; /* Seconds */
|
||||
char validJD; /* True (1) if iJD is valid */
|
||||
char rawS; /* Raw numeric value stored in s */
|
||||
char validYMD; /* True (1) if Y,M,D are valid */
|
||||
char validHMS; /* True (1) if h,m,s are valid */
|
||||
char validTZ; /* True (1) if tz is valid */
|
||||
char tzSet; /* Timezone was set explicitly */
|
||||
char isError; /* An overflow has occurred */
|
||||
};
|
||||
|
||||
|
||||
@@ -222,6 +224,7 @@ static int parseHhMmSs(const char *zDate, DateTime *p){
|
||||
s = 0;
|
||||
}
|
||||
p->validJD = 0;
|
||||
p->rawS = 0;
|
||||
p->validHMS = 1;
|
||||
p->h = h;
|
||||
p->m = m;
|
||||
@@ -231,6 +234,14 @@ static int parseHhMmSs(const char *zDate, DateTime *p){
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
** Put the DateTime object into its error state.
|
||||
*/
|
||||
static void datetimeError(DateTime *p){
|
||||
memset(p, 0, sizeof(*p));
|
||||
p->isError = 1;
|
||||
}
|
||||
|
||||
/*
|
||||
** Convert from YYYY-MM-DD HH:MM:SS to julian day. We always assume
|
||||
** that the YYYY-MM-DD is according to the Gregorian calendar.
|
||||
@@ -250,6 +261,10 @@ static void computeJD(DateTime *p){
|
||||
M = 1;
|
||||
D = 1;
|
||||
}
|
||||
if( Y<-4713 || Y>9999 || p->rawS ){
|
||||
datetimeError(p);
|
||||
return;
|
||||
}
|
||||
if( M<=2 ){
|
||||
Y--;
|
||||
M += 12;
|
||||
@@ -330,6 +345,21 @@ static int setDateTimeToCurrent(sqlite3_context *context, DateTime *p){
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** Input "r" is a numeric quantity which might be a julian day number,
|
||||
** or the number of seconds since 1970. If the value if r is within
|
||||
** range of a julian day number, install it as such and set validJD.
|
||||
** If the value is a valid unix timestamp, put it in p->s and set p->rawS.
|
||||
*/
|
||||
static void setRawDateNumber(DateTime *p, double r){
|
||||
p->s = r;
|
||||
p->rawS = 1;
|
||||
if( r>=0.0 && r<5373484.5 ){
|
||||
p->iJD = (sqlite3_int64)(r*86400000.0 + 0.5);
|
||||
p->validJD = 1;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** Attempt to parse the given string into a julian day number. Return
|
||||
** the number of errors.
|
||||
@@ -359,13 +389,21 @@ static int parseDateOrTime(
|
||||
}else if( sqlite3StrICmp(zDate,"now")==0){
|
||||
return setDateTimeToCurrent(context, p);
|
||||
}else if( sqlite3AtoF(zDate, &r, sqlite3Strlen30(zDate), SQLITE_UTF8) ){
|
||||
p->iJD = (sqlite3_int64)(r*86400000.0 + 0.5);
|
||||
p->validJD = 1;
|
||||
setRawDateNumber(p, r);
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
** Return TRUE if the given julian day number is within range.
|
||||
**
|
||||
** The input is the JulianDay times 86400000.
|
||||
*/
|
||||
static int validJulianDay(sqlite3_int64 iJD){
|
||||
return iJD>=0 && iJD<=464269060799999;
|
||||
}
|
||||
|
||||
/*
|
||||
** Compute the Year, Month, and Day from the julian day number.
|
||||
*/
|
||||
@@ -377,6 +415,7 @@ static void computeYMD(DateTime *p){
|
||||
p->M = 1;
|
||||
p->D = 1;
|
||||
}else{
|
||||
assert( validJulianDay(p->iJD) );
|
||||
Z = (int)((p->iJD + 43200000)/86400000);
|
||||
A = (int)((Z - 1867216.25)/36524.25);
|
||||
A = Z + 1 + A - (A/4);
|
||||
@@ -407,6 +446,7 @@ static void computeHMS(DateTime *p){
|
||||
s -= p->h*3600;
|
||||
p->m = s/60;
|
||||
p->s += s - p->m*60;
|
||||
p->rawS = 0;
|
||||
p->validHMS = 1;
|
||||
}
|
||||
|
||||
@@ -546,13 +586,38 @@ static sqlite3_int64 localtimeOffset(
|
||||
y.validYMD = 1;
|
||||
y.validHMS = 1;
|
||||
y.validJD = 0;
|
||||
y.rawS = 0;
|
||||
y.validTZ = 0;
|
||||
y.isError = 0;
|
||||
computeJD(&y);
|
||||
*pRc = SQLITE_OK;
|
||||
return y.iJD - x.iJD;
|
||||
}
|
||||
#endif /* SQLITE_OMIT_LOCALTIME */
|
||||
|
||||
/*
|
||||
** The following table defines various date transformations of the form
|
||||
**
|
||||
** 'NNN days'
|
||||
**
|
||||
** Where NNN is an arbitrary floating-point number and "days" can be one
|
||||
** of several units of time.
|
||||
*/
|
||||
static const struct {
|
||||
u8 eType; /* Transformation type code */
|
||||
u8 nName; /* Length of th name */
|
||||
char *zName; /* Name of the transformation */
|
||||
double rLimit; /* Maximum NNN value for this transform */
|
||||
double rXform; /* Constant used for this transform */
|
||||
} aXformType[] = {
|
||||
{ 0, 6, "second", 464269060800.0, 86400000.0/(24.0*60.0*60.0) },
|
||||
{ 0, 6, "minute", 7737817680.0, 86400000.0/(24.0*60.0) },
|
||||
{ 0, 4, "hour", 128963628.0, 86400000.0/24.0 },
|
||||
{ 0, 3, "day", 5373485.0, 86400000.0 },
|
||||
{ 1, 5, "month", 176546.0, 30.0*86400000.0 },
|
||||
{ 2, 4, "year", 14713.0, 365.0*86400000.0 },
|
||||
};
|
||||
|
||||
/*
|
||||
** Process a modifier to a date-time stamp. The modifiers are
|
||||
** as follows:
|
||||
@@ -577,17 +642,15 @@ static sqlite3_int64 localtimeOffset(
|
||||
** to context pCtx. If the error is an unrecognized modifier, no error is
|
||||
** written to pCtx.
|
||||
*/
|
||||
static int parseModifier(sqlite3_context *pCtx, const char *zMod, DateTime *p){
|
||||
static int parseModifier(
|
||||
sqlite3_context *pCtx, /* Function context */
|
||||
const char *z, /* The text of the modifier */
|
||||
int n, /* Length of zMod in bytes */
|
||||
DateTime *p /* The date/time value to be modified */
|
||||
){
|
||||
int rc = 1;
|
||||
int n;
|
||||
double r;
|
||||
char *z, zBuf[30];
|
||||
z = zBuf;
|
||||
for(n=0; n<ArraySize(zBuf)-1 && zMod[n]; n++){
|
||||
z[n] = (char)sqlite3UpperToLower[(u8)zMod[n]];
|
||||
}
|
||||
z[n] = 0;
|
||||
switch( z[0] ){
|
||||
switch(sqlite3UpperToLower[(u8)z[0]] ){
|
||||
#ifndef SQLITE_OMIT_LOCALTIME
|
||||
case 'l': {
|
||||
/* localtime
|
||||
@@ -595,7 +658,7 @@ static int parseModifier(sqlite3_context *pCtx, const char *zMod, DateTime *p){
|
||||
** Assuming the current time value is UTC (a.k.a. GMT), shift it to
|
||||
** show local time.
|
||||
*/
|
||||
if( strcmp(z, "localtime")==0 ){
|
||||
if( sqlite3_stricmp(z, "localtime")==0 ){
|
||||
computeJD(p);
|
||||
p->iJD += localtimeOffset(p, pCtx, &rc);
|
||||
clearYMD_HMS_TZ(p);
|
||||
@@ -607,16 +670,21 @@ static int parseModifier(sqlite3_context *pCtx, const char *zMod, DateTime *p){
|
||||
/*
|
||||
** unixepoch
|
||||
**
|
||||
** Treat the current value of p->iJD as the number of
|
||||
** Treat the current value of p->s as the number of
|
||||
** seconds since 1970. Convert to a real julian day number.
|
||||
*/
|
||||
if( strcmp(z, "unixepoch")==0 && p->validJD ){
|
||||
p->iJD = (p->iJD + 43200)/86400 + 21086676*(i64)10000000;
|
||||
clearYMD_HMS_TZ(p);
|
||||
rc = 0;
|
||||
if( sqlite3_stricmp(z, "unixepoch")==0 && p->rawS ){
|
||||
r = p->s*1000.0 + 210866760000000.0;
|
||||
if( r>=0.0 && r<464269060800000.0 ){
|
||||
clearYMD_HMS_TZ(p);
|
||||
p->iJD = (sqlite3_int64)r;
|
||||
p->validJD = 1;
|
||||
p->rawS = 0;
|
||||
rc = 0;
|
||||
}
|
||||
}
|
||||
#ifndef SQLITE_OMIT_LOCALTIME
|
||||
else if( strcmp(z, "utc")==0 ){
|
||||
else if( sqlite3_stricmp(z, "utc")==0 ){
|
||||
if( p->tzSet==0 ){
|
||||
sqlite3_int64 c1;
|
||||
computeJD(p);
|
||||
@@ -642,7 +710,7 @@ static int parseModifier(sqlite3_context *pCtx, const char *zMod, DateTime *p){
|
||||
** weekday N where 0==Sunday, 1==Monday, and so forth. If the
|
||||
** date is already on the appropriate weekday, this is a no-op.
|
||||
*/
|
||||
if( strncmp(z, "weekday ", 8)==0
|
||||
if( sqlite3_strnicmp(z, "weekday ", 8)==0
|
||||
&& sqlite3AtoF(&z[8], &r, sqlite3Strlen30(&z[8]), SQLITE_UTF8)
|
||||
&& (n=(int)r)==r && n>=0 && r<7 ){
|
||||
sqlite3_int64 Z;
|
||||
@@ -665,7 +733,7 @@ static int parseModifier(sqlite3_context *pCtx, const char *zMod, DateTime *p){
|
||||
** Move the date backwards to the beginning of the current day,
|
||||
** or month or year.
|
||||
*/
|
||||
if( strncmp(z, "start of ", 9)!=0 ) break;
|
||||
if( sqlite3_strnicmp(z, "start of ", 9)!=0 ) break;
|
||||
z += 9;
|
||||
computeYMD(p);
|
||||
p->validHMS = 1;
|
||||
@@ -673,15 +741,15 @@ static int parseModifier(sqlite3_context *pCtx, const char *zMod, DateTime *p){
|
||||
p->s = 0.0;
|
||||
p->validTZ = 0;
|
||||
p->validJD = 0;
|
||||
if( strcmp(z,"month")==0 ){
|
||||
if( sqlite3_stricmp(z,"month")==0 ){
|
||||
p->D = 1;
|
||||
rc = 0;
|
||||
}else if( strcmp(z,"year")==0 ){
|
||||
}else if( sqlite3_stricmp(z,"year")==0 ){
|
||||
computeYMD(p);
|
||||
p->M = 1;
|
||||
p->D = 1;
|
||||
rc = 0;
|
||||
}else if( strcmp(z,"day")==0 ){
|
||||
}else if( sqlite3_stricmp(z,"day")==0 ){
|
||||
rc = 0;
|
||||
}
|
||||
break;
|
||||
@@ -699,6 +767,7 @@ static int parseModifier(sqlite3_context *pCtx, const char *zMod, DateTime *p){
|
||||
case '8':
|
||||
case '9': {
|
||||
double rRounder;
|
||||
int i;
|
||||
for(n=1; z[n] && z[n]!=':' && !sqlite3Isspace(z[n]); n++){}
|
||||
if( !sqlite3AtoF(z, &r, n, SQLITE_UTF8) ){
|
||||
rc = 1;
|
||||
@@ -727,46 +796,48 @@ static int parseModifier(sqlite3_context *pCtx, const char *zMod, DateTime *p){
|
||||
rc = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
/* If control reaches this point, it means the transformation is
|
||||
** one of the forms like "+NNN days". */
|
||||
z += n;
|
||||
while( sqlite3Isspace(*z) ) z++;
|
||||
n = sqlite3Strlen30(z);
|
||||
if( n>10 || n<3 ) break;
|
||||
if( z[n-1]=='s' ){ z[n-1] = 0; n--; }
|
||||
if( sqlite3UpperToLower[(u8)z[n-1]]=='s' ) n--;
|
||||
computeJD(p);
|
||||
rc = 0;
|
||||
rc = 1;
|
||||
rRounder = r<0 ? -0.5 : +0.5;
|
||||
if( n==3 && strcmp(z,"day")==0 ){
|
||||
p->iJD += (sqlite3_int64)(r*86400000.0 + rRounder);
|
||||
}else if( n==4 && strcmp(z,"hour")==0 ){
|
||||
p->iJD += (sqlite3_int64)(r*(86400000.0/24.0) + rRounder);
|
||||
}else if( n==6 && strcmp(z,"minute")==0 ){
|
||||
p->iJD += (sqlite3_int64)(r*(86400000.0/(24.0*60.0)) + rRounder);
|
||||
}else if( n==6 && strcmp(z,"second")==0 ){
|
||||
p->iJD += (sqlite3_int64)(r*(86400000.0/(24.0*60.0*60.0)) + rRounder);
|
||||
}else if( n==5 && strcmp(z,"month")==0 ){
|
||||
int x, y;
|
||||
computeYMD_HMS(p);
|
||||
p->M += (int)r;
|
||||
x = p->M>0 ? (p->M-1)/12 : (p->M-12)/12;
|
||||
p->Y += x;
|
||||
p->M -= x*12;
|
||||
p->validJD = 0;
|
||||
computeJD(p);
|
||||
y = (int)r;
|
||||
if( y!=r ){
|
||||
p->iJD += (sqlite3_int64)((r - y)*30.0*86400000.0 + rRounder);
|
||||
for(i=0; i<ArraySize(aXformType); i++){
|
||||
if( aXformType[i].nName==n
|
||||
&& sqlite3_strnicmp(aXformType[i].zName, z, n)==0
|
||||
&& r>-aXformType[i].rLimit && r<aXformType[i].rLimit
|
||||
){
|
||||
switch( aXformType[i].eType ){
|
||||
case 1: { /* Special processing to add months */
|
||||
int x;
|
||||
computeYMD_HMS(p);
|
||||
p->M += (int)r;
|
||||
x = p->M>0 ? (p->M-1)/12 : (p->M-12)/12;
|
||||
p->Y += x;
|
||||
p->M -= x*12;
|
||||
p->validJD = 0;
|
||||
r -= (int)r;
|
||||
break;
|
||||
}
|
||||
case 2: { /* Special processing to add years */
|
||||
int y = (int)r;
|
||||
computeYMD_HMS(p);
|
||||
p->Y += y;
|
||||
p->validJD = 0;
|
||||
r -= (int)r;
|
||||
break;
|
||||
}
|
||||
}
|
||||
computeJD(p);
|
||||
p->iJD += (sqlite3_int64)(r*aXformType[i].rXform + rRounder);
|
||||
rc = 0;
|
||||
break;
|
||||
}
|
||||
}else if( n==4 && strcmp(z,"year")==0 ){
|
||||
int y = (int)r;
|
||||
computeYMD_HMS(p);
|
||||
p->Y += y;
|
||||
p->validJD = 0;
|
||||
computeJD(p);
|
||||
if( y!=r ){
|
||||
p->iJD += (sqlite3_int64)((r - y)*365.0*86400000.0 + rRounder);
|
||||
}
|
||||
}else{
|
||||
rc = 1;
|
||||
}
|
||||
clearYMD_HMS_TZ(p);
|
||||
break;
|
||||
@@ -793,7 +864,7 @@ static int isDate(
|
||||
sqlite3_value **argv,
|
||||
DateTime *p
|
||||
){
|
||||
int i;
|
||||
int i, n;
|
||||
const unsigned char *z;
|
||||
int eType;
|
||||
memset(p, 0, sizeof(*p));
|
||||
@@ -802,8 +873,7 @@ static int isDate(
|
||||
}
|
||||
if( (eType = sqlite3_value_type(argv[0]))==SQLITE_FLOAT
|
||||
|| eType==SQLITE_INTEGER ){
|
||||
p->iJD = (sqlite3_int64)(sqlite3_value_double(argv[0])*86400000.0 + 0.5);
|
||||
p->validJD = 1;
|
||||
setRawDateNumber(p, sqlite3_value_double(argv[0]));
|
||||
}else{
|
||||
z = sqlite3_value_text(argv[0]);
|
||||
if( !z || parseDateOrTime(context, (char*)z, p) ){
|
||||
@@ -812,8 +882,11 @@ static int isDate(
|
||||
}
|
||||
for(i=1; i<argc; i++){
|
||||
z = sqlite3_value_text(argv[i]);
|
||||
if( z==0 || parseModifier(context, (char*)z, p) ) return 1;
|
||||
n = sqlite3_value_bytes(argv[i]);
|
||||
if( z==0 || parseModifier(context, (char*)z, n, p) ) return 1;
|
||||
}
|
||||
computeJD(p);
|
||||
if( p->isError || !validJulianDay(p->iJD) ) return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -1112,7 +1185,6 @@ static void currentTimeFunc(
|
||||
){
|
||||
time_t t;
|
||||
char *zFormat = (char *)sqlite3_user_data(context);
|
||||
sqlite3 *db;
|
||||
sqlite3_int64 iT;
|
||||
struct tm *pTm;
|
||||
struct tm sNow;
|
||||
|
||||
+2
-2
@@ -602,7 +602,7 @@ static int statFilter(
|
||||
" UNION ALL "
|
||||
"SELECT name, rootpage, type"
|
||||
" FROM \"%w\".%s WHERE rootpage!=0"
|
||||
" ORDER BY name", pTab->db->aDb[pCsr->iDb].zName, zMaster);
|
||||
" ORDER BY name", pTab->db->aDb[pCsr->iDb].zDbSName, zMaster);
|
||||
if( zSql==0 ){
|
||||
return SQLITE_NOMEM_BKPT;
|
||||
}else{
|
||||
@@ -656,7 +656,7 @@ static int statColumn(
|
||||
default: { /* schema */
|
||||
sqlite3 *db = sqlite3_context_db_handle(ctx);
|
||||
int iDb = pCsr->iDb;
|
||||
sqlite3_result_text(ctx, db->aDb[iDb].zName, -1, SQLITE_STATIC);
|
||||
sqlite3_result_text(ctx, db->aDb[iDb].zDbSName, -1, SQLITE_STATIC);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
+9
-18
@@ -102,7 +102,7 @@ void sqlite3MaterializeView(
|
||||
if( pFrom ){
|
||||
assert( pFrom->nSrc==1 );
|
||||
pFrom->a[0].zName = sqlite3DbStrDup(db, pView->zName);
|
||||
pFrom->a[0].zDatabase = sqlite3DbStrDup(db, db->aDb[iDb].zName);
|
||||
pFrom->a[0].zDatabase = sqlite3DbStrDup(db, db->aDb[iDb].zDbSName);
|
||||
assert( pFrom->a[0].pOn==0 );
|
||||
assert( pFrom->a[0].pUsing==0 );
|
||||
}
|
||||
@@ -164,7 +164,7 @@ Expr *sqlite3LimitWhere(
|
||||
** );
|
||||
*/
|
||||
|
||||
pSelectRowid = sqlite3PExpr(pParse, TK_ROW, 0, 0, 0);
|
||||
pSelectRowid = sqlite3PExpr(pParse, TK_ROW, 0, 0);
|
||||
if( pSelectRowid == 0 ) goto limit_where_cleanup;
|
||||
pEList = sqlite3ExprListAppend(pParse, 0, pSelectRowid);
|
||||
if( pEList == 0 ) goto limit_where_cleanup;
|
||||
@@ -183,8 +183,8 @@ Expr *sqlite3LimitWhere(
|
||||
if( pSelect == 0 ) return 0;
|
||||
|
||||
/* now generate the new WHERE rowid IN clause for the DELETE/UDPATE */
|
||||
pWhereRowid = sqlite3PExpr(pParse, TK_ROW, 0, 0, 0);
|
||||
pInClause = pWhereRowid ? sqlite3PExpr(pParse, TK_IN, pWhereRowid, 0, 0) : 0;
|
||||
pWhereRowid = sqlite3PExpr(pParse, TK_ROW, 0, 0);
|
||||
pInClause = pWhereRowid ? sqlite3PExpr(pParse, TK_IN, pWhereRowid, 0) : 0;
|
||||
sqlite3PExprAddSelect(pParse, pInClause, pSelect);
|
||||
return pInClause;
|
||||
|
||||
@@ -212,7 +212,6 @@ void sqlite3DeleteFrom(
|
||||
){
|
||||
Vdbe *v; /* The virtual database engine */
|
||||
Table *pTab; /* The table from which records will be deleted */
|
||||
const char *zDb; /* Name of database holding pTab */
|
||||
int i; /* Loop counter */
|
||||
WhereInfo *pWInfo; /* Information about the WHERE clause */
|
||||
Index *pIdx; /* For looping over indices of the table */
|
||||
@@ -289,8 +288,8 @@ void sqlite3DeleteFrom(
|
||||
}
|
||||
iDb = sqlite3SchemaToIndex(db, pTab->pSchema);
|
||||
assert( iDb<db->nDb );
|
||||
zDb = db->aDb[iDb].zName;
|
||||
rcauth = sqlite3AuthCheck(pParse, SQLITE_DELETE, pTab->zName, 0, zDb);
|
||||
rcauth = sqlite3AuthCheck(pParse, SQLITE_DELETE, pTab->zName, 0,
|
||||
db->aDb[iDb].zDbSName);
|
||||
assert( rcauth==SQLITE_OK || rcauth==SQLITE_DENY || rcauth==SQLITE_IGNORE );
|
||||
if( rcauth==SQLITE_DENY ){
|
||||
goto delete_from_cleanup;
|
||||
@@ -450,7 +449,7 @@ void sqlite3DeleteFrom(
|
||||
nKey = 0; /* Zero tells OP_Found to use a composite key */
|
||||
sqlite3VdbeAddOp4(v, OP_MakeRecord, iPk, nPk, iKey,
|
||||
sqlite3IndexAffinityStr(pParse->db, pPk), nPk);
|
||||
sqlite3VdbeAddOp2(v, OP_IdxInsert, iEphCur, iKey);
|
||||
sqlite3VdbeAddOp4Int(v, OP_IdxInsert, iEphCur, iKey, iPk, nPk);
|
||||
}else{
|
||||
/* Add the rowid of the row to be deleted to the RowSet */
|
||||
nKey = 1; /* OP_Seek always uses a single rowid */
|
||||
@@ -474,7 +473,7 @@ void sqlite3DeleteFrom(
|
||||
if( !isView ){
|
||||
int iAddrOnce = 0;
|
||||
if( eOnePass==ONEPASS_MULTI ){
|
||||
iAddrOnce = sqlite3CodeOnce(pParse); VdbeCoverage(v);
|
||||
iAddrOnce = sqlite3VdbeAddOp0(v, OP_Once); VdbeCoverage(v);
|
||||
}
|
||||
testcase( IsVirtual(pTab) );
|
||||
sqlite3OpenTableAndIndices(pParse, pTab, OP_OpenWrite, OPFLAG_FORDELETE,
|
||||
@@ -496,7 +495,7 @@ void sqlite3DeleteFrom(
|
||||
}
|
||||
}else if( pPk ){
|
||||
addrLoop = sqlite3VdbeAddOp1(v, OP_Rewind, iEphCur); VdbeCoverage(v);
|
||||
sqlite3VdbeAddOp2(v, OP_RowKey, iEphCur, iKey);
|
||||
sqlite3VdbeAddOp2(v, OP_RowData, iEphCur, iKey);
|
||||
assert( nKey==0 ); /* OP_Found will use a composite key */
|
||||
}else{
|
||||
addrLoop = sqlite3VdbeAddOp3(v, OP_RowSetRead, iRowSet, 0, iKey);
|
||||
@@ -539,14 +538,6 @@ void sqlite3DeleteFrom(
|
||||
sqlite3VdbeGoto(v, addrLoop);
|
||||
sqlite3VdbeJumpHere(v, addrLoop);
|
||||
}
|
||||
|
||||
/* Close the cursors open on the table and its indexes. */
|
||||
if( !isView && !IsVirtual(pTab) ){
|
||||
if( !pPk ) sqlite3VdbeAddOp1(v, OP_Close, iDataCur);
|
||||
for(i=0, pIdx=pTab->pIndex; pIdx; i++, pIdx=pIdx->pNext){
|
||||
sqlite3VdbeAddOp1(v, OP_Close, iIdxCur + i);
|
||||
}
|
||||
}
|
||||
} /* End non-truncate path */
|
||||
|
||||
/* Update the sqlite_sequence table by storing the content of the
|
||||
|
||||
+1150
-433
File diff suppressed because it is too large
Load Diff
+14
-18
@@ -584,7 +584,7 @@ static void fkScanChildren(
|
||||
assert( iCol>=0 );
|
||||
zCol = pFKey->pFrom->aCol[iCol].zName;
|
||||
pRight = sqlite3Expr(db, TK_ID, zCol);
|
||||
pEq = sqlite3PExpr(pParse, TK_EQ, pLeft, pRight, 0);
|
||||
pEq = sqlite3PExpr(pParse, TK_EQ, pLeft, pRight);
|
||||
pWhere = sqlite3ExprAnd(db, pWhere, pEq);
|
||||
}
|
||||
|
||||
@@ -606,7 +606,7 @@ static void fkScanChildren(
|
||||
if( HasRowid(pTab) ){
|
||||
pLeft = exprTableRegister(pParse, pTab, regData, -1);
|
||||
pRight = exprTableColumn(db, pTab, pSrc->a[0].iCursor, -1);
|
||||
pNe = sqlite3PExpr(pParse, TK_NE, pLeft, pRight, 0);
|
||||
pNe = sqlite3PExpr(pParse, TK_NE, pLeft, pRight);
|
||||
}else{
|
||||
Expr *pEq, *pAll = 0;
|
||||
Index *pPk = sqlite3PrimaryKeyIndex(pTab);
|
||||
@@ -616,10 +616,10 @@ static void fkScanChildren(
|
||||
assert( iCol>=0 );
|
||||
pLeft = exprTableRegister(pParse, pTab, regData, iCol);
|
||||
pRight = exprTableColumn(db, pTab, pSrc->a[0].iCursor, iCol);
|
||||
pEq = sqlite3PExpr(pParse, TK_EQ, pLeft, pRight, 0);
|
||||
pEq = sqlite3PExpr(pParse, TK_EQ, pLeft, pRight);
|
||||
pAll = sqlite3ExprAnd(db, pAll, pEq);
|
||||
}
|
||||
pNe = sqlite3PExpr(pParse, TK_NOT, pAll, 0, 0);
|
||||
pNe = sqlite3PExpr(pParse, TK_NOT, pAll, 0);
|
||||
}
|
||||
pWhere = sqlite3ExprAnd(db, pWhere, pNe);
|
||||
}
|
||||
@@ -871,7 +871,7 @@ void sqlite3FkCheck(
|
||||
if( (db->flags&SQLITE_ForeignKeys)==0 ) return;
|
||||
|
||||
iDb = sqlite3SchemaToIndex(db, pTab->pSchema);
|
||||
zDb = db->aDb[iDb].zName;
|
||||
zDb = db->aDb[iDb].zDbSName;
|
||||
|
||||
/* Loop through all the foreign key constraints for which pTab is the
|
||||
** child table (the table that the foreign key definition is part of). */
|
||||
@@ -1205,10 +1205,9 @@ static Trigger *fkActionTrigger(
|
||||
pEq = sqlite3PExpr(pParse, TK_EQ,
|
||||
sqlite3PExpr(pParse, TK_DOT,
|
||||
sqlite3ExprAlloc(db, TK_ID, &tOld, 0),
|
||||
sqlite3ExprAlloc(db, TK_ID, &tToCol, 0)
|
||||
, 0),
|
||||
sqlite3ExprAlloc(db, TK_ID, &tToCol, 0)),
|
||||
sqlite3ExprAlloc(db, TK_ID, &tFromCol, 0)
|
||||
, 0);
|
||||
);
|
||||
pWhere = sqlite3ExprAnd(db, pWhere, pEq);
|
||||
|
||||
/* For ON UPDATE, construct the next term of the WHEN clause.
|
||||
@@ -1220,13 +1219,11 @@ static Trigger *fkActionTrigger(
|
||||
pEq = sqlite3PExpr(pParse, TK_IS,
|
||||
sqlite3PExpr(pParse, TK_DOT,
|
||||
sqlite3ExprAlloc(db, TK_ID, &tOld, 0),
|
||||
sqlite3ExprAlloc(db, TK_ID, &tToCol, 0),
|
||||
0),
|
||||
sqlite3ExprAlloc(db, TK_ID, &tToCol, 0)),
|
||||
sqlite3PExpr(pParse, TK_DOT,
|
||||
sqlite3ExprAlloc(db, TK_ID, &tNew, 0),
|
||||
sqlite3ExprAlloc(db, TK_ID, &tToCol, 0),
|
||||
0),
|
||||
0);
|
||||
sqlite3ExprAlloc(db, TK_ID, &tToCol, 0))
|
||||
);
|
||||
pWhen = sqlite3ExprAnd(db, pWhen, pEq);
|
||||
}
|
||||
|
||||
@@ -1235,17 +1232,16 @@ static Trigger *fkActionTrigger(
|
||||
if( action==OE_Cascade ){
|
||||
pNew = sqlite3PExpr(pParse, TK_DOT,
|
||||
sqlite3ExprAlloc(db, TK_ID, &tNew, 0),
|
||||
sqlite3ExprAlloc(db, TK_ID, &tToCol, 0)
|
||||
, 0);
|
||||
sqlite3ExprAlloc(db, TK_ID, &tToCol, 0));
|
||||
}else if( action==OE_SetDflt ){
|
||||
Expr *pDflt = pFKey->pFrom->aCol[iFromCol].pDflt;
|
||||
if( pDflt ){
|
||||
pNew = sqlite3ExprDup(db, pDflt, 0);
|
||||
}else{
|
||||
pNew = sqlite3PExpr(pParse, TK_NULL, 0, 0, 0);
|
||||
pNew = sqlite3ExprAlloc(db, TK_NULL, 0, 0);
|
||||
}
|
||||
}else{
|
||||
pNew = sqlite3PExpr(pParse, TK_NULL, 0, 0, 0);
|
||||
pNew = sqlite3ExprAlloc(db, TK_NULL, 0, 0);
|
||||
}
|
||||
pList = sqlite3ExprListAppend(pParse, pList, pNew);
|
||||
sqlite3ExprListSetName(pParse, pList, &tFromCol, 0);
|
||||
@@ -1292,7 +1288,7 @@ static Trigger *fkActionTrigger(
|
||||
pStep->pExprList = sqlite3ExprListDup(db, pList, EXPRDUP_REDUCE);
|
||||
pStep->pSelect = sqlite3SelectDup(db, pSelect, EXPRDUP_REDUCE);
|
||||
if( pWhen ){
|
||||
pWhen = sqlite3PExpr(pParse, TK_NOT, pWhen, 0, 0);
|
||||
pWhen = sqlite3PExpr(pParse, TK_NOT, pWhen, 0);
|
||||
pTrigger->pWhen = sqlite3ExprDup(db, pWhen, EXPRDUP_REDUCE);
|
||||
}
|
||||
}
|
||||
|
||||
+68
-26
@@ -208,6 +208,8 @@ static void instrFunc(
|
||||
zHaystack = sqlite3_value_text(argv[0]);
|
||||
zNeedle = sqlite3_value_text(argv[1]);
|
||||
isText = 1;
|
||||
if( zNeedle==0 ) return;
|
||||
assert( zHaystack );
|
||||
}
|
||||
while( nNeedle<=nHaystack && memcmp(zHaystack, zNeedle, nNeedle)!=0 ){
|
||||
N++;
|
||||
@@ -596,9 +598,19 @@ static const struct compareInfo likeInfoNorm = { '%', '_', 0, 1 };
|
||||
static const struct compareInfo likeInfoAlt = { '%', '_', 0, 0 };
|
||||
|
||||
/*
|
||||
** Compare two UTF-8 strings for equality where the first string can
|
||||
** potentially be a "glob" or "like" expression. Return true (1) if they
|
||||
** are the same and false (0) if they are different.
|
||||
** Possible error returns from patternMatch()
|
||||
*/
|
||||
#define SQLITE_MATCH 0
|
||||
#define SQLITE_NOMATCH 1
|
||||
#define SQLITE_NOWILDCARDMATCH 2
|
||||
|
||||
/*
|
||||
** Compare two UTF-8 strings for equality where the first string is
|
||||
** a GLOB or LIKE expression. Return values:
|
||||
**
|
||||
** SQLITE_MATCH: Match
|
||||
** SQLITE_NOMATCH: No match
|
||||
** SQLITE_NOWILDCARDMATCH: No match in spite of having * or % wildcards.
|
||||
**
|
||||
** Globbing rules:
|
||||
**
|
||||
@@ -649,30 +661,31 @@ static int patternCompare(
|
||||
** single character of the input string for each "?" skipped */
|
||||
while( (c=Utf8Read(zPattern)) == matchAll || c == matchOne ){
|
||||
if( c==matchOne && sqlite3Utf8Read(&zString)==0 ){
|
||||
return 0;
|
||||
return SQLITE_NOWILDCARDMATCH;
|
||||
}
|
||||
}
|
||||
if( c==0 ){
|
||||
return 1; /* "*" at the end of the pattern matches */
|
||||
return SQLITE_MATCH; /* "*" at the end of the pattern matches */
|
||||
}else if( c==matchOther ){
|
||||
if( pInfo->matchSet==0 ){
|
||||
c = sqlite3Utf8Read(&zPattern);
|
||||
if( c==0 ) return 0;
|
||||
if( c==0 ) return SQLITE_NOWILDCARDMATCH;
|
||||
}else{
|
||||
/* "[...]" immediately follows the "*". We have to do a slow
|
||||
** recursive search in this case, but it is an unusual case. */
|
||||
assert( matchOther<0x80 ); /* '[' is a single-byte character */
|
||||
while( *zString
|
||||
&& patternCompare(&zPattern[-1],zString,pInfo,matchOther)==0 ){
|
||||
while( *zString ){
|
||||
int bMatch = patternCompare(&zPattern[-1],zString,pInfo,matchOther);
|
||||
if( bMatch!=SQLITE_NOMATCH ) return bMatch;
|
||||
SQLITE_SKIP_UTF8(zString);
|
||||
}
|
||||
return *zString!=0;
|
||||
return SQLITE_NOWILDCARDMATCH;
|
||||
}
|
||||
}
|
||||
|
||||
/* At this point variable c contains the first character of the
|
||||
** pattern string past the "*". Search in the input string for the
|
||||
** first matching character and recursively contine the match from
|
||||
** first matching character and recursively continue the match from
|
||||
** that point.
|
||||
**
|
||||
** For a case-insensitive search, set variable cx to be the same as
|
||||
@@ -681,6 +694,7 @@ static int patternCompare(
|
||||
*/
|
||||
if( c<=0x80 ){
|
||||
u32 cx;
|
||||
int bMatch;
|
||||
if( noCase ){
|
||||
cx = sqlite3Toupper(c);
|
||||
c = sqlite3Tolower(c);
|
||||
@@ -689,27 +703,30 @@ static int patternCompare(
|
||||
}
|
||||
while( (c2 = *(zString++))!=0 ){
|
||||
if( c2!=c && c2!=cx ) continue;
|
||||
if( patternCompare(zPattern,zString,pInfo,matchOther) ) return 1;
|
||||
bMatch = patternCompare(zPattern,zString,pInfo,matchOther);
|
||||
if( bMatch!=SQLITE_NOMATCH ) return bMatch;
|
||||
}
|
||||
}else{
|
||||
int bMatch;
|
||||
while( (c2 = Utf8Read(zString))!=0 ){
|
||||
if( c2!=c ) continue;
|
||||
if( patternCompare(zPattern,zString,pInfo,matchOther) ) return 1;
|
||||
bMatch = patternCompare(zPattern,zString,pInfo,matchOther);
|
||||
if( bMatch!=SQLITE_NOMATCH ) return bMatch;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
return SQLITE_NOWILDCARDMATCH;
|
||||
}
|
||||
if( c==matchOther ){
|
||||
if( pInfo->matchSet==0 ){
|
||||
c = sqlite3Utf8Read(&zPattern);
|
||||
if( c==0 ) return 0;
|
||||
if( c==0 ) return SQLITE_NOMATCH;
|
||||
zEscaped = zPattern;
|
||||
}else{
|
||||
u32 prior_c = 0;
|
||||
int seen = 0;
|
||||
int invert = 0;
|
||||
c = sqlite3Utf8Read(&zString);
|
||||
if( c==0 ) return 0;
|
||||
if( c==0 ) return SQLITE_NOMATCH;
|
||||
c2 = sqlite3Utf8Read(&zPattern);
|
||||
if( c2=='^' ){
|
||||
invert = 1;
|
||||
@@ -733,7 +750,7 @@ static int patternCompare(
|
||||
c2 = sqlite3Utf8Read(&zPattern);
|
||||
}
|
||||
if( c2==0 || (seen ^ invert)==0 ){
|
||||
return 0;
|
||||
return SQLITE_NOMATCH;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
@@ -744,23 +761,25 @@ static int patternCompare(
|
||||
continue;
|
||||
}
|
||||
if( c==matchOne && zPattern!=zEscaped && c2!=0 ) continue;
|
||||
return 0;
|
||||
return SQLITE_NOMATCH;
|
||||
}
|
||||
return *zString==0;
|
||||
return *zString==0 ? SQLITE_MATCH : SQLITE_NOMATCH;
|
||||
}
|
||||
|
||||
/*
|
||||
** The sqlite3_strglob() interface.
|
||||
** The sqlite3_strglob() interface. Return 0 on a match (like strcmp()) and
|
||||
** non-zero if there is no match.
|
||||
*/
|
||||
int sqlite3_strglob(const char *zGlobPattern, const char *zString){
|
||||
return patternCompare((u8*)zGlobPattern, (u8*)zString, &globInfo, '[')==0;
|
||||
return patternCompare((u8*)zGlobPattern, (u8*)zString, &globInfo, '[');
|
||||
}
|
||||
|
||||
/*
|
||||
** The sqlite3_strlike() interface.
|
||||
** The sqlite3_strlike() interface. Return 0 on a match and non-zero for
|
||||
** a miss - like strcmp().
|
||||
*/
|
||||
int sqlite3_strlike(const char *zPattern, const char *zStr, unsigned int esc){
|
||||
return patternCompare((u8*)zPattern, (u8*)zStr, &likeInfoNorm, esc)==0;
|
||||
return patternCompare((u8*)zPattern, (u8*)zStr, &likeInfoNorm, esc);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -841,7 +860,7 @@ static void likeFunc(
|
||||
#ifdef SQLITE_TEST
|
||||
sqlite3_like_count++;
|
||||
#endif
|
||||
sqlite3_result_int(context, patternCompare(zB, zA, pInfo, escape));
|
||||
sqlite3_result_int(context, patternCompare(zB, zA, pInfo, escape)==SQLITE_MATCH);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1316,6 +1335,26 @@ static void trimFunc(
|
||||
}
|
||||
|
||||
|
||||
#ifdef SQLITE_ENABLE_UNKNOWN_SQL_FUNCTION
|
||||
/*
|
||||
** The "unknown" function is automatically substituted in place of
|
||||
** any unrecognized function name when doing an EXPLAIN or EXPLAIN QUERY PLAN
|
||||
** when the SQLITE_ENABLE_UNKNOWN_FUNCTION compile-time option is used.
|
||||
** When the "sqlite3" command-line shell is built using this functionality,
|
||||
** that allows an EXPLAIN or EXPLAIN QUERY PLAN for complex queries
|
||||
** involving application-defined functions to be examined in a generic
|
||||
** sqlite3 shell.
|
||||
*/
|
||||
static void unknownFunc(
|
||||
sqlite3_context *context,
|
||||
int argc,
|
||||
sqlite3_value **argv
|
||||
){
|
||||
/* no-op */
|
||||
}
|
||||
#endif /*SQLITE_ENABLE_UNKNOWN_SQL_FUNCTION*/
|
||||
|
||||
|
||||
/* IMP: R-25361-16150 This function is omitted from SQLite by default. It
|
||||
** is only available if the SQLITE_SOUNDEX compile-time option is used
|
||||
** when SQLite is built.
|
||||
@@ -1786,13 +1825,16 @@ void sqlite3RegisterBuiltinFunctions(void){
|
||||
AGGREGATE(group_concat, 2, 0, 0, groupConcatStep, groupConcatFinalize),
|
||||
|
||||
LIKEFUNC(glob, 2, &globInfo, SQLITE_FUNC_LIKE|SQLITE_FUNC_CASE),
|
||||
#ifdef SQLITE_CASE_SENSITIVE_LIKE
|
||||
#ifdef SQLITE_CASE_SENSITIVE_LIKE
|
||||
LIKEFUNC(like, 2, &likeInfoAlt, SQLITE_FUNC_LIKE|SQLITE_FUNC_CASE),
|
||||
LIKEFUNC(like, 3, &likeInfoAlt, SQLITE_FUNC_LIKE|SQLITE_FUNC_CASE),
|
||||
#else
|
||||
#else
|
||||
LIKEFUNC(like, 2, &likeInfoNorm, SQLITE_FUNC_LIKE),
|
||||
LIKEFUNC(like, 3, &likeInfoNorm, SQLITE_FUNC_LIKE),
|
||||
#endif
|
||||
#endif
|
||||
#ifdef SQLITE_ENABLE_UNKNOWN_SQL_FUNCTION
|
||||
FUNCTION(unknown, -1, 0, 0, unknownFunc ),
|
||||
#endif
|
||||
FUNCTION(coalesce, 1, 0, 0, 0 ),
|
||||
FUNCTION(coalesce, 0, 0, 0, 0 ),
|
||||
FUNCTION2(coalesce, -1, 0, 0, noopFunc, SQLITE_FUNC_COALESCE),
|
||||
|
||||
+6
-8
@@ -79,16 +79,13 @@ const unsigned char sqlite3UpperToLower[] = {
|
||||
**
|
||||
** (x & ~(map[x]&0x20))
|
||||
**
|
||||
** Standard function tolower() is implemented using the sqlite3UpperToLower[]
|
||||
** The equivalent of tolower() is implemented using the sqlite3UpperToLower[]
|
||||
** array. tolower() is used more often than toupper() by SQLite.
|
||||
**
|
||||
** Bit 0x40 is set if the character non-alphanumeric and can be used in an
|
||||
** Bit 0x40 is set if the character is non-alphanumeric and can be used in an
|
||||
** SQLite identifier. Identifiers are alphanumerics, "_", "$", and any
|
||||
** non-ASCII UTF character. Hence the test for whether or not a character is
|
||||
** part of an identifier is 0x46.
|
||||
**
|
||||
** SQLite's versions are identical to the standard versions assuming a
|
||||
** locale of "C". They are implemented as macros in sqliteInt.h.
|
||||
*/
|
||||
#ifdef SQLITE_ASCII
|
||||
const unsigned char sqlite3CtypeMap[256] = {
|
||||
@@ -161,7 +158,7 @@ const unsigned char sqlite3CtypeMap[256] = {
|
||||
#endif
|
||||
|
||||
/* Statement journals spill to disk when their size exceeds the following
|
||||
** threashold (in bytes). 0 means that statement journals are created and
|
||||
** threshold (in bytes). 0 means that statement journals are created and
|
||||
** written to disk immediately (the default behavior for SQLite versions
|
||||
** before 3.12.0). -1 means always keep the entire statement journal in
|
||||
** memory. (The statement journal is also always held entirely in memory
|
||||
@@ -225,7 +222,8 @@ SQLITE_WSD struct Sqlite3Config sqlite3Config = {
|
||||
#ifndef SQLITE_OMIT_BUILTIN_TEST
|
||||
0, /* xTestCallback */
|
||||
#endif
|
||||
0 /* bLocaltimeFault */
|
||||
0, /* bLocaltimeFault */
|
||||
0x7ffffffe /* iOnceResetThreshold */
|
||||
};
|
||||
|
||||
/*
|
||||
@@ -248,7 +246,7 @@ const Token sqlite3IntTokens[] = {
|
||||
** 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
|
||||
** to read or write that page. The pending byte page is set aside
|
||||
** for use by the VFS layers as space for managing file locks.
|
||||
**
|
||||
** During testing, it is often desirable to move the pending byte to
|
||||
|
||||
+5
-1
@@ -56,7 +56,11 @@ static unsigned int strHash(const char *z){
|
||||
unsigned int h = 0;
|
||||
unsigned char c;
|
||||
while( (c = (unsigned char)*z++)!=0 ){ /*OPTIMIZATION-IF-TRUE*/
|
||||
h = (h<<3) ^ h ^ sqlite3UpperToLower[c];
|
||||
/* Knuth multiplicative hashing. (Sorting & Searching, p. 510).
|
||||
** 0x9e3779b1 is 2654435761 which is the closest prime number to
|
||||
** (2**32)*golden_ratio, where golden_ratio = (sqrt(5) - 1)/2. */
|
||||
h += sqlite3UpperToLower[c];
|
||||
h *= 0x9e3779b1;
|
||||
}
|
||||
return h;
|
||||
}
|
||||
|
||||
+3
-3
@@ -12,8 +12,8 @@
|
||||
** This is the header file for the generic hash-table implementation
|
||||
** used in SQLite.
|
||||
*/
|
||||
#ifndef _SQLITE_HASH_H_
|
||||
#define _SQLITE_HASH_H_
|
||||
#ifndef SQLITE_HASH_H
|
||||
#define SQLITE_HASH_H
|
||||
|
||||
/* Forward declarations of structures. */
|
||||
typedef struct Hash Hash;
|
||||
@@ -93,4 +93,4 @@ void sqlite3HashClear(Hash*);
|
||||
*/
|
||||
/* #define sqliteHashCount(H) ((H)->count) // NOT USED */
|
||||
|
||||
#endif /* _SQLITE_HASH_H_ */
|
||||
#endif /* SQLITE_HASH_H */
|
||||
|
||||
+3
-3
@@ -13,8 +13,8 @@
|
||||
** This file contains inline asm code for retrieving "high-performance"
|
||||
** counters for x86 class CPUs.
|
||||
*/
|
||||
#ifndef _HWTIME_H_
|
||||
#define _HWTIME_H_
|
||||
#ifndef SQLITE_HWTIME_H
|
||||
#define SQLITE_HWTIME_H
|
||||
|
||||
/*
|
||||
** The following routine only works on pentium-class (or newer) processors.
|
||||
@@ -82,4 +82,4 @@
|
||||
|
||||
#endif
|
||||
|
||||
#endif /* !defined(_HWTIME_H_) */
|
||||
#endif /* !defined(SQLITE_HWTIME_H) */
|
||||
|
||||
@@ -0,0 +1,107 @@
|
||||
IN-Operator Implementation Notes
|
||||
================================
|
||||
|
||||
## Definitions:
|
||||
|
||||
An IN operator has one of the following formats:
|
||||
|
||||
>
|
||||
x IN (y1,y2,y3,...,yN)
|
||||
x IN (subquery)
|
||||
|
||||
The "x" is referred to as the LHS (left-hand side). The list or subquery
|
||||
on the right is called the RHS (right-hand side). If the RHS is a list
|
||||
it must be a non-empty list. But if the RHS is a subquery, it can be an
|
||||
empty set.
|
||||
|
||||
The LHS can be a scalar (a single quantity) or a vector (a list of
|
||||
two or or more values) or a subquery that returns one or more columns.
|
||||
We use the term "vector" to mean an actually list of values or a
|
||||
subquery that returns two or more columns. An isolated value or
|
||||
a subquery that returns a single columns is called a scalar.
|
||||
|
||||
The RHS can be a subquery that returns a single column, a subquery
|
||||
that returns two or more columns, or a list of scalars. It is not
|
||||
currently support for the RHS to be a list of vectors.
|
||||
|
||||
The number of columns for LHS must match the number of columns for
|
||||
the RHS. If the RHS is a list of values, then the LHS must be a
|
||||
scalar. If the RHS is a subquery returning N columns, then the LHS
|
||||
must be a vector of size N.
|
||||
|
||||
NULL values can occur in either or both of the LHS and RHS.
|
||||
If the LHS contains only
|
||||
NULL values then we say that it is a "total-NULL". If the LHS contains
|
||||
some NULL values and some non-NULL values, then it is a "partial-NULL".
|
||||
For a scalar, there is no difference between a partial-NULL and a total-NULL.
|
||||
The RHS is a partial-NULL if any row contains a NULL value. The RHS is
|
||||
a total-NULL if it contains one or more rows that contain only NULL values.
|
||||
The LHS is called "non-NULL" if it contains no NULL values. The RHS is
|
||||
called "non-NULL" if it contains no NULL values in any row.
|
||||
|
||||
The result of an IN operator is one of TRUE, FALSE, or NULL. A NULL result
|
||||
means that it cannot be determined if the LHS is contained in the RHS due
|
||||
to the presence of NULL values. In some contexts (for example, when the IN
|
||||
operator occurs in a WHERE clause)
|
||||
the system only needs a binary result: TRUE or NOT-TRUE. One can also
|
||||
to define a binary result of FALSE and NOT-FALSE, but
|
||||
it turns out that no extra optimizations are possible in that case, so if
|
||||
the FALSE/NOT-FALSE binary is needed, we have to compute the three-state
|
||||
TRUE/FALSE/NULL result and then combine the TRUE and NULL values into
|
||||
NOT-FALSE.
|
||||
|
||||
A "NOT IN" operator is computed by first computing the equivalent IN
|
||||
operator, then interchanging the TRUE and FALSE results.
|
||||
|
||||
## Simple Full-Scan Algorithm
|
||||
|
||||
The following algorithm always compute the correct answer. However, this
|
||||
algorithm is suboptimal, especially if there are many rows on the RHS.
|
||||
|
||||
1. Set the null-flag to false
|
||||
2. For each row in the RHS:
|
||||
<ol type='a'>
|
||||
<li> Compare the LHS against the RHS
|
||||
<li> If the LHS exactly matches the RHS, immediately return TRUE
|
||||
<li> If the comparison result is NULL, set the null-flag to true
|
||||
</ol>
|
||||
3. If the null-flag is true, return NULL.
|
||||
4. Return FALSE
|
||||
|
||||
## Optimized Algorithm
|
||||
|
||||
The following procedure computes the same answer as the simple full-scan
|
||||
algorithm, though it does so with less work in the common case. This
|
||||
is the algorithm that is implemented in SQLite.
|
||||
|
||||
1. If the RHS is a constant list of length 1 or 2, then rewrite the
|
||||
IN operator as a simple expression. Implement
|
||||
|
||||
x IN (y1,y2)
|
||||
|
||||
as if it were
|
||||
|
||||
x=y1 OR x=y2
|
||||
|
||||
This is the INDEX_NOOP optimization and is only undertaken if the
|
||||
IN operator is used for membership testing. If the IN operator is
|
||||
driving a loop, then skip this step entirely.
|
||||
|
||||
2. Check the LHS to see if it is a partial-NULL and if it is, jump
|
||||
ahead to step 5.
|
||||
|
||||
3. Do a binary search of the RHS using the LHS as a probe. If
|
||||
an exact match is found, return TRUE.
|
||||
|
||||
4. If the RHS is non-NULL then return FALSE.
|
||||
|
||||
5. If we do not need to distinguish between FALSE and NULL,
|
||||
then return FALSE.
|
||||
|
||||
6. For each row in the RHS, compare that row against the LHS and
|
||||
if the result is NULL, immediately return NULL. In the case
|
||||
of a scalar IN operator, we only need to look at the very first
|
||||
row the RHS because for a scalar RHS, all NULLs will always come
|
||||
first. If the RHS is empty, this step is a no-op.
|
||||
|
||||
7. Return FALSE.
|
||||
+60
-38
@@ -200,7 +200,9 @@ static int readsTable(Parse *p, int iDb, Table *pTab){
|
||||
/*
|
||||
** Locate or create an AutoincInfo structure associated with table pTab
|
||||
** which is in database iDb. Return the register number for the register
|
||||
** that holds the maximum rowid.
|
||||
** that holds the maximum rowid. Return zero if pTab is not an AUTOINCREMENT
|
||||
** table. (Also return zero when doing a VACUUM since we do not want to
|
||||
** update the AUTOINCREMENT counters during a VACUUM.)
|
||||
**
|
||||
** There is at most one AutoincInfo structure per table even if the
|
||||
** same table is autoincremented multiple times due to inserts within
|
||||
@@ -223,7 +225,9 @@ static int autoIncBegin(
|
||||
Table *pTab /* The table we are writing to */
|
||||
){
|
||||
int memId = 0; /* Register holding maximum rowid */
|
||||
if( pTab->tabFlags & TF_Autoincrement ){
|
||||
if( (pTab->tabFlags & TF_Autoincrement)!=0
|
||||
&& (pParse->db->flags & SQLITE_Vacuum)==0
|
||||
){
|
||||
Parse *pToplevel = sqlite3ParseToplevel(pParse);
|
||||
AutoincInfo *pInfo;
|
||||
|
||||
@@ -481,8 +485,7 @@ void sqlite3Insert(
|
||||
sqlite3 *db; /* The main database structure */
|
||||
Table *pTab; /* The table to insert into. aka TABLE */
|
||||
char *zTab; /* Name of the table into which we are inserting */
|
||||
const char *zDb; /* Name of the database holding this table */
|
||||
int i, j, idx; /* Loop counters */
|
||||
int i, j; /* Loop counters */
|
||||
Vdbe *v; /* Generate code into this virtual machine */
|
||||
Index *pIdx; /* For looping over indices of the table */
|
||||
int nColumn; /* Number of columns in the data */
|
||||
@@ -496,7 +499,6 @@ void sqlite3Insert(
|
||||
int addrCont = 0; /* Top of insert loop. Label "C" in templates 3 and 4 */
|
||||
SelectDest dest; /* Destination for SELECT on rhs of INSERT */
|
||||
int iDb; /* Index of database holding TABLE */
|
||||
Db *pDb; /* The database containing table being inserted into */
|
||||
u8 useTempTable = 0; /* Store SELECT results in intermediate table */
|
||||
u8 appendFlag = 0; /* True if the insert is likely to be an append */
|
||||
u8 withoutRowid; /* 0 for normal table. 1 for WITHOUT ROWID table */
|
||||
@@ -546,9 +548,8 @@ void sqlite3Insert(
|
||||
}
|
||||
iDb = sqlite3SchemaToIndex(db, pTab->pSchema);
|
||||
assert( iDb<db->nDb );
|
||||
pDb = &db->aDb[iDb];
|
||||
zDb = pDb->zName;
|
||||
if( sqlite3AuthCheck(pParse, SQLITE_INSERT, pTab->zName, 0, zDb) ){
|
||||
if( sqlite3AuthCheck(pParse, SQLITE_INSERT, pTab->zName, 0,
|
||||
db->aDb[iDb].zDbSName) ){
|
||||
goto insert_cleanup;
|
||||
}
|
||||
withoutRowid = !HasRowid(pTab);
|
||||
@@ -791,8 +792,10 @@ void sqlite3Insert(
|
||||
if( aRegIdx==0 ){
|
||||
goto insert_cleanup;
|
||||
}
|
||||
for(i=0; i<nIdx; i++){
|
||||
for(i=0, pIdx=pTab->pIndex; i<nIdx; pIdx=pIdx->pNext, i++){
|
||||
assert( pIdx );
|
||||
aRegIdx[i] = ++pParse->nMem;
|
||||
pParse->nMem += pIdx->nColumn;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -994,12 +997,26 @@ void sqlite3Insert(
|
||||
#endif
|
||||
{
|
||||
int isReplace; /* Set to true if constraints may cause a replace */
|
||||
int bUseSeek; /* True to use OPFLAG_SEEKRESULT */
|
||||
sqlite3GenerateConstraintChecks(pParse, pTab, aRegIdx, iDataCur, iIdxCur,
|
||||
regIns, 0, ipkColumn>=0, onError, endOfLoop, &isReplace, 0
|
||||
);
|
||||
sqlite3FkCheck(pParse, pTab, 0, regIns, 0, 0);
|
||||
|
||||
/* Set the OPFLAG_USESEEKRESULT flag if either (a) there are no REPLACE
|
||||
** constraints or (b) there are no triggers and this table is not a
|
||||
** parent table in a foreign key constraint. It is safe to set the
|
||||
** flag in the second case as if any REPLACE constraint is hit, an
|
||||
** OP_Delete or OP_IdxDelete instruction will be executed on each
|
||||
** cursor that is disturbed. And these instructions both clear the
|
||||
** VdbeCursor.seekResult variable, disabling the OPFLAG_USESEEKRESULT
|
||||
** functionality. */
|
||||
bUseSeek = (isReplace==0 || (pTrigger==0 &&
|
||||
((db->flags & SQLITE_ForeignKeys)==0 || sqlite3FkReferences(pTab)==0)
|
||||
));
|
||||
sqlite3CompleteInsertion(pParse, pTab, iDataCur, iIdxCur,
|
||||
regIns, aRegIdx, 0, appendFlag, isReplace==0);
|
||||
regIns, aRegIdx, 0, appendFlag, bUseSeek
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1028,14 +1045,6 @@ void sqlite3Insert(
|
||||
sqlite3VdbeJumpHere(v, addrInsTop);
|
||||
}
|
||||
|
||||
if( !IsVirtual(pTab) && !isView ){
|
||||
/* Close all tables opened */
|
||||
if( iDataCur<iIdxCur ) sqlite3VdbeAddOp1(v, OP_Close, iDataCur);
|
||||
for(idx=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, idx++){
|
||||
sqlite3VdbeAddOp1(v, OP_Close, idx+iIdxCur);
|
||||
}
|
||||
}
|
||||
|
||||
insert_end:
|
||||
/* Update the sqlite_sequence table by storing the content of the
|
||||
** maximum rowid counter values recorded while inserting into
|
||||
@@ -1242,7 +1251,6 @@ void sqlite3GenerateConstraintChecks(
|
||||
int ipkBottom = 0; /* Bottom of the rowid change constraint check */
|
||||
u8 isUpdate; /* True if this is an UPDATE operation */
|
||||
u8 bAffinityDone = 0; /* True if the OP_Affinity operation has been run */
|
||||
int regRowid = -1; /* Register holding ROWID value */
|
||||
|
||||
isUpdate = regOldData!=0;
|
||||
db = pParse->db;
|
||||
@@ -1362,7 +1370,7 @@ void sqlite3GenerateConstraintChecks(
|
||||
}
|
||||
|
||||
if( isUpdate ){
|
||||
/* pkChng!=0 does not mean that the rowid has change, only that
|
||||
/* pkChng!=0 does not mean that the rowid has changed, only that
|
||||
** it might have changed. Skip the conflict logic below if the rowid
|
||||
** is unchanged. */
|
||||
sqlite3VdbeAddOp3(v, OP_Eq, regNewData, addrRowidOk, regOldData);
|
||||
@@ -1497,7 +1505,7 @@ void sqlite3GenerateConstraintChecks(
|
||||
/* Create a record for this index entry as it should appear after
|
||||
** the insert or update. Store that record in the aRegIdx[ix] register
|
||||
*/
|
||||
regIdx = sqlite3GetTempRange(pParse, pIdx->nColumn);
|
||||
regIdx = aRegIdx[ix]+1;
|
||||
for(i=0; i<pIdx->nColumn; i++){
|
||||
int iField = pIdx->aiColumn[i];
|
||||
int x;
|
||||
@@ -1508,9 +1516,7 @@ void sqlite3GenerateConstraintChecks(
|
||||
VdbeComment((v, "%s column %d", pIdx->zName, i));
|
||||
}else{
|
||||
if( iField==XN_ROWID || iField==pTab->iPKey ){
|
||||
if( regRowid==regIdx+i ) continue; /* ROWID already in regIdx+i */
|
||||
x = regNewData;
|
||||
regRowid = pIdx->pPartIdxWhere ? -1 : regIdx+i;
|
||||
}else{
|
||||
x = iField + regNewData + 1;
|
||||
}
|
||||
@@ -1520,7 +1526,6 @@ void sqlite3GenerateConstraintChecks(
|
||||
}
|
||||
sqlite3VdbeAddOp3(v, OP_MakeRecord, regIdx, pIdx->nColumn, aRegIdx[ix]);
|
||||
VdbeComment((v, "for %s", pIdx->zName));
|
||||
sqlite3ExprCacheAffinityChange(pParse, regIdx, pIdx->nColumn);
|
||||
|
||||
/* In an UPDATE operation, if this index is the PRIMARY KEY index
|
||||
** of a WITHOUT ROWID table and there has been no change the
|
||||
@@ -1534,7 +1539,6 @@ void sqlite3GenerateConstraintChecks(
|
||||
/* Find out what action to take in case there is a uniqueness conflict */
|
||||
onError = pIdx->onError;
|
||||
if( onError==OE_None ){
|
||||
sqlite3ReleaseTempRange(pParse, regIdx, pIdx->nColumn);
|
||||
sqlite3VdbeResolveLabel(v, addrUniqueOk);
|
||||
continue; /* pIdx is not a UNIQUE index */
|
||||
}
|
||||
@@ -1543,6 +1547,12 @@ void sqlite3GenerateConstraintChecks(
|
||||
}else if( onError==OE_Default ){
|
||||
onError = OE_Abort;
|
||||
}
|
||||
|
||||
if( ix==0 && pPk==pIdx && onError==OE_Replace && pPk->pNext==0 ){
|
||||
sqlite3VdbeResolveLabel(v, addrUniqueOk);
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
/* Check to see if the new index entry will be unique */
|
||||
sqlite3VdbeAddOp4Int(v, OP_NoConflict, iThisCur, addrUniqueOk,
|
||||
@@ -1633,7 +1643,6 @@ void sqlite3GenerateConstraintChecks(
|
||||
}
|
||||
}
|
||||
sqlite3VdbeResolveLabel(v, addrUniqueOk);
|
||||
sqlite3ReleaseTempRange(pParse, regIdx, pIdx->nColumn);
|
||||
if( regR!=regIdx ) sqlite3ReleaseTempRange(pParse, regR, nPkField);
|
||||
}
|
||||
if( ipkTop ){
|
||||
@@ -1683,7 +1692,9 @@ void sqlite3CompleteInsertion(
|
||||
sqlite3VdbeAddOp2(v, OP_IsNull, aRegIdx[i], sqlite3VdbeCurrentAddr(v)+2);
|
||||
VdbeCoverage(v);
|
||||
}
|
||||
sqlite3VdbeAddOp2(v, OP_IdxInsert, iIdxCur+i, aRegIdx[i]);
|
||||
sqlite3VdbeAddOp4Int(v, OP_IdxInsert, iIdxCur+i, aRegIdx[i],
|
||||
aRegIdx[i]+1,
|
||||
pIdx->uniqNotNull ? pIdx->nKeyCol: pIdx->nColumn);
|
||||
pik_flags = 0;
|
||||
if( useSeekResult ) pik_flags = OPFLAG_USESEEKRESULT;
|
||||
if( IsPrimaryKeyIndex(pIdx) && !HasRowid(pTab) ){
|
||||
@@ -1696,8 +1707,10 @@ void sqlite3CompleteInsertion(
|
||||
regData = regNewData + 1;
|
||||
regRec = sqlite3GetTempReg(pParse);
|
||||
sqlite3VdbeAddOp3(v, OP_MakeRecord, regData, pTab->nCol, regRec);
|
||||
if( !bAffinityDone ) sqlite3TableAffinity(v, pTab, 0);
|
||||
sqlite3ExprCacheAffinityChange(pParse, regData, pTab->nCol);
|
||||
if( !bAffinityDone ){
|
||||
sqlite3TableAffinity(v, pTab, 0);
|
||||
sqlite3ExprCacheAffinityChange(pParse, regData, pTab->nCol);
|
||||
}
|
||||
if( pParse->nested ){
|
||||
pik_flags = 0;
|
||||
}else{
|
||||
@@ -1777,15 +1790,15 @@ int sqlite3OpenTableAndIndices(
|
||||
for(i=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, i++){
|
||||
int iIdxCur = iBase++;
|
||||
assert( pIdx->pSchema==pTab->pSchema );
|
||||
if( IsPrimaryKeyIndex(pIdx) && !HasRowid(pTab) ){
|
||||
if( piDataCur ) *piDataCur = iIdxCur;
|
||||
p5 = 0;
|
||||
}
|
||||
if( aToOpen==0 || aToOpen[i+1] ){
|
||||
sqlite3VdbeAddOp3(v, op, iIdxCur, pIdx->tnum, iDb);
|
||||
sqlite3VdbeSetP4KeyInfo(pParse, pIdx);
|
||||
VdbeComment((v, "%s", pIdx->zName));
|
||||
}
|
||||
if( IsPrimaryKeyIndex(pIdx) && !HasRowid(pTab) ){
|
||||
if( piDataCur ) *piDataCur = iIdxCur;
|
||||
}else{
|
||||
sqlite3VdbeChangeP5(v, p5);
|
||||
VdbeComment((v, "%s", pIdx->zName));
|
||||
}
|
||||
}
|
||||
if( iBase>pParse->nTab ) pParse->nTab = iBase;
|
||||
@@ -2095,6 +2108,7 @@ static int xferOptimization(
|
||||
sqlite3VdbeJumpHere(v, addr1);
|
||||
}
|
||||
if( HasRowid(pSrc) ){
|
||||
u8 insFlags;
|
||||
sqlite3OpenTable(pParse, iSrc, iDbSrc, pSrc, OP_OpenRead);
|
||||
emptySrcTest = sqlite3VdbeAddOp2(v, OP_Rewind, iSrc, 0); VdbeCoverage(v);
|
||||
if( pDest->iPKey>=0 ){
|
||||
@@ -2111,9 +2125,16 @@ static int xferOptimization(
|
||||
assert( (pDest->tabFlags & TF_Autoincrement)==0 );
|
||||
}
|
||||
sqlite3VdbeAddOp2(v, OP_RowData, iSrc, regData);
|
||||
if( db->flags & SQLITE_Vacuum ){
|
||||
sqlite3VdbeAddOp3(v, OP_Last, iDest, 0, -1);
|
||||
insFlags = OPFLAG_NCHANGE|OPFLAG_LASTROWID|
|
||||
OPFLAG_APPEND|OPFLAG_USESEEKRESULT;
|
||||
}else{
|
||||
insFlags = OPFLAG_NCHANGE|OPFLAG_LASTROWID|OPFLAG_APPEND;
|
||||
}
|
||||
sqlite3VdbeAddOp4(v, OP_Insert, iDest, regData, regRowid,
|
||||
(char*)pDest, P4_TABLE);
|
||||
sqlite3VdbeChangeP5(v, OPFLAG_NCHANGE|OPFLAG_LASTROWID|OPFLAG_APPEND);
|
||||
sqlite3VdbeChangeP5(v, insFlags);
|
||||
sqlite3VdbeAddOp2(v, OP_Next, iSrc, addr1); VdbeCoverage(v);
|
||||
sqlite3VdbeAddOp2(v, OP_Close, iSrc, 0);
|
||||
sqlite3VdbeAddOp2(v, OP_Close, iDest, 0);
|
||||
@@ -2135,7 +2156,7 @@ static int xferOptimization(
|
||||
sqlite3VdbeChangeP5(v, OPFLAG_BULKCSR);
|
||||
VdbeComment((v, "%s", pDestIdx->zName));
|
||||
addr1 = sqlite3VdbeAddOp2(v, OP_Rewind, iSrc, 0); VdbeCoverage(v);
|
||||
sqlite3VdbeAddOp2(v, OP_RowKey, iSrc, regData);
|
||||
sqlite3VdbeAddOp2(v, OP_RowData, iSrc, regData);
|
||||
if( db->flags & SQLITE_Vacuum ){
|
||||
/* This INSERT command is part of a VACUUM operation, which guarantees
|
||||
** that the destination table is empty. If all indexed columns use
|
||||
@@ -2165,8 +2186,8 @@ static int xferOptimization(
|
||||
if( !HasRowid(pSrc) && pDestIdx->idxType==2 ){
|
||||
idxInsFlags |= OPFLAG_NCHANGE;
|
||||
}
|
||||
sqlite3VdbeAddOp3(v, OP_IdxInsert, iDest, regData, 1);
|
||||
sqlite3VdbeChangeP5(v, idxInsFlags);
|
||||
sqlite3VdbeAddOp2(v, OP_IdxInsert, iDest, regData);
|
||||
sqlite3VdbeChangeP5(v, idxInsFlags|OPFLAG_APPEND);
|
||||
sqlite3VdbeAddOp2(v, OP_Next, iSrc, addr1+1); VdbeCoverage(v);
|
||||
sqlite3VdbeJumpHere(v, addr1);
|
||||
sqlite3VdbeAddOp2(v, OP_Close, iSrc, 0);
|
||||
@@ -2176,6 +2197,7 @@ static int xferOptimization(
|
||||
sqlite3ReleaseTempReg(pParse, regRowid);
|
||||
sqlite3ReleaseTempReg(pParse, regData);
|
||||
if( emptyDestTest ){
|
||||
sqlite3AutoincrementEnd(pParse);
|
||||
sqlite3VdbeAddOp2(v, OP_Halt, SQLITE_OK, 0);
|
||||
sqlite3VdbeJumpHere(v, emptyDestTest);
|
||||
sqlite3VdbeAddOp2(v, OP_Close, iDest, 0);
|
||||
|
||||
+27
-26
@@ -21,7 +21,6 @@
|
||||
#include <string.h>
|
||||
|
||||
#ifndef SQLITE_OMIT_LOAD_EXTENSION
|
||||
|
||||
/*
|
||||
** Some API routines are omitted when various features are
|
||||
** excluded from a build of SQLite. Substitute a NULL pointer
|
||||
@@ -91,7 +90,7 @@
|
||||
# define sqlite3_enable_shared_cache 0
|
||||
#endif
|
||||
|
||||
#ifdef SQLITE_OMIT_TRACE
|
||||
#if defined(SQLITE_OMIT_TRACE) || defined(SQLITE_OMIT_DEPRECATED)
|
||||
# define sqlite3_profile 0
|
||||
# define sqlite3_trace 0
|
||||
#endif
|
||||
@@ -111,6 +110,10 @@
|
||||
#define sqlite3_blob_reopen 0
|
||||
#endif
|
||||
|
||||
#if defined(SQLITE_OMIT_TRACE)
|
||||
# define sqlite3_trace_v2 0
|
||||
#endif
|
||||
|
||||
/*
|
||||
** The following structure contains pointers to all SQLite API routines.
|
||||
** A pointer to this structure is passed into extensions when they are
|
||||
@@ -416,7 +419,10 @@ static const sqlite3_api_routines sqlite3Apis = {
|
||||
sqlite3_strlike,
|
||||
sqlite3_db_cacheflush,
|
||||
/* Version 3.12.0 and later */
|
||||
sqlite3_system_errno
|
||||
sqlite3_system_errno,
|
||||
/* Version 3.14.0 and later */
|
||||
sqlite3_trace_v2,
|
||||
sqlite3_expanded_sql
|
||||
};
|
||||
|
||||
/*
|
||||
@@ -439,7 +445,7 @@ static int sqlite3LoadExtension(
|
||||
){
|
||||
sqlite3_vfs *pVfs = db->pVfs;
|
||||
void *handle;
|
||||
int (*xInit)(sqlite3*,char**,const sqlite3_api_routines*);
|
||||
sqlite3_loadext_entry xInit;
|
||||
char *zErrmsg = 0;
|
||||
const char *zEntry;
|
||||
char *zAltEntry = 0;
|
||||
@@ -498,8 +504,7 @@ static int sqlite3LoadExtension(
|
||||
}
|
||||
return SQLITE_ERROR;
|
||||
}
|
||||
xInit = (int(*)(sqlite3*,char**,const sqlite3_api_routines*))
|
||||
sqlite3OsDlSym(pVfs, handle, zEntry);
|
||||
xInit = (sqlite3_loadext_entry)sqlite3OsDlSym(pVfs, handle, zEntry);
|
||||
|
||||
/* If no entry point was specified and the default legacy
|
||||
** entry point name "sqlite3_extension_init" was not found, then
|
||||
@@ -531,8 +536,7 @@ static int sqlite3LoadExtension(
|
||||
}
|
||||
memcpy(zAltEntry+iEntry, "_init", 6);
|
||||
zEntry = zAltEntry;
|
||||
xInit = (int(*)(sqlite3*,char**,const sqlite3_api_routines*))
|
||||
sqlite3OsDlSym(pVfs, handle, zEntry);
|
||||
xInit = (sqlite3_loadext_entry)sqlite3OsDlSym(pVfs, handle, zEntry);
|
||||
}
|
||||
if( xInit==0 ){
|
||||
if( pzErrMsg ){
|
||||
@@ -616,18 +620,7 @@ int sqlite3_enable_load_extension(sqlite3 *db, int onoff){
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
#endif /* SQLITE_OMIT_LOAD_EXTENSION */
|
||||
|
||||
/*
|
||||
** The auto-extension code added regardless of whether or not extension
|
||||
** loading is supported. We need a dummy sqlite3Apis pointer for that
|
||||
** code if regular extension loading is not available. This is that
|
||||
** dummy pointer.
|
||||
*/
|
||||
#ifdef SQLITE_OMIT_LOAD_EXTENSION
|
||||
static const sqlite3_api_routines sqlite3Apis = { 0 };
|
||||
#endif
|
||||
|
||||
#endif /* !defined(SQLITE_OMIT_LOAD_EXTENSION) */
|
||||
|
||||
/*
|
||||
** The following object holds the list of automatically loaded
|
||||
@@ -662,7 +655,9 @@ static SQLITE_WSD struct sqlite3AutoExtList {
|
||||
** Register a statically linked extension that is automatically
|
||||
** loaded by every new database connection.
|
||||
*/
|
||||
int sqlite3_auto_extension(void (*xInit)(void)){
|
||||
int sqlite3_auto_extension(
|
||||
void (*xInit)(void)
|
||||
){
|
||||
int rc = SQLITE_OK;
|
||||
#ifndef SQLITE_OMIT_AUTOINIT
|
||||
rc = sqlite3_initialize();
|
||||
@@ -707,7 +702,9 @@ int sqlite3_auto_extension(void (*xInit)(void)){
|
||||
** Return 1 if xInit was found on the list and removed. Return 0 if xInit
|
||||
** was not on the list.
|
||||
*/
|
||||
int sqlite3_cancel_auto_extension(void (*xInit)(void)){
|
||||
int sqlite3_cancel_auto_extension(
|
||||
void (*xInit)(void)
|
||||
){
|
||||
#if SQLITE_THREADSAFE
|
||||
sqlite3_mutex *mutex = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER);
|
||||
#endif
|
||||
@@ -756,7 +753,7 @@ void sqlite3AutoLoadExtensions(sqlite3 *db){
|
||||
u32 i;
|
||||
int go = 1;
|
||||
int rc;
|
||||
int (*xInit)(sqlite3*,char**,const sqlite3_api_routines*);
|
||||
sqlite3_loadext_entry xInit;
|
||||
|
||||
wsdAutoextInit;
|
||||
if( wsdAutoext.nExt==0 ){
|
||||
@@ -767,18 +764,22 @@ void sqlite3AutoLoadExtensions(sqlite3 *db){
|
||||
char *zErrmsg;
|
||||
#if SQLITE_THREADSAFE
|
||||
sqlite3_mutex *mutex = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER);
|
||||
#endif
|
||||
#ifdef SQLITE_OMIT_LOAD_EXTENSION
|
||||
const sqlite3_api_routines *pThunk = 0;
|
||||
#else
|
||||
const sqlite3_api_routines *pThunk = &sqlite3Apis;
|
||||
#endif
|
||||
sqlite3_mutex_enter(mutex);
|
||||
if( i>=wsdAutoext.nExt ){
|
||||
xInit = 0;
|
||||
go = 0;
|
||||
}else{
|
||||
xInit = (int(*)(sqlite3*,char**,const sqlite3_api_routines*))
|
||||
wsdAutoext.aExt[i];
|
||||
xInit = (sqlite3_loadext_entry)wsdAutoext.aExt[i];
|
||||
}
|
||||
sqlite3_mutex_leave(mutex);
|
||||
zErrmsg = 0;
|
||||
if( xInit && (rc = xInit(db, &zErrmsg, &sqlite3Apis))!=0 ){
|
||||
if( xInit && (rc = xInit(db, &zErrmsg, pThunk))!=0 ){
|
||||
sqlite3ErrorWithMsg(db, rc,
|
||||
"automatic extension loading failed: %s", zErrmsg);
|
||||
go = 0;
|
||||
|
||||
+117
-20
@@ -789,6 +789,11 @@ int sqlite3_db_config(sqlite3 *db, int op, ...){
|
||||
int rc;
|
||||
va_start(ap, op);
|
||||
switch( op ){
|
||||
case SQLITE_DBCONFIG_MAINDBNAME: {
|
||||
db->aDb[0].zDbSName = va_arg(ap,char*);
|
||||
rc = SQLITE_OK;
|
||||
break;
|
||||
}
|
||||
case SQLITE_DBCONFIG_LOOKASIDE: {
|
||||
void *pBuf = va_arg(ap, void*); /* IMP: R-26835-10964 */
|
||||
int sz = va_arg(ap, int); /* IMP: R-47871-25994 */
|
||||
@@ -805,6 +810,7 @@ int sqlite3_db_config(sqlite3 *db, int op, ...){
|
||||
{ SQLITE_DBCONFIG_ENABLE_TRIGGER, SQLITE_EnableTrigger },
|
||||
{ SQLITE_DBCONFIG_ENABLE_FTS3_TOKENIZER, SQLITE_Fts3Tokenizer },
|
||||
{ SQLITE_DBCONFIG_ENABLE_LOAD_EXTENSION, SQLITE_LoadExtension },
|
||||
{ SQLITE_DBCONFIG_NO_CKPT_ON_CLOSE, SQLITE_NoCkptOnClose },
|
||||
};
|
||||
unsigned int i;
|
||||
rc = SQLITE_ERROR; /* IMP: R-42790-23372 */
|
||||
@@ -1033,6 +1039,9 @@ static int sqlite3Close(sqlite3 *db, int forceZombie){
|
||||
return SQLITE_MISUSE_BKPT;
|
||||
}
|
||||
sqlite3_mutex_enter(db->mutex);
|
||||
if( db->mTrace & SQLITE_TRACE_CLOSE ){
|
||||
db->xTrace(SQLITE_TRACE_CLOSE, db->pTraceArg, db, 0);
|
||||
}
|
||||
|
||||
/* Force xDisconnect calls on all virtual tables */
|
||||
disconnectAllVtab(db);
|
||||
@@ -1801,7 +1810,8 @@ int sqlite3_overload_function(
|
||||
** trace is a pointer to a function that is invoked at the start of each
|
||||
** SQL statement.
|
||||
*/
|
||||
void *sqlite3_trace(sqlite3 *db, void (*xTrace)(void*,const char*), void *pArg){
|
||||
#ifndef SQLITE_OMIT_DEPRECATED
|
||||
void *sqlite3_trace(sqlite3 *db, void(*xTrace)(void*,const char*), void *pArg){
|
||||
void *pOld;
|
||||
|
||||
#ifdef SQLITE_ENABLE_API_ARMOR
|
||||
@@ -1812,11 +1822,38 @@ void *sqlite3_trace(sqlite3 *db, void (*xTrace)(void*,const char*), void *pArg){
|
||||
#endif
|
||||
sqlite3_mutex_enter(db->mutex);
|
||||
pOld = db->pTraceArg;
|
||||
db->xTrace = xTrace;
|
||||
db->mTrace = xTrace ? SQLITE_TRACE_LEGACY : 0;
|
||||
db->xTrace = (int(*)(u32,void*,void*,void*))xTrace;
|
||||
db->pTraceArg = pArg;
|
||||
sqlite3_mutex_leave(db->mutex);
|
||||
return pOld;
|
||||
}
|
||||
#endif /* SQLITE_OMIT_DEPRECATED */
|
||||
|
||||
/* Register a trace callback using the version-2 interface.
|
||||
*/
|
||||
int sqlite3_trace_v2(
|
||||
sqlite3 *db, /* Trace this connection */
|
||||
unsigned mTrace, /* Mask of events to be traced */
|
||||
int(*xTrace)(unsigned,void*,void*,void*), /* Callback to invoke */
|
||||
void *pArg /* Context */
|
||||
){
|
||||
#ifdef SQLITE_ENABLE_API_ARMOR
|
||||
if( !sqlite3SafetyCheckOk(db) ){
|
||||
return SQLITE_MISUSE_BKPT;
|
||||
}
|
||||
#endif
|
||||
sqlite3_mutex_enter(db->mutex);
|
||||
if( mTrace==0 ) xTrace = 0;
|
||||
if( xTrace==0 ) mTrace = 0;
|
||||
db->mTrace = mTrace;
|
||||
db->xTrace = xTrace;
|
||||
db->pTraceArg = pArg;
|
||||
sqlite3_mutex_leave(db->mutex);
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
#ifndef SQLITE_OMIT_DEPRECATED
|
||||
/*
|
||||
** Register a profile function. The pArg from the previously registered
|
||||
** profile function is returned.
|
||||
@@ -1845,6 +1882,7 @@ void *sqlite3_profile(
|
||||
sqlite3_mutex_leave(db->mutex);
|
||||
return pOld;
|
||||
}
|
||||
#endif /* SQLITE_OMIT_DEPRECATED */
|
||||
#endif /* SQLITE_OMIT_TRACE */
|
||||
|
||||
/*
|
||||
@@ -2069,6 +2107,13 @@ int sqlite3_wal_checkpoint_v2(
|
||||
sqlite3Error(db, rc);
|
||||
}
|
||||
rc = sqlite3ApiExit(db, rc);
|
||||
|
||||
/* If there are no active statements, clear the interrupt flag at this
|
||||
** point. */
|
||||
if( db->nVdbeActive==0 ){
|
||||
db->u1.isInterrupted = 0;
|
||||
}
|
||||
|
||||
sqlite3_mutex_leave(db->mutex);
|
||||
return rc;
|
||||
#endif
|
||||
@@ -2571,6 +2616,7 @@ int sqlite3ParseUri(
|
||||
|
||||
assert( octet>=0 && octet<256 );
|
||||
if( octet==0 ){
|
||||
#ifndef SQLITE_ENABLE_URI_00_ERROR
|
||||
/* This branch is taken when "%00" appears within the URI. In this
|
||||
** case we ignore all text in the remainder of the path, name or
|
||||
** value currently being parsed. So ignore the current character
|
||||
@@ -2583,6 +2629,12 @@ int sqlite3ParseUri(
|
||||
iIn++;
|
||||
}
|
||||
continue;
|
||||
#else
|
||||
/* If ENABLE_URI_00_ERROR is defined, "%00" in a URI is an error. */
|
||||
*pzErrMsg = sqlite3_mprintf("unexpected %%00 in uri");
|
||||
rc = SQLITE_ERROR;
|
||||
goto parse_uri_out;
|
||||
#endif
|
||||
}
|
||||
c = octet;
|
||||
}else if( eState==1 && (c=='&' || c=='=') ){
|
||||
@@ -2902,9 +2954,9 @@ static int openDatabase(
|
||||
/* The default safety_level for the main database is FULL; for the temp
|
||||
** database it is OFF. This matches the pager layer defaults.
|
||||
*/
|
||||
db->aDb[0].zName = "main";
|
||||
db->aDb[0].zDbSName = "main";
|
||||
db->aDb[0].safety_level = SQLITE_DEFAULT_SYNCHRONOUS+1;
|
||||
db->aDb[1].zName = "temp";
|
||||
db->aDb[1].zDbSName = "temp";
|
||||
db->aDb[1].safety_level = PAGER_SYNCHRONOUS_OFF;
|
||||
|
||||
db->magic = SQLITE_MAGIC_OPEN;
|
||||
@@ -2918,11 +2970,20 @@ static int openDatabase(
|
||||
*/
|
||||
sqlite3Error(db, SQLITE_OK);
|
||||
sqlite3RegisterPerConnectionBuiltinFunctions(db);
|
||||
rc = sqlite3_errcode(db);
|
||||
|
||||
#ifdef SQLITE_ENABLE_FTS5
|
||||
/* Register any built-in FTS5 module before loading the automatic
|
||||
** extensions. This allows automatic extensions to register FTS5
|
||||
** tokenizers and auxiliary functions. */
|
||||
if( !db->mallocFailed && rc==SQLITE_OK ){
|
||||
rc = sqlite3Fts5Init(db);
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Load automatic extensions - extensions that have been registered
|
||||
** using the sqlite3_automatic_extension() API.
|
||||
*/
|
||||
rc = sqlite3_errcode(db);
|
||||
if( rc==SQLITE_OK ){
|
||||
sqlite3AutoLoadExtensions(db);
|
||||
rc = sqlite3_errcode(db);
|
||||
@@ -2951,12 +3012,6 @@ static int openDatabase(
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef SQLITE_ENABLE_FTS5
|
||||
if( !db->mallocFailed && rc==SQLITE_OK ){
|
||||
rc = sqlite3Fts5Init(db);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef SQLITE_ENABLE_ICU
|
||||
if( !db->mallocFailed && rc==SQLITE_OK ){
|
||||
rc = sqlite3IcuInit(db);
|
||||
@@ -3744,6 +3799,15 @@ int sqlite3_test_control(int op, ...){
|
||||
break;
|
||||
}
|
||||
|
||||
/* Set the threshold at which OP_Once counters reset back to zero.
|
||||
** By default this is 0x7ffffffe (over 2 billion), but that value is
|
||||
** too big to test in a reasonable amount of time, so this control is
|
||||
** provided to set a small and easily reachable reset value.
|
||||
*/
|
||||
case SQLITE_TESTCTRL_ONCE_RESET_THRESHOLD: {
|
||||
sqlite3GlobalConfig.iOnceResetThreshold = va_arg(ap, int);
|
||||
break;
|
||||
}
|
||||
|
||||
/* sqlite3_test_control(SQLITE_TESTCTRL_VDBE_COVERAGE, xCallback, ptr);
|
||||
**
|
||||
@@ -3865,7 +3929,7 @@ Btree *sqlite3DbNameToBtree(sqlite3 *db, const char *zDbName){
|
||||
int i;
|
||||
for(i=0; i<db->nDb; i++){
|
||||
if( db->aDb[i].pBt
|
||||
&& (zDbName==0 || sqlite3StrICmp(zDbName, db->aDb[i].zName)==0)
|
||||
&& (zDbName==0 || sqlite3StrICmp(zDbName, db->aDb[i].zDbSName)==0)
|
||||
){
|
||||
return db->aDb[i].pBt;
|
||||
}
|
||||
@@ -3917,7 +3981,6 @@ int sqlite3_snapshot_get(
|
||||
){
|
||||
int rc = SQLITE_ERROR;
|
||||
#ifndef SQLITE_OMIT_WAL
|
||||
int iDb;
|
||||
|
||||
#ifdef SQLITE_ENABLE_API_ARMOR
|
||||
if( !sqlite3SafetyCheckOk(db) ){
|
||||
@@ -3926,13 +3989,15 @@ int sqlite3_snapshot_get(
|
||||
#endif
|
||||
sqlite3_mutex_enter(db->mutex);
|
||||
|
||||
iDb = sqlite3FindDbName(db, zDb);
|
||||
if( iDb==0 || iDb>1 ){
|
||||
Btree *pBt = db->aDb[iDb].pBt;
|
||||
if( 0==sqlite3BtreeIsInTrans(pBt) ){
|
||||
rc = sqlite3BtreeBeginTrans(pBt, 0);
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = sqlite3PagerSnapshotGet(sqlite3BtreePager(pBt), ppSnapshot);
|
||||
if( db->autoCommit==0 ){
|
||||
int iDb = sqlite3FindDbName(db, zDb);
|
||||
if( iDb==0 || iDb>1 ){
|
||||
Btree *pBt = db->aDb[iDb].pBt;
|
||||
if( 0==sqlite3BtreeIsInTrans(pBt) ){
|
||||
rc = sqlite3BtreeBeginTrans(pBt, 0);
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = sqlite3PagerSnapshotGet(sqlite3BtreePager(pBt), ppSnapshot);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3979,6 +4044,38 @@ int sqlite3_snapshot_open(
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** Recover as many snapshots as possible from the wal file associated with
|
||||
** schema zDb of database db.
|
||||
*/
|
||||
int sqlite3_snapshot_recover(sqlite3 *db, const char *zDb){
|
||||
int rc = SQLITE_ERROR;
|
||||
int iDb;
|
||||
#ifndef SQLITE_OMIT_WAL
|
||||
|
||||
#ifdef SQLITE_ENABLE_API_ARMOR
|
||||
if( !sqlite3SafetyCheckOk(db) ){
|
||||
return SQLITE_MISUSE_BKPT;
|
||||
}
|
||||
#endif
|
||||
|
||||
sqlite3_mutex_enter(db->mutex);
|
||||
iDb = sqlite3FindDbName(db, zDb);
|
||||
if( iDb==0 || iDb>1 ){
|
||||
Btree *pBt = db->aDb[iDb].pBt;
|
||||
if( 0==sqlite3BtreeIsInReadTrans(pBt) ){
|
||||
rc = sqlite3BtreeBeginTrans(pBt, 0);
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = sqlite3PagerSnapshotRecover(sqlite3BtreePager(pBt));
|
||||
sqlite3BtreeCommit(pBt);
|
||||
}
|
||||
}
|
||||
}
|
||||
sqlite3_mutex_leave(db->mutex);
|
||||
#endif /* SQLITE_OMIT_WAL */
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** Free a snapshot handle obtained from sqlite3_snapshot_get().
|
||||
*/
|
||||
|
||||
+2
-3
@@ -726,9 +726,8 @@ char *sqlite3DbStrDup(sqlite3 *db, const char *z){
|
||||
if( z==0 ){
|
||||
return 0;
|
||||
}
|
||||
n = sqlite3Strlen30(z) + 1;
|
||||
assert( (n&0x7fffffff)==n );
|
||||
zNew = sqlite3DbMallocRaw(db, (int)n);
|
||||
n = strlen(z) + 1;
|
||||
zNew = sqlite3DbMallocRaw(db, n);
|
||||
if( zNew ){
|
||||
memcpy(zNew, z, n);
|
||||
}
|
||||
|
||||
+3
-3
@@ -12,8 +12,8 @@
|
||||
**
|
||||
** This file contains code that is specific to MSVC.
|
||||
*/
|
||||
#ifndef _MSVC_H_
|
||||
#define _MSVC_H_
|
||||
#ifndef SQLITE_MSVC_H
|
||||
#define SQLITE_MSVC_H
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
#pragma warning(disable : 4054)
|
||||
@@ -33,4 +33,4 @@
|
||||
#pragma warning(disable : 4706)
|
||||
#endif /* defined(_MSC_VER) */
|
||||
|
||||
#endif /* _MSVC_H_ */
|
||||
#endif /* SQLITE_MSVC_H */
|
||||
|
||||
@@ -13,9 +13,7 @@
|
||||
** This file contains OS interface code that is common to all
|
||||
** architectures.
|
||||
*/
|
||||
#define _SQLITE_OS_C_ 1
|
||||
#include "sqliteInt.h"
|
||||
#undef _SQLITE_OS_C_
|
||||
|
||||
/*
|
||||
** If we compile with the SQLITE_TEST macro set, then the following block
|
||||
|
||||
+3
-3
@@ -13,8 +13,8 @@
|
||||
** This file contains pre-processor directives related to operating system
|
||||
** detection and/or setup.
|
||||
*/
|
||||
#ifndef _OS_SETUP_H_
|
||||
#define _OS_SETUP_H_
|
||||
#ifndef SQLITE_OS_SETUP_H
|
||||
#define SQLITE_OS_SETUP_H
|
||||
|
||||
/*
|
||||
** Figure out if we are dealing with Unix, Windows, or some other operating
|
||||
@@ -54,4 +54,4 @@
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#endif /* _OS_SETUP_H_ */
|
||||
#endif /* SQLITE_OS_SETUP_H */
|
||||
|
||||
+43
-12
@@ -1061,7 +1061,14 @@ struct unixFileId {
|
||||
#if OS_VXWORKS
|
||||
struct vxworksFileId *pId; /* Unique file ID for vxworks. */
|
||||
#else
|
||||
ino_t ino; /* Inode number */
|
||||
/* We are told that some versions of Android contain a bug that
|
||||
** sizes ino_t at only 32-bits instead of 64-bits. (See
|
||||
** https://android-review.googlesource.com/#/c/115351/3/dist/sqlite3.c)
|
||||
** To work around this, always allocate 64-bits for the inode number.
|
||||
** On small machines that only have 32-bit inodes, this wastes 4 bytes,
|
||||
** but that should not be a big deal. */
|
||||
/* WAS: ino_t ino; */
|
||||
u64 ino; /* Inode number */
|
||||
#endif
|
||||
};
|
||||
|
||||
@@ -1306,7 +1313,7 @@ static int findInodeInfo(
|
||||
#if OS_VXWORKS
|
||||
fileId.pId = pFile->pId;
|
||||
#else
|
||||
fileId.ino = statbuf.st_ino;
|
||||
fileId.ino = (u64)statbuf.st_ino;
|
||||
#endif
|
||||
pInode = inodeList;
|
||||
while( pInode && memcmp(&fileId, &pInode->fileId, sizeof(fileId)) ){
|
||||
@@ -1340,7 +1347,8 @@ static int fileHasMoved(unixFile *pFile){
|
||||
#else
|
||||
struct stat buf;
|
||||
return pFile->pInode!=0 &&
|
||||
(osStat(pFile->zPath, &buf)!=0 || buf.st_ino!=pFile->pInode->fileId.ino);
|
||||
(osStat(pFile->zPath, &buf)!=0
|
||||
|| (u64)buf.st_ino!=pFile->pInode->fileId.ino);
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -5512,7 +5520,7 @@ static UnixUnusedFd *findReusableFd(const char *zPath, int flags){
|
||||
unixEnterMutex();
|
||||
pInode = inodeList;
|
||||
while( pInode && (pInode->fileId.dev!=sStat.st_dev
|
||||
|| pInode->fileId.ino!=sStat.st_ino) ){
|
||||
|| pInode->fileId.ino!=(u64)sStat.st_ino) ){
|
||||
pInode = pInode->pNext;
|
||||
}
|
||||
if( pInode ){
|
||||
@@ -5529,6 +5537,27 @@ static UnixUnusedFd *findReusableFd(const char *zPath, int flags){
|
||||
return pUnused;
|
||||
}
|
||||
|
||||
/*
|
||||
** Find the mode, uid and gid of file zFile.
|
||||
*/
|
||||
static int getFileMode(
|
||||
const char *zFile, /* File name */
|
||||
mode_t *pMode, /* OUT: Permissions of zFile */
|
||||
uid_t *pUid, /* OUT: uid of zFile. */
|
||||
gid_t *pGid /* OUT: gid of zFile. */
|
||||
){
|
||||
struct stat sStat; /* Output of stat() on database file */
|
||||
int rc = SQLITE_OK;
|
||||
if( 0==osStat(zFile, &sStat) ){
|
||||
*pMode = sStat.st_mode & 0777;
|
||||
*pUid = sStat.st_uid;
|
||||
*pGid = sStat.st_gid;
|
||||
}else{
|
||||
rc = SQLITE_IOERR_FSTAT;
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** 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
|
||||
@@ -5564,7 +5593,6 @@ static int findCreateFileMode(
|
||||
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 */
|
||||
|
||||
/* zPath is a path to a WAL or journal file. The following block derives
|
||||
** the path to the associated database file from zPath. This block handles
|
||||
@@ -5595,15 +5623,18 @@ static int findCreateFileMode(
|
||||
memcpy(zDb, zPath, nDb);
|
||||
zDb[nDb] = '\0';
|
||||
|
||||
if( 0==osStat(zDb, &sStat) ){
|
||||
*pMode = sStat.st_mode & 0777;
|
||||
*pUid = sStat.st_uid;
|
||||
*pGid = sStat.st_gid;
|
||||
}else{
|
||||
rc = SQLITE_IOERR_FSTAT;
|
||||
}
|
||||
rc = getFileMode(zDb, pMode, pUid, pGid);
|
||||
}else if( flags & SQLITE_OPEN_DELETEONCLOSE ){
|
||||
*pMode = 0600;
|
||||
}else if( flags & SQLITE_OPEN_URI ){
|
||||
/* If this is a main database file and the file was opened using a URI
|
||||
** filename, check for the "modeof" parameter. If present, interpret
|
||||
** its value as a filename and try to copy the mode, uid and gid from
|
||||
** that file. */
|
||||
const char *z = sqlite3_uri_parameter(zPath, "modeof");
|
||||
if( z ){
|
||||
rc = getFileMode(z, pMode, pUid, pGid);
|
||||
}
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
+227
-67
@@ -290,6 +290,17 @@ struct winFile {
|
||||
#endif
|
||||
};
|
||||
|
||||
/*
|
||||
** The winVfsAppData structure is used for the pAppData member for all of the
|
||||
** Win32 VFS variants.
|
||||
*/
|
||||
typedef struct winVfsAppData winVfsAppData;
|
||||
struct winVfsAppData {
|
||||
const sqlite3_io_methods *pMethod; /* The file I/O methods to use. */
|
||||
void *pAppData; /* The extra pAppData, if any. */
|
||||
BOOL bNoLock; /* Non-zero if locking is disabled. */
|
||||
};
|
||||
|
||||
/*
|
||||
** Allowed values for winFile.ctrlFlags
|
||||
*/
|
||||
@@ -2611,7 +2622,12 @@ static int winClose(sqlite3_file *id){
|
||||
}while( rc==0 && ++cnt < MX_CLOSE_ATTEMPT && (sqlite3_win32_sleep(100), 1) );
|
||||
#if SQLITE_OS_WINCE
|
||||
#define WINCE_DELETION_ATTEMPTS 3
|
||||
winceDestroyLock(pFile);
|
||||
{
|
||||
winVfsAppData *pAppData = (winVfsAppData*)pFile->pVfs->pAppData;
|
||||
if( pAppData==NULL || !pAppData->bNoLock ){
|
||||
winceDestroyLock(pFile);
|
||||
}
|
||||
}
|
||||
if( pFile->zDeleteOnClose ){
|
||||
int cnt = 0;
|
||||
while(
|
||||
@@ -3343,6 +3359,44 @@ static int winUnlock(sqlite3_file *id, int locktype){
|
||||
return rc;
|
||||
}
|
||||
|
||||
/******************************************************************************
|
||||
****************************** No-op Locking **********************************
|
||||
**
|
||||
** Of the various locking implementations available, this is by far the
|
||||
** simplest: locking is ignored. No attempt is made to lock the database
|
||||
** file for reading or writing.
|
||||
**
|
||||
** This locking mode is appropriate for use on read-only databases
|
||||
** (ex: databases that are burned into CD-ROM, for example.) It can
|
||||
** also be used if the application employs some external mechanism to
|
||||
** prevent simultaneous access of the same database by two or more
|
||||
** database connections. But there is a serious risk of database
|
||||
** corruption if this locking mode is used in situations where multiple
|
||||
** database connections are accessing the same database file at the same
|
||||
** time and one or more of those connections are writing.
|
||||
*/
|
||||
|
||||
static int winNolockLock(sqlite3_file *id, int locktype){
|
||||
UNUSED_PARAMETER(id);
|
||||
UNUSED_PARAMETER(locktype);
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
static int winNolockCheckReservedLock(sqlite3_file *id, int *pResOut){
|
||||
UNUSED_PARAMETER(id);
|
||||
UNUSED_PARAMETER(pResOut);
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
static int winNolockUnlock(sqlite3_file *id, int locktype){
|
||||
UNUSED_PARAMETER(id);
|
||||
UNUSED_PARAMETER(locktype);
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/******************* End of the no-op lock implementation *********************
|
||||
******************************************************************************/
|
||||
|
||||
/*
|
||||
** If *pArg is initially negative then this is a query. Set *pArg to
|
||||
** 1 or 0 depending on whether or not bit mask of pFile->ctrlFlags is set.
|
||||
@@ -3434,6 +3488,12 @@ static int winFileControl(sqlite3_file *id, int op, void *pArg){
|
||||
OSTRACE(("FCNTL file=%p, rc=SQLITE_OK\n", pFile->h));
|
||||
return SQLITE_OK;
|
||||
}
|
||||
case SQLITE_FCNTL_WIN32_GET_HANDLE: {
|
||||
LPHANDLE phFile = (LPHANDLE)pArg;
|
||||
*phFile = pFile->h;
|
||||
OSTRACE(("FCNTL file=%p, rc=SQLITE_OK\n", pFile->h));
|
||||
return SQLITE_OK;
|
||||
}
|
||||
#ifdef SQLITE_TEST
|
||||
case SQLITE_FCNTL_WIN32_SET_HANDLE: {
|
||||
LPHANDLE phFile = (LPHANDLE)pArg;
|
||||
@@ -3621,12 +3681,12 @@ struct winShm {
|
||||
/*
|
||||
** Apply advisory locks for all n bytes beginning at ofst.
|
||||
*/
|
||||
#define _SHM_UNLCK 1
|
||||
#define _SHM_RDLCK 2
|
||||
#define _SHM_WRLCK 3
|
||||
#define WINSHM_UNLCK 1
|
||||
#define WINSHM_RDLCK 2
|
||||
#define WINSHM_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 lockType, /* WINSHM_UNLCK, WINSHM_RDLCK, or WINSHM_WRLCK */
|
||||
int ofst, /* Offset to first byte to be locked/unlocked */
|
||||
int nByte /* Number of bytes to lock or unlock */
|
||||
){
|
||||
@@ -3639,12 +3699,12 @@ static int winShmSystemLock(
|
||||
pFile->hFile.h, lockType, ofst, nByte));
|
||||
|
||||
/* Release/Acquire the system-level lock */
|
||||
if( lockType==_SHM_UNLCK ){
|
||||
if( lockType==WINSHM_UNLCK ){
|
||||
rc = winUnlockFile(&pFile->hFile.h, ofst, 0, nByte, 0);
|
||||
}else{
|
||||
/* Initialize the locking parameters */
|
||||
DWORD dwFlags = LOCKFILE_FAIL_IMMEDIATELY;
|
||||
if( lockType == _SHM_WRLCK ) dwFlags |= LOCKFILE_EXCLUSIVE_LOCK;
|
||||
if( lockType == WINSHM_WRLCK ) dwFlags |= LOCKFILE_EXCLUSIVE_LOCK;
|
||||
rc = winLockFile(&pFile->hFile.h, dwFlags, ofst, 0, nByte, 0);
|
||||
}
|
||||
|
||||
@@ -3656,7 +3716,7 @@ static int winShmSystemLock(
|
||||
}
|
||||
|
||||
OSTRACE(("SHM-LOCK file=%p, func=%s, errno=%lu, rc=%s\n",
|
||||
pFile->hFile.h, (lockType == _SHM_UNLCK) ? "winUnlockFile" :
|
||||
pFile->hFile.h, (lockType == WINSHM_UNLCK) ? "winUnlockFile" :
|
||||
"winLockFile", pFile->lastErrno, sqlite3ErrName(rc)));
|
||||
|
||||
return rc;
|
||||
@@ -3784,7 +3844,7 @@ static int winOpenSharedMemory(winFile *pDbFd){
|
||||
/* 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 ){
|
||||
if( winShmSystemLock(pShmNode, WINSHM_WRLCK, WIN_SHM_DMS, 1)==SQLITE_OK ){
|
||||
rc = winTruncate((sqlite3_file *)&pShmNode->hFile, 0);
|
||||
if( rc!=SQLITE_OK ){
|
||||
rc = winLogError(SQLITE_IOERR_SHMOPEN, osGetLastError(),
|
||||
@@ -3792,8 +3852,8 @@ static int winOpenSharedMemory(winFile *pDbFd){
|
||||
}
|
||||
}
|
||||
if( rc==SQLITE_OK ){
|
||||
winShmSystemLock(pShmNode, _SHM_UNLCK, WIN_SHM_DMS, 1);
|
||||
rc = winShmSystemLock(pShmNode, _SHM_RDLCK, WIN_SHM_DMS, 1);
|
||||
winShmSystemLock(pShmNode, WINSHM_UNLCK, WIN_SHM_DMS, 1);
|
||||
rc = winShmSystemLock(pShmNode, WINSHM_RDLCK, WIN_SHM_DMS, 1);
|
||||
}
|
||||
if( rc ) goto shm_open_err;
|
||||
}
|
||||
@@ -3822,7 +3882,7 @@ static int winOpenSharedMemory(winFile *pDbFd){
|
||||
|
||||
/* Jump here on any error */
|
||||
shm_open_err:
|
||||
winShmSystemLock(pShmNode, _SHM_UNLCK, WIN_SHM_DMS, 1);
|
||||
winShmSystemLock(pShmNode, WINSHM_UNLCK, WIN_SHM_DMS, 1);
|
||||
winShmPurge(pDbFd->pVfs, 0); /* This call frees pShmNode if required */
|
||||
sqlite3_free(p);
|
||||
sqlite3_free(pNew);
|
||||
@@ -3911,7 +3971,7 @@ static int winShmLock(
|
||||
|
||||
/* Unlock the system-level locks */
|
||||
if( (mask & allMask)==0 ){
|
||||
rc = winShmSystemLock(pShmNode, _SHM_UNLCK, ofst+WIN_SHM_BASE, n);
|
||||
rc = winShmSystemLock(pShmNode, WINSHM_UNLCK, ofst+WIN_SHM_BASE, n);
|
||||
}else{
|
||||
rc = SQLITE_OK;
|
||||
}
|
||||
@@ -3939,7 +3999,7 @@ static int winShmLock(
|
||||
/* 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);
|
||||
rc = winShmSystemLock(pShmNode, WINSHM_RDLCK, ofst+WIN_SHM_BASE, n);
|
||||
}else{
|
||||
rc = SQLITE_OK;
|
||||
}
|
||||
@@ -3964,7 +4024,7 @@ static int winShmLock(
|
||||
** also mark the local connection as being locked.
|
||||
*/
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = winShmSystemLock(pShmNode, _SHM_WRLCK, ofst+WIN_SHM_BASE, n);
|
||||
rc = winShmSystemLock(pShmNode, WINSHM_WRLCK, ofst+WIN_SHM_BASE, n);
|
||||
if( rc==SQLITE_OK ){
|
||||
assert( (p->sharedMask & mask)==0 );
|
||||
p->exclMask |= mask;
|
||||
@@ -4407,6 +4467,44 @@ static const sqlite3_io_methods winIoMethod = {
|
||||
winUnfetch /* xUnfetch */
|
||||
};
|
||||
|
||||
/*
|
||||
** This vector defines all the methods that can operate on an
|
||||
** sqlite3_file for win32 without performing any locking.
|
||||
*/
|
||||
static const sqlite3_io_methods winIoNolockMethod = {
|
||||
3, /* iVersion */
|
||||
winClose, /* xClose */
|
||||
winRead, /* xRead */
|
||||
winWrite, /* xWrite */
|
||||
winTruncate, /* xTruncate */
|
||||
winSync, /* xSync */
|
||||
winFileSize, /* xFileSize */
|
||||
winNolockLock, /* xLock */
|
||||
winNolockUnlock, /* xUnlock */
|
||||
winNolockCheckReservedLock, /* xCheckReservedLock */
|
||||
winFileControl, /* xFileControl */
|
||||
winSectorSize, /* xSectorSize */
|
||||
winDeviceCharacteristics, /* xDeviceCharacteristics */
|
||||
winShmMap, /* xShmMap */
|
||||
winShmLock, /* xShmLock */
|
||||
winShmBarrier, /* xShmBarrier */
|
||||
winShmUnmap, /* xShmUnmap */
|
||||
winFetch, /* xFetch */
|
||||
winUnfetch /* xUnfetch */
|
||||
};
|
||||
|
||||
static winVfsAppData winAppData = {
|
||||
&winIoMethod, /* pMethod */
|
||||
0, /* pAppData */
|
||||
0 /* bNoLock */
|
||||
};
|
||||
|
||||
static winVfsAppData winNolockAppData = {
|
||||
&winIoNolockMethod, /* pMethod */
|
||||
0, /* pAppData */
|
||||
1 /* bNoLock */
|
||||
};
|
||||
|
||||
/****************************************************************************
|
||||
**************************** sqlite3_vfs methods ****************************
|
||||
**
|
||||
@@ -4739,7 +4837,7 @@ static int winIsDir(const void *zConverted){
|
||||
** Open a file.
|
||||
*/
|
||||
static int winOpen(
|
||||
sqlite3_vfs *pVfs, /* Used to get maximum path name length */
|
||||
sqlite3_vfs *pVfs, /* Used to get maximum path length and AppData */
|
||||
const char *zName, /* Name of the file (UTF-8) */
|
||||
sqlite3_file *id, /* Write the SQLite file handle here */
|
||||
int flags, /* Open mode flags */
|
||||
@@ -4754,6 +4852,7 @@ static int winOpen(
|
||||
#if SQLITE_OS_WINCE
|
||||
int isTemp = 0;
|
||||
#endif
|
||||
winVfsAppData *pAppData;
|
||||
winFile *pFile = (winFile*)id;
|
||||
void *zConverted; /* Filename in OS encoding */
|
||||
const char *zUtf8Name = zName; /* Filename in UTF-8 encoding */
|
||||
@@ -4975,15 +5074,20 @@ static int winOpen(
|
||||
"rc=%s\n", h, zUtf8Name, dwDesiredAccess, pOutFlags, pOutFlags ?
|
||||
*pOutFlags : 0, (h==INVALID_HANDLE_VALUE) ? "failed" : "ok"));
|
||||
|
||||
pAppData = (winVfsAppData*)pVfs->pAppData;
|
||||
|
||||
#if SQLITE_OS_WINCE
|
||||
if( isReadWrite && eType==SQLITE_OPEN_MAIN_DB
|
||||
&& (rc = winceCreateLock(zName, pFile))!=SQLITE_OK
|
||||
){
|
||||
osCloseHandle(h);
|
||||
sqlite3_free(zConverted);
|
||||
sqlite3_free(zTmpname);
|
||||
OSTRACE(("OPEN-CE-LOCK name=%s, rc=%s\n", zName, sqlite3ErrName(rc)));
|
||||
return rc;
|
||||
{
|
||||
if( isReadWrite && eType==SQLITE_OPEN_MAIN_DB
|
||||
&& ((pAppData==NULL) || !pAppData->bNoLock)
|
||||
&& (rc = winceCreateLock(zName, pFile))!=SQLITE_OK
|
||||
){
|
||||
osCloseHandle(h);
|
||||
sqlite3_free(zConverted);
|
||||
sqlite3_free(zTmpname);
|
||||
OSTRACE(("OPEN-CE-LOCK name=%s, rc=%s\n", zName, sqlite3ErrName(rc)));
|
||||
return rc;
|
||||
}
|
||||
}
|
||||
if( isTemp ){
|
||||
pFile->zDeleteOnClose = zConverted;
|
||||
@@ -4994,7 +5098,7 @@ static int winOpen(
|
||||
}
|
||||
|
||||
sqlite3_free(zTmpname);
|
||||
pFile->pMethod = &winIoMethod;
|
||||
pFile->pMethod = pAppData ? pAppData->pMethod : &winIoMethod;
|
||||
pFile->pVfs = pVfs;
|
||||
pFile->h = h;
|
||||
if( isReadonly ){
|
||||
@@ -5717,53 +5821,103 @@ static int winGetLastError(sqlite3_vfs *pVfs, int nBuf, char *zBuf){
|
||||
*/
|
||||
int sqlite3_os_init(void){
|
||||
static sqlite3_vfs winVfs = {
|
||||
3, /* iVersion */
|
||||
sizeof(winFile), /* szOsFile */
|
||||
3, /* iVersion */
|
||||
sizeof(winFile), /* szOsFile */
|
||||
SQLITE_WIN32_MAX_PATH_BYTES, /* 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 */
|
||||
winSetSystemCall, /* xSetSystemCall */
|
||||
winGetSystemCall, /* xGetSystemCall */
|
||||
winNextSystemCall, /* xNextSystemCall */
|
||||
0, /* pNext */
|
||||
"win32", /* zName */
|
||||
&winAppData, /* 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 */
|
||||
winSetSystemCall, /* xSetSystemCall */
|
||||
winGetSystemCall, /* xGetSystemCall */
|
||||
winNextSystemCall, /* xNextSystemCall */
|
||||
};
|
||||
#if defined(SQLITE_WIN32_HAS_WIDE)
|
||||
static sqlite3_vfs winLongPathVfs = {
|
||||
3, /* iVersion */
|
||||
sizeof(winFile), /* szOsFile */
|
||||
3, /* iVersion */
|
||||
sizeof(winFile), /* szOsFile */
|
||||
SQLITE_WINNT_MAX_PATH_BYTES, /* mxPathname */
|
||||
0, /* pNext */
|
||||
"win32-longpath", /* 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 */
|
||||
winSetSystemCall, /* xSetSystemCall */
|
||||
winGetSystemCall, /* xGetSystemCall */
|
||||
winNextSystemCall, /* xNextSystemCall */
|
||||
0, /* pNext */
|
||||
"win32-longpath", /* zName */
|
||||
&winAppData, /* 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 */
|
||||
winSetSystemCall, /* xSetSystemCall */
|
||||
winGetSystemCall, /* xGetSystemCall */
|
||||
winNextSystemCall, /* xNextSystemCall */
|
||||
};
|
||||
#endif
|
||||
static sqlite3_vfs winNolockVfs = {
|
||||
3, /* iVersion */
|
||||
sizeof(winFile), /* szOsFile */
|
||||
SQLITE_WIN32_MAX_PATH_BYTES, /* mxPathname */
|
||||
0, /* pNext */
|
||||
"win32-none", /* zName */
|
||||
&winNolockAppData, /* 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 */
|
||||
winSetSystemCall, /* xSetSystemCall */
|
||||
winGetSystemCall, /* xGetSystemCall */
|
||||
winNextSystemCall, /* xNextSystemCall */
|
||||
};
|
||||
#if defined(SQLITE_WIN32_HAS_WIDE)
|
||||
static sqlite3_vfs winLongPathNolockVfs = {
|
||||
3, /* iVersion */
|
||||
sizeof(winFile), /* szOsFile */
|
||||
SQLITE_WINNT_MAX_PATH_BYTES, /* mxPathname */
|
||||
0, /* pNext */
|
||||
"win32-longpath-none", /* zName */
|
||||
&winNolockAppData, /* 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 */
|
||||
winSetSystemCall, /* xSetSystemCall */
|
||||
winGetSystemCall, /* xGetSystemCall */
|
||||
winNextSystemCall, /* xNextSystemCall */
|
||||
};
|
||||
#endif
|
||||
|
||||
@@ -5787,6 +5941,12 @@ int sqlite3_os_init(void){
|
||||
sqlite3_vfs_register(&winLongPathVfs, 0);
|
||||
#endif
|
||||
|
||||
sqlite3_vfs_register(&winNolockVfs, 0);
|
||||
|
||||
#if defined(SQLITE_WIN32_HAS_WIDE)
|
||||
sqlite3_vfs_register(&winLongPathNolockVfs, 0);
|
||||
#endif
|
||||
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
|
||||
+3
-3
@@ -12,8 +12,8 @@
|
||||
**
|
||||
** This file contains code that is specific to Windows.
|
||||
*/
|
||||
#ifndef _OS_WIN_H_
|
||||
#define _OS_WIN_H_
|
||||
#ifndef SQLITE_OS_WIN_H
|
||||
#define SQLITE_OS_WIN_H
|
||||
|
||||
/*
|
||||
** Include the primary Windows SDK header file.
|
||||
@@ -85,4 +85,4 @@
|
||||
# define SQLITE_OS_WIN_THREADS 0
|
||||
#endif
|
||||
|
||||
#endif /* _OS_WIN_H_ */
|
||||
#endif /* SQLITE_OS_WIN_H */
|
||||
|
||||
+62
-9
@@ -817,9 +817,10 @@ static const unsigned char aJournalMagic[] = {
|
||||
** rollback journal. Otherwise false.
|
||||
*/
|
||||
#ifndef SQLITE_OMIT_WAL
|
||||
static int pagerUseWal(Pager *pPager){
|
||||
int sqlite3PagerUseWal(Pager *pPager){
|
||||
return (pPager->pWal!=0);
|
||||
}
|
||||
# define pagerUseWal(x) sqlite3PagerUseWal(x)
|
||||
#else
|
||||
# define pagerUseWal(x) 0
|
||||
# define pagerRollbackWal(x) 0
|
||||
@@ -4021,9 +4022,10 @@ static void pagerFreeMapHdrs(Pager *pPager){
|
||||
** a hot journal may be left in the filesystem but no error is returned
|
||||
** to the caller.
|
||||
*/
|
||||
int sqlite3PagerClose(Pager *pPager){
|
||||
int sqlite3PagerClose(Pager *pPager, sqlite3 *db){
|
||||
u8 *pTmp = (u8 *)pPager->pTmpSpace;
|
||||
|
||||
assert( db || pagerUseWal(pPager)==0 );
|
||||
assert( assert_pager_state(pPager) );
|
||||
disable_simulated_io_errors();
|
||||
sqlite3BeginBenignMalloc();
|
||||
@@ -4031,7 +4033,10 @@ int sqlite3PagerClose(Pager *pPager){
|
||||
/* pPager->errCode = 0; */
|
||||
pPager->exclusiveMode = 0;
|
||||
#ifndef SQLITE_OMIT_WAL
|
||||
sqlite3WalClose(pPager->pWal, pPager->ckptSyncFlags, pPager->pageSize, pTmp);
|
||||
assert( db || pPager->pWal==0 );
|
||||
sqlite3WalClose(pPager->pWal, db, pPager->ckptSyncFlags, pPager->pageSize,
|
||||
(db && (db->flags & SQLITE_NoCkptOnClose) ? 0 : pTmp)
|
||||
);
|
||||
pPager->pWal = 0;
|
||||
#endif
|
||||
pager_reset(pPager);
|
||||
@@ -6656,7 +6661,11 @@ int sqlite3PagerOpenSavepoint(Pager *pPager, int nSavepoint){
|
||||
** savepoint. If no errors occur, SQLITE_OK is returned.
|
||||
*/
|
||||
int sqlite3PagerSavepoint(Pager *pPager, int op, int iSavepoint){
|
||||
int rc = pPager->errCode; /* Return code */
|
||||
int rc = pPager->errCode;
|
||||
|
||||
#ifdef SQLITE_ENABLE_ZIPVFS
|
||||
if( op==SAVEPOINT_RELEASE ) rc = SQLITE_OK;
|
||||
#endif
|
||||
|
||||
assert( op==SAVEPOINT_RELEASE || op==SAVEPOINT_ROLLBACK );
|
||||
assert( iSavepoint>=0 || op==SAVEPOINT_ROLLBACK );
|
||||
@@ -6697,6 +6706,20 @@ int sqlite3PagerSavepoint(Pager *pPager, int op, int iSavepoint){
|
||||
rc = pagerPlaybackSavepoint(pPager, pSavepoint);
|
||||
assert(rc!=SQLITE_DONE);
|
||||
}
|
||||
|
||||
#ifdef SQLITE_ENABLE_ZIPVFS
|
||||
/* If the cache has been modified but the savepoint cannot be rolled
|
||||
** back journal_mode=off, put the pager in the error state. This way,
|
||||
** if the VFS used by this pager includes ZipVFS, the entire transaction
|
||||
** can be rolled back at the ZipVFS level. */
|
||||
else if(
|
||||
pPager->journalMode==PAGER_JOURNALMODE_OFF
|
||||
&& pPager->eState>=PAGER_WRITER_CACHEMOD
|
||||
){
|
||||
pPager->errCode = SQLITE_ABORT;
|
||||
pPager->eState = PAGER_ERROR;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
return rc;
|
||||
@@ -7157,6 +7180,17 @@ sqlite3_backup **sqlite3PagerBackupPtr(Pager *pPager){
|
||||
return &pPager->pBackup;
|
||||
}
|
||||
|
||||
#ifndef SQLITE_OMIT_VACUUM
|
||||
/*
|
||||
** Unless this is an in-memory or temporary database, clear the pager cache.
|
||||
*/
|
||||
void sqlite3PagerClearCache(Pager *pPager){
|
||||
assert( MEMDB==0 || pPager->tempFile );
|
||||
if( pPager->tempFile==0 ) pager_reset(pPager);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
#ifndef SQLITE_OMIT_WAL
|
||||
/*
|
||||
** This function is called when the user invokes "PRAGMA wal_checkpoint",
|
||||
@@ -7165,10 +7199,16 @@ sqlite3_backup **sqlite3PagerBackupPtr(Pager *pPager){
|
||||
**
|
||||
** Parameter eMode is one of SQLITE_CHECKPOINT_PASSIVE, FULL or RESTART.
|
||||
*/
|
||||
int sqlite3PagerCheckpoint(Pager *pPager, int eMode, int *pnLog, int *pnCkpt){
|
||||
int sqlite3PagerCheckpoint(
|
||||
Pager *pPager, /* Checkpoint on this pager */
|
||||
sqlite3 *db, /* Db handle used to check for interrupts */
|
||||
int eMode, /* Type of checkpoint */
|
||||
int *pnLog, /* OUT: Final number of frames in log */
|
||||
int *pnCkpt /* OUT: Final number of checkpointed frames */
|
||||
){
|
||||
int rc = SQLITE_OK;
|
||||
if( pPager->pWal ){
|
||||
rc = sqlite3WalCheckpoint(pPager->pWal, eMode,
|
||||
rc = sqlite3WalCheckpoint(pPager->pWal, db, eMode,
|
||||
(eMode==SQLITE_CHECKPOINT_PASSIVE ? 0 : pPager->xBusyHandler),
|
||||
pPager->pBusyHandlerArg,
|
||||
pPager->ckptSyncFlags, pPager->pageSize, (u8 *)pPager->pTmpSpace,
|
||||
@@ -7300,7 +7340,7 @@ int sqlite3PagerOpenWal(
|
||||
** 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 sqlite3PagerCloseWal(Pager *pPager, sqlite3 *db){
|
||||
int rc = SQLITE_OK;
|
||||
|
||||
assert( pPager->journalMode==PAGER_JOURNALMODE_WAL );
|
||||
@@ -7328,7 +7368,7 @@ int sqlite3PagerCloseWal(Pager *pPager){
|
||||
if( rc==SQLITE_OK && pPager->pWal ){
|
||||
rc = pagerExclusiveLock(pPager);
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = sqlite3WalClose(pPager->pWal, pPager->ckptSyncFlags,
|
||||
rc = sqlite3WalClose(pPager->pWal, db, pPager->ckptSyncFlags,
|
||||
pPager->pageSize, (u8*)pPager->pTmpSpace);
|
||||
pPager->pWal = 0;
|
||||
pagerFixMaplimit(pPager);
|
||||
@@ -7365,6 +7405,20 @@ int sqlite3PagerSnapshotOpen(Pager *pPager, sqlite3_snapshot *pSnapshot){
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** If this is a WAL database, call sqlite3WalSnapshotRecover(). If this
|
||||
** is not a WAL database, return an error.
|
||||
*/
|
||||
int sqlite3PagerSnapshotRecover(Pager *pPager){
|
||||
int rc;
|
||||
if( pPager->pWal ){
|
||||
rc = sqlite3WalSnapshotRecover(pPager->pWal);
|
||||
}else{
|
||||
rc = SQLITE_ERROR;
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
#endif /* SQLITE_ENABLE_SNAPSHOT */
|
||||
#endif /* !SQLITE_OMIT_WAL */
|
||||
|
||||
@@ -7382,5 +7436,4 @@ int sqlite3PagerWalFramesize(Pager *pPager){
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
#endif /* SQLITE_OMIT_DISKIO */
|
||||
|
||||
+11
-6
@@ -14,8 +14,8 @@
|
||||
** at a time and provides a journal for rollback.
|
||||
*/
|
||||
|
||||
#ifndef _PAGER_H_
|
||||
#define _PAGER_H_
|
||||
#ifndef SQLITE_PAGER_H
|
||||
#define SQLITE_PAGER_H
|
||||
|
||||
/*
|
||||
** Default maximum size for persistent journal files. A negative
|
||||
@@ -122,7 +122,7 @@ int sqlite3PagerOpen(
|
||||
int,
|
||||
void(*)(DbPage*)
|
||||
);
|
||||
int sqlite3PagerClose(Pager *pPager);
|
||||
int sqlite3PagerClose(Pager *pPager, sqlite3*);
|
||||
int sqlite3PagerReadFileheader(Pager*, int, unsigned char*);
|
||||
|
||||
/* Functions used to configure a Pager object. */
|
||||
@@ -173,15 +173,19 @@ int sqlite3PagerSavepoint(Pager *pPager, int op, int iSavepoint);
|
||||
int sqlite3PagerSharedLock(Pager *pPager);
|
||||
|
||||
#ifndef SQLITE_OMIT_WAL
|
||||
int sqlite3PagerCheckpoint(Pager *pPager, int, int*, int*);
|
||||
int sqlite3PagerCheckpoint(Pager *pPager, sqlite3*, int, int*, int*);
|
||||
int sqlite3PagerWalSupported(Pager *pPager);
|
||||
int sqlite3PagerWalCallback(Pager *pPager);
|
||||
int sqlite3PagerOpenWal(Pager *pPager, int *pisOpen);
|
||||
int sqlite3PagerCloseWal(Pager *pPager);
|
||||
int sqlite3PagerCloseWal(Pager *pPager, sqlite3*);
|
||||
int sqlite3PagerUseWal(Pager *pPager);
|
||||
# ifdef SQLITE_ENABLE_SNAPSHOT
|
||||
int sqlite3PagerSnapshotGet(Pager *pPager, sqlite3_snapshot **ppSnapshot);
|
||||
int sqlite3PagerSnapshotOpen(Pager *pPager, sqlite3_snapshot *pSnapshot);
|
||||
int sqlite3PagerSnapshotRecover(Pager *pPager);
|
||||
# endif
|
||||
#else
|
||||
# define sqlite3PagerUseWal(x) 0
|
||||
#endif
|
||||
|
||||
#ifdef SQLITE_ENABLE_ZIPVFS
|
||||
@@ -203,6 +207,7 @@ const char *sqlite3PagerJournalname(Pager*);
|
||||
void *sqlite3PagerTempSpace(Pager*);
|
||||
int sqlite3PagerIsMemdb(Pager*);
|
||||
void sqlite3PagerCacheStat(Pager *, int, int, int *);
|
||||
void sqlite3PagerClearCache(Pager*);
|
||||
int sqlite3SectorSize(sqlite3_file *);
|
||||
|
||||
/* Functions used to truncate the database file. */
|
||||
@@ -229,4 +234,4 @@ void *sqlite3PagerCodec(DbPage *);
|
||||
# define enable_simulated_io_errors()
|
||||
#endif
|
||||
|
||||
#endif /* _PAGER_H_ */
|
||||
#endif /* SQLITE_PAGER_H */
|
||||
|
||||
+91
-58
@@ -80,15 +80,6 @@ struct LimitVal {
|
||||
Expr *pOffset; /* The OFFSET expression. NULL if there is none */
|
||||
};
|
||||
|
||||
/*
|
||||
** An instance of this structure is used to store the LIKE,
|
||||
** GLOB, NOT LIKE, and NOT GLOB operators.
|
||||
*/
|
||||
struct LikeOp {
|
||||
Token eOperator; /* "like" or "glob" or "regexp" */
|
||||
int bNot; /* True if the NOT keyword is present */
|
||||
};
|
||||
|
||||
/*
|
||||
** An instance of the following structure describes the event of a
|
||||
** TRIGGER. "a" is the event type, one of TK_UPDATE, TK_INSERT,
|
||||
@@ -100,11 +91,6 @@ struct LikeOp {
|
||||
*/
|
||||
struct TrigEvent { int a; IdList * b; };
|
||||
|
||||
/*
|
||||
** An instance of this structure holds the ATTACH key and the key type.
|
||||
*/
|
||||
struct AttachKey { int type; Token key; };
|
||||
|
||||
/*
|
||||
** Disable lookaside memory allocation for objects that might be
|
||||
** shared across database connections.
|
||||
@@ -282,7 +268,7 @@ ccons ::= DEFAULT LP expr(X) RP. {sqlite3AddDefaultValue(pParse,&X);}
|
||||
ccons ::= DEFAULT PLUS term(X). {sqlite3AddDefaultValue(pParse,&X);}
|
||||
ccons ::= DEFAULT MINUS(A) term(X). {
|
||||
ExprSpan v;
|
||||
v.pExpr = sqlite3PExpr(pParse, TK_UMINUS, X.pExpr, 0, 0);
|
||||
v.pExpr = sqlite3PExpr(pParse, TK_UMINUS, X.pExpr, 0);
|
||||
v.zStart = A.z;
|
||||
v.zEnd = X.zEnd;
|
||||
sqlite3AddDefaultValue(pParse,&v);
|
||||
@@ -556,10 +542,10 @@ selcollist(A) ::= sclp(A) STAR. {
|
||||
Expr *p = sqlite3Expr(pParse->db, TK_ASTERISK, 0);
|
||||
A = sqlite3ExprListAppend(pParse, A, p);
|
||||
}
|
||||
selcollist(A) ::= sclp(A) nm(X) DOT STAR(Y). {
|
||||
Expr *pRight = sqlite3PExpr(pParse, TK_ASTERISK, 0, 0, &Y);
|
||||
Expr *pLeft = sqlite3PExpr(pParse, TK_ID, 0, 0, &X);
|
||||
Expr *pDot = sqlite3PExpr(pParse, TK_DOT, pLeft, pRight, 0);
|
||||
selcollist(A) ::= sclp(A) nm(X) DOT STAR. {
|
||||
Expr *pRight = sqlite3PExpr(pParse, TK_ASTERISK, 0, 0);
|
||||
Expr *pLeft = sqlite3ExprAlloc(pParse->db, TK_ID, &X, 1);
|
||||
Expr *pDot = sqlite3PExpr(pParse, TK_DOT, pLeft, pRight);
|
||||
A = sqlite3ExprListAppend(pParse,A, pDot);
|
||||
}
|
||||
|
||||
@@ -789,10 +775,16 @@ setlist(A) ::= setlist(A) COMMA nm(X) EQ expr(Y). {
|
||||
A = sqlite3ExprListAppend(pParse, A, Y.pExpr);
|
||||
sqlite3ExprListSetName(pParse, A, &X, 1);
|
||||
}
|
||||
setlist(A) ::= setlist(A) COMMA LP idlist(X) RP EQ expr(Y). {
|
||||
A = sqlite3ExprListAppendVector(pParse, A, X, Y.pExpr);
|
||||
}
|
||||
setlist(A) ::= nm(X) EQ expr(Y). {
|
||||
A = sqlite3ExprListAppend(pParse, 0, Y.pExpr);
|
||||
sqlite3ExprListSetName(pParse, A, &X, 1);
|
||||
}
|
||||
setlist(A) ::= LP idlist(X) RP EQ expr(Y). {
|
||||
A = sqlite3ExprListAppendVector(pParse, 0, X, Y.pExpr);
|
||||
}
|
||||
|
||||
////////////////////////// The INSERT command /////////////////////////////////
|
||||
//
|
||||
@@ -845,7 +837,24 @@ idlist(A) ::= nm(Y).
|
||||
** that created the expression.
|
||||
*/
|
||||
static void spanExpr(ExprSpan *pOut, Parse *pParse, int op, Token t){
|
||||
pOut->pExpr = sqlite3PExpr(pParse, op, 0, 0, &t);
|
||||
Expr *p = sqlite3DbMallocRawNN(pParse->db, sizeof(Expr)+t.n+1);
|
||||
if( p ){
|
||||
memset(p, 0, sizeof(Expr));
|
||||
p->op = (u8)op;
|
||||
p->flags = EP_Leaf;
|
||||
p->iAgg = -1;
|
||||
p->u.zToken = (char*)&p[1];
|
||||
memcpy(p->u.zToken, t.z, t.n);
|
||||
p->u.zToken[t.n] = 0;
|
||||
if( sqlite3Isquote(p->u.zToken[0]) ){
|
||||
if( p->u.zToken[0]=='"' ) p->flags |= EP_DblQuoted;
|
||||
sqlite3Dequote(p->u.zToken);
|
||||
}
|
||||
#if SQLITE_MAX_EXPR_DEPTH>0
|
||||
p->nHeight = 1;
|
||||
#endif
|
||||
}
|
||||
pOut->pExpr = p;
|
||||
pOut->zStart = t.z;
|
||||
pOut->zEnd = &t.z[t.n];
|
||||
}
|
||||
@@ -858,25 +867,32 @@ term(A) ::= NULL(X). {spanExpr(&A,pParse,@X,X);/*A-overwrites-X*/}
|
||||
expr(A) ::= id(X). {spanExpr(&A,pParse,TK_ID,X); /*A-overwrites-X*/}
|
||||
expr(A) ::= JOIN_KW(X). {spanExpr(&A,pParse,TK_ID,X); /*A-overwrites-X*/}
|
||||
expr(A) ::= nm(X) DOT nm(Y). {
|
||||
Expr *temp1 = sqlite3PExpr(pParse, TK_ID, 0, 0, &X);
|
||||
Expr *temp2 = sqlite3PExpr(pParse, TK_ID, 0, 0, &Y);
|
||||
Expr *temp1 = sqlite3ExprAlloc(pParse->db, TK_ID, &X, 1);
|
||||
Expr *temp2 = sqlite3ExprAlloc(pParse->db, TK_ID, &Y, 1);
|
||||
spanSet(&A,&X,&Y); /*A-overwrites-X*/
|
||||
A.pExpr = sqlite3PExpr(pParse, TK_DOT, temp1, temp2, 0);
|
||||
A.pExpr = sqlite3PExpr(pParse, TK_DOT, temp1, temp2);
|
||||
}
|
||||
expr(A) ::= nm(X) DOT nm(Y) DOT nm(Z). {
|
||||
Expr *temp1 = sqlite3PExpr(pParse, TK_ID, 0, 0, &X);
|
||||
Expr *temp2 = sqlite3PExpr(pParse, TK_ID, 0, 0, &Y);
|
||||
Expr *temp3 = sqlite3PExpr(pParse, TK_ID, 0, 0, &Z);
|
||||
Expr *temp4 = sqlite3PExpr(pParse, TK_DOT, temp2, temp3, 0);
|
||||
Expr *temp1 = sqlite3ExprAlloc(pParse->db, TK_ID, &X, 1);
|
||||
Expr *temp2 = sqlite3ExprAlloc(pParse->db, TK_ID, &Y, 1);
|
||||
Expr *temp3 = sqlite3ExprAlloc(pParse->db, TK_ID, &Z, 1);
|
||||
Expr *temp4 = sqlite3PExpr(pParse, TK_DOT, temp2, temp3);
|
||||
spanSet(&A,&X,&Z); /*A-overwrites-X*/
|
||||
A.pExpr = sqlite3PExpr(pParse, TK_DOT, temp1, temp4, 0);
|
||||
A.pExpr = sqlite3PExpr(pParse, TK_DOT, temp1, temp4);
|
||||
}
|
||||
term(A) ::= FLOAT|BLOB(X). {spanExpr(&A,pParse,@X,X);/*A-overwrites-X*/}
|
||||
term(A) ::= STRING(X). {spanExpr(&A,pParse,@X,X);/*A-overwrites-X*/}
|
||||
term(A) ::= INTEGER(X). {
|
||||
A.pExpr = sqlite3ExprAlloc(pParse->db, TK_INTEGER, &X, 1);
|
||||
A.zStart = X.z;
|
||||
A.zEnd = X.z + X.n;
|
||||
if( A.pExpr ) A.pExpr->flags |= EP_Leaf;
|
||||
}
|
||||
term(A) ::= INTEGER|FLOAT|BLOB(X). {spanExpr(&A,pParse,@X,X);/*A-overwrites-X*/}
|
||||
term(A) ::= STRING(X). {spanExpr(&A,pParse,@X,X);/*A-overwrites-X*/}
|
||||
expr(A) ::= VARIABLE(X). {
|
||||
if( !(X.z[0]=='#' && sqlite3Isdigit(X.z[1])) ){
|
||||
u32 n = X.n;
|
||||
spanExpr(&A, pParse, TK_VARIABLE, X);
|
||||
sqlite3ExprAssignVarNumber(pParse, A.pExpr);
|
||||
sqlite3ExprAssignVarNumber(pParse, A.pExpr, n);
|
||||
}else{
|
||||
/* When doing a nested parse, one can include terms in an expression
|
||||
** that look like this: #1 #2 ... These terms refer to registers
|
||||
@@ -888,7 +904,7 @@ expr(A) ::= VARIABLE(X). {
|
||||
sqlite3ErrorMsg(pParse, "near \"%T\": syntax error", &t);
|
||||
A.pExpr = 0;
|
||||
}else{
|
||||
A.pExpr = sqlite3PExpr(pParse, TK_REGISTER, 0, 0, &t);
|
||||
A.pExpr = sqlite3PExpr(pParse, TK_REGISTER, 0, 0);
|
||||
if( A.pExpr ) sqlite3GetInt32(&t.z[1], &A.pExpr->iTable);
|
||||
}
|
||||
}
|
||||
@@ -900,7 +916,8 @@ expr(A) ::= expr(A) COLLATE ids(C). {
|
||||
%ifndef SQLITE_OMIT_CAST
|
||||
expr(A) ::= CAST(X) LP expr(E) AS typetoken(T) RP(Y). {
|
||||
spanSet(&A,&X,&Y); /*A-overwrites-X*/
|
||||
A.pExpr = sqlite3PExpr(pParse, TK_CAST, E.pExpr, 0, &T);
|
||||
A.pExpr = sqlite3ExprAlloc(pParse->db, TK_CAST, &T, 1);
|
||||
sqlite3ExprAttachSubtrees(pParse->db, A.pExpr, E.pExpr, 0);
|
||||
}
|
||||
%endif SQLITE_OMIT_CAST
|
||||
expr(A) ::= id(X) LP distinct(D) exprlist(Y) RP(E). {
|
||||
@@ -932,7 +949,7 @@ term(A) ::= CTIME_KW(OP). {
|
||||
ExprSpan *pLeft, /* The left operand, and output */
|
||||
ExprSpan *pRight /* The right operand */
|
||||
){
|
||||
pLeft->pExpr = sqlite3PExpr(pParse, op, pLeft->pExpr, pRight->pExpr, 0);
|
||||
pLeft->pExpr = sqlite3PExpr(pParse, op, pLeft->pExpr, pRight->pExpr);
|
||||
pLeft->zEnd = pRight->zEnd;
|
||||
}
|
||||
|
||||
@@ -941,11 +958,22 @@ term(A) ::= CTIME_KW(OP). {
|
||||
*/
|
||||
static void exprNot(Parse *pParse, int doNot, ExprSpan *pSpan){
|
||||
if( doNot ){
|
||||
pSpan->pExpr = sqlite3PExpr(pParse, TK_NOT, pSpan->pExpr, 0, 0);
|
||||
pSpan->pExpr = sqlite3PExpr(pParse, TK_NOT, pSpan->pExpr, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
expr(A) ::= LP(L) nexprlist(X) COMMA expr(Y) RP(R). {
|
||||
ExprList *pList = sqlite3ExprListAppend(pParse, X, Y.pExpr);
|
||||
A.pExpr = sqlite3PExpr(pParse, TK_VECTOR, 0, 0);
|
||||
if( A.pExpr ){
|
||||
A.pExpr->x.pList = pList;
|
||||
spanSet(&A, &L, &R);
|
||||
}else{
|
||||
sqlite3ExprListDelete(pParse->db, pList);
|
||||
}
|
||||
}
|
||||
|
||||
expr(A) ::= expr(A) AND(OP) expr(Y). {spanBinaryExpr(pParse,@OP,&A,&Y);}
|
||||
expr(A) ::= expr(A) OR(OP) expr(Y). {spanBinaryExpr(pParse,@OP,&A,&Y);}
|
||||
expr(A) ::= expr(A) LT|GT|GE|LE(OP) expr(Y).
|
||||
@@ -958,25 +986,29 @@ expr(A) ::= expr(A) PLUS|MINUS(OP) expr(Y).
|
||||
expr(A) ::= expr(A) STAR|SLASH|REM(OP) expr(Y).
|
||||
{spanBinaryExpr(pParse,@OP,&A,&Y);}
|
||||
expr(A) ::= expr(A) CONCAT(OP) expr(Y). {spanBinaryExpr(pParse,@OP,&A,&Y);}
|
||||
%type likeop {struct LikeOp}
|
||||
likeop(A) ::= LIKE_KW|MATCH(X). {A.eOperator = X; A.bNot = 0;/*A-overwrites-X*/}
|
||||
likeop(A) ::= NOT LIKE_KW|MATCH(X). {A.eOperator = X; A.bNot = 1;}
|
||||
%type likeop {Token}
|
||||
likeop(A) ::= LIKE_KW|MATCH(X). {A=X;/*A-overwrites-X*/}
|
||||
likeop(A) ::= NOT LIKE_KW|MATCH(X). {A=X; A.n|=0x80000000; /*A-overwrite-X*/}
|
||||
expr(A) ::= expr(A) likeop(OP) expr(Y). [LIKE_KW] {
|
||||
ExprList *pList;
|
||||
int bNot = OP.n & 0x80000000;
|
||||
OP.n &= 0x7fffffff;
|
||||
pList = sqlite3ExprListAppend(pParse,0, Y.pExpr);
|
||||
pList = sqlite3ExprListAppend(pParse,pList, A.pExpr);
|
||||
A.pExpr = sqlite3ExprFunction(pParse, pList, &OP.eOperator);
|
||||
exprNot(pParse, OP.bNot, &A);
|
||||
A.pExpr = sqlite3ExprFunction(pParse, pList, &OP);
|
||||
exprNot(pParse, bNot, &A);
|
||||
A.zEnd = Y.zEnd;
|
||||
if( A.pExpr ) A.pExpr->flags |= EP_InfixFunc;
|
||||
}
|
||||
expr(A) ::= expr(A) likeop(OP) expr(Y) ESCAPE expr(E). [LIKE_KW] {
|
||||
ExprList *pList;
|
||||
int bNot = OP.n & 0x80000000;
|
||||
OP.n &= 0x7fffffff;
|
||||
pList = sqlite3ExprListAppend(pParse,0, Y.pExpr);
|
||||
pList = sqlite3ExprListAppend(pParse,pList, A.pExpr);
|
||||
pList = sqlite3ExprListAppend(pParse,pList, E.pExpr);
|
||||
A.pExpr = sqlite3ExprFunction(pParse, pList, &OP.eOperator);
|
||||
exprNot(pParse, OP.bNot, &A);
|
||||
A.pExpr = sqlite3ExprFunction(pParse, pList, &OP);
|
||||
exprNot(pParse, bNot, &A);
|
||||
A.zEnd = E.zEnd;
|
||||
if( A.pExpr ) A.pExpr->flags |= EP_InfixFunc;
|
||||
}
|
||||
@@ -990,7 +1022,7 @@ expr(A) ::= expr(A) likeop(OP) expr(Y) ESCAPE expr(E). [LIKE_KW] {
|
||||
ExprSpan *pOperand, /* The operand, and output */
|
||||
Token *pPostOp /* The operand token for setting the span */
|
||||
){
|
||||
pOperand->pExpr = sqlite3PExpr(pParse, op, pOperand->pExpr, 0, 0);
|
||||
pOperand->pExpr = sqlite3PExpr(pParse, op, pOperand->pExpr, 0);
|
||||
pOperand->zEnd = &pPostOp->z[pPostOp->n];
|
||||
}
|
||||
}
|
||||
@@ -1037,7 +1069,7 @@ expr(A) ::= expr(A) IS NOT expr(Y). {
|
||||
Token *pPreOp /* The operand token for setting the span */
|
||||
){
|
||||
pOut->zStart = pPreOp->z;
|
||||
pOut->pExpr = sqlite3PExpr(pParse, op, pOperand->pExpr, 0, 0);
|
||||
pOut->pExpr = sqlite3PExpr(pParse, op, pOperand->pExpr, 0);
|
||||
pOut->zEnd = pOperand->zEnd;
|
||||
}
|
||||
}
|
||||
@@ -1059,7 +1091,7 @@ between_op(A) ::= NOT BETWEEN. {A = 1;}
|
||||
expr(A) ::= expr(A) between_op(N) expr(X) AND expr(Y). [BETWEEN] {
|
||||
ExprList *pList = sqlite3ExprListAppend(pParse,0, X.pExpr);
|
||||
pList = sqlite3ExprListAppend(pParse,pList, Y.pExpr);
|
||||
A.pExpr = sqlite3PExpr(pParse, TK_BETWEEN, A.pExpr, 0, 0);
|
||||
A.pExpr = sqlite3PExpr(pParse, TK_BETWEEN, A.pExpr, 0);
|
||||
if( A.pExpr ){
|
||||
A.pExpr->x.pList = pList;
|
||||
}else{
|
||||
@@ -1083,7 +1115,7 @@ expr(A) ::= expr(A) between_op(N) expr(X) AND expr(Y). [BETWEEN] {
|
||||
** regardless of the value of expr1.
|
||||
*/
|
||||
sqlite3ExprDelete(pParse->db, A.pExpr);
|
||||
A.pExpr = sqlite3PExpr(pParse, TK_INTEGER, 0, 0, &sqlite3IntTokens[N]);
|
||||
A.pExpr = sqlite3ExprAlloc(pParse->db, TK_INTEGER,&sqlite3IntTokens[N],1);
|
||||
}else if( Y->nExpr==1 ){
|
||||
/* Expressions of the form:
|
||||
**
|
||||
@@ -1110,9 +1142,9 @@ expr(A) ::= expr(A) between_op(N) expr(X) AND expr(Y). [BETWEEN] {
|
||||
pRHS->flags &= ~EP_Collate;
|
||||
pRHS->flags |= EP_Generic;
|
||||
}
|
||||
A.pExpr = sqlite3PExpr(pParse, N ? TK_NE : TK_EQ, A.pExpr, pRHS, 0);
|
||||
A.pExpr = sqlite3PExpr(pParse, N ? TK_NE : TK_EQ, A.pExpr, pRHS);
|
||||
}else{
|
||||
A.pExpr = sqlite3PExpr(pParse, TK_IN, A.pExpr, 0, 0);
|
||||
A.pExpr = sqlite3PExpr(pParse, TK_IN, A.pExpr, 0);
|
||||
if( A.pExpr ){
|
||||
A.pExpr->x.pList = Y;
|
||||
sqlite3ExprSetHeightAndFlags(pParse, A.pExpr);
|
||||
@@ -1125,11 +1157,11 @@ expr(A) ::= expr(A) between_op(N) expr(X) AND expr(Y). [BETWEEN] {
|
||||
}
|
||||
expr(A) ::= LP(B) select(X) RP(E). {
|
||||
spanSet(&A,&B,&E); /*A-overwrites-B*/
|
||||
A.pExpr = sqlite3PExpr(pParse, TK_SELECT, 0, 0, 0);
|
||||
A.pExpr = sqlite3PExpr(pParse, TK_SELECT, 0, 0);
|
||||
sqlite3PExprAddSelect(pParse, A.pExpr, X);
|
||||
}
|
||||
expr(A) ::= expr(A) in_op(N) LP select(Y) RP(E). [IN] {
|
||||
A.pExpr = sqlite3PExpr(pParse, TK_IN, A.pExpr, 0, 0);
|
||||
A.pExpr = sqlite3PExpr(pParse, TK_IN, A.pExpr, 0);
|
||||
sqlite3PExprAddSelect(pParse, A.pExpr, Y);
|
||||
exprNot(pParse, N, &A);
|
||||
A.zEnd = &E.z[E.n];
|
||||
@@ -1138,7 +1170,7 @@ expr(A) ::= expr(A) between_op(N) expr(X) AND expr(Y). [BETWEEN] {
|
||||
SrcList *pSrc = sqlite3SrcListAppend(pParse->db, 0,&Y,&Z);
|
||||
Select *pSelect = sqlite3SelectNew(pParse, 0,pSrc,0,0,0,0,0,0,0);
|
||||
if( E ) sqlite3SrcListFuncArgs(pParse, pSelect ? pSrc : 0, E);
|
||||
A.pExpr = sqlite3PExpr(pParse, TK_IN, A.pExpr, 0, 0);
|
||||
A.pExpr = sqlite3PExpr(pParse, TK_IN, A.pExpr, 0);
|
||||
sqlite3PExprAddSelect(pParse, A.pExpr, pSelect);
|
||||
exprNot(pParse, N, &A);
|
||||
A.zEnd = Z.z ? &Z.z[Z.n] : &Y.z[Y.n];
|
||||
@@ -1146,7 +1178,7 @@ expr(A) ::= expr(A) between_op(N) expr(X) AND expr(Y). [BETWEEN] {
|
||||
expr(A) ::= EXISTS(B) LP select(Y) RP(E). {
|
||||
Expr *p;
|
||||
spanSet(&A,&B,&E); /*A-overwrites-B*/
|
||||
p = A.pExpr = sqlite3PExpr(pParse, TK_EXISTS, 0, 0, 0);
|
||||
p = A.pExpr = sqlite3PExpr(pParse, TK_EXISTS, 0, 0);
|
||||
sqlite3PExprAddSelect(pParse, p, Y);
|
||||
}
|
||||
%endif SQLITE_OMIT_SUBQUERY
|
||||
@@ -1154,7 +1186,7 @@ expr(A) ::= expr(A) between_op(N) expr(X) AND expr(Y). [BETWEEN] {
|
||||
/* CASE expressions */
|
||||
expr(A) ::= CASE(C) case_operand(X) case_exprlist(Y) case_else(Z) END(E). {
|
||||
spanSet(&A,&C,&E); /*A-overwrites-C*/
|
||||
A.pExpr = sqlite3PExpr(pParse, TK_CASE, X, 0, 0);
|
||||
A.pExpr = sqlite3PExpr(pParse, TK_CASE, X, 0);
|
||||
if( A.pExpr ){
|
||||
A.pExpr->x.pList = Z ? sqlite3ExprListAppend(pParse,Y,Z) : Y;
|
||||
sqlite3ExprSetHeightAndFlags(pParse, A.pExpr);
|
||||
@@ -1194,13 +1226,14 @@ nexprlist(A) ::= nexprlist(A) COMMA expr(Y).
|
||||
nexprlist(A) ::= expr(Y).
|
||||
{A = sqlite3ExprListAppend(pParse,0,Y.pExpr); /*A-overwrites-Y*/}
|
||||
|
||||
%ifndef SQLITE_OMIT_SUBQUERY
|
||||
/* A paren_exprlist is an optional expression list contained inside
|
||||
** of parenthesis */
|
||||
%type paren_exprlist {ExprList*}
|
||||
%destructor paren_exprlist {sqlite3ExprListDelete(pParse->db, $$);}
|
||||
paren_exprlist(A) ::= . {A = 0;}
|
||||
paren_exprlist(A) ::= LP exprlist(X) RP. {A = X;}
|
||||
|
||||
%endif SQLITE_OMIT_SUBQUERY
|
||||
|
||||
|
||||
///////////////////////////// The CREATE INDEX command ///////////////////////
|
||||
@@ -1284,8 +1317,8 @@ cmd ::= DROP INDEX ifexists(E) fullname(X). {sqlite3DropIndex(pParse, X, E);}
|
||||
//
|
||||
%ifndef SQLITE_OMIT_VACUUM
|
||||
%ifndef SQLITE_OMIT_ATTACH
|
||||
cmd ::= VACUUM. {sqlite3Vacuum(pParse);}
|
||||
cmd ::= VACUUM nm. {sqlite3Vacuum(pParse);}
|
||||
cmd ::= VACUUM. {sqlite3Vacuum(pParse,0);}
|
||||
cmd ::= VACUUM nm(X). {sqlite3Vacuum(pParse,&X);}
|
||||
%endif SQLITE_OMIT_ATTACH
|
||||
%endif SQLITE_OMIT_VACUUM
|
||||
|
||||
@@ -1413,14 +1446,14 @@ trigger_cmd(A) ::= select(X).
|
||||
// The special RAISE expression that may occur in trigger programs
|
||||
expr(A) ::= RAISE(X) LP IGNORE RP(Y). {
|
||||
spanSet(&A,&X,&Y); /*A-overwrites-X*/
|
||||
A.pExpr = sqlite3PExpr(pParse, TK_RAISE, 0, 0, 0);
|
||||
A.pExpr = sqlite3PExpr(pParse, TK_RAISE, 0, 0);
|
||||
if( A.pExpr ){
|
||||
A.pExpr->affinity = OE_Ignore;
|
||||
}
|
||||
}
|
||||
expr(A) ::= RAISE(X) LP raisetype(T) COMMA nm(Z) RP(Y). {
|
||||
spanSet(&A,&X,&Y); /*A-overwrites-X*/
|
||||
A.pExpr = sqlite3PExpr(pParse, TK_RAISE, 0, 0, &Z);
|
||||
A.pExpr = sqlite3ExprAlloc(pParse->db, TK_RAISE, &Z, 1);
|
||||
if( A.pExpr ) {
|
||||
A.pExpr->affinity = (char)T;
|
||||
}
|
||||
|
||||
+1
-1
@@ -461,7 +461,7 @@ static SQLITE_NOINLINE PgHdr *pcacheFetchFinishWithInit(
|
||||
assert( pPage!=0 );
|
||||
pPgHdr = (PgHdr*)pPage->pExtra;
|
||||
assert( pPgHdr->pPage==0 );
|
||||
memset(pPgHdr, 0, sizeof(PgHdr));
|
||||
memset(&pPgHdr->pDirty, 0, sizeof(PgHdr) - offsetof(PgHdr,pDirty));
|
||||
pPgHdr->pPage = pPage;
|
||||
pPgHdr->pData = pPage->pBuf;
|
||||
pPgHdr->pExtra = (void *)&pPgHdr[1];
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user