Compare commits
628 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 95484726f6 | |||
| 936f7deee4 | |||
| 7335f75daf | |||
| 0d83c71b34 | |||
| 7309b50fbc | |||
| af8980bdce | |||
| fe18355267 | |||
| ff9ff54801 | |||
| c131675401 | |||
| f05630fb1c | |||
| baa574ef32 | |||
| 8c5611a169 | |||
| b93760e1e7 | |||
| 00eee07911 | |||
| f0c1ea9e86 | |||
| db9a708ee7 | |||
| 9541fbc283 | |||
| 1f72b53461 | |||
| 592ae2823c | |||
| 07ef60350d | |||
| 8a6888c19e | |||
| 202651fe86 | |||
| 54402964b0 | |||
| d8a94dbb36 | |||
| 46451fa8b5 | |||
| 890a9ede3b | |||
| 7c036fabe5 | |||
| a6f09a0193 | |||
| 68522e1627 | |||
| 5b3a754360 | |||
| c216df75c6 | |||
| 613390680d | |||
| 238bea2ae6 | |||
| 6c6badd2ef | |||
| 48dcdd3b74 | |||
| a6746456d9 | |||
| 783f5b5aa2 | |||
| 2a28cff2e7 | |||
| 16c8c96745 | |||
| 7009c43eaf | |||
| c3b7e1c4e8 | |||
| b949244ea1 | |||
| 7e13152952 | |||
| 59d01de837 | |||
| b8a0fc52a2 | |||
| 91c0092917 | |||
| a0423e0fc1 | |||
| 5c9f5e4022 | |||
| 0c08c8c208 | |||
| a52b8c8845 | |||
| 89e1caf294 | |||
| 2ee01b0c9a | |||
| bb51af68f4 | |||
| 6de6121e36 | |||
| a22a2d266f | |||
| 92f699d384 | |||
| 1528014703 | |||
| 61186d1243 | |||
| e51fae4134 | |||
| 0a8cc1f634 | |||
| 34fac74362 | |||
| ec8298fbd1 | |||
| 63fb588d30 | |||
| 6a61a19f9a | |||
| f703dfa381 | |||
| eda67031de | |||
| 0c6df29cba | |||
| 2a91065145 | |||
| 58c2ca4483 | |||
| 50a17a1b3a | |||
| 3a0e45d0af | |||
| 84c1a7b49f | |||
| e35a703b76 | |||
| 35af4c5ff1 | |||
| bef0369ff4 | |||
| e41c09ac8c | |||
| e0fb44090b | |||
| 7f66a3eeb2 | |||
| 16e620e07f | |||
| 5d2a618340 | |||
| 583fc5b963 | |||
| 589857b965 | |||
| bd87174bae | |||
| 65c7226e31 | |||
| 5b400552be | |||
| aa6b35cc80 | |||
| 4340f27e17 | |||
| 5158a9a9a7 | |||
| ff6b15fbb2 | |||
| aec9aa9289 | |||
| 283e87146c | |||
| e6c29da777 | |||
| 23a35c2145 | |||
| 41f9449016 | |||
| 78fc4ae4d9 | |||
| c783fd36dc | |||
| 4d3aa08f67 | |||
| c15dce9f92 | |||
| af825cd50b | |||
| 746bb422ea | |||
| 80c438613a | |||
| 73cdbeb632 | |||
| bff14ecfcc | |||
| 358c531e9b | |||
| 671757f4d4 | |||
| aa29140637 | |||
| 5f4e94722b | |||
| 405dffd591 | |||
| ec9b37b33e | |||
| c693bd59f7 | |||
| aeae7904a5 | |||
| 58eebdaa25 | |||
| ec8edf3295 | |||
| e9ed375876 | |||
| b7f75b7bb2 | |||
| fdeaee5f57 | |||
| 70679d135d | |||
| 3897a882f7 | |||
| 786a9dcf5d | |||
| be3ab76bfb | |||
| c7f7b45a15 | |||
| be7aef1f85 | |||
| becf29a36c | |||
| 7c26811c2f | |||
| 5d48fb1784 | |||
| 005baf67d5 | |||
| 8af781fc4d | |||
| 60745265e1 | |||
| d85f9bf6d5 | |||
| af90dcf324 | |||
| 1a2563a950 | |||
| 5558624b43 | |||
| 9c4c16c80d | |||
| 6a9364a518 | |||
| 57645b67fa | |||
| 1bce6b468e | |||
| 153288dc89 | |||
| 28e95830ad | |||
| 67214a9594 | |||
| 27cb7d6a1d | |||
| 47c5db7f77 | |||
| fdeb2c8121 | |||
| 695d0b698a | |||
| 4f07be39b7 | |||
| ec71e555c4 | |||
| 977b6919f2 | |||
| 0c179dd5b4 | |||
| 195611d8e6 | |||
| cc8202b646 | |||
| c7795cfd47 | |||
| b87278f414 | |||
| 5e208f712f | |||
| b15223bce6 | |||
| b383744835 | |||
| c0952c11a7 | |||
| e133a0ec05 | |||
| a24769454e | |||
| 444424dab8 | |||
| 1050f1d2a1 | |||
| e98fa205f8 | |||
| 86d77fd95d | |||
| 038ac625af | |||
| 9b1334b7d3 | |||
| ed86e0a0a7 | |||
| decc1425a3 | |||
| c874d60353 | |||
| d3229ce23e | |||
| b1dcebfea3 | |||
| 26cf4af5d7 | |||
| aa250db66a | |||
| 5e82c06cf8 | |||
| 4178849736 | |||
| cbaef88980 | |||
| 2877c43e6c | |||
| 887f6e5082 | |||
| a08d363f67 | |||
| c1a07d3bab | |||
| ed77e13f05 | |||
| e209d144c3 | |||
| 0bc72d2b91 | |||
| b9894f88b8 | |||
| d192b4441d | |||
| 00d635dda4 | |||
| 98ab970bac | |||
| 1f097a2f29 | |||
| c8c6763782 | |||
| db6f0bef91 | |||
| e3e9bdcd0b | |||
| 8f714e21a0 | |||
| ede8900912 | |||
| e2e696d11f | |||
| 452108b4fc | |||
| 46c46e400a | |||
| 41a7ac637a | |||
| 9c1c6da930 | |||
| 09947d0aad | |||
| 91710673e7 | |||
| 72bb29283f | |||
| 13b059025f | |||
| 502a5c2e26 | |||
| 45fe10d02b | |||
| 9c113744d1 | |||
| 70576de5e0 | |||
| fecad503a9 | |||
| b162c68d62 | |||
| 00c0c5883a | |||
| f52de0fdaf | |||
| ca379859c9 | |||
| f0c701c410 | |||
| b47e1c7866 | |||
| e809d84618 | |||
| 808cf29b55 | |||
| fb546c0bf4 | |||
| 5d800cdb6e | |||
| 0c6b869263 | |||
| 104e225074 | |||
| 3e053c50b4 | |||
| 9a897cb0de | |||
| 33c8e9d233 | |||
| 58b5921ca4 | |||
| 888e14f551 | |||
| 58bf53d573 | |||
| f69084aaf7 | |||
| 8a056d486f | |||
| 859dd4fc2f | |||
| ab12b750de | |||
| b1926192fa | |||
| c7f602497b | |||
| 069029011e | |||
| f326577619 | |||
| 09c2640fe3 | |||
| 75d3b1b5a2 | |||
| 7d207bf483 | |||
| 48a8352a39 | |||
| 93269a9e41 | |||
| caf7e26c4f | |||
| 8ba5d79c35 | |||
| fd022a23a5 | |||
| 1a95091b49 | |||
| fd250c2cb5 | |||
| 6bc4baf313 | |||
| eead0ffef8 | |||
| ea6bccaa44 | |||
| dff3443939 | |||
| 594f5e24c4 | |||
| 9cf8961557 | |||
| 95daf355a7 | |||
| 9fcca3d27c | |||
| 4cfd54256d | |||
| 93853a4846 | |||
| e94e132994 | |||
| f7a164f345 | |||
| 3f3dc2f5d8 | |||
| beb3fb6904 | |||
| 5e22dfb6a5 | |||
| 5028dd89ab | |||
| a1016b4275 | |||
| 9095063e14 | |||
| 7286c59896 | |||
| 2b57197066 | |||
| 7725370ff4 | |||
| 8c7ee455ff | |||
| aef4fbf203 | |||
| 9a184daad3 | |||
| 3ba6914121 | |||
| a757026c5e | |||
| 02e683e467 | |||
| 44f53b9647 | |||
| 0f200bc580 | |||
| 59b8e666f6 | |||
| 440e696d5e | |||
| 0c39b13e46 | |||
| cbf4a0fa8e | |||
| 5326953e57 | |||
| 3f874b58fb | |||
| 4bca124845 | |||
| f02cc9a324 | |||
| 039d494d5f | |||
| 322a325703 | |||
| b715fe9d80 | |||
| 3d2a559ecb | |||
| 27553579c0 | |||
| 24730de8d1 | |||
| 9125d5ab6c | |||
| c76a8862a7 | |||
| 5202b7ca75 | |||
| 7d70571142 | |||
| 479cfd5af3 | |||
| 2ecadd8869 | |||
| 2159292ce0 | |||
| 5d03b1610f | |||
| 61a5b6e3bc | |||
| b2ee272075 | |||
| 21f22f9088 | |||
| 330e36c2c6 | |||
| 4e0c157d02 | |||
| 89f07b1276 | |||
| 028acd974f | |||
| 569700a72e | |||
| eee8687a9f | |||
| 7a2280fe65 | |||
| c8f4fec2de | |||
| bfe6dd0100 | |||
| 3068d6622e | |||
| 35325a37ce | |||
| 3a51f8c307 | |||
| d6f5aa824e | |||
| 51d7869096 | |||
| 0b3791b537 | |||
| 0b518b8d65 | |||
| f9bfc32b73 | |||
| 93b3c1f398 | |||
| 5e4d77fa88 | |||
| d1fbaa071b | |||
| 55f318e53f | |||
| 96cb7007a9 | |||
| 50834d8b74 | |||
| 8376ae7295 | |||
| 7a816e771a | |||
| eafb4418a0 | |||
| cd302a5e32 | |||
| 47079b19df | |||
| 37fd50df1f | |||
| 780bc4c557 | |||
| 9246c85b61 | |||
| 6a9be2cbc7 | |||
| d05bf0fe61 | |||
| aed5abcc0f | |||
| 0649a1a05d | |||
| b0dd9d427f | |||
| 3f47e7551c | |||
| d1db37a2f3 | |||
| a5b1f34125 | |||
| d69e6e6efb | |||
| cb1d6b997e | |||
| 263a8e8c0a | |||
| dec4cea24c | |||
| 2cccced140 | |||
| 55e0fd4a9d | |||
| 24433bea19 | |||
| 99d4a2db46 | |||
| ed640943c4 | |||
| 2bf2ff605f | |||
| 039081123c | |||
| 8d67d501e3 | |||
| 1ff2903785 | |||
| d703d25a35 | |||
| a34f27daed | |||
| 24873723f4 | |||
| da6a42a921 | |||
| d62c464541 | |||
| aa1b2a80ee | |||
| 8449dacbd4 | |||
| d0ae50411f | |||
| 28d46cce0b | |||
| 38d1db9b79 | |||
| 0dac350f3c | |||
| e393f6eff0 | |||
| eadabc6513 | |||
| bb65feb869 | |||
| 88af76f62d | |||
| 100bc4429a | |||
| bee3213145 | |||
| 92bf6fd4f6 | |||
| 50c61825fc | |||
| a02d6d8237 | |||
| 8ba619b5f5 | |||
| f4449dc0ba | |||
| d655290c99 | |||
| bb9549e0be | |||
| d51cefd1bb | |||
| bc6d949aa3 | |||
| cc336321f8 | |||
| d41d454fc1 | |||
| 0f0e5700ba | |||
| 01bdff7a1c | |||
| 81602595a0 | |||
| 5da49ff562 | |||
| a5512fb179 | |||
| fc158d3870 | |||
| 9f6c0d0027 | |||
| 9faa3bbdbb | |||
| b3b64179df | |||
| a17b04f840 | |||
| b184c91076 | |||
| b92669db73 | |||
| 8a1104940d | |||
| d23f210377 | |||
| 66422b57f9 | |||
| 6788c7b7c0 | |||
| c0b6112d04 | |||
| 7994d9dab7 | |||
| 5d3cc0c09b | |||
| cc9380f68d | |||
| 4c40b7b819 | |||
| bc532ae15f | |||
| 50ba4e3efd | |||
| afef7fc136 | |||
| bae91082dd | |||
| 7d0103bb06 | |||
| 68c88ea04f | |||
| ff96718b52 | |||
| 3e1e7aad3d | |||
| 59f13119cf | |||
| 26cd8bc1af | |||
| 45d7562843 | |||
| 3748b7329f | |||
| efd0cf87df | |||
| 728650ecb3 | |||
| e68899f1e4 | |||
| 85ca6d7ace | |||
| 1790ccb98b | |||
| 6aa5a42beb | |||
| 7623795018 | |||
| aa4356da68 | |||
| 930e4167bd | |||
| 521f20e3d3 | |||
| c9f240cea4 | |||
| 1b3c6e727e | |||
| 0478d1d88a | |||
| b884158434 | |||
| 841e596ad0 | |||
| bc2b65832e | |||
| 5270d7424d | |||
| 37b188fbca | |||
| c8885d42fe | |||
| c27bda0b79 | |||
| fecdff4354 | |||
| 453be55b85 | |||
| 17c20bb15e | |||
| 9ee9444a0a | |||
| 42d042e602 | |||
| 3f073f9ef8 | |||
| aebeabacff | |||
| 002330dc48 | |||
| f8be243fef | |||
| e6d7ae24e3 | |||
| aec0aa83a9 | |||
| a1b0ff1735 | |||
| 24b368da8d | |||
| 4cd3a59a79 | |||
| 07d76f7b95 | |||
| 1bfe92a691 | |||
| 48114d08cf | |||
| dd8b12a6c5 | |||
| 60f41362cf | |||
| 2ddfa6a360 | |||
| ec3e57fa92 | |||
| 500ca334bd | |||
| 0b1a1e8c37 | |||
| a75011a891 | |||
| cd88a74115 | |||
| 9ea7632cca | |||
| 7f5fe1faf3 | |||
| 8fbb335d9f | |||
| 60783f47b2 | |||
| d847c73153 | |||
| 12b198f1a2 | |||
| 502c618f01 | |||
| b9561384d0 | |||
| d2b9cdd592 | |||
| cc19bed8b1 | |||
| 077efc2730 | |||
| 7f9dcd664d | |||
| d6c671e626 | |||
| d4170ac0eb | |||
| 71aac8763f | |||
| 3a202be6f1 | |||
| 40ee72947e | |||
| c439b9e0c6 | |||
| f6e04b6e29 | |||
| bcc76bd419 | |||
| f82c8cb0d5 | |||
| 1e24dc987a | |||
| 1be61ad278 | |||
| 8e5cef7bca | |||
| 0587a1a8bb | |||
| 56a410741b | |||
| 2e4d526996 | |||
| f025fc164d | |||
| 733ef7e8e6 | |||
| 0c04f2e576 | |||
| e30ecbfb84 | |||
| d34ce81867 | |||
| d97e0d0e38 | |||
| 16702ec69d | |||
| 997670f997 | |||
| d679f9e5d1 | |||
| 26d71f03fc | |||
| b102d62749 | |||
| 26e136bb9a | |||
| 04b92471f7 | |||
| 6bbc5b3730 | |||
| 7520116441 | |||
| 6e4ff8707e | |||
| 2f764eb4f4 | |||
| 8efe749ab3 | |||
| fd5d18045a | |||
| 4fa5952090 | |||
| 4eda84a456 | |||
| 55be21647e | |||
| c5a5a54c51 | |||
| 1d7c88bf98 | |||
| bc91738e66 | |||
| 25e87ed1ba | |||
| 4246a4bf14 | |||
| dc608d8a5f | |||
| c81dcaabe8 | |||
| 706047470e | |||
| 59714faa68 | |||
| 979715bb45 | |||
| 10ca116b3e | |||
| 63329ce6c7 | |||
| b3d7f1c2db | |||
| f1967a7018 | |||
| 038158e94a | |||
| a3f943942c | |||
| 4e8a0eb4e7 | |||
| 4ffcce88ad | |||
| 179c32cb57 | |||
| 39a7a1a9be | |||
| f96148c4c6 | |||
| e411b69537 | |||
| d8cf688469 | |||
| 29e70da4a4 | |||
| d3b54d624f | |||
| d8091fe979 | |||
| 8eb9c06e65 | |||
| 7d65f1bec0 | |||
| 73d806dc1f | |||
| e32fef1400 | |||
| cc4bfd9623 | |||
| 30bb082d3a | |||
| 36a5f1e90c | |||
| 6abbef89e6 | |||
| 1936a1933c | |||
| 0cbc2143b9 | |||
| 4308b88dcd | |||
| 870fe69c72 | |||
| 39bd6a0d46 | |||
| 4e8e33ba84 | |||
| 583a5cf5d2 | |||
| ddf6b27579 | |||
| 3195b88b79 | |||
| 9d31508a54 | |||
| d294792d1a | |||
| 0503cd6d11 | |||
| b838a229be | |||
| a6ab50bc42 | |||
| 02be13cea6 | |||
| a382d236da | |||
| fe19414331 | |||
| 7fe416fae1 | |||
| 416aeb16ee | |||
| 2492b6c0fb | |||
| 8e2fd6eae3 | |||
| 1cb4e2e196 | |||
| 2c6c98dbf0 | |||
| a03be7987b | |||
| 653882ed8c | |||
| 60fd5c34ab | |||
| 9c9b33cbaf | |||
| 32a5feb53e | |||
| 010bd47b98 | |||
| ea1b2fa034 | |||
| 73ab708b5c | |||
| 89efa89778 | |||
| 89caab8147 | |||
| e680b6dcf7 | |||
| aeb064c069 | |||
| ff4e771c0f | |||
| 4042d2b424 | |||
| 307db965c6 | |||
| b579db6d5c | |||
| eb4455e4e4 | |||
| fa746af4a3 | |||
| b8d689b666 | |||
| 5e65cd1caa | |||
| fd0791587b | |||
| ea51c09a8b | |||
| 273c2a9425 | |||
| 59b6a2bcbe | |||
| 83be5586a0 | |||
| 4f3dc93978 | |||
| fb74019a14 | |||
| 69bc8c70f9 | |||
| 26a3e56649 | |||
| 61791c6f4c | |||
| 6b429dc11b | |||
| 78b404d976 | |||
| 5ca40d84ad | |||
| 418d212308 | |||
| 0c7bcf4c0e | |||
| 161f742856 | |||
| 28f4e7b286 | |||
| 0051a56dec | |||
| 5af043c2e7 | |||
| d733ee5cca | |||
| 88a99437b9 | |||
| 5fbb2adf7d | |||
| d9be387361 | |||
| bbd0ae2e87 | |||
| addc7e0e5b | |||
| c42bbfc550 | |||
| 6d841fdc89 | |||
| 28f32bedd1 | |||
| 4e73863fd4 | |||
| 77eb3e305c | |||
| ed96436f23 | |||
| 3e49355e17 | |||
| e030619109 | |||
| fb18d81038 | |||
| cad6aae45e | |||
| 0498caec89 | |||
| d2bef86709 | |||
| 88813681c2 | |||
| 6242c9386f | |||
| 11d5efde75 | |||
| 51f0c762f0 | |||
| 9023444fea | |||
| ee7c667a36 | |||
| 101a6b87c7 | |||
| 67ae593ffe | |||
| 5b6c3c8f7d | |||
| c6effdbfba | |||
| b15c63cbb9 | |||
| aecd7f9d75 | |||
| 8aa22ffe98 |
+40
-2
@@ -599,7 +599,7 @@ SHELL_OPT += -DSQLITE_ENABLE_DBPAGE_VTAB
|
||||
SHELL_OPT += -DSQLITE_ENABLE_DBSTAT_VTAB
|
||||
SHELL_OPT += -DSQLITE_ENABLE_BYTECODE_VTAB
|
||||
SHELL_OPT += -DSQLITE_ENABLE_OFFSET_SQL_FUNC
|
||||
FUZZERSHELL_OPT =
|
||||
FUZZERSHELL_OPT =
|
||||
FUZZCHECK_OPT += -I$(TOP)/test
|
||||
FUZZCHECK_OPT += -I$(TOP)/ext/recover
|
||||
FUZZCHECK_OPT += \
|
||||
@@ -690,6 +690,12 @@ srcck1$(BEXE): $(TOP)/tool/srcck1.c
|
||||
sourcetest: srcck1$(BEXE) sqlite3.c
|
||||
./srcck1 sqlite3.c
|
||||
|
||||
src-verify: $(TOP)/tool/src-verify.c
|
||||
$(BCC) -o src-verify$(BEXE) $(TOP)/tool/src-verify.c
|
||||
|
||||
verify-source: ./src-verify
|
||||
./src-verify $(TOP)
|
||||
|
||||
fuzzershell$(TEXE): $(TOP)/tool/fuzzershell.c sqlite3.c sqlite3.h
|
||||
$(LTLINK) -o $@ $(FUZZERSHELL_OPT) \
|
||||
$(TOP)/tool/fuzzershell.c sqlite3.c $(TLIBS)
|
||||
@@ -775,7 +781,7 @@ mptest: mptester$(TEXE)
|
||||
cp fts5.c fts5.h tsrc
|
||||
touch .target_source
|
||||
|
||||
sqlite3.c: .target_source $(TOP)/tool/mksqlite3c.tcl
|
||||
sqlite3.c: .target_source $(TOP)/tool/mksqlite3c.tcl src-verify
|
||||
$(TCLSH_CMD) $(TOP)/tool/mksqlite3c.tcl $(AMALGAMATION_LINE_MACROS)
|
||||
cp tsrc/sqlite3ext.h .
|
||||
cp $(TOP)/ext/session/sqlite3session.h .
|
||||
@@ -1133,6 +1139,7 @@ SHELL_SRC = \
|
||||
$(TOP)/ext/expert/sqlite3expert.h \
|
||||
$(TOP)/ext/misc/zipfile.c \
|
||||
$(TOP)/ext/misc/memtrace.c \
|
||||
$(TOP)/ext/misc/pcachetrace.c \
|
||||
$(TOP)/ext/recover/dbdata.c \
|
||||
$(TOP)/ext/recover/sqlite3recover.c \
|
||||
$(TOP)/ext/recover/sqlite3recover.h \
|
||||
@@ -1307,6 +1314,9 @@ testrunner: testfixture$(TEXE)
|
||||
#
|
||||
devtest: testfixture$(TEXE) fuzztest testrunner
|
||||
|
||||
mdevtest:
|
||||
$(TCLSH_CMD) $(TOP)/test/testrunner.tcl mdevtest
|
||||
|
||||
# Testing for a release
|
||||
#
|
||||
releasetest: testfixture$(TEXE)
|
||||
@@ -1506,6 +1516,7 @@ clean:
|
||||
rm -f LogEst$(TEXE) fts3view$(TEXE) rollback-test$(TEXE) showdb$(TEXE)
|
||||
rm -f showjournal$(TEXE) showstat4$(TEXE) showwal$(TEXE) speedtest1$(TEXE)
|
||||
rm -f wordcount$(TEXE) changeset$(TEXE)
|
||||
rm -f version-info$(TEXT)
|
||||
rm -f sqlite3.dll sqlite3.lib sqlite3.exp sqlite3.def
|
||||
rm -f sqlite3.c
|
||||
rm -f sqlite3rc.h
|
||||
@@ -1521,6 +1532,8 @@ clean:
|
||||
rm -f dbhash dbhash.exe
|
||||
rm -f fts5.* fts5parse.*
|
||||
rm -f threadtest5
|
||||
rm -f src-verify
|
||||
rm -f custom.rws
|
||||
|
||||
distclean: clean
|
||||
rm -f sqlite_cfg.h config.log config.status libtool Makefile sqlite3.pc \
|
||||
@@ -1549,3 +1562,28 @@ sqlite3.dll: $(REAL_LIBOBJ) sqlite3.def
|
||||
#
|
||||
fiddle: sqlite3.c shell.c
|
||||
make -C ext/wasm fiddle emcc_opt=-Os
|
||||
|
||||
#
|
||||
# Spell-checking for source comments
|
||||
# The sources checked are either C sources or C source templates.
|
||||
# Their comments are extracted and processed through aspell using
|
||||
# a custom dictionary that contains scads of odd identifiers that
|
||||
# find their way into the comments.
|
||||
#
|
||||
# Currently, this target is setup to be "made" in-tree only.
|
||||
# The output is ephemeral. Redirect it to guide spelling fixups,
|
||||
# either to correct spelling or add words to tool/custom.txt.
|
||||
#
|
||||
./custom.rws: ./tool/custom.txt
|
||||
@echo 'Updating custom dictionary from tool/custom.txt'
|
||||
aspell --lang=en create master ./custom.rws < $<
|
||||
|
||||
misspell: ./custom.rws
|
||||
$(TCLSH_CMD) ./tool/spellsift.tcl ./src/*.c ./src/*.h ./src/*.in
|
||||
|
||||
#
|
||||
# tool/version-info: a utility for emitting sqlite3 version info
|
||||
# in various forms.
|
||||
#
|
||||
version-info$(TEXE): $(TOP)/tool/version-info.c Makefile sqlite3.h
|
||||
$(LTLINK) $(ST_OPT) -o $@ $(TOP)/tool/version-info.c
|
||||
|
||||
@@ -22,12 +22,6 @@ TOP = ../sqlite
|
||||
BCC = gcc -g -O0
|
||||
#BCC = /opt/ancic/bin/c89 -0
|
||||
|
||||
#### If the target operating system supports the "usleep()" system
|
||||
# call, then define the HAVE_USLEEP macro for all C modules.
|
||||
#
|
||||
#USLEEP =
|
||||
USLEEP = -DHAVE_USLEEP=1
|
||||
|
||||
#### If you want the SQLite library to be safe for use within a
|
||||
# multi-threaded program, then define the following macro
|
||||
# appropriately:
|
||||
|
||||
+40
-15
@@ -52,6 +52,13 @@ MINIMAL_AMALGAMATION = 0
|
||||
USE_STDCALL = 0
|
||||
!ENDIF
|
||||
|
||||
# Set this non-0 to use structured exception handling (SEH) for WAL mode
|
||||
# in the core library.
|
||||
#
|
||||
!IFNDEF USE_SEH
|
||||
USE_SEH = 1
|
||||
!ENDIF
|
||||
|
||||
# Set this non-0 to have the shell executable link against the core dynamic
|
||||
# link library.
|
||||
#
|
||||
@@ -389,6 +396,13 @@ OPT_FEATURE_FLAGS = $(OPT_FEATURE_FLAGS) -DSQLITE_ENABLE_MATH_FUNCTIONS
|
||||
OPT_FEATURE_FLAGS = $(OPT_FEATURE_FLAGS) -DSQLITE_ENABLE_RBU=1
|
||||
!ENDIF
|
||||
|
||||
# Should structured exception handling (SEH) be enabled for WAL mode in
|
||||
# the core library?
|
||||
#
|
||||
!IF $(USE_SEH)!=0
|
||||
OPT_FEATURE_FLAGS = $(OPT_FEATURE_FLAGS) -DSQLITE_USE_SEH=1
|
||||
!ENDIF
|
||||
|
||||
# These are the "extended" SQLite compilation options used when compiling for
|
||||
# the Windows 10 platform.
|
||||
#
|
||||
@@ -1807,6 +1821,12 @@ srcck1.exe: $(TOP)\tool\srcck1.c
|
||||
sourcetest: srcck1.exe $(SQLITE3C)
|
||||
srcck1.exe $(SQLITE3C)
|
||||
|
||||
src-verify.exe: $(TOP)\tool\src-verify.c
|
||||
$(LTLINK) $(NO_WARN) $(TOP)\tool\src-verify.c
|
||||
|
||||
verify-source: src-verify.exe
|
||||
src-verify.exe $(TOP)
|
||||
|
||||
fuzzershell.exe: $(TOP)\tool\fuzzershell.c $(SQLITE3C) $(SQLITE3H)
|
||||
$(LTLINK) $(NO_WARN) $(FUZZERSHELL_COMPILE_OPTS) $(TOP)\tool\fuzzershell.c $(SQLITE3C) /link $(LDFLAGS) $(LTLINKOPTS)
|
||||
|
||||
@@ -1814,7 +1834,7 @@ dbfuzz.exe: $(TOP)\test\dbfuzz.c $(SQLITE3C) $(SQLITE3H)
|
||||
$(LTLINK) $(NO_WARN) $(DBFUZZ_COMPILE_OPTS) $(TOP)\test\dbfuzz.c $(SQLITE3C) /link $(LDFLAGS) $(LTLINKOPTS)
|
||||
|
||||
fuzzcheck.exe: $(FUZZCHECK_SRC) $(SQLITE3C) $(SQLITE3H)
|
||||
$(LTLINK) $(NO_WARN) $(FUZZCHECK_OPTS) $(FUZZCHECK_SRC) $(SQLITE3C) /link $(LDFLAGS) $(LTLINKOPTS)
|
||||
$(LTLINK) /F 8388608 $(NO_WARN) $(FUZZCHECK_OPTS) $(FUZZCHECK_SRC) $(SQLITE3C) /link $(LDFLAGS) $(LTLINKOPTS)
|
||||
|
||||
fuzzcheck-asan.exe: $(FUZZCHECK_SRC) $(SQLITE3C) $(SQLITE3H)
|
||||
$(LTLINK) $(NO_WARN) /fsanitize=address $(FUZZCHECK_OPTS) $(FUZZCHECK_SRC) $(SQLITE3C) /link $(LDFLAGS) $(LTLINKOPTS)
|
||||
@@ -1870,7 +1890,7 @@ mptest: mptester.exe
|
||||
move vdbe.new tsrc\vdbe.c
|
||||
echo > .target_source
|
||||
|
||||
sqlite3.c: .target_source sqlite3ext.h sqlite3session.h $(MKSQLITE3C_TOOL)
|
||||
sqlite3.c: .target_source sqlite3ext.h sqlite3session.h $(MKSQLITE3C_TOOL) src-verify.exe
|
||||
$(TCLSH_CMD) $(MKSQLITE3C_TOOL) $(MKSQLITE3C_ARGS)
|
||||
|
||||
sqlite3-all.c: sqlite3.c $(TOP)\tool\split-sqlite3c.tcl
|
||||
@@ -2226,8 +2246,8 @@ SHELL_SRC = \
|
||||
$(TOP)\src\shell.c.in \
|
||||
$(TOP)\ext\misc\appendvfs.c \
|
||||
$(TOP)\ext\misc\completion.c \
|
||||
$(TOP)\ext\misc\base64.c \
|
||||
$(TOP)\ext\misc\base85.c \
|
||||
$(TOP)\ext\misc\base64.c \
|
||||
$(TOP)\ext\misc\base85.c \
|
||||
$(TOP)\ext\misc\decimal.c \
|
||||
$(TOP)\ext\misc\fileio.c \
|
||||
$(TOP)\ext\misc\ieee754.c \
|
||||
@@ -2238,9 +2258,10 @@ SHELL_SRC = \
|
||||
$(TOP)\ext\expert\sqlite3expert.c \
|
||||
$(TOP)\ext\expert\sqlite3expert.h \
|
||||
$(TOP)\ext\misc\memtrace.c \
|
||||
$(TOP)/ext/recover/dbdata.c \
|
||||
$(TOP)/ext/recover/sqlite3recover.c \
|
||||
$(TOP)/ext/recover/sqlite3recover.h \
|
||||
$(TOP)\ext\misc\pcachetrace.c \
|
||||
$(TOP)\ext\recover\dbdata.c \
|
||||
$(TOP)\ext\recover\sqlite3recover.c \
|
||||
$(TOP)\ext\recover\sqlite3recover.h \
|
||||
$(TOP)\src\test_windirent.c
|
||||
|
||||
# If use of zlib is enabled, add the "zipfile.c" source file.
|
||||
@@ -2484,10 +2505,13 @@ testrunner: testfixture.exe
|
||||
#
|
||||
devtest: testfixture.exe fuzztest testrunner
|
||||
|
||||
mdevtest:
|
||||
$(TCLSH_CMD) $(TOP)\test\testrunner.tcl mdevtest
|
||||
|
||||
# Testing for a release
|
||||
#
|
||||
releasetest: testfixture.exe fuzztest
|
||||
testfixture.exe $(TOP)/test/testrunner.tcl release
|
||||
testfixture.exe $(TOP)\test\testrunner.tcl release
|
||||
|
||||
|
||||
smoketest: $(TESTPROGS)
|
||||
@@ -2515,14 +2539,14 @@ sqlite3_expert.exe: $(SQLITE3C) $(TOP)\ext\expert\sqlite3expert.h $(TOP)\ext\exp
|
||||
$(LTLINK) $(NO_WARN) $(TOP)\ext\expert\sqlite3expert.c $(TOP)\ext\expert\expert.c $(SQLITE3C) $(TLIBS)
|
||||
|
||||
CHECKER_DEPS =\
|
||||
$(TOP)/tool/mkccode.tcl \
|
||||
$(TOP)\tool\mkccode.tcl \
|
||||
sqlite3.c \
|
||||
$(TOP)/src/tclsqlite.c \
|
||||
$(TOP)/ext/repair/sqlite3_checker.tcl \
|
||||
$(TOP)/ext/repair/checkindex.c \
|
||||
$(TOP)/ext/repair/checkfreelist.c \
|
||||
$(TOP)/ext/misc/btreeinfo.c \
|
||||
$(TOP)/ext/repair/sqlite3_checker.c.in
|
||||
$(TOP)\src\tclsqlite.c \
|
||||
$(TOP)\ext\repair\sqlite3_checker.tcl \
|
||||
$(TOP)\ext\repair\checkindex.c \
|
||||
$(TOP)\ext\repair\checkfreelist.c \
|
||||
$(TOP)\ext\misc\btreeinfo.c \
|
||||
$(TOP)\ext\repair\sqlite3_checker.c.in
|
||||
|
||||
sqlite3_checker.c: $(CHECKER_DEPS)
|
||||
$(TCLSH_CMD) $(TOP)\tool\mkccode.tcl $(TOP)\ext\repair\sqlite3_checker.c.in > $@
|
||||
@@ -2661,4 +2685,5 @@ clean:
|
||||
del /Q showshm.exe sqlite3_checker.* sqlite3_expert.exe 2>NUL
|
||||
del /Q fts5.* fts5parse.* 2>NUL
|
||||
del /Q lsm.h lsm1.c 2>NUL
|
||||
del /q src-verify.exe 2>NUL
|
||||
# <</mark>>
|
||||
|
||||
@@ -25,7 +25,29 @@ If you pulled your SQLite source code from a secondary source and want to
|
||||
verify its integrity, there are hints on how to do that in the
|
||||
[Verifying Code Authenticity](#vauth) section below.
|
||||
|
||||
## Obtaining The Code
|
||||
## Contacting The SQLite Developers
|
||||
|
||||
The preferred way to ask questions or make comments about SQLite or to
|
||||
report bugs against SQLite is to visit the
|
||||
[SQLite Forum](https://sqlite.org/forum) at <https://sqlite.org/forum/>.
|
||||
Anonymous postings are permitted.
|
||||
|
||||
If you think you have found a bug that has security implications and
|
||||
you do not want to report it on the public forum, you can send a private
|
||||
email to drh at sqlite dot org.
|
||||
|
||||
## Public Domain
|
||||
|
||||
The SQLite source code is in the public domain. See
|
||||
<https://sqlite.org/copyright.html> for details.
|
||||
|
||||
Because SQLite is in the public domain,
|
||||
we cannot accept pull requests, because
|
||||
if we did accept a pull request, the changes in that pull request would
|
||||
carry a copyright and the SQLite source code would no longer be fully in
|
||||
the public domain.
|
||||
|
||||
## Obtaining The SQLite Source Code
|
||||
|
||||
If you do not want to use Fossil, you can download tarballs or ZIP
|
||||
archives or [SQLite archives](https://sqlite.org/cli.html#sqlar) as follows:
|
||||
@@ -82,9 +104,9 @@ For example:
|
||||
mkdir bld ;# Build will occur in a sibling directory
|
||||
cd bld ;# Change to the build directory
|
||||
../sqlite/configure ;# Run the configure script
|
||||
make ;# Run the makefile.
|
||||
make ;# Builds the "sqlite3" command-line tool
|
||||
make sqlite3.c ;# Build the "amalgamation" source file
|
||||
make test ;# Run some tests (requires Tcl)
|
||||
make devtest ;# Run some tests (requires Tcl)
|
||||
|
||||
See the makefile for additional targets.
|
||||
|
||||
@@ -97,29 +119,30 @@ show what changes are needed.
|
||||
## Using MSVC for Windows systems
|
||||
|
||||
On Windows, all applicable build products can be compiled with MSVC.
|
||||
First open the command prompt window associated with the desired compiler
|
||||
version (e.g. "Developer Command Prompt for VS2013"). Next, use NMAKE
|
||||
with the provided "Makefile.msc" to build one of the supported targets.
|
||||
You will also need a working installation of TCL.
|
||||
See the [compile-for-windows.md](doc/compile-for-windows.md) document for
|
||||
additional information about how to install MSVC and TCL and configure your
|
||||
build environment.
|
||||
|
||||
For example, from the parent directory of the source subtree named "sqlite":
|
||||
If you want to run tests, you need to let SQLite know the location of your
|
||||
TCL library, using a command like this:
|
||||
|
||||
mkdir bld
|
||||
cd bld
|
||||
nmake /f ..\sqlite\Makefile.msc TOP=..\sqlite
|
||||
nmake /f ..\sqlite\Makefile.msc sqlite3.c TOP=..\sqlite
|
||||
nmake /f ..\sqlite\Makefile.msc sqlite3.dll TOP=..\sqlite
|
||||
nmake /f ..\sqlite\Makefile.msc sqlite3.exe TOP=..\sqlite
|
||||
nmake /f ..\sqlite\Makefile.msc test TOP=..\sqlite
|
||||
set TCLDIR=c:\Tcl
|
||||
|
||||
There are several build options that can be set via the NMAKE command
|
||||
line. For example, to build for WinRT, simply add "FOR_WINRT=1" argument
|
||||
to the "sqlite3.dll" command line above. When debugging into the SQLite
|
||||
code, adding the "DEBUG=1" argument to one of the above command lines is
|
||||
recommended.
|
||||
SQLite uses "tclsh.exe" as part of the build process, and so that utility
|
||||
program will need to be somewhere on your %PATH%. The finished SQLite library
|
||||
does not contain any TCL code, but it does use TCL to help with the build process
|
||||
and to run tests.
|
||||
|
||||
SQLite does not require [Tcl](http://www.tcl.tk/) to run, but a Tcl installation
|
||||
is required by the makefiles (including those for MSVC). SQLite contains
|
||||
a lot of generated code and Tcl is used to do much of that code generation.
|
||||
Build using Makefile.msc. Example:
|
||||
|
||||
nmake /f Makefile.msc
|
||||
nmake /f Makefile.msc sqlite3.c
|
||||
nmake /f Makefile.msc devtest
|
||||
nmake /f Makefile.msc releasetest
|
||||
|
||||
There are many other makefile targets. See comments in Makefile.msc for
|
||||
details.
|
||||
|
||||
## Source Code Tour
|
||||
|
||||
@@ -290,16 +313,13 @@ Key files:
|
||||
is not part of the core SQLite library. But as most of the tests in this
|
||||
repository are written in Tcl, the Tcl language bindings are important.
|
||||
|
||||
* **test*.c** - Files in the src/ folder that begin with "test" go into
|
||||
* **test\*.c** - Files in the src/ folder that begin with "test" go into
|
||||
building the "testfixture.exe" program. The testfixture.exe program is
|
||||
an enhanced Tcl shell. The testfixture.exe program runs scripts in the
|
||||
test/ folder to validate the core SQLite code. The testfixture program
|
||||
(and some other test programs too) is built and run when you type
|
||||
"make test".
|
||||
|
||||
* **ext/misc/json1.c** - This file implements the various JSON functions
|
||||
that are built into SQLite.
|
||||
|
||||
There are many other source files. Each has a succinct header comment that
|
||||
describes its purpose and role within the larger system.
|
||||
|
||||
@@ -307,8 +327,8 @@ describes its purpose and role within the larger system.
|
||||
## Verifying Code Authenticity
|
||||
|
||||
The `manifest` file at the root directory of the source tree
|
||||
contains either a SHA3-256 hash (for newer files) or a SHA1 hash (for
|
||||
older files) for every source file in the repository.
|
||||
contains either a SHA3-256 hash or a SHA1 hash
|
||||
for every source file in the repository.
|
||||
The name of the version of the entire source tree is just the
|
||||
SHA3-256 hash of the `manifest` file itself, possibly with the
|
||||
last line of that file omitted if the last line begins with
|
||||
@@ -316,14 +336,25 @@ last line of that file omitted if the last line begins with
|
||||
The `manifest.uuid` file should contain the SHA3-256 hash of the
|
||||
`manifest` file. If all of the above hash comparisons are correct, then
|
||||
you can be confident that your source tree is authentic and unadulterated.
|
||||
Details on the format for the `manifest` files are available
|
||||
[on the Fossil website](https://fossil-scm.org/fossil/doc/trunk/www/fileformat.wiki#manifest).
|
||||
|
||||
The format of the `manifest` file should be mostly self-explanatory, but
|
||||
if you want details, they are available
|
||||
[here](https://fossil-scm.org/fossil/doc/trunk/www/fileformat.wiki#manifest).
|
||||
The process of checking source code authenticity is automated by the
|
||||
makefile:
|
||||
|
||||
> make verify-source
|
||||
|
||||
Or on windows:
|
||||
|
||||
> nmake /f Makefile.msc verify-source
|
||||
|
||||
Using the makefile to verify source integrity is good for detecting
|
||||
accidental changes to the source tree, but malicious changes could be
|
||||
hidden by also modifying the makefiles.
|
||||
|
||||
## Contacts
|
||||
|
||||
The main SQLite website is [http://www.sqlite.org/](http://www.sqlite.org/)
|
||||
The main SQLite website is [http:/sqlite.org/](http://sqlite.org/)
|
||||
with geographically distributed backups at
|
||||
[http://www2.sqlite.org/](http://www2.sqlite.org) and
|
||||
[http://www3.sqlite.org/](http://www3.sqlite.org).
|
||||
|
||||
@@ -9,7 +9,7 @@ sqlite3_SOURCES = shell.c sqlite3.h
|
||||
EXTRA_sqlite3_SOURCES = sqlite3.c
|
||||
sqlite3_LDADD = @EXTRA_SHELL_OBJ@ @READLINE_LIBS@
|
||||
sqlite3_DEPENDENCIES = @EXTRA_SHELL_OBJ@
|
||||
sqlite3_CFLAGS = $(AM_CFLAGS) -DSQLITE_ENABLE_EXPLAIN_COMMENTS -DSQLITE_ENABLE_DBPAGE_VTAB -DSQLITE_ENABLE_STMTVTAB -DSQLITE_ENABLE_DBSTAT_VTAB $(SHELL_CFLAGS)
|
||||
sqlite3_CFLAGS = $(AM_CFLAGS) -DSQLITE_ENABLE_EXPLAIN_COMMENTS -DSQLITE_DQS=0 -DSQLITE_ENABLE_DBPAGE_VTAB -DSQLITE_ENABLE_STMTVTAB -DSQLITE_ENABLE_DBSTAT_VTAB $(SHELL_CFLAGS)
|
||||
|
||||
include_HEADERS = sqlite3.h sqlite3ext.h
|
||||
|
||||
|
||||
@@ -52,6 +52,13 @@ MINIMAL_AMALGAMATION = 0
|
||||
USE_STDCALL = 0
|
||||
!ENDIF
|
||||
|
||||
# Set this non-0 to use structured exception handling (SEH) for WAL mode
|
||||
# in the core library.
|
||||
#
|
||||
!IFNDEF USE_SEH
|
||||
USE_SEH = 1
|
||||
!ENDIF
|
||||
|
||||
# Set this non-0 to have the shell executable link against the core dynamic
|
||||
# link library.
|
||||
#
|
||||
@@ -311,6 +318,13 @@ OPT_FEATURE_FLAGS = $(OPT_FEATURE_FLAGS) -DSQLITE_ENABLE_MATH_FUNCTIONS
|
||||
OPT_FEATURE_FLAGS = $(OPT_FEATURE_FLAGS) -DSQLITE_ENABLE_RBU=1
|
||||
!ENDIF
|
||||
|
||||
# Should structured exception handling (SEH) be enabled for WAL mode in
|
||||
# the core library?
|
||||
#
|
||||
!IF $(USE_SEH)!=0
|
||||
OPT_FEATURE_FLAGS = $(OPT_FEATURE_FLAGS) -DSQLITE_USE_SEH=1
|
||||
!ENDIF
|
||||
|
||||
# These are the "extended" SQLite compilation options used when compiling for
|
||||
# the Windows 10 platform.
|
||||
#
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#! /bin/sh
|
||||
# Guess values for system-dependent variables and create Makefiles.
|
||||
# Generated by GNU Autoconf 2.69 for sqlite 3.42.0.
|
||||
# Generated by GNU Autoconf 2.69 for sqlite 3.43.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.42.0'
|
||||
PACKAGE_STRING='sqlite 3.42.0'
|
||||
PACKAGE_VERSION='3.43.0'
|
||||
PACKAGE_STRING='sqlite 3.43.0'
|
||||
PACKAGE_BUGREPORT=''
|
||||
PACKAGE_URL=''
|
||||
|
||||
@@ -1470,7 +1470,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.42.0 to adapt to many kinds of systems.
|
||||
\`configure' configures sqlite 3.43.0 to adapt to many kinds of systems.
|
||||
|
||||
Usage: $0 [OPTION]... [VAR=VALUE]...
|
||||
|
||||
@@ -1535,7 +1535,7 @@ fi
|
||||
|
||||
if test -n "$ac_init_help"; then
|
||||
case $ac_init_help in
|
||||
short | recursive ) echo "Configuration of sqlite 3.42.0:";;
|
||||
short | recursive ) echo "Configuration of sqlite 3.43.0:";;
|
||||
esac
|
||||
cat <<\_ACEOF
|
||||
|
||||
@@ -1665,7 +1665,7 @@ fi
|
||||
test -n "$ac_init_help" && exit $ac_status
|
||||
if $ac_init_version; then
|
||||
cat <<\_ACEOF
|
||||
sqlite configure 3.42.0
|
||||
sqlite configure 3.43.0
|
||||
generated by GNU Autoconf 2.69
|
||||
|
||||
Copyright (C) 2012 Free Software Foundation, Inc.
|
||||
@@ -2084,7 +2084,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.42.0, which was
|
||||
It was created by sqlite $as_me 3.43.0, which was
|
||||
generated by GNU Autoconf 2.69. Invocation command line was
|
||||
|
||||
$ $0 $@
|
||||
@@ -12457,7 +12457,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.42.0, which was
|
||||
This file was extended by sqlite $as_me 3.43.0, which was
|
||||
generated by GNU Autoconf 2.69. Invocation command line was
|
||||
|
||||
CONFIG_FILES = $CONFIG_FILES
|
||||
@@ -12523,7 +12523,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.42.0
|
||||
sqlite config.status 3.43.0
|
||||
configured by $0, generated by GNU Autoconf 2.69,
|
||||
with options \\"\$ac_cs_config\\"
|
||||
|
||||
|
||||
@@ -0,0 +1,62 @@
|
||||
# Notes On Compiling SQLite On Windows 11
|
||||
|
||||
Here are step-by-step instructions on how to build SQLite from
|
||||
canonical source on a new Windows 11 PC, as of 2023-08-16:
|
||||
|
||||
1. Install Microsoft Visual Studio. The free "community edition"
|
||||
will work fine. Do a standard install for C++ development.
|
||||
SQLite only needs the
|
||||
"cl" compiler and the "nmake" build tool.
|
||||
|
||||
2. Under the "Start" menu, find "All Apps" then go to "Visual Studio 20XX"
|
||||
and find "x64 Native Tools Command Prompt for VS 20XX". Pin that
|
||||
application to your task bar, as you will use it a lot. Bring up
|
||||
an instance of this command prompt and do all of the subsequent steps
|
||||
in that "x64 Native Tools" command prompt. (Or use "x86" if you want
|
||||
a 32-bit build.) The subsequent steps will not work in a vanilla
|
||||
DOS prompt. Nor will they work in PowerShell.
|
||||
|
||||
3. Install TCL development libraries. This note assumes that you wil
|
||||
install the TCL development libraries in the "`c:\Tcl`" directory.
|
||||
Make adjustments
|
||||
if you want TCL installed somewhere else. SQLite needs both the
|
||||
"tclsh.exe" command-line tool as part of the build process, and
|
||||
the "tcl86.lib" library in order to run tests. You will need
|
||||
TCL version 8.6 or later.
|
||||
<ol type="a">
|
||||
<li>Get the TCL source archive, perhaps from
|
||||
<https://www.tcl.tk/software/tcltk/download.html>.
|
||||
<li>Untar or unzip the source archive. CD into the "win/" subfolder
|
||||
of the source tree.
|
||||
<li>Run: `nmake /f makefile.vc release`
|
||||
<li>Run: `nmake /f makefile.vc INSTALLDIR=c:\Tcl install`
|
||||
<li>CD to c:\\Tcl\\lib. In that subfolder make a copy of the
|
||||
"`tcl86t.lib`" file to the alternative name "`tcl86.lib`"
|
||||
(omitting the second 't'). Leave the copy in the same directory
|
||||
as the original.
|
||||
<li>CD to c:\\Tcl\\bin. Make a copy of the "`tclsh86t.exe`"
|
||||
file into "`tclsh.exe`" (without the "86t") in the same directory.
|
||||
<li>Add c:\\Tcl\\bin to your %PATH%. To do this, go to Settings
|
||||
and search for "path". Select "edit environment variables for
|
||||
your account" and modify your default PATH accordingly.
|
||||
You will need to close and reopen your command prompts after
|
||||
making this change.
|
||||
</ol>
|
||||
|
||||
4. Download the SQLite source tree and unpack it. CD into the
|
||||
toplevel directory of the source tree.
|
||||
|
||||
5. Set the TCLDIR environment variable to point to your TCL installation.
|
||||
Like this:
|
||||
<ul>
|
||||
<li> `set TCLDIR=c:\Tcl`
|
||||
</ul>
|
||||
|
||||
6. Run the "`Makefile.msc`" makefile with an appropriate target.
|
||||
Examples:
|
||||
<ul>
|
||||
<li> `nmake /f makefile.msc`
|
||||
<li> `nmake /f makefile.msc sqlite3.c`
|
||||
<li> `nmake /f makefile.msc devtest`
|
||||
<li> `nmake /f makefile.msc releasetest`
|
||||
</ul>
|
||||
+1
-1
@@ -322,7 +322,7 @@ These are the steps:
|
||||
<ul>
|
||||
<li> Declare a local variable of type "yyParser"
|
||||
<li> Initialize the variable using ParseInit()
|
||||
<li> Pass a pointer to the variable in calls ot Parse()
|
||||
<li> Pass a pointer to the variable in calls to Parse()
|
||||
<li> Deallocate substructure in the parse variable using ParseFinalize().
|
||||
</ul>
|
||||
|
||||
|
||||
@@ -4347,6 +4347,7 @@ static int fts3IncrmergeLoad(
|
||||
|
||||
for(i=nHeight; i>=0 && rc==SQLITE_OK; i--){
|
||||
NodeReader reader;
|
||||
memset(&reader, 0, sizeof(reader));
|
||||
pNode = &pWriter->aNodeWriter[i];
|
||||
|
||||
if( pNode->block.a){
|
||||
|
||||
+4
-4
@@ -263,7 +263,7 @@ struct Fts5PhraseIter {
|
||||
** See xPhraseFirstColumn above.
|
||||
*/
|
||||
struct Fts5ExtensionApi {
|
||||
int iVersion; /* Currently always set to 3 */
|
||||
int iVersion; /* Currently always set to 2 */
|
||||
|
||||
void *(*xUserData)(Fts5Context*);
|
||||
|
||||
@@ -541,7 +541,7 @@ struct fts5_api {
|
||||
int (*xCreateTokenizer)(
|
||||
fts5_api *pApi,
|
||||
const char *zName,
|
||||
void *pContext,
|
||||
void *pUserData,
|
||||
fts5_tokenizer *pTokenizer,
|
||||
void (*xDestroy)(void*)
|
||||
);
|
||||
@@ -550,7 +550,7 @@ struct fts5_api {
|
||||
int (*xFindTokenizer)(
|
||||
fts5_api *pApi,
|
||||
const char *zName,
|
||||
void **ppContext,
|
||||
void **ppUserData,
|
||||
fts5_tokenizer *pTokenizer
|
||||
);
|
||||
|
||||
@@ -558,7 +558,7 @@ struct fts5_api {
|
||||
int (*xCreateFunction)(
|
||||
fts5_api *pApi,
|
||||
const char *zName,
|
||||
void *pContext,
|
||||
void *pUserData,
|
||||
fts5_extension_function xFunction,
|
||||
void (*xDestroy)(void*)
|
||||
);
|
||||
|
||||
@@ -154,6 +154,10 @@ typedef struct Fts5Config Fts5Config;
|
||||
** attempt to merge together. A value of 1 sets the object to use the
|
||||
** compile time default. Zero disables auto-merge altogether.
|
||||
**
|
||||
** bContentlessDelete:
|
||||
** True if the contentless_delete option was present in the CREATE
|
||||
** VIRTUAL TABLE statement.
|
||||
**
|
||||
** zContent:
|
||||
**
|
||||
** zContentRowid:
|
||||
@@ -188,6 +192,7 @@ struct Fts5Config {
|
||||
int nPrefix; /* Number of prefix indexes */
|
||||
int *aPrefix; /* Sizes in bytes of nPrefix prefix indexes */
|
||||
int eContent; /* An FTS5_CONTENT value */
|
||||
int bContentlessDelete; /* "contentless_delete=" option (dflt==0) */
|
||||
char *zContent; /* content table */
|
||||
char *zContentRowid; /* "content_rowid=" option value */
|
||||
int bColumnsize; /* "columnsize=" option value (dflt==1) */
|
||||
@@ -209,6 +214,7 @@ struct Fts5Config {
|
||||
char *zRank; /* Name of rank function */
|
||||
char *zRankArgs; /* Arguments to rank function */
|
||||
int bSecureDelete; /* 'secure-delete' */
|
||||
int nDeleteMerge; /* 'deletemerge' */
|
||||
|
||||
/* If non-NULL, points to sqlite3_vtab.base.zErrmsg. Often NULL. */
|
||||
char **pzErrmsg;
|
||||
@@ -531,6 +537,9 @@ int sqlite3Fts5IndexReset(Fts5Index *p);
|
||||
|
||||
int sqlite3Fts5IndexLoadConfig(Fts5Index *p);
|
||||
|
||||
int sqlite3Fts5IndexGetOrigin(Fts5Index *p, i64 *piOrigin);
|
||||
int sqlite3Fts5IndexContentlessDelete(Fts5Index *p, i64 iOrigin, i64 iRowid);
|
||||
|
||||
/*
|
||||
** End of interface to code in fts5_index.c.
|
||||
**************************************************************************/
|
||||
@@ -615,6 +624,11 @@ int sqlite3Fts5HashWrite(
|
||||
*/
|
||||
void sqlite3Fts5HashClear(Fts5Hash*);
|
||||
|
||||
/*
|
||||
** Return true if the hash is empty, false otherwise.
|
||||
*/
|
||||
int sqlite3Fts5HashIsEmpty(Fts5Hash*);
|
||||
|
||||
int sqlite3Fts5HashQuery(
|
||||
Fts5Hash*, /* Hash table to query */
|
||||
int nPre,
|
||||
@@ -636,6 +650,7 @@ void sqlite3Fts5HashScanEntry(Fts5Hash *,
|
||||
);
|
||||
|
||||
|
||||
|
||||
/*
|
||||
** End of interface to code in fts5_hash.c.
|
||||
**************************************************************************/
|
||||
|
||||
@@ -22,6 +22,8 @@
|
||||
#define FTS5_DEFAULT_CRISISMERGE 16
|
||||
#define FTS5_DEFAULT_HASHSIZE (1024*1024)
|
||||
|
||||
#define FTS5_DEFAULT_DELETE_AUTOMERGE 10 /* default 10% */
|
||||
|
||||
/* Maximum allowed page size */
|
||||
#define FTS5_MAX_PAGE_SIZE (64*1024)
|
||||
|
||||
@@ -352,6 +354,16 @@ static int fts5ConfigParseSpecial(
|
||||
return rc;
|
||||
}
|
||||
|
||||
if( sqlite3_strnicmp("contentless_delete", zCmd, nCmd)==0 ){
|
||||
if( (zArg[0]!='0' && zArg[0]!='1') || zArg[1]!='\0' ){
|
||||
*pzErr = sqlite3_mprintf("malformed contentless_delete=... directive");
|
||||
rc = SQLITE_ERROR;
|
||||
}else{
|
||||
pConfig->bContentlessDelete = (zArg[0]=='1');
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
if( sqlite3_strnicmp("content_rowid", zCmd, nCmd)==0 ){
|
||||
if( pConfig->zContentRowid ){
|
||||
*pzErr = sqlite3_mprintf("multiple content_rowid=... directives");
|
||||
@@ -596,6 +608,28 @@ int sqlite3Fts5ConfigParse(
|
||||
sqlite3_free(zTwo);
|
||||
}
|
||||
|
||||
/* We only allow contentless_delete=1 if the table is indeed contentless. */
|
||||
if( rc==SQLITE_OK
|
||||
&& pRet->bContentlessDelete
|
||||
&& pRet->eContent!=FTS5_CONTENT_NONE
|
||||
){
|
||||
*pzErr = sqlite3_mprintf(
|
||||
"contentless_delete=1 requires a contentless table"
|
||||
);
|
||||
rc = SQLITE_ERROR;
|
||||
}
|
||||
|
||||
/* We only allow contentless_delete=1 if columnsize=0 is not present.
|
||||
**
|
||||
** This restriction may be removed at some point.
|
||||
*/
|
||||
if( rc==SQLITE_OK && pRet->bContentlessDelete && pRet->bColumnsize==0 ){
|
||||
*pzErr = sqlite3_mprintf(
|
||||
"contentless_delete=1 is incompatible with columnsize=0"
|
||||
);
|
||||
rc = SQLITE_ERROR;
|
||||
}
|
||||
|
||||
/* If a tokenizer= option was successfully parsed, the tokenizer has
|
||||
** already been allocated. Otherwise, allocate an instance of the default
|
||||
** tokenizer (unicode61) now. */
|
||||
@@ -890,6 +924,18 @@ int sqlite3Fts5ConfigSetValue(
|
||||
}
|
||||
}
|
||||
|
||||
else if( 0==sqlite3_stricmp(zKey, "deletemerge") ){
|
||||
int nVal = -1;
|
||||
if( SQLITE_INTEGER==sqlite3_value_numeric_type(pVal) ){
|
||||
nVal = sqlite3_value_int(pVal);
|
||||
}else{
|
||||
*pbBadkey = 1;
|
||||
}
|
||||
if( nVal<0 ) nVal = FTS5_DEFAULT_DELETE_AUTOMERGE;
|
||||
if( nVal>100 ) nVal = 0;
|
||||
pConfig->nDeleteMerge = nVal;
|
||||
}
|
||||
|
||||
else if( 0==sqlite3_stricmp(zKey, "rank") ){
|
||||
const char *zIn = (const char*)sqlite3_value_text(pVal);
|
||||
char *zRank;
|
||||
@@ -938,6 +984,7 @@ int sqlite3Fts5ConfigLoad(Fts5Config *pConfig, int iCookie){
|
||||
pConfig->nUsermerge = FTS5_DEFAULT_USERMERGE;
|
||||
pConfig->nCrisisMerge = FTS5_DEFAULT_CRISISMERGE;
|
||||
pConfig->nHashSize = FTS5_DEFAULT_HASHSIZE;
|
||||
pConfig->nDeleteMerge = FTS5_DEFAULT_DELETE_AUTOMERGE;
|
||||
|
||||
zSql = sqlite3Fts5Mprintf(&rc, zSelect, pConfig->zDb, pConfig->zName);
|
||||
if( zSql ){
|
||||
|
||||
+55
-3
@@ -17,6 +17,10 @@
|
||||
#include "fts5Int.h"
|
||||
#include "fts5parse.h"
|
||||
|
||||
#ifndef SQLITE_FTS5_MAX_EXPR_DEPTH
|
||||
# define SQLITE_FTS5_MAX_EXPR_DEPTH 256
|
||||
#endif
|
||||
|
||||
/*
|
||||
** All token types in the generated fts5parse.h file are greater than 0.
|
||||
*/
|
||||
@@ -57,11 +61,17 @@ struct Fts5Expr {
|
||||
** FTS5_NOT (nChild, apChild valid)
|
||||
** FTS5_STRING (pNear valid)
|
||||
** FTS5_TERM (pNear valid)
|
||||
**
|
||||
** iHeight:
|
||||
** Distance from this node to furthest leaf. This is always 0 for nodes
|
||||
** of type FTS5_STRING and FTS5_TERM. For all other nodes it is one
|
||||
** greater than the largest child value.
|
||||
*/
|
||||
struct Fts5ExprNode {
|
||||
int eType; /* Node type */
|
||||
int bEof; /* True at EOF */
|
||||
int bNomatch; /* True if entry is not a match */
|
||||
int iHeight; /* Distance to tree leaf nodes */
|
||||
|
||||
/* Next method for this node. */
|
||||
int (*xNext)(Fts5Expr*, Fts5ExprNode*, int, i64);
|
||||
@@ -131,6 +141,31 @@ struct Fts5Parse {
|
||||
int bPhraseToAnd; /* Convert "a+b" to "a AND b" */
|
||||
};
|
||||
|
||||
/*
|
||||
** Check that the Fts5ExprNode.iHeight variables are set correctly in
|
||||
** the expression tree passed as the only argument.
|
||||
*/
|
||||
#ifndef NDEBUG
|
||||
static void assert_expr_depth_ok(int rc, Fts5ExprNode *p){
|
||||
if( rc==SQLITE_OK ){
|
||||
if( p->eType==FTS5_TERM || p->eType==FTS5_STRING || p->eType==0 ){
|
||||
assert( p->iHeight==0 );
|
||||
}else{
|
||||
int ii;
|
||||
int iMaxChild = 0;
|
||||
for(ii=0; ii<p->nChild; ii++){
|
||||
Fts5ExprNode *pChild = p->apChild[ii];
|
||||
iMaxChild = MAX(iMaxChild, pChild->iHeight);
|
||||
assert_expr_depth_ok(SQLITE_OK, pChild);
|
||||
}
|
||||
assert( p->iHeight==iMaxChild+1 );
|
||||
}
|
||||
}
|
||||
}
|
||||
#else
|
||||
# define assert_expr_depth_ok(rc, p)
|
||||
#endif
|
||||
|
||||
void sqlite3Fts5ParseError(Fts5Parse *pParse, const char *zFmt, ...){
|
||||
va_list ap;
|
||||
va_start(ap, zFmt);
|
||||
@@ -245,6 +280,8 @@ int sqlite3Fts5ExprNew(
|
||||
}while( sParse.rc==SQLITE_OK && t!=FTS5_EOF );
|
||||
sqlite3Fts5ParserFree(pEngine, fts5ParseFree);
|
||||
|
||||
assert_expr_depth_ok(sParse.rc, sParse.pExpr);
|
||||
|
||||
/* If the LHS of the MATCH expression was a user column, apply the
|
||||
** implicit column-filter. */
|
||||
if( iCol<pConfig->nCol && sParse.pExpr && sParse.rc==SQLITE_OK ){
|
||||
@@ -2206,6 +2243,7 @@ static void fts5ExprAssignXNext(Fts5ExprNode *pNode){
|
||||
}
|
||||
|
||||
static void fts5ExprAddChildren(Fts5ExprNode *p, Fts5ExprNode *pSub){
|
||||
int ii = p->nChild;
|
||||
if( p->eType!=FTS5_NOT && pSub->eType==p->eType ){
|
||||
int nByte = sizeof(Fts5ExprNode*) * pSub->nChild;
|
||||
memcpy(&p->apChild[p->nChild], pSub->apChild, nByte);
|
||||
@@ -2214,6 +2252,9 @@ static void fts5ExprAddChildren(Fts5ExprNode *p, Fts5ExprNode *pSub){
|
||||
}else{
|
||||
p->apChild[p->nChild++] = pSub;
|
||||
}
|
||||
for( ; ii<p->nChild; ii++){
|
||||
p->iHeight = MAX(p->iHeight, p->apChild[ii]->iHeight + 1);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -2244,6 +2285,7 @@ static Fts5ExprNode *fts5ParsePhraseToAnd(
|
||||
if( pRet ){
|
||||
pRet->eType = FTS5_AND;
|
||||
pRet->nChild = nTerm;
|
||||
pRet->iHeight = 1;
|
||||
fts5ExprAssignXNext(pRet);
|
||||
pParse->nPhrase--;
|
||||
for(ii=0; ii<nTerm; ii++){
|
||||
@@ -2349,6 +2391,14 @@ Fts5ExprNode *sqlite3Fts5ParseNode(
|
||||
}else{
|
||||
fts5ExprAddChildren(pRet, pLeft);
|
||||
fts5ExprAddChildren(pRet, pRight);
|
||||
if( pRet->iHeight>SQLITE_FTS5_MAX_EXPR_DEPTH ){
|
||||
sqlite3Fts5ParseError(pParse,
|
||||
"fts5 expression tree is too large (maximum depth %d)",
|
||||
SQLITE_FTS5_MAX_EXPR_DEPTH
|
||||
);
|
||||
sqlite3_free(pRet);
|
||||
pRet = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2427,7 +2477,7 @@ Fts5ExprNode *sqlite3Fts5ParseImplicitAnd(
|
||||
return pRet;
|
||||
}
|
||||
|
||||
#ifdef SQLITE_TEST
|
||||
#if defined(SQLITE_TEST) || defined(SQLITE_FTS5_DEBUG)
|
||||
static char *fts5ExprTermPrint(Fts5ExprTerm *pTerm){
|
||||
sqlite3_int64 nByte = 0;
|
||||
Fts5ExprTerm *p;
|
||||
@@ -2533,6 +2583,8 @@ static char *fts5ExprPrintTcl(
|
||||
if( zRet==0 ) return 0;
|
||||
}
|
||||
|
||||
}else if( pExpr->eType==0 ){
|
||||
zRet = sqlite3_mprintf("{}");
|
||||
}else{
|
||||
char const *zOp = 0;
|
||||
int i;
|
||||
@@ -2794,14 +2846,14 @@ static void fts5ExprFold(
|
||||
sqlite3_result_int(pCtx, sqlite3Fts5UnicodeFold(iCode, bRemoveDiacritics));
|
||||
}
|
||||
}
|
||||
#endif /* ifdef SQLITE_TEST */
|
||||
#endif /* if SQLITE_TEST || SQLITE_FTS5_DEBUG */
|
||||
|
||||
/*
|
||||
** This is called during initialization to register the fts5_expr() scalar
|
||||
** UDF with the SQLite handle passed as the only argument.
|
||||
*/
|
||||
int sqlite3Fts5ExprInit(Fts5Global *pGlobal, sqlite3 *db){
|
||||
#ifdef SQLITE_TEST
|
||||
#if defined(SQLITE_TEST) || defined(SQLITE_FTS5_DEBUG)
|
||||
struct Fts5ExprFunc {
|
||||
const char *z;
|
||||
void (*x)(sqlite3_context*,int,sqlite3_value**);
|
||||
|
||||
+22
-1
@@ -475,7 +475,6 @@ static int fts5HashEntrySort(
|
||||
pList = fts5HashEntryMerge(pList, ap[i]);
|
||||
}
|
||||
|
||||
pHash->nEntry = 0;
|
||||
sqlite3_free(ap);
|
||||
*ppSorted = pList;
|
||||
return SQLITE_OK;
|
||||
@@ -529,6 +528,28 @@ int sqlite3Fts5HashScanInit(
|
||||
return fts5HashEntrySort(p, pTerm, nTerm, &p->pScan);
|
||||
}
|
||||
|
||||
#ifdef SQLITE_DEBUG
|
||||
static int fts5HashCount(Fts5Hash *pHash){
|
||||
int nEntry = 0;
|
||||
int ii;
|
||||
for(ii=0; ii<pHash->nSlot; ii++){
|
||||
Fts5HashEntry *p = 0;
|
||||
for(p=pHash->aSlot[ii]; p; p=p->pHashNext){
|
||||
nEntry++;
|
||||
}
|
||||
}
|
||||
return nEntry;
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
** Return true if the hash table is empty, false otherwise.
|
||||
*/
|
||||
int sqlite3Fts5HashIsEmpty(Fts5Hash *pHash){
|
||||
assert( pHash->nEntry==fts5HashCount(pHash) );
|
||||
return pHash->nEntry==0;
|
||||
}
|
||||
|
||||
void sqlite3Fts5HashScanNext(Fts5Hash *p){
|
||||
assert( !sqlite3Fts5HashScanEof(p) );
|
||||
p->pScan = p->pScan->pScanNext;
|
||||
|
||||
+1237
-226
File diff suppressed because it is too large
Load Diff
+22
-7
@@ -1624,7 +1624,6 @@ static int fts5UpdateMethod(
|
||||
int eType0; /* value_type() of apVal[0] */
|
||||
int rc = SQLITE_OK; /* Return code */
|
||||
int bUpdateOrDelete = 0;
|
||||
|
||||
|
||||
/* A transaction must be open when this is called. */
|
||||
assert( pTab->ts.eState==1 || pTab->ts.eState==2 );
|
||||
@@ -1654,7 +1653,14 @@ static int fts5UpdateMethod(
|
||||
if( pConfig->eContent!=FTS5_CONTENT_NORMAL
|
||||
&& 0==sqlite3_stricmp("delete", z)
|
||||
){
|
||||
rc = fts5SpecialDelete(pTab, apVal);
|
||||
if( pConfig->bContentlessDelete ){
|
||||
fts5SetVtabError(pTab,
|
||||
"'delete' may not be used with a contentless_delete=1 table"
|
||||
);
|
||||
rc = SQLITE_ERROR;
|
||||
}else{
|
||||
rc = fts5SpecialDelete(pTab, apVal);
|
||||
}
|
||||
}else{
|
||||
rc = fts5SpecialInsert(pTab, z, apVal[2 + pConfig->nCol + 1]);
|
||||
}
|
||||
@@ -1671,7 +1677,7 @@ static int fts5UpdateMethod(
|
||||
** Cases 3 and 4 may violate the rowid constraint.
|
||||
*/
|
||||
int eConflict = SQLITE_ABORT;
|
||||
if( pConfig->eContent==FTS5_CONTENT_NORMAL ){
|
||||
if( pConfig->eContent==FTS5_CONTENT_NORMAL || pConfig->bContentlessDelete ){
|
||||
eConflict = sqlite3_vtab_on_conflict(pConfig->db);
|
||||
}
|
||||
|
||||
@@ -1679,8 +1685,12 @@ static int fts5UpdateMethod(
|
||||
assert( nArg!=1 || eType0==SQLITE_INTEGER );
|
||||
|
||||
/* Filter out attempts to run UPDATE or DELETE on contentless tables.
|
||||
** This is not suported. */
|
||||
if( eType0==SQLITE_INTEGER && fts5IsContentless(pTab) ){
|
||||
** This is not suported. Except - DELETE is supported if the CREATE
|
||||
** VIRTUAL TABLE statement contained "contentless_delete=1". */
|
||||
if( eType0==SQLITE_INTEGER
|
||||
&& pConfig->eContent==FTS5_CONTENT_NONE
|
||||
&& pConfig->bContentlessDelete==0
|
||||
){
|
||||
pTab->p.base.zErrMsg = sqlite3_mprintf(
|
||||
"cannot %s contentless fts5 table: %s",
|
||||
(nArg>1 ? "UPDATE" : "DELETE from"), pConfig->zName
|
||||
@@ -1767,8 +1777,7 @@ static int fts5SyncMethod(sqlite3_vtab *pVtab){
|
||||
Fts5FullTable *pTab = (Fts5FullTable*)pVtab;
|
||||
fts5CheckTransactionState(pTab, FTS5_SYNC, 0);
|
||||
pTab->p.pConfig->pzErrmsg = &pTab->p.base.zErrMsg;
|
||||
fts5TripCursors(pTab);
|
||||
rc = sqlite3Fts5StorageSync(pTab->pStorage);
|
||||
rc = sqlite3Fts5FlushToDisk(&pTab->p);
|
||||
pTab->p.pConfig->pzErrmsg = 0;
|
||||
return rc;
|
||||
}
|
||||
@@ -2535,6 +2544,12 @@ static int fts5ColumnMethod(
|
||||
sqlite3_result_value(pCtx, sqlite3_column_value(pCsr->pStmt, iCol+1));
|
||||
}
|
||||
pConfig->pzErrmsg = 0;
|
||||
}else if( pConfig->bContentlessDelete && sqlite3_vtab_nochange(pCtx) ){
|
||||
char *zErr = sqlite3_mprintf("cannot UPDATE a subset of "
|
||||
"columns on fts5 contentless-delete table: %s", pConfig->zName
|
||||
);
|
||||
sqlite3_result_error(pCtx, zErr, -1);
|
||||
sqlite3_free(zErr);
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
+72
-12
@@ -77,10 +77,10 @@ static int fts5StorageGetStmt(
|
||||
"INSERT INTO %Q.'%q_content' VALUES(%s)", /* INSERT_CONTENT */
|
||||
"REPLACE INTO %Q.'%q_content' VALUES(%s)", /* REPLACE_CONTENT */
|
||||
"DELETE FROM %Q.'%q_content' WHERE id=?", /* DELETE_CONTENT */
|
||||
"REPLACE INTO %Q.'%q_docsize' VALUES(?,?)", /* REPLACE_DOCSIZE */
|
||||
"REPLACE INTO %Q.'%q_docsize' VALUES(?,?%s)", /* REPLACE_DOCSIZE */
|
||||
"DELETE FROM %Q.'%q_docsize' WHERE id=?", /* DELETE_DOCSIZE */
|
||||
|
||||
"SELECT sz FROM %Q.'%q_docsize' WHERE id=?", /* LOOKUP_DOCSIZE */
|
||||
"SELECT sz%s FROM %Q.'%q_docsize' WHERE id=?", /* LOOKUP_DOCSIZE */
|
||||
|
||||
"REPLACE INTO %Q.'%q_config' VALUES(?,?)", /* REPLACE_CONFIG */
|
||||
"SELECT %s FROM %s AS T", /* SCAN */
|
||||
@@ -128,6 +128,19 @@ static int fts5StorageGetStmt(
|
||||
break;
|
||||
}
|
||||
|
||||
case FTS5_STMT_REPLACE_DOCSIZE:
|
||||
zSql = sqlite3_mprintf(azStmt[eStmt], pC->zDb, pC->zName,
|
||||
(pC->bContentlessDelete ? ",?" : "")
|
||||
);
|
||||
break;
|
||||
|
||||
case FTS5_STMT_LOOKUP_DOCSIZE:
|
||||
zSql = sqlite3_mprintf(azStmt[eStmt],
|
||||
(pC->bContentlessDelete ? ",origin" : ""),
|
||||
pC->zDb, pC->zName
|
||||
);
|
||||
break;
|
||||
|
||||
default:
|
||||
zSql = sqlite3_mprintf(azStmt[eStmt], pC->zDb, pC->zName);
|
||||
break;
|
||||
@@ -317,9 +330,11 @@ int sqlite3Fts5StorageOpen(
|
||||
}
|
||||
|
||||
if( rc==SQLITE_OK && pConfig->bColumnsize ){
|
||||
rc = sqlite3Fts5CreateTable(
|
||||
pConfig, "docsize", "id INTEGER PRIMARY KEY, sz BLOB", 0, pzErr
|
||||
);
|
||||
const char *zCols = "id INTEGER PRIMARY KEY, sz BLOB";
|
||||
if( pConfig->bContentlessDelete ){
|
||||
zCols = "id INTEGER PRIMARY KEY, sz BLOB, origin INTEGER";
|
||||
}
|
||||
rc = sqlite3Fts5CreateTable(pConfig, "docsize", zCols, 0, pzErr);
|
||||
}
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = sqlite3Fts5CreateTable(
|
||||
@@ -396,7 +411,7 @@ static int fts5StorageDeleteFromIndex(
|
||||
){
|
||||
Fts5Config *pConfig = p->pConfig;
|
||||
sqlite3_stmt *pSeek = 0; /* SELECT to read row iDel from %_data */
|
||||
int rc; /* Return code */
|
||||
int rc = SQLITE_OK; /* Return code */
|
||||
int rc2; /* sqlite3_reset() return code */
|
||||
int iCol;
|
||||
Fts5InsertCtx ctx;
|
||||
@@ -412,7 +427,6 @@ static int fts5StorageDeleteFromIndex(
|
||||
|
||||
ctx.pStorage = p;
|
||||
ctx.iCol = -1;
|
||||
rc = sqlite3Fts5IndexBeginWrite(p->pIndex, 1, iDel);
|
||||
for(iCol=1; rc==SQLITE_OK && iCol<=pConfig->nCol; iCol++){
|
||||
if( pConfig->abUnindexed[iCol-1]==0 ){
|
||||
const char *zText;
|
||||
@@ -449,6 +463,37 @@ static int fts5StorageDeleteFromIndex(
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** This function is called to process a DELETE on a contentless_delete=1
|
||||
** table. It adds the tombstone required to delete the entry with rowid
|
||||
** iDel. If successful, SQLITE_OK is returned. Or, if an error occurs,
|
||||
** an SQLite error code.
|
||||
*/
|
||||
static int fts5StorageContentlessDelete(Fts5Storage *p, i64 iDel){
|
||||
i64 iOrigin = 0;
|
||||
sqlite3_stmt *pLookup = 0;
|
||||
int rc = SQLITE_OK;
|
||||
|
||||
assert( p->pConfig->bContentlessDelete );
|
||||
assert( p->pConfig->eContent==FTS5_CONTENT_NONE );
|
||||
|
||||
/* Look up the origin of the document in the %_docsize table. Store
|
||||
** this in stack variable iOrigin. */
|
||||
rc = fts5StorageGetStmt(p, FTS5_STMT_LOOKUP_DOCSIZE, &pLookup, 0);
|
||||
if( rc==SQLITE_OK ){
|
||||
sqlite3_bind_int64(pLookup, 1, iDel);
|
||||
if( SQLITE_ROW==sqlite3_step(pLookup) ){
|
||||
iOrigin = sqlite3_column_int64(pLookup, 1);
|
||||
}
|
||||
rc = sqlite3_reset(pLookup);
|
||||
}
|
||||
|
||||
if( rc==SQLITE_OK && iOrigin!=0 ){
|
||||
rc = sqlite3Fts5IndexContentlessDelete(p->pIndex, iOrigin, iDel);
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** Insert a record into the %_docsize table. Specifically, do:
|
||||
@@ -469,10 +514,17 @@ static int fts5StorageInsertDocsize(
|
||||
rc = fts5StorageGetStmt(p, FTS5_STMT_REPLACE_DOCSIZE, &pReplace, 0);
|
||||
if( rc==SQLITE_OK ){
|
||||
sqlite3_bind_int64(pReplace, 1, iRowid);
|
||||
sqlite3_bind_blob(pReplace, 2, pBuf->p, pBuf->n, SQLITE_STATIC);
|
||||
sqlite3_step(pReplace);
|
||||
rc = sqlite3_reset(pReplace);
|
||||
sqlite3_bind_null(pReplace, 2);
|
||||
if( p->pConfig->bContentlessDelete ){
|
||||
i64 iOrigin = 0;
|
||||
rc = sqlite3Fts5IndexGetOrigin(p->pIndex, &iOrigin);
|
||||
sqlite3_bind_int64(pReplace, 3, iOrigin);
|
||||
}
|
||||
if( rc==SQLITE_OK ){
|
||||
sqlite3_bind_blob(pReplace, 2, pBuf->p, pBuf->n, SQLITE_STATIC);
|
||||
sqlite3_step(pReplace);
|
||||
rc = sqlite3_reset(pReplace);
|
||||
sqlite3_bind_null(pReplace, 2);
|
||||
}
|
||||
}
|
||||
}
|
||||
return rc;
|
||||
@@ -536,7 +588,15 @@ int sqlite3Fts5StorageDelete(Fts5Storage *p, i64 iDel, sqlite3_value **apVal){
|
||||
|
||||
/* Delete the index records */
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = fts5StorageDeleteFromIndex(p, iDel, apVal);
|
||||
rc = sqlite3Fts5IndexBeginWrite(p->pIndex, 1, iDel);
|
||||
}
|
||||
|
||||
if( rc==SQLITE_OK ){
|
||||
if( p->pConfig->bContentlessDelete ){
|
||||
rc = fts5StorageContentlessDelete(p, iDel);
|
||||
}else{
|
||||
rc = fts5StorageDeleteFromIndex(p, iDel, apVal);
|
||||
}
|
||||
}
|
||||
|
||||
/* Delete the %_docsize record */
|
||||
|
||||
@@ -180,7 +180,11 @@ if {[detail_is_full]} {
|
||||
} {1 2}
|
||||
}
|
||||
|
||||
do_execsql_test 4.5 {
|
||||
do_execsql_test 4.5.1 {
|
||||
SELECT rowid FROM s1 WHERE s1 MATCH 'a AND x'
|
||||
} {1 2}
|
||||
|
||||
do_execsql_test 4.5.2 {
|
||||
SELECT rowid FROM s1 WHERE s1 MATCH 'a x'
|
||||
} {1 2}
|
||||
|
||||
|
||||
@@ -0,0 +1,62 @@
|
||||
# 2023 May 28
|
||||
#
|
||||
# The author disclaims copyright to this source code. In place of
|
||||
# a legal notice, here is a blessing:
|
||||
#
|
||||
# May you do good and not evil.
|
||||
# May you find forgiveness for yourself and forgive others.
|
||||
# May you share freely, never taking more than you give.
|
||||
#
|
||||
#*************************************************************************
|
||||
#
|
||||
|
||||
source [file join [file dirname [info script]] fts5_common.tcl]
|
||||
set testprefix fts5bigid
|
||||
|
||||
# If SQLITE_ENABLE_FTS5 is not defined, omit this file.
|
||||
ifcapable !fts5 {
|
||||
finish_test
|
||||
return
|
||||
}
|
||||
|
||||
set nRow 20000
|
||||
|
||||
proc do_ascdesc_test {tn query} {
|
||||
set ::lAsc [db eval { SELECT rowid FROM x1($query) }]
|
||||
set ::lDesc [db eval { SELECT rowid FROM x1($query) ORDER BY rowid DESC }]
|
||||
do_test $tn.1 { lsort -integer $::lAsc } $::lAsc
|
||||
do_test $tn.2 { lsort -integer -decr $::lDesc } $::lDesc
|
||||
do_test $tn.3 { lsort -integer $::lDesc } $::lAsc
|
||||
}
|
||||
|
||||
do_execsql_test 1.0 {
|
||||
CREATE VIRTUAL TABLE x1 USING fts5(a);
|
||||
}
|
||||
|
||||
do_test 1.1 {
|
||||
for {set ii 0} {$ii < $nRow} {incr ii} {
|
||||
db eval {
|
||||
REPLACE INTO x1(rowid, a) VALUES(random(), 'movement at the station');
|
||||
}
|
||||
}
|
||||
} {}
|
||||
|
||||
do_ascdesc_test 1.2 "the"
|
||||
|
||||
do_execsql_test 1.3 {
|
||||
DELETE FROM x1
|
||||
}
|
||||
|
||||
do_test 1.4 {
|
||||
for {set ii 0} {$ii < $nRow} {incr ii} {
|
||||
db eval {
|
||||
INSERT INTO x1(rowid, a) VALUES(
|
||||
$ii + 0x6FFFFFFFFFFFFFFF, 'movement at the station'
|
||||
);
|
||||
}
|
||||
}
|
||||
} {}
|
||||
|
||||
do_ascdesc_test 1.5 "movement"
|
||||
|
||||
finish_test
|
||||
@@ -0,0 +1,271 @@
|
||||
# 2014 Dec 20
|
||||
#
|
||||
# The author disclaims copyright to this source code. In place of
|
||||
# a legal notice, here is a blessing:
|
||||
#
|
||||
# May you do good and not evil.
|
||||
# May you find forgiveness for yourself and forgive others.
|
||||
# May you share freely, never taking more than you give.
|
||||
#
|
||||
#***********************************************************************
|
||||
#
|
||||
# This file contains tests for the content= and content_rowid= options.
|
||||
#
|
||||
|
||||
source [file join [file dirname [info script]] fts5_common.tcl]
|
||||
set testprefix fts5contentless
|
||||
|
||||
# If SQLITE_ENABLE_FTS5 is defined, omit this file.
|
||||
ifcapable !fts5 {
|
||||
finish_test
|
||||
return
|
||||
}
|
||||
|
||||
# Check that it is not possible to specify "contentless_delete=1" for
|
||||
# anything other than a contentless table.
|
||||
#
|
||||
set res(0) {0 {}}
|
||||
set res(1) {1 {contentless_delete=1 requires a contentless table}}
|
||||
foreach {tn sql bError} {
|
||||
1 "(a, b, contentless_delete=1)" 1
|
||||
2 "(a, b, contentless_delete=1, content=abc)" 1
|
||||
3 "(a, b, contentless_delete=1, content=)" 0
|
||||
4 "(content=, contentless_delete=1, a)" 0
|
||||
5 "(content='', contentless_delete=1, hello)" 0
|
||||
} {
|
||||
execsql { BEGIN }
|
||||
do_catchsql_test 1.$tn "CREATE VIRTUAL TABLE t1 USING fts5 $sql" $res($bError)
|
||||
execsql { ROLLBACK }
|
||||
}
|
||||
|
||||
# Check that it is not possible to specify "contentless_delete=1"
|
||||
# along with columnsize=1.
|
||||
#
|
||||
set res(0) {0 {}}
|
||||
set res(1) {1 {contentless_delete=1 is incompatible with columnsize=0}}
|
||||
foreach {tn sql bError} {
|
||||
2 "(a, b, content='', contentless_delete=1, columnsize=0)" 1
|
||||
} {
|
||||
execsql { BEGIN }
|
||||
do_catchsql_test 1.$tn "CREATE VIRTUAL TABLE t1 USING fts5 $sql" $res($bError)
|
||||
execsql { ROLLBACK }
|
||||
}
|
||||
|
||||
# Check that if contentless_delete=1 is specified, then the "origin"
|
||||
# column is added to the %_docsize table.
|
||||
reset_db
|
||||
do_execsql_test 3.0 {
|
||||
CREATE VIRTUAL TABLE x1 USING fts5(c, content='');
|
||||
CREATE VIRTUAL TABLE x2 USING fts5(c, content='', contentless_delete=1);
|
||||
}
|
||||
do_execsql_test 3.1 {
|
||||
SELECT sql FROM sqlite_schema WHERE name IN ('x1_docsize', 'x2_docsize');
|
||||
} {
|
||||
{CREATE TABLE 'x1_docsize'(id INTEGER PRIMARY KEY, sz BLOB)}
|
||||
{CREATE TABLE 'x2_docsize'(id INTEGER PRIMARY KEY, sz BLOB, origin INTEGER)}
|
||||
}
|
||||
|
||||
do_execsql_test 3.2.1 {
|
||||
SELECT hex(block) FROM x1_data WHERE id=10
|
||||
} {00000000000000}
|
||||
do_execsql_test 3.2.2 {
|
||||
SELECT hex(block) FROM x2_data WHERE id=10
|
||||
} {00000000FF000001000000}
|
||||
|
||||
do_execsql_test 3.3 {
|
||||
INSERT INTO x2 VALUES('first text');
|
||||
INSERT INTO x2 VALUES('second text');
|
||||
}
|
||||
do_execsql_test 3.4 {
|
||||
SELECT id, origin FROM x2_docsize
|
||||
} {1 1 2 2}
|
||||
do_execsql_test 3.5 {
|
||||
SELECT level, segment, loc1, loc2 FROM fts5_structure(
|
||||
(SELECT block FROM x2_data WHERE id=10)
|
||||
)
|
||||
} {
|
||||
0 0 1 1
|
||||
0 1 2 2
|
||||
}
|
||||
do_execsql_test 3.6 {
|
||||
INSERT INTO x2(x2) VALUES('optimize');
|
||||
}
|
||||
do_execsql_test 3.7 {
|
||||
SELECT level, segment, loc1, loc2 FROM fts5_structure(
|
||||
(SELECT block FROM x2_data WHERE id=10)
|
||||
)
|
||||
} {
|
||||
1 0 1 2
|
||||
}
|
||||
|
||||
do_execsql_test 3.8 {
|
||||
DELETE FROM x2 WHERE rowid=2;
|
||||
}
|
||||
|
||||
do_execsql_test 3.9 {
|
||||
SELECT rowid FROM x2('text')
|
||||
} {1}
|
||||
|
||||
#--------------------------------------------------------------------------
|
||||
reset_db
|
||||
proc document {n} {
|
||||
set vocab [list A B C D E F G H I J K L M N O P Q R S T U V W X Y Z]
|
||||
set ret [list]
|
||||
for {set ii 0} {$ii < $n} {incr ii} {
|
||||
lappend ret [lindex $vocab [expr int(rand()*[llength $vocab])]]
|
||||
}
|
||||
set ret
|
||||
}
|
||||
|
||||
set nRow 1000
|
||||
|
||||
do_execsql_test 4.0 {
|
||||
CREATE TABLE t1(x);
|
||||
CREATE VIRTUAL TABLE ft USING fts5(x, content='', contentless_delete=1);
|
||||
INSERT INTO ft(ft, rank) VALUES('pgsz', 100);
|
||||
}
|
||||
do_test 4.1 {
|
||||
for {set ii 0} {$ii < $nRow} {incr ii} {
|
||||
set doc [document 6]
|
||||
execsql {
|
||||
INSERT INTO t1 VALUES($doc);
|
||||
INSERT INTO ft VALUES($doc);
|
||||
}
|
||||
}
|
||||
} {}
|
||||
|
||||
foreach v {A B C D E F G H I J K L M N O P Q R S T U V W X Y Z} {
|
||||
set L1 [execsql {SELECT rowid FROM t1 WHERE x LIKE '%'||$v||'%'}]
|
||||
set L2 [execsql {SELECT rowid FROM ft($v)}]
|
||||
do_test 4.2.$v { set L1 } $L2
|
||||
}
|
||||
|
||||
do_test 4.3 {
|
||||
for {set ii 1} {$ii < $nRow} {incr ii 2} {
|
||||
execsql {
|
||||
DELETE FROM ft WHERE rowid=$ii;
|
||||
DELETE FROM t1 WHERE rowid=$ii;
|
||||
}
|
||||
}
|
||||
} {}
|
||||
|
||||
foreach v {A B C D E F G H I J K L M N O P Q R S T U V W X Y Z} {
|
||||
set L1 [execsql {SELECT rowid FROM t1 WHERE x LIKE '%'||$v||'%'}]
|
||||
set L2 [execsql {SELECT rowid FROM ft($v)}]
|
||||
do_test 4.4.$v { set L1 } $L2
|
||||
}
|
||||
|
||||
do_execsql_test 4.5 {
|
||||
INSERT INTO ft(ft) VALUES('optimize');
|
||||
} {}
|
||||
|
||||
foreach v {A B C D E F G H I J K L M N O P Q R S T U V W X Y Z} {
|
||||
set L1 [execsql {SELECT rowid FROM t1 WHERE x LIKE '%'||$v||'%'}]
|
||||
set L2 [execsql {SELECT rowid FROM ft($v)}]
|
||||
do_test 4.6.$v { set L1 } $L2
|
||||
}
|
||||
|
||||
#execsql_pp { SELECT fts5_decode(id, block) FROM ft_data }
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
reset_db
|
||||
do_execsql_test 5.0 {
|
||||
CREATE VIRTUAL TABLE ft USING fts5(x, content='', contentless_delete=1);
|
||||
INSERT INTO ft(rowid, x) VALUES(1, 'one two three');
|
||||
INSERT INTO ft(rowid, x) VALUES(2, 'one two four');
|
||||
INSERT INTO ft(rowid, x) VALUES(3, 'one two five');
|
||||
INSERT INTO ft(rowid, x) VALUES(4, 'one two seven');
|
||||
INSERT INTO ft(rowid, x) VALUES(5, 'one two eight');
|
||||
}
|
||||
|
||||
do_execsql_test 5.1 {
|
||||
DELETE FROM ft WHERE rowid=2
|
||||
}
|
||||
|
||||
do_execsql_test 5.2 {
|
||||
SELECT rowid FROM ft
|
||||
} {1 3 4 5}
|
||||
|
||||
do_catchsql_test 5.3 {
|
||||
UPDATE ft SET x='four six' WHERE rowid=3
|
||||
} {0 {}}
|
||||
|
||||
do_execsql_test 5.4 {
|
||||
SELECT rowid FROM ft('one');
|
||||
} {1 4 5}
|
||||
|
||||
do_execsql_test 5.5 {
|
||||
REPLACE INTO ft(rowid, x) VALUES(3, 'four six');
|
||||
SELECT rowid FROM ft('one');
|
||||
} {1 4 5}
|
||||
|
||||
do_execsql_test 5.6 {
|
||||
REPLACE INTO ft(rowid, x) VALUES(6, 'one two eleven');
|
||||
SELECT rowid FROM ft('one');
|
||||
} {1 4 5 6}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
reset_db
|
||||
do_execsql_test 6.0 {
|
||||
CREATE VIRTUAL TABLE ft USING fts5(x, content='', contentless_delete=1);
|
||||
INSERT INTO ft(rowid, x) VALUES(1, 'one two three');
|
||||
INSERT INTO ft(rowid, x) VALUES(2, 'one two four');
|
||||
}
|
||||
|
||||
do_test 6.1 {
|
||||
db eval { SELECT rowid FROM ft('one two') } {
|
||||
if {$rowid==1} {
|
||||
db eval { INSERT INTO ft(rowid, x) VALUES(3, 'one two four') }
|
||||
}
|
||||
}
|
||||
} {}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
reset_db
|
||||
do_execsql_test 7.0 {
|
||||
CREATE VIRTUAL TABLE ft USING fts5(x, content='', contentless_delete=1);
|
||||
}
|
||||
|
||||
set lRowid [list -450 0 1 2 42]
|
||||
|
||||
do_test 7.1 {
|
||||
execsql BEGIN
|
||||
foreach r $lRowid {
|
||||
execsql { INSERT INTO ft(rowid, x) VALUES($r, 'one one one'); }
|
||||
}
|
||||
execsql COMMIT
|
||||
} {}
|
||||
|
||||
do_test 7.2 {
|
||||
execsql BEGIN
|
||||
foreach r $lRowid {
|
||||
execsql { REPLACE INTO ft(rowid, x) VALUES($r, 'two two two'); }
|
||||
}
|
||||
execsql COMMIT
|
||||
} {}
|
||||
|
||||
do_execsql_test 7.3 { SELECT rowid FROM ft('one'); } {}
|
||||
do_execsql_test 7.4 { SELECT rowid FROM ft('two'); } $lRowid
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
reset_db
|
||||
do_execsql_test 8.0 {
|
||||
CREATE VIRTUAL TABLE ft USING fts5(x, content='', contentless_delete=1);
|
||||
INSERT INTO ft VALUES('hello world');
|
||||
INSERT INTO ft VALUES('one two three');
|
||||
}
|
||||
|
||||
do_catchsql_test 8.1 {
|
||||
INSERT INTO ft(ft, rowid, x) VALUES('delete', 1, 'hello world');
|
||||
} {1 {'delete' may not be used with a contentless_delete=1 table}}
|
||||
|
||||
do_execsql_test 8.2 {
|
||||
BEGIN;
|
||||
INSERT INTO ft(rowid, x) VALUES(3, 'four four four');
|
||||
DELETE FROM ft WHERE rowid=3;
|
||||
COMMIT;
|
||||
SELECT rowid FROM ft('four');
|
||||
} {}
|
||||
|
||||
finish_test
|
||||
|
||||
@@ -0,0 +1,208 @@
|
||||
# 2023 July 19
|
||||
#
|
||||
# The author disclaims copyright to this source code. In place of
|
||||
# a legal notice, here is a blessing:
|
||||
#
|
||||
# May you do good and not evil.
|
||||
# May you find forgiveness for yourself and forgive others.
|
||||
# May you share freely, never taking more than you give.
|
||||
#
|
||||
#***********************************************************************
|
||||
#
|
||||
# This file contains tests for the content= and content_rowid= options.
|
||||
#
|
||||
|
||||
source [file join [file dirname [info script]] fts5_common.tcl]
|
||||
set testprefix fts5contentless2
|
||||
|
||||
# If SQLITE_ENABLE_FTS5 is defined, omit this file.
|
||||
ifcapable !fts5 {
|
||||
finish_test
|
||||
return
|
||||
}
|
||||
|
||||
proc vocab {} {
|
||||
list aaa bbb ccc ddd eee fff ggg hhh iii jjj kkk lll mmm nnn ooo ppp
|
||||
}
|
||||
|
||||
proc document {nToken} {
|
||||
set doc [list]
|
||||
set vocab [vocab]
|
||||
for {set ii 0} {$ii < $nToken} {incr ii} {
|
||||
lappend doc [lindex $vocab [expr int(rand()*[llength $vocab])]]
|
||||
}
|
||||
set doc
|
||||
}
|
||||
db func document document
|
||||
|
||||
proc contains {doc token} {
|
||||
expr {[lsearch $doc $token]>=0}
|
||||
}
|
||||
db func contains contains
|
||||
|
||||
proc do_compare_tables_test {tn} {
|
||||
uplevel [list do_test $tn {
|
||||
foreach v [vocab] {
|
||||
set l1 [execsql { SELECT rowid FROM t1 WHERE contains(doc, $v) }]
|
||||
set l2 [execsql { SELECT rowid FROM t2($v) }]
|
||||
if {$l1!=$l2} { error "1: query mismatch ($l1) ($l2)" }
|
||||
|
||||
set w "[string range $v 0 1]*"
|
||||
set l1 [execsql { SELECT rowid FROM t1 WHERE contains(doc, $w) }]
|
||||
set l2 [execsql { SELECT rowid FROM t2($w) }]
|
||||
if {$l1!=$l2} { error "2: query mismatch ($l1) ($l2)" }
|
||||
|
||||
set w "[string range $v 0 0]*"
|
||||
set l1 [execsql { SELECT rowid FROM t1 WHERE contains(doc, $w) }]
|
||||
set l2 [execsql { SELECT rowid FROM t2($w) }]
|
||||
if {$l1!=$l2} { error "2: query mismatch ($l1) ($l2)" }
|
||||
|
||||
set l1 [execsql {
|
||||
SELECT rowid FROM t1 WHERE contains(doc, $v) ORDER BY rowid DESC
|
||||
}]
|
||||
set l2 [execsql { SELECT rowid FROM t2($v) ORDER BY rowid DESC }]
|
||||
if {$l1!=$l2} { error "1: query mismatch ($l1) ($l2)" }
|
||||
}
|
||||
set {} {}
|
||||
} {}]
|
||||
}
|
||||
|
||||
proc lshuffle {in} {
|
||||
set L [list]
|
||||
set ret [list]
|
||||
foreach elem $in { lappend L [list [expr rand()] $elem] }
|
||||
foreach pair [lsort -index 0 $L] { lappend ret [lindex $pair 1] }
|
||||
set ret
|
||||
}
|
||||
|
||||
expr srand(0)
|
||||
|
||||
do_execsql_test 1.0 {
|
||||
CREATE VIRTUAL TABLE t2 USING fts5(
|
||||
doc, prefix=2, content=, contentless_delete=1
|
||||
);
|
||||
|
||||
CREATE TABLE t1(doc);
|
||||
CREATE TRIGGER tr1 AFTER DELETE ON t1 BEGIN
|
||||
DELETE FROM t2 WHERE rowid = old.rowid;
|
||||
END;
|
||||
}
|
||||
|
||||
set SMALLEST64 -9223372036854775808
|
||||
set LARGEST64 9223372036854775807
|
||||
|
||||
foreach {tn r1 r2} {
|
||||
1 0 50
|
||||
2 $SMALLEST64 $SMALLEST64+50
|
||||
3 $LARGEST64-50 $LARGEST64
|
||||
4 -50 -1
|
||||
} {
|
||||
set r1 [expr $r1]
|
||||
set r2 [expr $r2]
|
||||
|
||||
do_test 1.1.$tn {
|
||||
execsql BEGIN
|
||||
for {set ii $r1} {$ii <= $r2} {incr ii} {
|
||||
execsql { INSERT INTO t1(rowid, doc) VALUES ($ii, document(8)); }
|
||||
}
|
||||
execsql COMMIT
|
||||
} {}
|
||||
}
|
||||
do_test 1.2 {
|
||||
db eval { SELECT rowid, doc FROM t1 } {
|
||||
execsql { INSERT INTO t2(rowid, doc) VALUES($rowid, $doc) }
|
||||
}
|
||||
} {}
|
||||
|
||||
foreach {tn rowid} {
|
||||
1 $SMALLEST64
|
||||
2 0
|
||||
3 -5
|
||||
4 -30
|
||||
5 $LARGEST64
|
||||
6 $LARGEST64-1
|
||||
} {
|
||||
set rowid [expr $rowid]
|
||||
do_execsql_test 1.3.$tn.1 {
|
||||
DELETE FROM t1 WHERE rowid=$rowid
|
||||
}
|
||||
do_compare_tables_test 1.3.$tn.2
|
||||
}
|
||||
|
||||
set iTest 1
|
||||
foreach r [lshuffle [execsql {SELECT rowid FROM t1}]] {
|
||||
if {($iTest % 50)==0} {
|
||||
execsql { INSERT INTO t2(t2) VALUES('optimize') }
|
||||
}
|
||||
if {($iTest % 5)==0} {
|
||||
execsql { INSERT INTO t2(t2, rank) VALUES('merge', 5) }
|
||||
}
|
||||
do_execsql_test 1.4.$iTest.1($r) {
|
||||
DELETE FROM t1 WHERE rowid=$r
|
||||
}
|
||||
do_compare_tables_test 1.4.$iTest.2
|
||||
incr iTest
|
||||
}
|
||||
|
||||
do_execsql_test 1.5 {
|
||||
SELECT * FROM t1
|
||||
} {}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
reset_db
|
||||
db func document document
|
||||
|
||||
do_execsql_test 2.0 {
|
||||
CREATE VIRTUAL TABLE t2 USING fts5(doc, content=, contentless_delete=1);
|
||||
WITH s(i) AS (
|
||||
SELECT 1 UNION ALL SELECT i+1 FROM s WHERE i<1000
|
||||
)
|
||||
INSERT INTO t2(rowid, doc) SELECT i, i || ' ' || i FROM s;
|
||||
}
|
||||
|
||||
do_execsql_test 2.1 {
|
||||
BEGIN;
|
||||
DELETE FROM t2 WHERE rowid=32;
|
||||
DELETE FROM t2 WHERE rowid=64;
|
||||
DELETE FROM t2 WHERE rowid=96;
|
||||
DELETE FROM t2 WHERE rowid=128;
|
||||
DELETE FROM t2 WHERE rowid=160;
|
||||
DELETE FROM t2 WHERE rowid=192;
|
||||
COMMIT;
|
||||
}
|
||||
|
||||
do_execsql_test 2.2 {
|
||||
SELECT * FROM t2('128');
|
||||
} {}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
|
||||
foreach {tn step} {
|
||||
1 3
|
||||
2 7
|
||||
3 15
|
||||
} {
|
||||
set step [expr $step]
|
||||
|
||||
reset_db
|
||||
db func document document
|
||||
do_execsql_test 3.$tn.0 {
|
||||
CREATE VIRTUAL TABLE t2 USING fts5(doc, content=, contentless_delete=1);
|
||||
INSERT INTO t2(t2, rank) VALUES('pgsz', 100);
|
||||
WITH s(i) AS (
|
||||
SELECT 1 UNION ALL SELECT i+1 FROM s WHERE i<1000
|
||||
)
|
||||
INSERT INTO t2(rowid, doc) SELECT i, i || ' ' || i FROM s;
|
||||
}
|
||||
do_execsql_test 3.$tn.1 {
|
||||
DELETE FROM t2 WHERE (rowid % $step)==0
|
||||
}
|
||||
do_execsql_test 3.$tn.2 {
|
||||
SELECT * FROM t2( $step * 5 )
|
||||
} {}
|
||||
}
|
||||
|
||||
|
||||
|
||||
finish_test
|
||||
|
||||
@@ -0,0 +1,196 @@
|
||||
# 2023 July 21
|
||||
#
|
||||
# The author disclaims copyright to this source code. In place of
|
||||
# a legal notice, here is a blessing:
|
||||
#
|
||||
# May you do good and not evil.
|
||||
# May you find forgiveness for yourself and forgive others.
|
||||
# May you share freely, never taking more than you give.
|
||||
#
|
||||
#***********************************************************************
|
||||
#
|
||||
# This file contains tests for the content= and content_rowid= options.
|
||||
#
|
||||
|
||||
source [file join [file dirname [info script]] fts5_common.tcl]
|
||||
set testprefix fts5contentless3
|
||||
|
||||
# If SQLITE_ENABLE_FTS5 is defined, omit this file.
|
||||
ifcapable !fts5 {
|
||||
finish_test
|
||||
return
|
||||
}
|
||||
|
||||
do_execsql_test 1.0 {
|
||||
CREATE VIRTUAL TABLE ft USING fts5(x, content=, contentless_delete=1);
|
||||
BEGIN;
|
||||
INSERT INTO ft VALUES('one one one');
|
||||
INSERT INTO ft VALUES('two two two');
|
||||
INSERT INTO ft VALUES('three three three');
|
||||
INSERT INTO ft VALUES('four four four');
|
||||
INSERT INTO ft VALUES('five five five');
|
||||
INSERT INTO ft VALUES('six six six');
|
||||
INSERT INTO ft VALUES('seven seven seven');
|
||||
INSERT INTO ft VALUES('eight eight eight');
|
||||
INSERT INTO ft VALUES('nine nine nine');
|
||||
COMMIT;
|
||||
|
||||
DELETE FROM ft WHERE rowid=3;
|
||||
}
|
||||
|
||||
proc myhex {hex} { binary decode hex $hex }
|
||||
db func myhex myhex
|
||||
|
||||
do_execsql_test 1.1 {
|
||||
UPDATE ft_data SET block =
|
||||
myhex('04000000 00000001' ||
|
||||
'01020304 01020304 01020304 01020304' ||
|
||||
'01020304 01020304 01020304 01020304'
|
||||
)
|
||||
WHERE id = (SELECT max(id) FROM ft_data);
|
||||
}
|
||||
|
||||
do_execsql_test 1.2 {
|
||||
DELETE FROM ft WHERE rowid=1
|
||||
}
|
||||
|
||||
do_execsql_test 1.3 {
|
||||
SELECT rowid FROM ft('two');
|
||||
} {2}
|
||||
|
||||
do_execsql_test 1.3 {
|
||||
UPDATE ft_data SET block =
|
||||
myhex('08000000 00000001' ||
|
||||
'0000000001020304 0000000001020304 0000000001020304 0000000001020304' ||
|
||||
'0000000001020304 0000000001020304 0000000001020304 0000000001020304'
|
||||
)
|
||||
WHERE id = (SELECT max(id) FROM ft_data);
|
||||
}
|
||||
|
||||
do_execsql_test 1.4 {
|
||||
SELECT rowid FROM ft('two');
|
||||
} {2}
|
||||
|
||||
do_execsql_test 1.5 {
|
||||
DELETE FROM ft WHERE rowid=4
|
||||
}
|
||||
|
||||
do_execsql_test 1.6 {
|
||||
UPDATE ft_data SET block = myhex('04000000 00000000')
|
||||
WHERE id = (SELECT max(id) FROM ft_data);
|
||||
}
|
||||
do_execsql_test 1.7 {
|
||||
SELECT rowid FROM ft('two');
|
||||
} {2}
|
||||
|
||||
do_execsql_test 1.8 {
|
||||
UPDATE ft_data SET block = myhex('04000000 00000000')
|
||||
WHERE id = (SELECT max(id) FROM ft_data);
|
||||
}
|
||||
do_execsql_test 1.9 {
|
||||
DELETE FROM ft WHERE rowid=8
|
||||
} {}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
reset_db
|
||||
do_execsql_test 2.0 {
|
||||
CREATE VIRTUAL TABLE ft USING fts5(x, content=, contentless_delete=1);
|
||||
INSERT INTO ft VALUES('one one one');
|
||||
INSERT INTO ft VALUES('two two two');
|
||||
INSERT INTO ft VALUES('three three three');
|
||||
INSERT INTO ft VALUES('four four four');
|
||||
INSERT INTO ft VALUES('five five five');
|
||||
INSERT INTO ft VALUES('six six six');
|
||||
INSERT INTO ft VALUES('seven seven seven');
|
||||
INSERT INTO ft VALUES('eight eight eight');
|
||||
INSERT INTO ft VALUES('nine nine nine');
|
||||
}
|
||||
|
||||
do_execsql_test 2.1 {
|
||||
INSERT INTO ft(ft) VALUES('optimize');
|
||||
}
|
||||
do_execsql_test 2.2 {
|
||||
SELECT count(*) FROM ft_data
|
||||
} {3}
|
||||
do_execsql_test 2.3 {
|
||||
DELETE FROM ft WHERE rowid=5
|
||||
}
|
||||
do_execsql_test 2.4 {
|
||||
SELECT count(*) FROM ft_data
|
||||
} {4}
|
||||
|
||||
# Check that an 'optimize' works (rewrites the index) if there is a single
|
||||
# segment with one or more tombstone hash pages.
|
||||
do_execsql_test 2.5 {
|
||||
INSERT INTO ft(ft) VALUES('optimize');
|
||||
}
|
||||
do_execsql_test 2.6 {
|
||||
SELECT count(*) FROM ft_data
|
||||
} {3}
|
||||
|
||||
# Check that an 'optimize' is a no-op if there is a single segment
|
||||
# and no tombstone hash pages.
|
||||
do_execsql_test 2.7 {
|
||||
INSERT INTO ft(ft) VALUES('optimize');
|
||||
SELECT rowid FROM ft_data;
|
||||
} [db eval {SELECT rowid FROM ft_data}]
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
reset_db
|
||||
do_execsql_test 3.0 {
|
||||
CREATE VIRTUAL TABLE ft USING fts5(x, content=, contentless_delete=1);
|
||||
INSERT INTO ft(ft, rank) VALUES('pgsz', 64);
|
||||
WITH s(i) AS (
|
||||
SELECT 1 UNION ALL SELECT i+1 FROM s WHERE i<1000
|
||||
)
|
||||
INSERT INTO ft(rowid, x) SELECT i, i||' '||i||' '||i||' '||i FROM s;
|
||||
INSERT INTO ft(ft) VALUES('optimize');
|
||||
}
|
||||
|
||||
do_execsql_test 3.1 {
|
||||
SELECT count(*) FROM ft_data
|
||||
} {200}
|
||||
|
||||
do_execsql_test 3.2 {
|
||||
DELETE FROM ft WHERE (rowid % 50)==0;
|
||||
SELECT count(*) FROM ft_data;
|
||||
} {203}
|
||||
|
||||
do_execsql_test 3.3 {
|
||||
INSERT INTO ft(ft, rank) VALUES('merge', 500);
|
||||
SELECT rowid FROM ft_data;
|
||||
} [db eval {SELECT rowid FROM ft_data}]
|
||||
|
||||
do_execsql_test 3.4 {
|
||||
INSERT INTO ft(ft, rank) VALUES('merge', -1000);
|
||||
SELECT count(*) FROM ft_data;
|
||||
} {197}
|
||||
|
||||
do_execsql_test 3.5 {
|
||||
DELETE FROM ft WHERE (rowid % 50)==1;
|
||||
SELECT count(*) FROM ft_data;
|
||||
} {200}
|
||||
|
||||
do_execsql_test 3.6 {
|
||||
SELECT level, segment, npgtombstone FROM fts5_structure(
|
||||
(SELECT block FROM ft_data WHERE id=10)
|
||||
)
|
||||
} {1 0 3}
|
||||
|
||||
do_test 3.6 {
|
||||
while 1 {
|
||||
set nChange [db total_changes]
|
||||
execsql { INSERT INTO ft(ft, rank) VALUES('merge', -5) }
|
||||
if {([db total_changes] - $nChange)<2} break
|
||||
}
|
||||
} {}
|
||||
|
||||
do_execsql_test 3.7 {
|
||||
SELECT level, segment, npgtombstone FROM fts5_structure(
|
||||
(SELECT block FROM ft_data WHERE id=10)
|
||||
)
|
||||
} {2 0 0}
|
||||
|
||||
|
||||
finish_test
|
||||
|
||||
@@ -0,0 +1,248 @@
|
||||
# 2023 July 21
|
||||
#
|
||||
# The author disclaims copyright to this source code. In place of
|
||||
# a legal notice, here is a blessing:
|
||||
#
|
||||
# May you do good and not evil.
|
||||
# May you find forgiveness for yourself and forgive others.
|
||||
# May you share freely, never taking more than you give.
|
||||
#
|
||||
#***********************************************************************
|
||||
#
|
||||
# This file contains tests for the content= and content_rowid= options.
|
||||
#
|
||||
|
||||
source [file join [file dirname [info script]] fts5_common.tcl]
|
||||
set testprefix fts5contentless4
|
||||
|
||||
# If SQLITE_ENABLE_FTS5 is defined, omit this file.
|
||||
ifcapable !fts5 {
|
||||
finish_test
|
||||
return
|
||||
}
|
||||
|
||||
proc document {n} {
|
||||
set vocab [list A B C D E F G H I J K L M N O P Q R S T U V W X Y Z]
|
||||
set ret [list]
|
||||
for {set ii 0} {$ii < $n} {incr ii} {
|
||||
lappend ret [lindex $vocab [expr int(rand()*[llength $vocab])]]
|
||||
}
|
||||
set ret
|
||||
}
|
||||
db func document document
|
||||
|
||||
do_execsql_test 1.0 {
|
||||
CREATE VIRTUAL TABLE ft USING fts5(x, content='', contentless_delete=1);
|
||||
INSERT INTO ft(ft, rank) VALUES('pgsz', 240);
|
||||
WITH s(i) AS (
|
||||
SELECT 1 UNION ALL SELECT i+1 FROM s WHERE i<1000
|
||||
)
|
||||
INSERT INTO ft SELECT document(12) FROM s;
|
||||
}
|
||||
|
||||
do_execsql_test 1.1 {
|
||||
INSERT INTO ft(ft) VALUES('optimize');
|
||||
}
|
||||
|
||||
do_execsql_test 1.2 {
|
||||
SELECT level, segment, nentry, nentrytombstone FROM fts5_structure((
|
||||
SELECT block FROM ft_data WHERE id=10
|
||||
))
|
||||
} {0 0 1000 0}
|
||||
|
||||
do_execsql_test 1.3 {
|
||||
DELETE FROM ft WHERE rowid < 50
|
||||
}
|
||||
|
||||
do_execsql_test 1.4 {
|
||||
SELECT level, segment, nentry, nentrytombstone FROM fts5_structure((
|
||||
SELECT block FROM ft_data WHERE id=10
|
||||
))
|
||||
} {0 0 1000 49}
|
||||
|
||||
do_execsql_test 1.5 {
|
||||
DELETE FROM ft WHERE rowid < 1000
|
||||
}
|
||||
|
||||
do_execsql_test 1.6 {
|
||||
SELECT level, segment, nentry, nentrytombstone FROM fts5_structure((
|
||||
SELECT block FROM ft_data WHERE id=10
|
||||
))
|
||||
} {1 0 1 0}
|
||||
|
||||
#--------------------------------------------------------------------------
|
||||
reset_db
|
||||
db func document document
|
||||
|
||||
do_execsql_test 2.0 {
|
||||
CREATE VIRTUAL TABLE ft USING fts5(x, content='', contentless_delete=1);
|
||||
}
|
||||
|
||||
do_test 2.1 {
|
||||
for {set ii 0} {$ii < 5000} {incr ii} {
|
||||
execsql { INSERT INTO ft VALUES( document(12) ) }
|
||||
}
|
||||
} {}
|
||||
|
||||
do_execsql_test 2.2 {
|
||||
SELECT sum(nentry) - sum(nentrytombstone) FROM fts5_structure((
|
||||
SELECT block FROM ft_data WHERE id=10
|
||||
))
|
||||
} {5000}
|
||||
|
||||
for {set ii 5000} {$ii >= 0} {incr ii -100} {
|
||||
do_execsql_test 2.3.$ii {
|
||||
DELETE FROM ft WHERE rowid > $ii
|
||||
}
|
||||
do_execsql_test 2.3.$ii.2 {
|
||||
SELECT
|
||||
CAST((total(nentry) - total(nentrytombstone)) AS integer)
|
||||
FROM
|
||||
fts5_structure( (SELECT block FROM ft_data WHERE id=10) )
|
||||
} $ii
|
||||
}
|
||||
|
||||
execsql_pp {
|
||||
SELECT * FROM fts5_structure((
|
||||
SELECT block FROM ft_data WHERE id=10
|
||||
))
|
||||
}
|
||||
|
||||
do_test 2.4 {
|
||||
for {set ii 0} {$ii < 5000} {incr ii} {
|
||||
execsql { INSERT INTO ft VALUES( document(12) ) }
|
||||
}
|
||||
} {}
|
||||
|
||||
for {set ii 1} {$ii <= 5000} {incr ii 10} {
|
||||
do_execsql_test 2.3.$ii {
|
||||
DELETE FROM ft WHERE rowid = $ii;
|
||||
INSERT INTO ft VALUES( document(12) );
|
||||
INSERT INTO ft(ft, rank) VALUES('merge', -10);
|
||||
}
|
||||
|
||||
do_execsql_test 2.3.$ii.2 {
|
||||
SELECT
|
||||
CAST((total(nentry) - total(nentrytombstone)) AS integer)
|
||||
FROM
|
||||
fts5_structure( (SELECT block FROM ft_data WHERE id=10) )
|
||||
} 5000
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
reset_db
|
||||
db func document document
|
||||
do_execsql_test 3.0 {
|
||||
CREATE VIRTUAL TABLE ft USING fts5(x, content='', contentless_delete=1);
|
||||
WITH s(i) AS (
|
||||
SELECT 1 UNION ALL SELECT i+1 FROM s WHERE i<100
|
||||
)
|
||||
INSERT INTO ft SELECT document(12) FROM s;
|
||||
}
|
||||
|
||||
do_catchsql_test 3.1 {
|
||||
INSERT INTO ft(ft, rank) VALUES('deletemerge', 'text');
|
||||
} {1 {SQL logic error}}
|
||||
do_catchsql_test 3.2 {
|
||||
INSERT INTO ft(ft, rank) VALUES('deletemerge', 50);
|
||||
} {0 {}}
|
||||
do_execsql_test 3.3 {
|
||||
SELECT * FROM ft_config WHERE k='deletemerge'
|
||||
} {deletemerge 50}
|
||||
do_catchsql_test 3.4 {
|
||||
INSERT INTO ft(ft, rank) VALUES('deletemerge', 101);
|
||||
} {0 {}}
|
||||
do_execsql_test 3.5 {
|
||||
SELECT * FROM ft_config WHERE k='deletemerge'
|
||||
} {deletemerge 101}
|
||||
|
||||
do_execsql_test 3.6 {
|
||||
DELETE FROM ft WHERE rowid<95
|
||||
}
|
||||
|
||||
do_execsql_test 3.7 {
|
||||
SELECT nentrytombstone, nentry FROM fts5_structure((
|
||||
SELECT block FROM ft_data WHERE id=10
|
||||
))
|
||||
} {94 100}
|
||||
|
||||
do_execsql_test 3.8 {
|
||||
DELETE FROM ft WHERE rowid=95
|
||||
}
|
||||
|
||||
do_execsql_test 3.9 {
|
||||
SELECT nentrytombstone, nentry FROM fts5_structure((
|
||||
SELECT block FROM ft_data WHERE id=10
|
||||
))
|
||||
} {95 100}
|
||||
|
||||
do_execsql_test 3.10 {
|
||||
DELETE FROM ft;
|
||||
WITH s(i) AS (
|
||||
SELECT 1 UNION ALL SELECT i+1 FROM s WHERE i<100
|
||||
)
|
||||
INSERT INTO ft SELECT document(12) FROM s;
|
||||
INSERT INTO ft(ft, rank) VALUES('deletemerge', 50);
|
||||
}
|
||||
|
||||
do_execsql_test 3.11 {
|
||||
DELETE FROM ft WHERE rowid<95
|
||||
}
|
||||
|
||||
do_execsql_test 3.12 {
|
||||
SELECT nentrytombstone, nentry FROM fts5_structure((
|
||||
SELECT block FROM ft_data WHERE id=10
|
||||
))
|
||||
} {0 6}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
reset_db
|
||||
db func document document
|
||||
do_execsql_test 4.0 {
|
||||
CREATE VIRTUAL TABLE x1 USING fts5(x, content='', contentless_delete=1);
|
||||
INSERT INTO x1(x1, rank) VALUES('usermerge', 16);
|
||||
INSERT INTO x1(x1, rank) VALUES('deletemerge', 40);
|
||||
INSERT INTO x1 VALUES('one');
|
||||
INSERT INTO x1 VALUES('two');
|
||||
INSERT INTO x1 VALUES('three');
|
||||
INSERT INTO x1 VALUES('four');
|
||||
INSERT INTO x1 VALUES('five');
|
||||
INSERT INTO x1 VALUES('six');
|
||||
INSERT INTO x1 VALUES('seven');
|
||||
INSERT INTO x1 VALUES('eight');
|
||||
INSERT INTO x1 VALUES('nine');
|
||||
INSERT INTO x1 VALUES('ten');
|
||||
}
|
||||
|
||||
do_execsql_test 4.1 {
|
||||
SELECT level, segment FROM fts5_structure((
|
||||
SELECT block FROM x1_data WHERE id=10
|
||||
))
|
||||
} {
|
||||
0 0 0 1 0 2 0 3 0 4 0 5 0 6 0 7 0 8 0 9
|
||||
}
|
||||
|
||||
for {set ii 1} {$ii < 4} {incr ii} {
|
||||
do_execsql_test 4.2.$ii {
|
||||
DELETE FROM x1 WHERE rowid = $ii;
|
||||
INSERT INTO x1(x1, rank) VALUES('merge', 5);
|
||||
SELECT level, segment FROM fts5_structure((
|
||||
SELECT block FROM x1_data WHERE id=10
|
||||
))
|
||||
} {
|
||||
0 0 0 1 0 2 0 3 0 4 0 5 0 6 0 7 0 8 0 9
|
||||
}
|
||||
}
|
||||
|
||||
do_execsql_test 4.3 {
|
||||
DELETE FROM x1 WHERE rowid = $ii;
|
||||
INSERT INTO x1(x1, rank) VALUES('merge', 5);
|
||||
SELECT level, segment, nentry FROM fts5_structure((
|
||||
SELECT block FROM x1_data WHERE id=10
|
||||
))
|
||||
} {
|
||||
1 0 6
|
||||
}
|
||||
|
||||
finish_test
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
# 2023 August 7
|
||||
#
|
||||
# The author disclaims copyright to this source code. In place of
|
||||
# a legal notice, here is a blessing:
|
||||
#
|
||||
# May you do good and not evil.
|
||||
# May you find forgiveness for yourself and forgive others.
|
||||
# May you share freely, never taking more than you give.
|
||||
#
|
||||
#***********************************************************************
|
||||
#
|
||||
# This file contains tests for the content= and content_rowid= options.
|
||||
#
|
||||
|
||||
source [file join [file dirname [info script]] fts5_common.tcl]
|
||||
set testprefix fts5contentless5
|
||||
|
||||
# If SQLITE_ENABLE_FTS5 is defined, omit this file.
|
||||
ifcapable !fts5 {
|
||||
finish_test
|
||||
return
|
||||
}
|
||||
|
||||
do_execsql_test 1.0 {
|
||||
CREATE VIRTUAL TABLE t1 USING fts5(a, b, c, content='', contentless_delete=1);
|
||||
INSERT INTO t1 VALUES('A', 'B', 'C');
|
||||
INSERT INTO t1 VALUES('D', 'E', 'F');
|
||||
INSERT INTO t1 VALUES('G', 'H', 'I');
|
||||
}
|
||||
|
||||
do_execsql_test 1.01 {
|
||||
CREATE TABLE t2(x, y);
|
||||
INSERT INTO t2 VALUES('x', 'y');
|
||||
}
|
||||
|
||||
# explain_i "UPDATE t1 SET a='a' WHERE t1.rowid=1"
|
||||
breakpoint
|
||||
explain_i "UPDATE t1 SET a='a' FROM t2 WHERE t1.rowid=1 AND b IS NULL"
|
||||
|
||||
#breakpoint
|
||||
#explain_i "UPDATE t1 SET a='a' WHERE b IS NULL AND rowid=?"
|
||||
|
||||
foreach {tn up err} {
|
||||
1 "UPDATE t1 SET a='a', b='b', c='c' WHERE rowid=1" 0
|
||||
2 "UPDATE t1 SET a='a', b='b' WHERE rowid=1" 1
|
||||
3 "UPDATE t1 SET b='b', c='c' WHERE rowid=1" 1
|
||||
4 "UPDATE t1 SET a='a', c='c' WHERE rowid=1" 1
|
||||
5 "UPDATE t1 SET a='a', c='c' WHERE t1.rowid=1 AND b IS NULL" 1
|
||||
6 "UPDATE t1 SET a='a' FROM t2 WHERE t1.rowid=1" 1
|
||||
7 "UPDATE t1 SET a='a', b='b', c='c' FROM t2 WHERE t1.rowid=1" 0
|
||||
} {
|
||||
|
||||
set res(0) {0 {}}
|
||||
set res(1) {1 {cannot UPDATE a subset of columns on fts5 contentless-delete table: t1}}
|
||||
do_catchsql_test 1.$tn $up $res($err)
|
||||
}
|
||||
|
||||
finish_test
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
#
|
||||
|
||||
source [file join [file dirname [info script]] fts5_common.tcl]
|
||||
set testprefix fts5corrupt3
|
||||
set testprefix fts5corrupt5
|
||||
|
||||
# If SQLITE_ENABLE_FTS5 is defined, omit this file.
|
||||
ifcapable !fts5 {
|
||||
@@ -793,6 +793,94 @@ do_catchsql_test 4.5 {
|
||||
REPLACE INTO t1(rowid,a,b,rowid) VALUES(200,1,2,3);
|
||||
} {1 {database disk image is malformed}}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
reset_db
|
||||
do_test 5.0 {
|
||||
sqlite3 db {}
|
||||
db deserialize [decode_hexdb {
|
||||
.open --hexdb
|
||||
| size 28672 pagesize 4096 filename crash-0c6d3451d11597.db
|
||||
| page 1 offset 0
|
||||
| 0: 53 51 4c 69 74 65 20 66 6f 72 6d 61 74 20 33 00 SQLite format 3.
|
||||
| 16: 10 00 01 01 00 40 20 20 00 00 00 00 00 00 00 07 .....@ ........
|
||||
| 32: 00 00 00 00 00 00 00 00 00 00 00 07 00 00 00 04 ................
|
||||
| 96: 00 00 00 00 0d 00 00 00 07 0d d2 00 0f c4 0f 6d ...............m
|
||||
| 112: 0f 02 0e ab 0e 4e 0d f6 0d d2 00 00 00 00 00 00 .....N..........
|
||||
| 3536: 00 00 22 07 06 17 11 11 01 31 74 61 62 6c 65 74 .........1tablet
|
||||
| 3552: 32 74 32 07 43 52 45 41 54 45 20 54 41 42 4c 45 2t2.CREATE TABLE
|
||||
| 3568: 20 74 32 28 78 29 56 06 06 17 1f 1f 01 7d 74 61 t2(x)V.......ta
|
||||
| 3584: 62 6c 65 74 31 5f 63 6f 6e 66 69 67 74 31 5f 63 blet1_configt1_c
|
||||
| 3600: 6f 6e 66 69 67 06 43 52 45 41 54 45 20 54 41 42 onfig.CREATE TAB
|
||||
| 3616: 4c 45 20 27 74 31 5f 63 6f 6e 66 69 67 27 28 6b LE 't1_config'(k
|
||||
| 3632: 20 50 52 49 4d 41 52 59 20 4b 45 59 2c 20 76 29 PRIMARY KEY, v)
|
||||
| 3648: 20 57 49 54 48 4f 55 54 20 52 4f 57 49 44 5b 05 WITHOUT ROWID[.
|
||||
| 3664: 07 17 21 21 01 81 01 74 61 62 6c 65 74 31 5f 64 ..!!...tablet1_d
|
||||
| 3680: 6f 63 73 69 7a 65 74 31 5f 64 6f 63 73 69 7a 65 ocsizet1_docsize
|
||||
| 3696: 05 43 52 45 41 54 45 20 54 41 42 4c 45 20 27 74 .CREATE TABLE 't
|
||||
| 3712: 31 5f 64 6f 63 73 69 7a 65 27 28 69 64 20 49 4e 1_docsize'(id IN
|
||||
| 3728: 54 45 47 45 52 20 50 52 49 4d 41 52 59 20 4b 45 TEGER PRIMARY KE
|
||||
| 3744: 59 2c 20 73 7a 20 42 4c 4f 42 29 55 04 06 17 21 Y, sz BLOB)U...!
|
||||
| 3760: 21 01 77 74 61 62 6c 65 74 31 5f 63 6f 6e 74 65 !.wtablet1_conte
|
||||
| 3776: 6e 74 74 31 5f 63 6f 6e 74 65 6e 74 04 43 52 45 ntt1_content.CRE
|
||||
| 3792: 41 54 45 20 54 41 42 4c 45 20 27 74 31 5f 63 6f ATE TABLE 't1_co
|
||||
| 3808: 6e 74 65 6e 74 27 28 69 64 20 49 4e 54 45 47 45 ntent'(id INTEGE
|
||||
| 3824: 52 20 50 52 49 4d 41 52 59 20 4b 45 59 2c 20 63 R PRIMARY KEY, c
|
||||
| 3840: 30 29 69 03 07 17 19 19 01 81 2d 74 61 62 6c 65 0)i.......-table
|
||||
| 3856: 74 31 5f 69 64 78 74 31 5f 69 64 78 03 43 52 45 t1_idxt1_idx.CRE
|
||||
| 3872: 41 54 45 20 54 41 42 4c 45 20 27 74 31 5f 69 64 ATE TABLE 't1_id
|
||||
| 3888: 78 27 28 73 65 67 69 64 2c 20 74 65 72 6d 2c 20 x'(segid, term,
|
||||
| 3904: 70 67 6e 6f 2c 20 50 52 49 4d 41 52 59 20 4b 45 pgno, PRIMARY KE
|
||||
| 3920: 59 28 73 65 67 69 64 2c 20 74 65 72 6d 29 29 20 Y(segid, term))
|
||||
| 3936: 57 49 54 48 4f 55 54 20 52 4f 57 49 44 55 02 07 WITHOUT ROWIDU..
|
||||
| 3952: 17 1b 1b 01 81 01 74 61 62 6c 65 74 31 5f 64 61 ......tablet1_da
|
||||
| 3968: 74 61 74 31 5f 64 61 74 61 02 43 52 45 41 54 45 tat1_data.CREATE
|
||||
| 3984: 20 54 41 42 4c 45 20 27 74 31 5f 64 61 74 61 27 TABLE 't1_data'
|
||||
| 4000: 28 69 64 20 49 4e 54 45 47 45 52 20 50 52 49 4d (id INTEGER PRIM
|
||||
| 4016: 41 52 b9 20 4b 45 59 2c 20 62 6c 6f 63 6b 20 42 AR. KEY, block B
|
||||
| 4032: 4c 4f 42 29 3a 01 06 17 11 11 08 63 74 61 62 6c LOB):......ctabl
|
||||
| 4048: 65 74 31 74 31 43 52 45 41 54 45 20 56 49 52 54 et1t1CREATE VIRT
|
||||
| 4064: 55 41 4c 20 54 41 42 4c 45 20 74 31 20 55 53 49 UAL TABLE t1 USI
|
||||
| 4080: 4e 47 20 66 74 73 35 28 63 6f 6e 74 65 6e 74 29 NG fts5(content)
|
||||
| page 2 offset 4096
|
||||
| 0: 0d 00 00 00 03 0f bd 00 0f e8 0f ef 0f bd 00 00 ................
|
||||
| 16: 00 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
|
||||
| 4016: 00 00 00 00 00 00 00 00 00 00 00 00 00 24 84 80 .............$..
|
||||
| 4032: 80 80 80 01 03 00 4e 00 00 00 1e 06 30 61 62 61 ......N.....0aba
|
||||
| 4048: 63 6b 01 02 02 04 02 66 74 02 02 02 04 04 6e 64 ck.....ft.....nd
|
||||
| 4064: 6f 6e 03 02 02 04 0a 07 05 01 03 00 10 03 03 0f on..............
|
||||
| 4080: 0a 03 00 24 00 00 00 00 01 01 01 00 01 01 01 11 ...$............
|
||||
| page 3 offset 8192
|
||||
| 0: 0a 00 00 00 01 0f 00 00 00 00 00 00 00 00 00 00 ................
|
||||
| 4080: 00 00 00 00 00 00 00 00 00 00 05 04 09 0c 01 02 ................
|
||||
| page 4 offset 12288
|
||||
| 0: 0d 00 00 00 03 0f e0 00 0f f6 0f ec 0f e0 00 00 ................
|
||||
| 4064: 0a 03 03 00 1b 61 62 61 6e 64 6f 6e 08 02 03 00 .....abandon....
|
||||
| 4080: 17 61 62 61 66 74 08 01 03 00 17 61 62 61 63 6b .abaft.....aback
|
||||
| page 5 offset 16384
|
||||
| 0: 0d 00 00 00 03 0f ee 00 0f fa 0f f4 0f ee 00 00 ................
|
||||
| 16: 00 00 01 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
|
||||
| 4064: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 04 03 ................
|
||||
| 4080: 03 00 0e 01 04 02 03 00 0e 01 04 01 03 00 0e 01 ................
|
||||
| page 6 offset 20480
|
||||
| 0: 0a 00 00 00 01 0f f4 00 0f f4 00 00 00 00 00 00 ................
|
||||
| 4080: 00 00 00 00 0b 03 1b 01 76 65 72 73 69 6f 6e 04 ........version.
|
||||
| page 7 offset 24576
|
||||
| 0: 0d 00 00 10 03 0f d6 00 0f f4 10 e1 0f d6 00 00 ................
|
||||
| 16: 00 00 00 01 00 00 00 00 00 00 00 00 00 00 00 00 ................
|
||||
| 4048: 00 00 00 00 00 00 09 03 02 1b 72 65 62 75 69 6c ..........rebuil
|
||||
| 4064: 64 11 02 02 2b 69 6e 74 65 67 72 69 74 79 2d 63 d...+integrity-c
|
||||
| 4080: 68 65 63 6b 0a 01 02 1d 6f 70 74 69 6d 00 00 00 heck....optim...
|
||||
| end crash-0c6d3451d11597.db
|
||||
}]} {}
|
||||
|
||||
do_execsql_test 5.1 {
|
||||
INSERT INTO t1(t1,rank) VALUES('secure-delete',1);
|
||||
}
|
||||
do_catchsql_test 5.4 {
|
||||
UPDATE t1 SET content=randomblob(500);
|
||||
} {1 {database disk image is malformed}}
|
||||
|
||||
|
||||
sqlite3_fts5_may_be_corrupt 0
|
||||
finish_test
|
||||
|
||||
|
||||
@@ -96,4 +96,33 @@ set r 137438953475
|
||||
db close
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
reset_db
|
||||
do_execsql_test 2.0 {
|
||||
CREATE VIRTUAL TABLE t1 USING fts5(x);
|
||||
BEGIN;
|
||||
INSERT INTO t1 VALUES('abc');
|
||||
INSERT INTO t1 VALUES('b d d d');
|
||||
COMMIT;
|
||||
INSERT INTO t1(t1, rank) VALUES('secure-delete', 1);
|
||||
}
|
||||
|
||||
execsql_pp {
|
||||
SELECT id, quote(block) FROM t1_data
|
||||
}
|
||||
|
||||
do_execsql_test 2.1 {
|
||||
SELECT quote(block) FROM t1_data WHERE id > 10;
|
||||
} {X'0000001A04306162630102020101620202020101640206030303040806'}
|
||||
|
||||
do_execsql_test 2.2 {
|
||||
UPDATE t1_data SET
|
||||
block=X'0000001A04306162630102025501620202020101640206030303040806'
|
||||
WHERE id>10
|
||||
}
|
||||
|
||||
do_catchsql_test 2.3 {
|
||||
DELETE FROM t1 WHERE rowid = 1
|
||||
} {1 {database disk image is malformed}}
|
||||
|
||||
finish_test
|
||||
|
||||
@@ -95,6 +95,9 @@ do_execsql_test 3.3 {
|
||||
SELECT rowid, bm25(e1) FROM e1 WHERE e1 MATCH '"/" OR "just"' ORDER BY rank;
|
||||
} {1 -1e-06}
|
||||
|
||||
do_execsql_test 3.4 "
|
||||
SELECT fts5_expr_tcl('e AND \" \"');
|
||||
" {{AND [nearset -- {e}] [{}]}}
|
||||
|
||||
|
||||
finish_test
|
||||
|
||||
@@ -0,0 +1,111 @@
|
||||
# 2023 July 20
|
||||
#
|
||||
# The author disclaims copyright to this source code. In place of
|
||||
# a legal notice, here is a blessing:
|
||||
#
|
||||
# May you do good and not evil.
|
||||
# May you find forgiveness for yourself and forgive others.
|
||||
# May you share freely, never taking more than you give.
|
||||
#
|
||||
#*************************************************************************
|
||||
#
|
||||
# This file is focused on OOM errors. Particularly those that may occur
|
||||
# when using contentless_delete=1 databases.
|
||||
#
|
||||
|
||||
source [file join [file dirname [info script]] fts5_common.tcl]
|
||||
source $testdir/malloc_common.tcl
|
||||
set testprefix fts5faultF
|
||||
|
||||
# If SQLITE_ENABLE_FTS5 is not defined, omit this file.
|
||||
ifcapable !fts5 {
|
||||
finish_test
|
||||
return
|
||||
}
|
||||
|
||||
faultsim_save_and_close
|
||||
do_faultsim_test 1 -prep {
|
||||
faultsim_restore_and_reopen
|
||||
} -body {
|
||||
execsql {
|
||||
CREATE VIRTUAL TABLE t1 USING fts5(x, y, content=, contentless_delete=1)
|
||||
}
|
||||
} -test {
|
||||
faultsim_test_result {0 {}} {1 {vtable constructor failed: t1}}
|
||||
}
|
||||
|
||||
reset_db
|
||||
do_execsql_test 2.0 {
|
||||
CREATE VIRTUAL TABLE t1 USING fts5(doc, content=, contentless_delete=1);
|
||||
BEGIN;
|
||||
INSERT INTO t1(rowid, doc) VALUES(1, 'a b c d');
|
||||
INSERT INTO t1(rowid, doc) VALUES(2, 'a b c d');
|
||||
INSERT INTO t1(rowid, doc) VALUES(3, 'a b c d');
|
||||
INSERT INTO t1(rowid, doc) VALUES(4, 'a b c d');
|
||||
COMMIT;
|
||||
DELETE FROM t1 WHERE rowid IN (2, 4);
|
||||
}
|
||||
|
||||
do_faultsim_test 2 -prep {
|
||||
sqlite3 db test.db
|
||||
execsql { SELECT rowid FROM t1 }
|
||||
} -body {
|
||||
execsql {
|
||||
SELECT rowid FROM t1('b c');
|
||||
}
|
||||
} -test {
|
||||
faultsim_test_result {0 {1 3}}
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
reset_db
|
||||
do_execsql_test 3.0 {
|
||||
CREATE VIRTUAL TABLE t1 USING fts5(doc, content=, contentless_delete=1);
|
||||
BEGIN;
|
||||
INSERT INTO t1(rowid, doc) VALUES(1, 'a b c d');
|
||||
INSERT INTO t1(rowid, doc) VALUES(2, 'a b c d');
|
||||
INSERT INTO t1(rowid, doc) VALUES(3, 'a b c d');
|
||||
INSERT INTO t1(rowid, doc) VALUES(4, 'a b c d');
|
||||
COMMIT;
|
||||
}
|
||||
|
||||
faultsim_save_and_close
|
||||
do_faultsim_test 3 -prep {
|
||||
faultsim_restore_and_reopen
|
||||
execsql { SELECT rowid FROM t1 }
|
||||
} -body {
|
||||
execsql {
|
||||
INSERT INTO t1(rowid, doc) VALUES(5, 'a b c d');
|
||||
}
|
||||
} -test {
|
||||
faultsim_test_result {0 {}}
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
reset_db
|
||||
do_execsql_test 4.0 {
|
||||
CREATE VIRTUAL TABLE t1 USING fts5(doc, content=, contentless_delete=1);
|
||||
INSERT INTO t1(t1, rank) VALUES('pgsz', 64);
|
||||
WITH s(i) AS (
|
||||
SELECT 1 UNION ALL SELECT i+1 FROM s WHERE i<1000
|
||||
)
|
||||
INSERT INTO t1(rowid, doc) SELECT i, 'a b c d' FROM s;
|
||||
}
|
||||
|
||||
do_execsql_test 4.1 { DELETE FROM t1 WHERE rowid <= 25 }
|
||||
|
||||
faultsim_save_and_close
|
||||
do_faultsim_test 4 -faults oom-t* -prep {
|
||||
faultsim_restore_and_reopen
|
||||
execsql { SELECT rowid FROM t1 }
|
||||
} -body {
|
||||
execsql {
|
||||
DELETE FROM t1 WHERE rowid < 100
|
||||
}
|
||||
} -test {
|
||||
faultsim_test_result {0 {}}
|
||||
}
|
||||
|
||||
|
||||
finish_test
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
# 2023 May 16
|
||||
#
|
||||
# The author disclaims copyright to this source code. In place of
|
||||
# a legal notice, here is a blessing:
|
||||
#
|
||||
# May you do good and not evil.
|
||||
# May you find forgiveness for yourself and forgive others.
|
||||
# May you share freely, never taking more than you give.
|
||||
#
|
||||
#*************************************************************************
|
||||
#
|
||||
|
||||
source [file join [file dirname [info script]] fts5_common.tcl]
|
||||
set testprefix fts5limits
|
||||
return_if_no_fts5
|
||||
|
||||
|
||||
do_execsql_test 1.0 {
|
||||
CREATE VIRTUAL TABLE ft USING fts5(x);
|
||||
}
|
||||
|
||||
# Default limit for expression depth is 256
|
||||
#
|
||||
foreach {tn nRepeat op bErr} {
|
||||
1 200 AND 0
|
||||
2 200 NOT 0
|
||||
3 200 OR 0
|
||||
|
||||
4 260 AND 0
|
||||
5 260 NOT 1
|
||||
6 260 OR 0
|
||||
} {
|
||||
set L [string repeat "abc " $nRepeat]
|
||||
set Q [join $L " $op "]
|
||||
|
||||
set res {0 {}}
|
||||
if {$bErr} {
|
||||
set res "1 {fts5 expression tree is too large (maximum depth 256)}"
|
||||
}
|
||||
|
||||
do_catchsql_test 1.$tn {
|
||||
SELECT * FROM ft($Q)
|
||||
} $res
|
||||
}
|
||||
|
||||
finish_test
|
||||
|
||||
@@ -329,7 +329,7 @@ do_execsql_test 12.3 {
|
||||
reset_db
|
||||
sqlite3_db_config db DEFENSIVE 1
|
||||
|
||||
do_execsql_test 13.0 {
|
||||
do_execsql_test 13.1.0 {
|
||||
CREATE TABLE a (id INTEGER PRIMARY KEY, name TEXT);
|
||||
CREATE VIRTUAL TABLE b USING fts5(name);
|
||||
CREATE TRIGGER a_trigger AFTER INSERT ON a BEGIN
|
||||
@@ -337,18 +337,44 @@ do_execsql_test 13.0 {
|
||||
END;
|
||||
}
|
||||
|
||||
do_test 13.1 {
|
||||
do_test 13.1.1 {
|
||||
set ::STMT [
|
||||
sqlite3_prepare db "INSERT INTO a VALUES (1, 'foo') RETURNING id;" -1 dummy
|
||||
]
|
||||
sqlite3_step $::STMT
|
||||
} {SQLITE_ROW}
|
||||
|
||||
do_test 13.2 {
|
||||
do_test 13.1.2 {
|
||||
sqlite3_finalize $::STMT
|
||||
} {SQLITE_OK}
|
||||
|
||||
do_test 13.3 {
|
||||
do_test 13.1.3 {
|
||||
sqlite3_errmsg db
|
||||
} {not an error}
|
||||
|
||||
reset_db
|
||||
sqlite3_db_config db DEFENSIVE 1
|
||||
do_execsql_test 13.2.0 {
|
||||
BEGIN;
|
||||
CREATE TABLE a (id INTEGER PRIMARY KEY, name TEXT);
|
||||
CREATE VIRTUAL TABLE b USING fts5(name);
|
||||
CREATE TRIGGER a_trigger AFTER INSERT ON a BEGIN
|
||||
INSERT INTO b (name) VALUES ('foo');
|
||||
END;
|
||||
}
|
||||
|
||||
do_test 13.2.1 {
|
||||
set ::STMT [
|
||||
sqlite3_prepare db "INSERT INTO a VALUES (1, 'foo') RETURNING id;" -1 dummy
|
||||
]
|
||||
sqlite3_step $::STMT
|
||||
} {SQLITE_ROW}
|
||||
|
||||
do_test 13.2.2 {
|
||||
sqlite3_finalize $::STMT
|
||||
} {SQLITE_OK}
|
||||
|
||||
do_test 13.2.3 {
|
||||
sqlite3_errmsg db
|
||||
} {not an error}
|
||||
|
||||
|
||||
@@ -0,0 +1,346 @@
|
||||
# Quick-and-dirty makefile to bootstrap the sqlite3-jni project. This
|
||||
# build assumes a Linux-like system.
|
||||
default: all
|
||||
|
||||
JAVA_HOME ?= $(HOME)/jdk/current
|
||||
# e.g. /usr/lib/jvm/default-javajava-19-openjdk-amd64
|
||||
JDK_HOME ?= $(JAVA_HOME)
|
||||
# ^^^ JDK_HOME is not as widely used as JAVA_HOME
|
||||
bin.javac := $(JDK_HOME)/bin/javac
|
||||
bin.java := $(JDK_HOME)/bin/java
|
||||
bin.jar := $(JDK_HOME)/bin/jar
|
||||
ifeq (,$(wildcard $(JDK_HOME)))
|
||||
$(error set JDK_HOME to the top-most dir of your JDK installation.)
|
||||
endif
|
||||
MAKEFILE := $(lastword $(MAKEFILE_LIST))
|
||||
$(MAKEFILE):
|
||||
|
||||
package.jar := sqlite3-jni.jar
|
||||
|
||||
dir.top := ../..
|
||||
dir.tool := ../../tool
|
||||
dir.jni := $(patsubst %/,%,$(dir $(MAKEFILE)))
|
||||
|
||||
dir.src := $(dir.jni)/src
|
||||
dir.src.c := $(dir.src)/c
|
||||
dir.bld := $(dir.jni)/bld
|
||||
dir.bld.c := $(dir.bld)
|
||||
dir.src.jni := $(dir.src)/org/sqlite/jni
|
||||
dir.src.jni.tester := $(dir.src.jni)/tester
|
||||
$(dir.bld.c):
|
||||
mkdir -p $@
|
||||
|
||||
classpath := $(dir.src)
|
||||
CLEAN_FILES := $(package.jar)
|
||||
DISTCLEAN_FILES := $(dir.jni)/*~ $(dir.src.c)/*~ $(dir.src.jni)/*~
|
||||
|
||||
sqlite3-jni.h := $(dir.src.c)/sqlite3-jni.h
|
||||
.NOTPARALLEL: $(sqlite3-jni.h)
|
||||
SQLite3Jni.java := src/org/sqlite/jni/SQLite3Jni.java
|
||||
SQLTester.java := src/org/sqlite/jni/tester/SQLTester.java
|
||||
SQLite3Jni.class := $(SQLite3Jni.java:.java=.class)
|
||||
SQLTester.class := $(SQLTester.java:.java=.class)
|
||||
|
||||
########################################################################
|
||||
# The future of FTS5 customization in this API is as yet unclear.
|
||||
# It would be a real doozy to bind to JNI.
|
||||
enable.fts5 ?= 1
|
||||
# If enable.tester is 0, the org/sqlite/jni/tester/* bits are elided.
|
||||
enable.tester ?= 1
|
||||
|
||||
# bin.version-info = binary to output various sqlite3 version info
|
||||
# building the distribution zip file.
|
||||
bin.version-info := $(dir.top)/version-info
|
||||
.NOTPARALLEL: $(bin.version-info)
|
||||
$(bin.version-info): $(dir.tool)/version-info.c $(sqlite3.h) $(dir.top)/Makefile
|
||||
$(MAKE) -C $(dir.top) version-info
|
||||
|
||||
# Be explicit about which Java files to compile so that we can work on
|
||||
# in-progress files without requiring them to be in a compilable statae.
|
||||
JAVA_FILES.main := $(patsubst %,$(dir.src.jni)/%,\
|
||||
BusyHandler.java \
|
||||
Collation.java \
|
||||
CollationNeeded.java \
|
||||
CommitHook.java \
|
||||
NativePointerHolder.java \
|
||||
OutputPointer.java \
|
||||
ProgressHandler.java \
|
||||
ResultCode.java \
|
||||
RollbackHook.java \
|
||||
SQLFunction.java \
|
||||
sqlite3_context.java \
|
||||
sqlite3.java \
|
||||
SQLite3Jni.java \
|
||||
sqlite3_stmt.java \
|
||||
sqlite3_value.java \
|
||||
Tester1.java \
|
||||
Tracer.java \
|
||||
UpdateHook.java \
|
||||
ValueHolder.java \
|
||||
)
|
||||
ifeq (1,$(enable.fts5))
|
||||
JAVA_FILES.main += $(patsubst %,$(dir.src.jni)/%,\
|
||||
fts5_api.java \
|
||||
fts5_extension_function.java \
|
||||
fts5_tokenizer.java \
|
||||
Fts5.java \
|
||||
Fts5Context.java \
|
||||
Fts5ExtensionApi.java \
|
||||
Fts5Function.java \
|
||||
Fts5PhraseIter.java \
|
||||
Fts5Tokenizer.java \
|
||||
TesterFts5.java \
|
||||
)
|
||||
endif
|
||||
JAVA_FILES.tester := $(dir.src.jni.tester)/SQLTester.java
|
||||
|
||||
CLASS_FILES.main := $(JAVA_FILES.main:.java=.class)
|
||||
CLASS_FILES.tester := $(JAVA_FILES.tester:.java=.class)
|
||||
|
||||
JAVA_FILES += $(JAVA_FILES.main)
|
||||
ifeq (1,$(enable.tester))
|
||||
JAVA_FILES += $(JAVA_FILES.tester)
|
||||
endif
|
||||
|
||||
CLASS_FILES :=
|
||||
define DOTCLASS_DEPS
|
||||
$(1).class: $(1).java $(MAKEFILE)
|
||||
all: $(1).class
|
||||
CLASS_FILES += $(1).class
|
||||
endef
|
||||
$(foreach B,$(basename $(JAVA_FILES)),$(eval $(call DOTCLASS_DEPS,$(B))))
|
||||
$(CLASS_FILES.tester): $(CLASS_FILES.main)
|
||||
javac.flags ?= -Xlint:unchecked -Xlint:deprecation
|
||||
java.flags ?=
|
||||
jnicheck ?= 1
|
||||
ifeq (1,$(jnicheck))
|
||||
java.flags += -Xcheck:jni
|
||||
endif
|
||||
$(SQLite3Jni.class): $(JAVA_FILES)
|
||||
$(bin.javac) $(javac.flags) -h $(dir.bld.c) -cp $(classpath) $(JAVA_FILES)
|
||||
all: $(SQLite3Jni.class)
|
||||
#.PHONY: classfiles
|
||||
|
||||
########################################################################
|
||||
# Set up sqlite3.c and sqlite3.h...
|
||||
#
|
||||
# To build with SEE (https://sqlite.org/see), either put sqlite3-see.c
|
||||
# in the top of this build tree or pass
|
||||
# sqlite3.c=PATH_TO_sqlite3-see.c to the build. Note that only
|
||||
# encryption modules with no 3rd-party dependencies will currently
|
||||
# work here: AES256-OFB, AES128-OFB, and AES128-CCM. Not
|
||||
# coincidentally, those 3 modules are included in the sqlite3-see.c
|
||||
# bundle.
|
||||
#
|
||||
# A custom sqlite3.c must not have any spaces in its name.
|
||||
# $(sqlite3.canonical.c) must point to the sqlite3.c in
|
||||
# the sqlite3 canonical source tree, as that source file
|
||||
# is required for certain utility and test code.
|
||||
sqlite3.canonical.c := $(firstword $(wildcard $(dir.src.c)/sqlite3.c) $(dir.top)/sqlite3.c)
|
||||
sqlite3.canonical.h := $(firstword $(wildcard $(dir.src.c)/sqlite3.h) $(dir.top)/sqlite3.h)
|
||||
sqlite3.c := $(sqlite3.canonical.c)
|
||||
sqlite3.h := $(sqlite3.canonical.h)
|
||||
#ifeq (,$(shell grep sqlite3_activate_see $(sqlite3.c) 2>/dev/null))
|
||||
# SQLITE_C_IS_SEE := 0
|
||||
#else
|
||||
# SQLITE_C_IS_SEE := 1
|
||||
# $(info This is an SEE build.)
|
||||
#endif
|
||||
|
||||
.NOTPARALLEL: $(sqlite3.h)
|
||||
$(sqlite3.h):
|
||||
$(MAKE) -C $(dir.top) sqlite3.c
|
||||
$(sqlite3.c): $(sqlite3.h)
|
||||
|
||||
SQLITE_OPT = \
|
||||
-DSQLITE_ENABLE_RTREE \
|
||||
-DSQLITE_ENABLE_EXPLAIN_COMMENTS \
|
||||
-DSQLITE_ENABLE_STMTVTAB \
|
||||
-DSQLITE_ENABLE_DBPAGE_VTAB \
|
||||
-DSQLITE_ENABLE_DBSTAT_VTAB \
|
||||
-DSQLITE_ENABLE_BYTECODE_VTAB \
|
||||
-DSQLITE_ENABLE_OFFSET_SQL_FUNC \
|
||||
-DSQLITE_OMIT_LOAD_EXTENSION \
|
||||
-DSQLITE_OMIT_DEPRECATED \
|
||||
-DSQLITE_OMIT_SHARED_CACHE \
|
||||
-DSQLITE_THREADSAFE=0 \
|
||||
-DSQLITE_TEMP_STORE=2 \
|
||||
-DSQLITE_USE_URI=1 \
|
||||
-DSQLITE_C=$(sqlite3.c)
|
||||
# -DSQLITE_DEBUG
|
||||
# -DSQLITE_DEBUG is just to work around a -Wall warning
|
||||
# for a var which gets set in all builds but only read
|
||||
# via assert().
|
||||
|
||||
SQLITE_OPT += -g -DDEBUG -UNDEBUG
|
||||
|
||||
ifeq (1,$(enable.fts5))
|
||||
SQLITE_OPT += -DSQLITE_ENABLE_FTS5
|
||||
endif
|
||||
|
||||
sqlite3-jni.c := $(dir.src.c)/sqlite3-jni.c
|
||||
sqlite3-jni.o := $(dir.bld.c)/sqlite3-jni.o
|
||||
sqlite3-jni.h := $(dir.src.c)/sqlite3-jni.h
|
||||
sqlite3-jni.dll := $(dir.bld.c)/libsqlite3-jni.so
|
||||
# All javac-generated .h files must be listed in $(sqlite3-jni.h.in):
|
||||
sqlite3-jni.h.in :=
|
||||
define ADD_JNI_H
|
||||
sqlite3-jni.h.in += $$(dir.bld.c)/org_sqlite_jni_$(1).h
|
||||
$$(dir.bld.c)/org_sqlite_jni_$(1).h: $$(dir.src.jni)/$(1).java
|
||||
endef
|
||||
$(eval $(call ADD_JNI_H,SQLite3Jni))
|
||||
ifeq (1,$(enable.fts5))
|
||||
$(eval $(call ADD_JNI_H,Fts5ExtensionApi))
|
||||
$(eval $(call ADD_JNI_H,fts5_api))
|
||||
$(eval $(call ADD_JNI_H,fts5_tokenizer))
|
||||
endif
|
||||
ifeq (1,$(enable.tester))
|
||||
sqlite3-jni.h.in += $(dir.bld.c)/org_sqlite_jni_tester_SQLTester.h
|
||||
$(dir.bld.c)/org_sqlite_jni_tester_SQLTester.h: $(dir.src.jni.tester)/SQLTester.java
|
||||
endif
|
||||
#sqlite3-jni.dll.cfiles := $(dir.src.c)
|
||||
sqlite3-jni.dll.cflags = \
|
||||
-fPIC \
|
||||
-I. \
|
||||
-I$(dir $(sqlite3.h)) \
|
||||
-I$(dir.src.c) \
|
||||
-I$(JDK_HOME)/include \
|
||||
$(patsubst %,-I%,$(patsubst %.h,,$(wildcard $(JDK_HOME)/include/*))) \
|
||||
-Wall
|
||||
# Using (-Wall -Wextra) triggers an untennable number of
|
||||
# gcc warnings from sqlite3.c for mundane things like
|
||||
# unused parameters.
|
||||
#
|
||||
# The gross $(patsubst...) above is to include the platform-specific
|
||||
# subdir which lives under $(JDK_HOME)/include and is a required
|
||||
# include path for client-level code.
|
||||
########################################################################
|
||||
ifeq (1,$(enable.tester))
|
||||
sqlite3-jni.dll.cflags += -DS3JNI_ENABLE_SQLTester
|
||||
endif
|
||||
$(sqlite3-jni.h): $(sqlite3-jni.h.in) $(MAKEFILE)
|
||||
cat $(sqlite3-jni.h.in) > $@
|
||||
$(sqlite3-jni.dll): $(sqlite3-jni.h) $(sqlite3.c) $(sqlite3.h)
|
||||
$(sqlite3-jni.dll): $(dir.bld.c) $(sqlite3-jni.c) $(SQLite3Jni.java) $(MAKEFILE)
|
||||
$(CC) $(sqlite3-jni.dll.cflags) $(SQLITE_OPT) \
|
||||
$(sqlite3-jni.c) -shared -o $@
|
||||
all: $(sqlite3-jni.dll)
|
||||
|
||||
.PHONY: test
|
||||
test.flags ?= -v
|
||||
test: $(SQLite3Jni.class) $(sqlite3-jni.dll)
|
||||
$(bin.java) -ea -Djava.library.path=$(dir.bld.c) \
|
||||
$(java.flags) -cp $(classpath) \
|
||||
org.sqlite.jni.Tester1 $(if $(test.flags),-- $(test.flags),)
|
||||
|
||||
tester.scripts := $(sort $(wildcard $(dir.src)/tests/*.test))
|
||||
tester.flags ?= # --verbose
|
||||
.PHONY: tester tester-local tester-ext
|
||||
ifeq (1,$(enable.tester))
|
||||
tester-local: $(CLASS_FILES.tester) $(sqlite3-jni.dll)
|
||||
$(bin.java) -ea -Djava.library.path=$(dir.bld.c) \
|
||||
$(java.flags) -cp $(classpath) \
|
||||
org.sqlite.jni.tester.SQLTester $(tester.flags) $(tester.scripts)
|
||||
tester: tester-local
|
||||
else
|
||||
tester:
|
||||
@echo "SQLTester support is disabled. Build with enable.tester=1 to enable it."
|
||||
endif
|
||||
|
||||
tester.extdir.default := src/tests/ext
|
||||
tester.extdir ?= $(tester.extdir.default)
|
||||
tester.extern-scripts := $(wildcard $(tester.extdir)/*.test)
|
||||
ifneq (,$(tester.extern-scripts))
|
||||
tester-ext:
|
||||
$(bin.java) -ea -Djava.library.path=$(dir.bld.c) \
|
||||
$(java.flags) -cp $(classpath) \
|
||||
org.sqlite.jni.tester.SQLTester $(tester.flags) $(tester.extern-scripts)
|
||||
else
|
||||
tester-ext:
|
||||
@echo "******************************************************"; \
|
||||
echo "*** Include the out-of-tree test suite in the 'tester'"; \
|
||||
echo "*** target by either symlinking its directory to"; \
|
||||
echo "*** $(tester.extdir.default) or passing it to make"; \
|
||||
echo "*** as tester.extdir=/path/to/that/dir."; \
|
||||
echo "******************************************************";
|
||||
endif
|
||||
|
||||
tester-ext: tester-local
|
||||
tester: tester-ext
|
||||
tests: test tester
|
||||
package.jar.in := $(abspath $(dir.src)/jar.in)
|
||||
CLEAN_FILES += $(package.jar.in)
|
||||
$(package.jar.in): $(MAKEFILE) $(CLASS_FILES.main)
|
||||
cd $(dir.src); ls -1 org/sqlite/jni/*.java org/sqlite/jni/*.class > $@
|
||||
@ls -la $@
|
||||
@echo "To use this jar you will need the -Djava.library.path=DIR/WITH/libsqlite3-jni.so flag."
|
||||
@echo "e.g. java -jar $@ -Djava.library.path=bld"
|
||||
|
||||
$(package.jar): $(CLASS_FILES) $(MAKEFILE) $(package.jar.in)
|
||||
rm -f $(dir.src)/c/*~ $(dir.src.jni)/*~
|
||||
cd $(dir.src); $(bin.jar) -cfe ../$@ org.sqlite.jni.Tester1 @$(package.jar.in)
|
||||
|
||||
jar: $(package.jar)
|
||||
|
||||
CLEAN_FILES += $(dir.bld.c)/* \
|
||||
$(dir.src.jni)/*.class \
|
||||
$(dir.src.jni.tester)/*.class \
|
||||
$(sqlite3-jni.dll) \
|
||||
hs_err_pid*.log
|
||||
|
||||
.PHONY: clean distclean
|
||||
clean:
|
||||
-rm -f $(CLEAN_FILES)
|
||||
distclean: clean
|
||||
-rm -f $(DISTCLEAN_FILES)
|
||||
-rm -fr $(dir.bld.c)
|
||||
|
||||
########################################################################
|
||||
# disttribution bundle rules...
|
||||
|
||||
ifeq (,$(filter snapshot,$(MAKECMDGOALS)))
|
||||
dist-name-prefix := sqlite-jni
|
||||
else
|
||||
dist-name-prefix := sqlite-jni-snapshot-$(shell /usr/bin/date +%Y%m%d)
|
||||
endif
|
||||
dist-name := $(dist-name-prefix)-TEMP
|
||||
|
||||
|
||||
dist-dir.top := $(dist-name)
|
||||
dist-dir.src := $(dist-dir.top)/src
|
||||
dist.top.extras := \
|
||||
README.md
|
||||
|
||||
.PHONY: dist snapshot
|
||||
|
||||
dist: \
|
||||
$(bin.version-info) $(sqlite3.canonical.c) \
|
||||
$(package.jar) $(MAKEFILE)
|
||||
@echo "Making end-user deliverables..."
|
||||
@rm -fr $(dist-dir.top)
|
||||
@mkdir -p $(dist-dir.src)
|
||||
@cp -p $(dist.top.extras) $(dist-dir.top)/.
|
||||
@cp -p jar-dist.make $(dist-dir.top)/Makefile
|
||||
@cp -p $(dir.src.c)/*.[ch] $(dist-dir.src)/.
|
||||
@cp -p $(sqlite3.canonical.c) $(sqlite3.canonical.h) $(dist-dir.src)/.
|
||||
@set -e; \
|
||||
vnum=$$($(bin.version-info) --download-version); \
|
||||
vjar=$$($(bin.version-info) --version); \
|
||||
vdir=$(dist-name-prefix)-$$vnum; \
|
||||
arczip=$$vdir.zip; \
|
||||
cp -p $(package.jar) $(dist-dir.top)/sqlite3-jni-$${vjar}.jar; \
|
||||
echo "Making $$arczip ..."; \
|
||||
rm -fr $$arczip $$vdir; \
|
||||
mv $(dist-dir.top) $$vdir; \
|
||||
zip -qr $$arczip $$vdir; \
|
||||
rm -fr $$vdir; \
|
||||
ls -la $$arczip; \
|
||||
set +e; \
|
||||
unzip -lv $$arczip || echo "Missing unzip app? Not fatal."
|
||||
|
||||
snapshot: dist
|
||||
|
||||
.PHONY: dist-clean
|
||||
clean: dist-clean
|
||||
dist-clean:
|
||||
rm -fr $(dist-name) $(wildcard sqlite-jni-*.zip)
|
||||
@@ -0,0 +1,234 @@
|
||||
SQLite3 via JNI
|
||||
========================================================================
|
||||
|
||||
This directory houses a Java Native Interface (JNI) binding for the
|
||||
sqlite3 API. If you are reading this from the distribution ZIP file,
|
||||
links to resources in the canonical source tree will note work. The
|
||||
canonical copy of this file can be browsed at:
|
||||
|
||||
<https://sqlite.org/src/doc/trunk/ext/jni/README.md>
|
||||
|
||||
Technical support is available in the forum:
|
||||
|
||||
<https://sqlite.org/forum>
|
||||
|
||||
|
||||
> **FOREWARNING:** this subproject is very much in development and
|
||||
subject to any number of changes. Please do not rely on any
|
||||
information about its API until this disclaimer is removed.
|
||||
|
||||
Project goals/requirements:
|
||||
|
||||
- A [1-to-1(-ish) mapping of the C API](#1to1ish) to Java via JNI,
|
||||
insofar as cross-language semantics allow for. A closely-related
|
||||
goal is that [the C documentation](https://sqlite.org/c3ref/intro.html)
|
||||
should be usable as-is, insofar as possible, for the JNI binding.
|
||||
|
||||
- Support Java as far back as version 8 (2014).
|
||||
|
||||
- Environment-independent. Should work everywhere both Java
|
||||
and SQLite3 do.
|
||||
|
||||
- No 3rd-party dependencies beyond the JDK. That includes no
|
||||
build-level dependencies for specific IDEs and toolchains. We
|
||||
welcome the addition of build files for arbitrary environments
|
||||
insofar as they neither interfere with each other nor become
|
||||
a maintenance burden for the sqlite developers.
|
||||
|
||||
Non-goals:
|
||||
|
||||
- Creation of high-level OO wrapper APIs. Clients are free to create
|
||||
them off of the C-style API.
|
||||
|
||||
|
||||
Significant TODOs
|
||||
========================================================================
|
||||
|
||||
- The initial beta release with version 3.43 has severe threading
|
||||
limitations. Namely, two threads cannot call into the JNI-bound API
|
||||
at once. This limitation will be remove in a subsequent release.
|
||||
|
||||
|
||||
Building
|
||||
========================================================================
|
||||
|
||||
The canonical builds assumes a Linux-like environment and requires:
|
||||
|
||||
- GNU Make
|
||||
- A JDK supporting Java 8 or higher
|
||||
- A modern C compiler. gcc and clang should both work.
|
||||
|
||||
Put simply:
|
||||
|
||||
```
|
||||
$ export JAVA_HOME=/path/to/jdk/root
|
||||
$ make
|
||||
$ make test
|
||||
$ make clean
|
||||
```
|
||||
|
||||
<a id='1to1ish'></a>
|
||||
One-to-One(-ish) Mapping to C
|
||||
========================================================================
|
||||
|
||||
This JNI binding aims to provide as close to a 1-to-1 experience with
|
||||
the C API as cross-language semantics allow. Exceptions are
|
||||
necessarily made where cross-language semantics do not allow a 1-to-1,
|
||||
and judiciously made where a 1-to-1 mapping would be unduly cumbersome
|
||||
to use in Java.
|
||||
|
||||
Golden Rule: _Never_ Throw from Callbacks
|
||||
------------------------------------------------------------------------
|
||||
|
||||
JNI bindings which accept client-defined functions _must never throw
|
||||
exceptions_ unless _very explicitly documented_ as being
|
||||
throw-safe. Exceptions are generally reserved for higher-level
|
||||
bindings which are constructed to specifically deal with them and
|
||||
ensure that they do not leak C-level resources. Some of the JNI
|
||||
bindings are provided as Java functions which expect this rule to
|
||||
always hold.
|
||||
|
||||
UTF-8(-ish)
|
||||
------------------------------------------------------------------------
|
||||
|
||||
SQLite internally uses UTF-8 encoding, whereas Java natively uses
|
||||
UTF-16. Java JNI has routines for converting to and from UTF-8, _but_
|
||||
Java uses what its docs call "[modified UTF-8][modutf8]." Care must be
|
||||
taken when converting Java strings to UTF-8 to ensure that the proper
|
||||
conversion is performed. In short,
|
||||
`String.getBytes(StandardCharsets.UTF_8)` performs the proper
|
||||
conversion in Java, and there is no JNI C API for that conversion
|
||||
(JNI's `NewStringUTF()` returns MUTF-8).
|
||||
|
||||
Known consequences and limitations of this discrepancy include:
|
||||
|
||||
- Names of databases, tables, and collations must not contain
|
||||
characters which differ in MUTF-8 and UTF-8, or certain APIs will
|
||||
mis-translate them on their way between languages. APIs which
|
||||
transfer other client-side data to Java take extra care to
|
||||
convert the data at the cost of performance.
|
||||
|
||||
[modutf8]: https://docs.oracle.com/javase/8/docs/api/java/io/DataInput.html#modified-utf-8
|
||||
|
||||
|
||||
Unwieldy Constructs are Re-mapped
|
||||
------------------------------------------------------------------------
|
||||
|
||||
Some constructs, when modelled 1-to-1 from C to Java, are unduly
|
||||
clumsy to work with in Java because they try to shoehorn C's way of
|
||||
doing certain things into Java's wildly different ways. The following
|
||||
subsections cover those, starting with a verbose explanation and
|
||||
demonstration of where such changes are "really necessary"...
|
||||
|
||||
### Custom Collations
|
||||
|
||||
A prime example of where interface changes for Java are necessary for
|
||||
usability is [registration of a custom
|
||||
collation](https://sqlite.org/c3ref/create_collation.html):
|
||||
|
||||
```
|
||||
// C:
|
||||
int sqlite3_create_collation(sqlite3 * db, const char * name, int eTextRep,
|
||||
void *pUserData,
|
||||
int (*xCompare)(void*,int,void const *,int,void const *));
|
||||
|
||||
int sqlite3_create_collation_v2(sqlite3 * db, const char * name, int eTextRep,
|
||||
void *pUserData,
|
||||
int (*xCompare)(void*,int,void const *,int,void const *),
|
||||
void (*xDestroy)(void*));
|
||||
```
|
||||
|
||||
The `pUserData` object is optional client-defined state for the
|
||||
`xCompare()` and/or `xDestroy()` callback functions, both of which are
|
||||
passed that object as their first argument. That data is passed around
|
||||
"externally" in C because that's how C models the world. If we were to
|
||||
bind that part as-is to Java, the result would be awkward to use (^Yes,
|
||||
we tried this.):
|
||||
|
||||
```
|
||||
// Java:
|
||||
int sqlite3_create_collation(sqlite3 db, String name, int eTextRep,
|
||||
Object pUserData, xCompareType xCompare);
|
||||
|
||||
int sqlite3_create_collation_v2(sqlite3 db, String name, int eTextRep,
|
||||
Object pUserData,
|
||||
xCompareType xCompare, xDestroyType xDestroy);
|
||||
```
|
||||
|
||||
The awkwardness comes from (A) having two distinctly different objects
|
||||
for callbacks and (B) having their internal state provided separately,
|
||||
which is ill-fitting in Java. For the sake of usability, C APIs which
|
||||
follow that pattern use a slightly different Java interface:
|
||||
|
||||
```
|
||||
int sqlite3_create_collation(sqlite3 db, String name, int eTextRep,
|
||||
Collation collation);
|
||||
```
|
||||
|
||||
Where the `Collation` class has an abstract `xCompare()` method and
|
||||
no-op `xDestroy()` method which can be overridden if needed, leading to
|
||||
a much more Java-esque usage:
|
||||
|
||||
```
|
||||
int rc = sqlite3_create_collation(db, "mycollation", SQLITE_UTF8, new Collation(){
|
||||
|
||||
// Required comparison function:
|
||||
@Override public int xCompare(byte[] lhs, byte[] rhs){ ... }
|
||||
|
||||
// Optional finalizer function:
|
||||
@Override public void xDestroy(){ ... }
|
||||
|
||||
// Optional local state:
|
||||
private String localState1 =
|
||||
"This is local state. There are many like it, but this one is mine.";
|
||||
private MyStateType localState2 = new MyStateType();
|
||||
...
|
||||
});
|
||||
```
|
||||
|
||||
Noting that:
|
||||
|
||||
- It is still possible to bind in call-scope-local state via closures,
|
||||
if desired.
|
||||
|
||||
- No capabilities of the C API are lost or unduly obscured via the
|
||||
above API reshaping, so power users need not make any compromises.
|
||||
|
||||
- In the specific example above, `sqlite3_create_collation_v2()`
|
||||
becomes superfluous because the provided interface effectively
|
||||
provides both the v1 and v2 interfaces, the difference being that
|
||||
overriding the `xDestroy()` method effectively gives it v2
|
||||
semantics.
|
||||
|
||||
### User-defined SQL Functions (a.k.a. UDFs)
|
||||
|
||||
The [`sqlite3_create_function()`](https://sqlite.org/c3ref/create_function.html)
|
||||
family of APIs make heavy use of function pointers to provide
|
||||
client-defined callbacks, necessitating interface changes in the JNI
|
||||
binding. The Java API has only one core function-registration function:
|
||||
|
||||
```
|
||||
int sqlite3_create_function(sqlite3 db, String funcName, int nArgs,
|
||||
int encoding, SQLFunction func);
|
||||
```
|
||||
|
||||
> Design question: does the encoding argument serve any purpose in JS?
|
||||
|
||||
`SQLFunction` is not used directly, but is instead instantiated via
|
||||
one of its three subclasses:
|
||||
|
||||
- `SQLFunction.Scalar` implements simple scalar functions using but a
|
||||
single callback.
|
||||
- `SQLFunction.Aggregate` implements aggregate functions using two
|
||||
callbacks.
|
||||
- `SQLFunction.Window` implements window functions using four
|
||||
callbacks.
|
||||
|
||||
Search [`Tester1.java`](/file/ext/jni/src/org/sqlite/jni/Tester1.java) for
|
||||
`SQLFunction` for how it's used.
|
||||
|
||||
Reminder: see the disclaimer at the top of this document regarding the
|
||||
in-flux nature of this API.
|
||||
|
||||
[jsrc]: /file/
|
||||
[www]: https://sqlite.org
|
||||
@@ -0,0 +1,55 @@
|
||||
#!/this/is/make
|
||||
#^^^^ help emacs out
|
||||
#
|
||||
# This is a POSIX-make-compatible makefile for building the sqlite3
|
||||
# JNI library from "dist" zip file. It must be edited to set the
|
||||
# proper top-level JDK directory and, depending on the platform, add a
|
||||
# platform-specific -I directory. It should build as-is with any
|
||||
# 2020s-era version of gcc or clang. It requires JDK version 8 or
|
||||
# higher.
|
||||
|
||||
default: all
|
||||
|
||||
JAVA_HOME = /usr/lib/jvm/java-1.8.0-openjdk-amd64
|
||||
CFLAGS = \
|
||||
-fPIC \
|
||||
-Isrc \
|
||||
-I$(JAVA_HOME)/include \
|
||||
-I$(JAVA_HOME)/include/linux \
|
||||
-I$(JAVA_HOME)/include/apple \
|
||||
-I$(JAVA_HOME)/include/bsd \
|
||||
-Wall
|
||||
|
||||
SQLITE_OPT = \
|
||||
-DSQLITE_ENABLE_RTREE \
|
||||
-DSQLITE_ENABLE_EXPLAIN_COMMENTS \
|
||||
-DSQLITE_ENABLE_STMTVTAB \
|
||||
-DSQLITE_ENABLE_DBPAGE_VTAB \
|
||||
-DSQLITE_ENABLE_DBSTAT_VTAB \
|
||||
-DSQLITE_ENABLE_BYTECODE_VTAB \
|
||||
-DSQLITE_ENABLE_OFFSET_SQL_FUNC \
|
||||
-DSQLITE_OMIT_LOAD_EXTENSION \
|
||||
-DSQLITE_OMIT_DEPRECATED \
|
||||
-DSQLITE_OMIT_SHARED_CACHE \
|
||||
-DSQLITE_THREADSAFE=0 \
|
||||
-DSQLITE_TEMP_STORE=2 \
|
||||
-DSQLITE_USE_URI=1 \
|
||||
-DSQLITE_ENABLE_FTS5
|
||||
|
||||
sqlite3-jni.dll = libsqlite3-jni.so
|
||||
$(sqlite3-jni.dll):
|
||||
@echo "************************************************************************"; \
|
||||
echo "*** If this fails to build, be sure to edit this makefile ***"; \
|
||||
echo "*** to configure it for your system. ***"; \
|
||||
echo "************************************************************************"
|
||||
$(CC) $(CFLAGS) $(SQLITE_OPT) \
|
||||
src/sqlite3-jni.c -shared -o $@
|
||||
@echo "Now try running it with: make test"
|
||||
|
||||
test: $(sqlite3-jni.dll)
|
||||
java -jar -Djava.library.path=. sqlite3-jni-*.jar
|
||||
|
||||
clean:
|
||||
-rm -f $(sqlite3-jni.dll)
|
||||
|
||||
all: $(sqlite3-jni.dll)
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
** 2023-08-05
|
||||
**
|
||||
** The author disclaims copyright to this source code. In place of
|
||||
** a legal notice, here is a blessing:
|
||||
**
|
||||
** May you do good and not evil.
|
||||
** May you find forgiveness for yourself and forgive others.
|
||||
** May you share freely, never taking more than you give.
|
||||
**
|
||||
*************************************************************************
|
||||
** This file is part of the JNI bindings for the sqlite3 C API.
|
||||
*/
|
||||
package org.sqlite.jni;
|
||||
|
||||
/**
|
||||
A callback for use with sqlite3_set_authorizer().
|
||||
*/
|
||||
public interface Authorizer {
|
||||
/**
|
||||
Must functions as described for the sqlite3_set_authorizer()
|
||||
callback, with one caveat: the string values passed here were
|
||||
initially (at the C level) encoded in standard UTF-8. If they
|
||||
contained any constructs which are not compatible with MUTF-8,
|
||||
these strings will not have the expected values. For further
|
||||
details, see the documentation for the SQLite3Jni class.
|
||||
|
||||
Must not throw.
|
||||
*/
|
||||
int xAuth(int opId, @Nullable String s1, @Nullable String s2,
|
||||
@Nullable String s3, @Nullable String s4);
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
** 2023-08-05
|
||||
**
|
||||
** The author disclaims copyright to this source code. In place of
|
||||
** a legal notice, here is a blessing:
|
||||
**
|
||||
** May you do good and not evil.
|
||||
** May you find forgiveness for yourself and forgive others.
|
||||
** May you share freely, never taking more than you give.
|
||||
**
|
||||
*************************************************************************
|
||||
** This file is part of the JNI bindings for the sqlite3 C API.
|
||||
*/
|
||||
package org.sqlite.jni;
|
||||
|
||||
/**
|
||||
A callback for use with sqlite3_auto_extension().
|
||||
*/
|
||||
public interface AutoExtension {
|
||||
/**
|
||||
Must function as described for the sqlite3_auto_extension(),
|
||||
with the caveat that the signature is more limited.
|
||||
|
||||
As an exception (as it were) to the callbacks-must-not-throw
|
||||
rule, AutoExtensions may do so and the exception's error message
|
||||
will be set as the db's error string.
|
||||
|
||||
Results are undefined if db is closed by an auto-extension.
|
||||
*/
|
||||
int xEntryPoint(sqlite3 db);
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
** 2023-07-22
|
||||
**
|
||||
** The author disclaims copyright to this source code. In place of
|
||||
** a legal notice, here is a blessing:
|
||||
**
|
||||
** May you do good and not evil.
|
||||
** May you find forgiveness for yourself and forgive others.
|
||||
** May you share freely, never taking more than you give.
|
||||
**
|
||||
*************************************************************************
|
||||
** This file is part of the JNI bindings for the sqlite3 C API.
|
||||
*/
|
||||
package org.sqlite.jni;
|
||||
|
||||
/**
|
||||
Callback proxy for use with sqlite3_busy_handler().
|
||||
*/
|
||||
public abstract class BusyHandler {
|
||||
/**
|
||||
Must function as documented for the sqlite3_busy_handler()
|
||||
callback argument, minus the (void*) argument the C-level
|
||||
function requires.
|
||||
|
||||
Any exceptions thrown by this callback are suppressed in order to
|
||||
retain the C-style API semantics of the JNI bindings.
|
||||
*/
|
||||
public abstract int xCallback(int n);
|
||||
|
||||
/**
|
||||
Optionally override to perform any cleanup when this busy
|
||||
handler is destroyed. It is destroyed when:
|
||||
|
||||
- The associated db is passed to sqlite3_close() or
|
||||
sqlite3_close_v2().
|
||||
|
||||
- sqlite3_busy_handler() is called to replace the handler,
|
||||
whether it's passed a null handler or any other instance of
|
||||
this class.
|
||||
|
||||
- sqlite3_busy_timeout() is called, which implicitly installs
|
||||
a busy handler.
|
||||
*/
|
||||
public void xDestroy(){}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
/*
|
||||
** 2023-07-22
|
||||
**
|
||||
** The author disclaims copyright to this source code. In place of
|
||||
** a legal notice, here is a blessing:
|
||||
**
|
||||
** May you do good and not evil.
|
||||
** May you find forgiveness for yourself and forgive others.
|
||||
** May you share freely, never taking more than you give.
|
||||
**
|
||||
*************************************************************************
|
||||
** This file is part of the JNI bindings for the sqlite3 C API.
|
||||
*/
|
||||
package org.sqlite.jni;
|
||||
|
||||
/**
|
||||
*/
|
||||
public abstract class Collation {
|
||||
/**
|
||||
Must compare the given byte arrays using memcmp() semantics.
|
||||
*/
|
||||
public abstract int xCompare(byte[] lhs, byte[] rhs);
|
||||
/**
|
||||
Called by SQLite when the collation is destroyed. If a Collation
|
||||
requires custom cleanup, override this method.
|
||||
*/
|
||||
public void xDestroy() {}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
/*
|
||||
** 2023-07-30
|
||||
**
|
||||
** The author disclaims copyright to this source code. In place of
|
||||
** a legal notice, here is a blessing:
|
||||
**
|
||||
** May you do good and not evil.
|
||||
** May you find forgiveness for yourself and forgive others.
|
||||
** May you share freely, never taking more than you give.
|
||||
**
|
||||
*************************************************************************
|
||||
** This file is part of the JNI bindings for the sqlite3 C API.
|
||||
*/
|
||||
package org.sqlite.jni;
|
||||
|
||||
/**
|
||||
Callback proxy for use with sqlite3_collation_needed().
|
||||
*/
|
||||
public interface CollationNeeded {
|
||||
/**
|
||||
Has the same semantics as the C-level sqlite3_create_collation()
|
||||
callback.
|
||||
|
||||
If it throws, the exception message is passed on to the db and
|
||||
the exception is suppressed.
|
||||
*/
|
||||
int xCollationNeeded(sqlite3 db, int eTextRep, String collationName);
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
** 2023-07-22
|
||||
**
|
||||
** The author disclaims copyright to this source code. In place of
|
||||
** a legal notice, here is a blessing:
|
||||
**
|
||||
** May you do good and not evil.
|
||||
** May you find forgiveness for yourself and forgive others.
|
||||
** May you share freely, never taking more than you give.
|
||||
**
|
||||
*************************************************************************
|
||||
** This file is part of the JNI bindings for the sqlite3 C API.
|
||||
*/
|
||||
package org.sqlite.jni;
|
||||
|
||||
/**
|
||||
Callback proxy for use with sqlite3_commit_hook().
|
||||
*/
|
||||
public interface CommitHook {
|
||||
/**
|
||||
Works as documented for the sqlite3_commit_hook() callback.
|
||||
Must not throw.
|
||||
*/
|
||||
int xCommitHook();
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
** 2023-08-05
|
||||
**
|
||||
** The author disclaims copyright to this source code. In place of
|
||||
** a legal notice, here is a blessing:
|
||||
**
|
||||
** May you do good and not evil.
|
||||
** May you find forgiveness for yourself and forgive others.
|
||||
** May you share freely, never taking more than you give.
|
||||
**
|
||||
*************************************************************************
|
||||
** This file is part of the JNI bindings for the sqlite3 C API.
|
||||
*/
|
||||
package org.sqlite.jni;
|
||||
|
||||
/**
|
||||
INCOMPLETE AND COMPLETELY UNTESTED.
|
||||
|
||||
A wrapper for communicating C-level (fts5_api*) instances with
|
||||
Java. These wrappers do not own their associated pointer, they
|
||||
simply provide a type-safe way to communicate it between Java and C
|
||||
via JNI.
|
||||
*/
|
||||
public final class Fts5 {
|
||||
/* Not used */
|
||||
private Fts5(){}
|
||||
|
||||
//! Callback type for use with xTokenize() variants
|
||||
public static interface xTokenizeCallback {
|
||||
int xToken(int tFlags, byte txt[], int iStart, int iEnd);
|
||||
}
|
||||
|
||||
public static final int FTS5_TOKENIZE_QUERY = 0x0001;
|
||||
public static final int FTS5_TOKENIZE_PREFIX = 0x0002;
|
||||
public static final int FTS5_TOKENIZE_DOCUMENT = 0x0004;
|
||||
public static final int FTS5_TOKENIZE_AUX = 0x0008;
|
||||
public static final int FTS5_TOKEN_COLOCATED = 0x0001;
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
/*
|
||||
** 2023-08-04
|
||||
**
|
||||
** The author disclaims copyright to this source code. In place of
|
||||
** a legal notice, here is a blessing:
|
||||
**
|
||||
** May you do good and not evil.
|
||||
** May you find forgiveness for yourself and forgive others.
|
||||
** May you share freely, never taking more than you give.
|
||||
**
|
||||
*************************************************************************
|
||||
** This file is part of the JNI bindings for the sqlite3 C API.
|
||||
*/
|
||||
package org.sqlite.jni;
|
||||
|
||||
/**
|
||||
A wrapper for communicating C-level (Fts5Context*) instances with
|
||||
Java. These wrappers do not own their associated pointer, they
|
||||
simply provide a type-safe way to communicate it between Java and C
|
||||
via JNI.
|
||||
*/
|
||||
public final class Fts5Context extends NativePointerHolder<Fts5Context> {
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
/*
|
||||
** 2023-08-04
|
||||
**
|
||||
** The author disclaims copyright to this source code. In place of
|
||||
** a legal notice, here is a blessing:
|
||||
**
|
||||
** May you do good and not evil.
|
||||
** May you find forgiveness for yourself and forgive others.
|
||||
** May you share freely, never taking more than you give.
|
||||
**
|
||||
*************************************************************************
|
||||
** This file is part of the JNI bindings for the sqlite3 C API.
|
||||
*/
|
||||
package org.sqlite.jni;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
/**
|
||||
ALMOST COMPLETELY UNTESTED.
|
||||
|
||||
FAR FROM COMPLETE and the feasibility of binding this to Java
|
||||
is still undetermined. This might be removed.
|
||||
*/
|
||||
public final class Fts5ExtensionApi extends NativePointerHolder<Fts5ExtensionApi> {
|
||||
//! Only called from JNI
|
||||
private Fts5ExtensionApi(){}
|
||||
private int iVersion = 2;
|
||||
|
||||
/* Callback type for used by xQueryPhrase(). */
|
||||
public static interface xQueryPhraseCallback {
|
||||
int xCallback(Fts5ExtensionApi fapi, Fts5Context cx);
|
||||
}
|
||||
|
||||
/**
|
||||
Returns the singleton instance of this class.
|
||||
*/
|
||||
public static synchronized native Fts5ExtensionApi getInstance();
|
||||
|
||||
public synchronized native int xColumnCount(@NotNull Fts5Context fcx);
|
||||
public synchronized native int xColumnSize(@NotNull Fts5Context cx, int iCol,
|
||||
@NotNull OutputPointer.Int32 pnToken);
|
||||
public synchronized native int xColumnText(@NotNull Fts5Context cx, int iCol,
|
||||
@NotNull OutputPointer.String txt);
|
||||
public synchronized native int xColumnTotalSize(@NotNull Fts5Context fcx, int iCol,
|
||||
@NotNull OutputPointer.Int64 pnToken);
|
||||
public synchronized native Object xGetAuxdata(@NotNull Fts5Context cx, boolean clearIt);
|
||||
public synchronized native int xInst(@NotNull Fts5Context cx, int iIdx,
|
||||
@NotNull OutputPointer.Int32 piPhrase,
|
||||
@NotNull OutputPointer.Int32 piCol,
|
||||
@NotNull OutputPointer.Int32 piOff);
|
||||
public synchronized native int xInstCount(@NotNull Fts5Context fcx,
|
||||
@NotNull OutputPointer.Int32 pnInst);
|
||||
public synchronized native int xPhraseCount(@NotNull Fts5Context fcx);
|
||||
public synchronized native int xPhraseFirst(@NotNull Fts5Context cx, int iPhrase,
|
||||
@NotNull Fts5PhraseIter iter,
|
||||
@NotNull OutputPointer.Int32 iCol,
|
||||
@NotNull OutputPointer.Int32 iOff);
|
||||
public synchronized native int xPhraseFirstColumn(@NotNull Fts5Context cx, int iPhrase,
|
||||
@NotNull Fts5PhraseIter iter,
|
||||
@NotNull OutputPointer.Int32 iCol);
|
||||
public synchronized native void xPhraseNext(@NotNull Fts5Context cx,
|
||||
@NotNull Fts5PhraseIter iter,
|
||||
@NotNull OutputPointer.Int32 iCol,
|
||||
@NotNull OutputPointer.Int32 iOff);
|
||||
public synchronized native void xPhraseNextColumn(@NotNull Fts5Context cx,
|
||||
@NotNull Fts5PhraseIter iter,
|
||||
@NotNull OutputPointer.Int32 iCol);
|
||||
public synchronized native int xPhraseSize(@NotNull Fts5Context fcx, int iPhrase);
|
||||
public synchronized native int xQueryPhrase(@NotNull Fts5Context cx, int iPhrase,
|
||||
@NotNull xQueryPhraseCallback callback);
|
||||
public synchronized native int xRowCount(@NotNull Fts5Context fcx,
|
||||
@NotNull OutputPointer.Int64 nRow);
|
||||
public synchronized native long xRowid(@NotNull Fts5Context cx);
|
||||
/* Note that the JNI binding lacks the C version's xDelete()
|
||||
callback argument. Instead, if pAux has an xDestroy() method, it
|
||||
is called if the FTS5 API finalizes the aux state (including if
|
||||
allocation of storage for the auxdata fails). Any reference to
|
||||
pAux held by the JNI layer will be relinquished regardless of
|
||||
whether pAux has an xDestroy() method. */
|
||||
public synchronized native int xSetAuxdata(@NotNull Fts5Context cx, @Nullable Object pAux);
|
||||
public synchronized native int xTokenize(@NotNull Fts5Context cx, @NotNull byte pText[],
|
||||
@NotNull Fts5.xTokenizeCallback callback);
|
||||
|
||||
public synchronized native Object xUserData(Fts5Context cx);
|
||||
//^^^ returns the pointer passed as the 3rd arg to the C-level
|
||||
// fts5_api::xCreateFunction.
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
** 2023-08-04
|
||||
**
|
||||
** The author disclaims copyright to this source code. In place of
|
||||
** a legal notice, here is a blessing:
|
||||
**
|
||||
** May you do good and not evil.
|
||||
** May you find forgiveness for yourself and forgive others.
|
||||
** May you share freely, never taking more than you give.
|
||||
**
|
||||
*************************************************************************
|
||||
** This file is part of the JNI bindings for the sqlite3 C API.
|
||||
*/
|
||||
package org.sqlite.jni;
|
||||
|
||||
/**
|
||||
Fts5Function is used in conjunction with the
|
||||
sqlite3_create_fts_function() JNI-bound API to give that native code
|
||||
access to the callback functions needed in order to implement
|
||||
FTS5 auxiliary functions in Java.
|
||||
*/
|
||||
public abstract class Fts5Function {
|
||||
|
||||
public abstract void xFunction(Fts5ExtensionApi pApi, Fts5Context pFts,
|
||||
sqlite3_context pCtx, sqlite3_value argv[]);
|
||||
public void xDestroy() {}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
/*
|
||||
** 2023-08-04
|
||||
**
|
||||
** The author disclaims copyright to this source code. In place of
|
||||
** a legal notice, here is a blessing:
|
||||
**
|
||||
** May you do good and not evil.
|
||||
** May you find forgiveness for yourself and forgive others.
|
||||
** May you share freely, never taking more than you give.
|
||||
**
|
||||
*************************************************************************
|
||||
** This file is part of the JNI bindings for the sqlite3 C API.
|
||||
*/
|
||||
package org.sqlite.jni;
|
||||
|
||||
/**
|
||||
A wrapper for C-level Fts5PhraseIter. They are only modified and
|
||||
inspected by native-level code.
|
||||
*/
|
||||
public final class Fts5PhraseIter extends NativePointerHolder<Fts5PhraseIter> {
|
||||
//! Updated and used only by native code.
|
||||
private long a;
|
||||
private long b;
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
** 2023-08-05x
|
||||
**
|
||||
** The author disclaims copyright to this source code. In place of
|
||||
** a legal notice, here is a blessing:
|
||||
**
|
||||
** May you do good and not evil.
|
||||
** May you find forgiveness for yourself and forgive others.
|
||||
** May you share freely, never taking more than you give.
|
||||
**
|
||||
*************************************************************************
|
||||
** This file is part of the JNI bindings for the sqlite3 C API.
|
||||
*/
|
||||
package org.sqlite.jni;
|
||||
|
||||
/**
|
||||
INCOMPLETE AND COMPLETELY UNTESTED.
|
||||
|
||||
A wrapper for communicating C-level (Fts5Tokenizer*) instances with
|
||||
Java. These wrappers do not own their associated pointer, they
|
||||
simply provide a type-safe way to communicate it between Java and C
|
||||
via JNI.
|
||||
|
||||
At the C level, the Fts5Tokenizer type is essentially a void
|
||||
pointer used specifically for tokenizers.
|
||||
*/
|
||||
public final class Fts5Tokenizer extends NativePointerHolder<Fts5Tokenizer> {
|
||||
//! Only called from JNI.
|
||||
private Fts5Tokenizer(){}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
** 2023-07-21
|
||||
**
|
||||
** The author disclaims copyright to this source code. In place of
|
||||
** a legal notice, here is a blessing:
|
||||
**
|
||||
** May you do good and not evil.
|
||||
** May you find forgiveness for yourself and forgive others.
|
||||
** May you share freely, never taking more than you give.
|
||||
**
|
||||
*************************************************************************
|
||||
** This file is part of the JNI bindings for the sqlite3 C API.
|
||||
*/
|
||||
package org.sqlite.jni;
|
||||
|
||||
/**
|
||||
A helper for passing pointers between JNI C code and Java, in
|
||||
particular for output pointers of high-level object types in the
|
||||
sqlite3 C API, e.g. (sqlite3**) and (sqlite3_stmt**). This is
|
||||
intended to be subclassed and the ContextType is intended to be the
|
||||
class which is doing the subclassing. The intent of the ContextType
|
||||
is strictly to provide some level of type safety by avoiding that
|
||||
NativePointerHolder is not inadvertently passed to an incompatible
|
||||
function signature.
|
||||
|
||||
These objects do not _own_ the pointer they refer to. They are
|
||||
intended simply to communicate that pointer between C and Java.
|
||||
*/
|
||||
public class NativePointerHolder<ContextType> {
|
||||
//! Only set from JNI, where access permissions don't matter.
|
||||
private long nativePointer = 0;
|
||||
public final long getNativePointer(){ return nativePointer; }
|
||||
}
|
||||
@@ -0,0 +1,165 @@
|
||||
/*
|
||||
** 2023-07-21
|
||||
**
|
||||
** The author disclaims copyright to this source code. In place of
|
||||
** a legal notice, here is a blessing:
|
||||
**
|
||||
** May you do good and not evil.
|
||||
** May you find forgiveness for yourself and forgive others.
|
||||
** May you share freely, never taking more than you give.
|
||||
**
|
||||
*************************************************************************
|
||||
** This file is part of the JNI bindings for the sqlite3 C API.
|
||||
*/
|
||||
package org.sqlite.jni;
|
||||
|
||||
/**
|
||||
Helper classes for handling JNI output pointers.
|
||||
|
||||
We do not use a generic OutputPointer<T> because working with those
|
||||
from the native JNI code is unduly quirky due to a lack of
|
||||
autoboxing at that level.
|
||||
|
||||
The usage is similar for all of thes types:
|
||||
|
||||
```
|
||||
OutputPointer.sqlite3 out = new OutputPointer.sqlite3();
|
||||
assert( null==out.get() );
|
||||
int rc = sqlite3_open(":memory:", out);
|
||||
if( 0!=rc ) ... error;
|
||||
assert( null!=out.get() );
|
||||
sqlite3 db = out.take();
|
||||
assert( null==out.get() );
|
||||
```
|
||||
|
||||
With the minor exception that the primitive types permit direct
|
||||
access to the object's value via the `value` property, whereas the
|
||||
JNI-level opaque types do not permit client-level code to set that
|
||||
property.
|
||||
*/
|
||||
public final class OutputPointer {
|
||||
|
||||
/**
|
||||
Output pointer for use with routines, such as sqlite3_open(),
|
||||
which return a database handle via an output pointer. These
|
||||
pointers can only be set by the JNI layer, not by client-level
|
||||
code.
|
||||
*/
|
||||
public static final class sqlite3 {
|
||||
private org.sqlite.jni.sqlite3 value;
|
||||
//! Initializes with a null value.
|
||||
public sqlite3(){value = null;}
|
||||
//! Sets the current value to null.
|
||||
public void clear(){value = null;}
|
||||
//! Returns the current value.
|
||||
public final org.sqlite.jni.sqlite3 get(){return value;}
|
||||
//! Equivalent to calling get() then clear().
|
||||
public final org.sqlite.jni.sqlite3 take(){
|
||||
final org.sqlite.jni.sqlite3 v = value;
|
||||
value = null;
|
||||
return v;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
Output pointer for use with routines, such as sqlite3_prepare(),
|
||||
which return a statement handle via an output pointer. These
|
||||
pointers can only be set by the JNI layer, not by client-level
|
||||
code.
|
||||
*/
|
||||
public static final class sqlite3_stmt {
|
||||
private org.sqlite.jni.sqlite3_stmt value;
|
||||
//! Initializes with a null value.
|
||||
public sqlite3_stmt(){value = null;}
|
||||
//! Sets the current value to null.
|
||||
public void clear(){value = null;}
|
||||
//! Returns the current value.
|
||||
public final org.sqlite.jni.sqlite3_stmt get(){return value;}
|
||||
//! Equivalent to calling get() then clear().
|
||||
public final org.sqlite.jni.sqlite3_stmt take(){
|
||||
final org.sqlite.jni.sqlite3_stmt v = value;
|
||||
value = null;
|
||||
return v;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
Output pointer for use with native routines which return integers via
|
||||
output pointers.
|
||||
*/
|
||||
public static final class Int32 {
|
||||
/**
|
||||
This is public for ease of use. Accessors are provided for
|
||||
consistency with the higher-level types.
|
||||
*/
|
||||
public int value;
|
||||
//! Initializes with the value 0.
|
||||
public Int32(){this(0);}
|
||||
//! Initializes with the value v.
|
||||
public Int32(int v){value = v;}
|
||||
//! Returns the current value.
|
||||
public final int get(){return value;}
|
||||
//! Sets the current value to v.
|
||||
public final void set(int v){value = v;}
|
||||
}
|
||||
|
||||
/**
|
||||
Output pointer for use with native routines which return 64-bit integers
|
||||
via output pointers.
|
||||
*/
|
||||
public static final class Int64 {
|
||||
/**
|
||||
This is public for ease of use. Accessors are provided for
|
||||
consistency with the higher-level types.
|
||||
*/
|
||||
public long value;
|
||||
//! Initializes with the value 0.
|
||||
public Int64(){this(0);}
|
||||
//! Initializes with the value v.
|
||||
public Int64(long v){value = v;}
|
||||
//! Returns the current value.
|
||||
public final long get(){return value;}
|
||||
//! Sets the current value.
|
||||
public final void set(long v){value = v;}
|
||||
}
|
||||
|
||||
/**
|
||||
Output pointer for use with native routines which return strings via
|
||||
output pointers.
|
||||
*/
|
||||
public static final class String {
|
||||
/**
|
||||
This is public for ease of use. Accessors are provided for
|
||||
consistency with the higher-level types.
|
||||
*/
|
||||
public java.lang.String value;
|
||||
//! Initializes with a null value.
|
||||
public String(){this(null);}
|
||||
//! Initializes with the value v.
|
||||
public String(java.lang.String v){value = v;}
|
||||
//! Returns the current value.
|
||||
public final java.lang.String get(){return value;}
|
||||
//! Sets the current value.
|
||||
public final void set(java.lang.String v){value = v;}
|
||||
}
|
||||
|
||||
/**
|
||||
Output pointer for use with native routines which return byte
|
||||
arrays via output pointers.
|
||||
*/
|
||||
public static final class ByteArray {
|
||||
/**
|
||||
This is public for ease of use. Accessors are provided for
|
||||
consistency with the higher-level types.
|
||||
*/
|
||||
public byte[] value;
|
||||
//! Initializes with the value null.
|
||||
public ByteArray(){this(null);}
|
||||
//! Initializes with the value v.
|
||||
public ByteArray(byte[] v){value = v;}
|
||||
//! Returns the current value.
|
||||
public final byte[] get(){return value;}
|
||||
//! Sets the current value.
|
||||
public final void set(byte[] v){value = v;}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
** 2023-07-22
|
||||
**
|
||||
** The author disclaims copyright to this source code. In place of
|
||||
** a legal notice, here is a blessing:
|
||||
**
|
||||
** May you do good and not evil.
|
||||
** May you find forgiveness for yourself and forgive others.
|
||||
** May you share freely, never taking more than you give.
|
||||
**
|
||||
*************************************************************************
|
||||
** This file is part of the JNI bindings for the sqlite3 C API.
|
||||
*/
|
||||
package org.sqlite.jni;
|
||||
|
||||
/**
|
||||
Callback proxy for use with sqlite3_progress_handler().
|
||||
*/
|
||||
public interface ProgressHandler {
|
||||
/**
|
||||
Works as documented for the sqlite3_progress_handler() callback.
|
||||
|
||||
If it throws, the exception message is passed on to the db and
|
||||
the exception is suppressed.
|
||||
*/
|
||||
int xCallback();
|
||||
}
|
||||
@@ -0,0 +1,155 @@
|
||||
/*
|
||||
** 2023-07-21
|
||||
**
|
||||
** The author disclaims copyright to this source code. In place of
|
||||
** a legal notice, here is a blessing:
|
||||
**
|
||||
** May you do good and not evil.
|
||||
** May you find forgiveness for yourself and forgive others.
|
||||
** May you share freely, never taking more than you give.
|
||||
**
|
||||
*************************************************************************
|
||||
** This file is part of the JNI bindings for the sqlite3 C API.
|
||||
*/
|
||||
package org.sqlite.jni;
|
||||
|
||||
/**
|
||||
This enum contains all of the core and "extended" result codes used
|
||||
by the sqlite3 library. It is provided not for use with the C-style
|
||||
API (with which it won't work) but for higher-level code which may
|
||||
find it useful to map SQLite result codes to human-readable names.
|
||||
*/
|
||||
public enum ResultCode {
|
||||
SQLITE_OK(SQLite3Jni.SQLITE_OK),
|
||||
SQLITE_ERROR(SQLite3Jni.SQLITE_ERROR),
|
||||
SQLITE_INTERNAL(SQLite3Jni.SQLITE_INTERNAL),
|
||||
SQLITE_PERM(SQLite3Jni.SQLITE_PERM),
|
||||
SQLITE_ABORT(SQLite3Jni.SQLITE_ABORT),
|
||||
SQLITE_BUSY(SQLite3Jni.SQLITE_BUSY),
|
||||
SQLITE_LOCKED(SQLite3Jni.SQLITE_LOCKED),
|
||||
SQLITE_NOMEM(SQLite3Jni.SQLITE_NOMEM),
|
||||
SQLITE_READONLY(SQLite3Jni.SQLITE_READONLY),
|
||||
SQLITE_INTERRUPT(SQLite3Jni.SQLITE_INTERRUPT),
|
||||
SQLITE_IOERR(SQLite3Jni.SQLITE_IOERR),
|
||||
SQLITE_CORRUPT(SQLite3Jni.SQLITE_CORRUPT),
|
||||
SQLITE_NOTFOUND(SQLite3Jni.SQLITE_NOTFOUND),
|
||||
SQLITE_FULL(SQLite3Jni.SQLITE_FULL),
|
||||
SQLITE_CANTOPEN(SQLite3Jni.SQLITE_CANTOPEN),
|
||||
SQLITE_PROTOCOL(SQLite3Jni.SQLITE_PROTOCOL),
|
||||
SQLITE_EMPTY(SQLite3Jni.SQLITE_EMPTY),
|
||||
SQLITE_SCHEMA(SQLite3Jni.SQLITE_SCHEMA),
|
||||
SQLITE_TOOBIG(SQLite3Jni.SQLITE_TOOBIG),
|
||||
SQLITE_CONSTRAINT(SQLite3Jni.SQLITE_CONSTRAINT),
|
||||
SQLITE_MISMATCH(SQLite3Jni.SQLITE_MISMATCH),
|
||||
SQLITE_MISUSE(SQLite3Jni.SQLITE_MISUSE),
|
||||
SQLITE_NOLFS(SQLite3Jni.SQLITE_NOLFS),
|
||||
SQLITE_AUTH(SQLite3Jni.SQLITE_AUTH),
|
||||
SQLITE_FORMAT(SQLite3Jni.SQLITE_FORMAT),
|
||||
SQLITE_RANGE(SQLite3Jni.SQLITE_RANGE),
|
||||
SQLITE_NOTADB(SQLite3Jni.SQLITE_NOTADB),
|
||||
SQLITE_NOTICE(SQLite3Jni.SQLITE_NOTICE),
|
||||
SQLITE_WARNING(SQLite3Jni.SQLITE_WARNING),
|
||||
SQLITE_ROW(SQLite3Jni.SQLITE_ROW),
|
||||
SQLITE_DONE(SQLite3Jni.SQLITE_DONE),
|
||||
SQLITE_ERROR_MISSING_COLLSEQ(SQLite3Jni.SQLITE_ERROR_MISSING_COLLSEQ),
|
||||
SQLITE_ERROR_RETRY(SQLite3Jni.SQLITE_ERROR_RETRY),
|
||||
SQLITE_ERROR_SNAPSHOT(SQLite3Jni.SQLITE_ERROR_SNAPSHOT),
|
||||
SQLITE_IOERR_READ(SQLite3Jni.SQLITE_IOERR_READ),
|
||||
SQLITE_IOERR_SHORT_READ(SQLite3Jni.SQLITE_IOERR_SHORT_READ),
|
||||
SQLITE_IOERR_WRITE(SQLite3Jni.SQLITE_IOERR_WRITE),
|
||||
SQLITE_IOERR_FSYNC(SQLite3Jni.SQLITE_IOERR_FSYNC),
|
||||
SQLITE_IOERR_DIR_FSYNC(SQLite3Jni.SQLITE_IOERR_DIR_FSYNC),
|
||||
SQLITE_IOERR_TRUNCATE(SQLite3Jni.SQLITE_IOERR_TRUNCATE),
|
||||
SQLITE_IOERR_FSTAT(SQLite3Jni.SQLITE_IOERR_FSTAT),
|
||||
SQLITE_IOERR_UNLOCK(SQLite3Jni.SQLITE_IOERR_UNLOCK),
|
||||
SQLITE_IOERR_RDLOCK(SQLite3Jni.SQLITE_IOERR_RDLOCK),
|
||||
SQLITE_IOERR_DELETE(SQLite3Jni.SQLITE_IOERR_DELETE),
|
||||
SQLITE_IOERR_BLOCKED(SQLite3Jni.SQLITE_IOERR_BLOCKED),
|
||||
SQLITE_IOERR_NOMEM(SQLite3Jni.SQLITE_IOERR_NOMEM),
|
||||
SQLITE_IOERR_ACCESS(SQLite3Jni.SQLITE_IOERR_ACCESS),
|
||||
SQLITE_IOERR_CHECKRESERVEDLOCK(SQLite3Jni.SQLITE_IOERR_CHECKRESERVEDLOCK),
|
||||
SQLITE_IOERR_LOCK(SQLite3Jni.SQLITE_IOERR_LOCK),
|
||||
SQLITE_IOERR_CLOSE(SQLite3Jni.SQLITE_IOERR_CLOSE),
|
||||
SQLITE_IOERR_DIR_CLOSE(SQLite3Jni.SQLITE_IOERR_DIR_CLOSE),
|
||||
SQLITE_IOERR_SHMOPEN(SQLite3Jni.SQLITE_IOERR_SHMOPEN),
|
||||
SQLITE_IOERR_SHMSIZE(SQLite3Jni.SQLITE_IOERR_SHMSIZE),
|
||||
SQLITE_IOERR_SHMLOCK(SQLite3Jni.SQLITE_IOERR_SHMLOCK),
|
||||
SQLITE_IOERR_SHMMAP(SQLite3Jni.SQLITE_IOERR_SHMMAP),
|
||||
SQLITE_IOERR_SEEK(SQLite3Jni.SQLITE_IOERR_SEEK),
|
||||
SQLITE_IOERR_DELETE_NOENT(SQLite3Jni.SQLITE_IOERR_DELETE_NOENT),
|
||||
SQLITE_IOERR_MMAP(SQLite3Jni.SQLITE_IOERR_MMAP),
|
||||
SQLITE_IOERR_GETTEMPPATH(SQLite3Jni.SQLITE_IOERR_GETTEMPPATH),
|
||||
SQLITE_IOERR_CONVPATH(SQLite3Jni.SQLITE_IOERR_CONVPATH),
|
||||
SQLITE_IOERR_VNODE(SQLite3Jni.SQLITE_IOERR_VNODE),
|
||||
SQLITE_IOERR_AUTH(SQLite3Jni.SQLITE_IOERR_AUTH),
|
||||
SQLITE_IOERR_BEGIN_ATOMIC(SQLite3Jni.SQLITE_IOERR_BEGIN_ATOMIC),
|
||||
SQLITE_IOERR_COMMIT_ATOMIC(SQLite3Jni.SQLITE_IOERR_COMMIT_ATOMIC),
|
||||
SQLITE_IOERR_ROLLBACK_ATOMIC(SQLite3Jni.SQLITE_IOERR_ROLLBACK_ATOMIC),
|
||||
SQLITE_IOERR_DATA(SQLite3Jni.SQLITE_IOERR_DATA),
|
||||
SQLITE_IOERR_CORRUPTFS(SQLite3Jni.SQLITE_IOERR_CORRUPTFS),
|
||||
SQLITE_LOCKED_SHAREDCACHE(SQLite3Jni.SQLITE_LOCKED_SHAREDCACHE),
|
||||
SQLITE_LOCKED_VTAB(SQLite3Jni.SQLITE_LOCKED_VTAB),
|
||||
SQLITE_BUSY_RECOVERY(SQLite3Jni.SQLITE_BUSY_RECOVERY),
|
||||
SQLITE_BUSY_SNAPSHOT(SQLite3Jni.SQLITE_BUSY_SNAPSHOT),
|
||||
SQLITE_BUSY_TIMEOUT(SQLite3Jni.SQLITE_BUSY_TIMEOUT),
|
||||
SQLITE_CANTOPEN_NOTEMPDIR(SQLite3Jni.SQLITE_CANTOPEN_NOTEMPDIR),
|
||||
SQLITE_CANTOPEN_ISDIR(SQLite3Jni.SQLITE_CANTOPEN_ISDIR),
|
||||
SQLITE_CANTOPEN_FULLPATH(SQLite3Jni.SQLITE_CANTOPEN_FULLPATH),
|
||||
SQLITE_CANTOPEN_CONVPATH(SQLite3Jni.SQLITE_CANTOPEN_CONVPATH),
|
||||
SQLITE_CANTOPEN_SYMLINK(SQLite3Jni.SQLITE_CANTOPEN_SYMLINK),
|
||||
SQLITE_CORRUPT_VTAB(SQLite3Jni.SQLITE_CORRUPT_VTAB),
|
||||
SQLITE_CORRUPT_SEQUENCE(SQLite3Jni.SQLITE_CORRUPT_SEQUENCE),
|
||||
SQLITE_CORRUPT_INDEX(SQLite3Jni.SQLITE_CORRUPT_INDEX),
|
||||
SQLITE_READONLY_RECOVERY(SQLite3Jni.SQLITE_READONLY_RECOVERY),
|
||||
SQLITE_READONLY_CANTLOCK(SQLite3Jni.SQLITE_READONLY_CANTLOCK),
|
||||
SQLITE_READONLY_ROLLBACK(SQLite3Jni.SQLITE_READONLY_ROLLBACK),
|
||||
SQLITE_READONLY_DBMOVED(SQLite3Jni.SQLITE_READONLY_DBMOVED),
|
||||
SQLITE_READONLY_CANTINIT(SQLite3Jni.SQLITE_READONLY_CANTINIT),
|
||||
SQLITE_READONLY_DIRECTORY(SQLite3Jni.SQLITE_READONLY_DIRECTORY),
|
||||
SQLITE_ABORT_ROLLBACK(SQLite3Jni.SQLITE_ABORT_ROLLBACK),
|
||||
SQLITE_CONSTRAINT_CHECK(SQLite3Jni.SQLITE_CONSTRAINT_CHECK),
|
||||
SQLITE_CONSTRAINT_COMMITHOOK(SQLite3Jni.SQLITE_CONSTRAINT_COMMITHOOK),
|
||||
SQLITE_CONSTRAINT_FOREIGNKEY(SQLite3Jni.SQLITE_CONSTRAINT_FOREIGNKEY),
|
||||
SQLITE_CONSTRAINT_FUNCTION(SQLite3Jni.SQLITE_CONSTRAINT_FUNCTION),
|
||||
SQLITE_CONSTRAINT_NOTNULL(SQLite3Jni.SQLITE_CONSTRAINT_NOTNULL),
|
||||
SQLITE_CONSTRAINT_PRIMARYKEY(SQLite3Jni.SQLITE_CONSTRAINT_PRIMARYKEY),
|
||||
SQLITE_CONSTRAINT_TRIGGER(SQLite3Jni.SQLITE_CONSTRAINT_TRIGGER),
|
||||
SQLITE_CONSTRAINT_UNIQUE(SQLite3Jni.SQLITE_CONSTRAINT_UNIQUE),
|
||||
SQLITE_CONSTRAINT_VTAB(SQLite3Jni.SQLITE_CONSTRAINT_VTAB),
|
||||
SQLITE_CONSTRAINT_ROWID(SQLite3Jni.SQLITE_CONSTRAINT_ROWID),
|
||||
SQLITE_CONSTRAINT_PINNED(SQLite3Jni.SQLITE_CONSTRAINT_PINNED),
|
||||
SQLITE_CONSTRAINT_DATATYPE(SQLite3Jni.SQLITE_CONSTRAINT_DATATYPE),
|
||||
SQLITE_NOTICE_RECOVER_WAL(SQLite3Jni.SQLITE_NOTICE_RECOVER_WAL),
|
||||
SQLITE_NOTICE_RECOVER_ROLLBACK(SQLite3Jni.SQLITE_NOTICE_RECOVER_ROLLBACK),
|
||||
SQLITE_WARNING_AUTOINDEX(SQLite3Jni.SQLITE_WARNING_AUTOINDEX),
|
||||
SQLITE_AUTH_USER(SQLite3Jni.SQLITE_AUTH_USER),
|
||||
SQLITE_OK_LOAD_PERMANENTLY(SQLite3Jni.SQLITE_OK_LOAD_PERMANENTLY);
|
||||
|
||||
public final int value;
|
||||
|
||||
ResultCode(int rc){
|
||||
value = rc;
|
||||
ResultCodeMap.set(rc, this);
|
||||
}
|
||||
|
||||
/**
|
||||
Returns the entry from this enum for the given result code, or
|
||||
null if no match is found.
|
||||
*/
|
||||
public static ResultCode getEntryForInt(int rc){
|
||||
return ResultCodeMap.get(rc);
|
||||
}
|
||||
|
||||
/**
|
||||
Internal level of indirection required because we cannot initialize
|
||||
static enum members in an enum before the enum constructor is
|
||||
invoked.
|
||||
*/
|
||||
private static final class ResultCodeMap {
|
||||
private static final java.util.Map<Integer,ResultCode> i2e
|
||||
= new java.util.HashMap<>();
|
||||
private static void set(int rc, ResultCode e){ i2e.put(rc, e); }
|
||||
private static ResultCode get(int rc){ return i2e.get(rc); }
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
** 2023-07-22
|
||||
**
|
||||
** The author disclaims copyright to this source code. In place of
|
||||
** a legal notice, here is a blessing:
|
||||
**
|
||||
** May you do good and not evil.
|
||||
** May you find forgiveness for yourself and forgive others.
|
||||
** May you share freely, never taking more than you give.
|
||||
**
|
||||
*************************************************************************
|
||||
** This file is part of the JNI bindings for the sqlite3 C API.
|
||||
*/
|
||||
package org.sqlite.jni;
|
||||
|
||||
/**
|
||||
Callback proxy for use with sqlite3_rollback_hook().
|
||||
*/
|
||||
public interface RollbackHook {
|
||||
/**
|
||||
Works as documented for the sqlite3_rollback_hook() callback.
|
||||
Must not throw.
|
||||
*/
|
||||
void xRollbackHook();
|
||||
}
|
||||
@@ -0,0 +1,172 @@
|
||||
/*
|
||||
** 2023-07-22
|
||||
**
|
||||
** The author disclaims copyright to this source code. In place of
|
||||
** a legal notice, here is a blessing:
|
||||
**
|
||||
** May you do good and not evil.
|
||||
** May you find forgiveness for yourself and forgive others.
|
||||
** May you share freely, never taking more than you give.
|
||||
**
|
||||
*************************************************************************
|
||||
** This file is part of the JNI bindings for the sqlite3 C API.
|
||||
*/
|
||||
package org.sqlite.jni;
|
||||
|
||||
/**
|
||||
SQLFunction is used in conjunction with the
|
||||
sqlite3_create_function() JNI-bound API to give that native code
|
||||
access to the callback functions needed in order to implement SQL
|
||||
functions in Java.
|
||||
|
||||
This class is not used by itself, but is a marker base class. The
|
||||
three UDF types are modelled by the inner classes Scalar,
|
||||
Aggregate<T>, and Window<T>. Most simply, clients may create
|
||||
anonymous classes from those to implement UDFs. Clients are free to
|
||||
create their own classes for use with UDFs, so long as they conform
|
||||
to the public interfaces defined by those three classes. The JNI
|
||||
layer only actively relies on the SQLFunction base class.
|
||||
*/
|
||||
public abstract class SQLFunction {
|
||||
|
||||
/**
|
||||
PerContextState assists aggregate and window functions in
|
||||
managinga their accumulator state across calls to the UDF's
|
||||
callbacks.
|
||||
|
||||
If a given aggregate or window function is called multiple times
|
||||
in a single SQL statement, e.g. SELECT MYFUNC(A), MYFUNC(B)...,
|
||||
then the clients need some way of knowing which call is which so
|
||||
that they can map their state between their various UDF callbacks
|
||||
and reset it via xFinal(). This class takes care of such
|
||||
mappings.
|
||||
|
||||
This class works by mapping
|
||||
sqlite3_context.getAggregateContext() to a single piece of
|
||||
state, of a client-defined type (the T part of this class), which
|
||||
persists across a "matching set" of the UDF's callbacks.
|
||||
|
||||
This class is a helper providing commonly-needed functionality -
|
||||
it is not required for use with aggregate or window functions.
|
||||
Client UDFs are free to perform such mappings using custom
|
||||
approaches. The provided Aggregate<T> and Window<T> classes
|
||||
use this.
|
||||
*/
|
||||
public static final class PerContextState<T> {
|
||||
private final java.util.Map<Long,ValueHolder<T>> map
|
||||
= new java.util.HashMap<>();
|
||||
|
||||
/**
|
||||
Should be called from a UDF's xStep(), xValue(), and xInverse()
|
||||
methods, passing it that method's first argument and an initial
|
||||
value for the persistent state. If there is currently no
|
||||
mapping for cx.getAggregateContext() within the map, one is
|
||||
created using the given initial value, else the existing one is
|
||||
used and the 2nd argument is ignored. It returns a
|
||||
ValueHolder<T> which can be used to modify that state directly
|
||||
without requiring that the client update the underlying map's
|
||||
entry.
|
||||
|
||||
T must be of a type which can be legally stored as a value in
|
||||
java.util.HashMap<KeyType,T>.
|
||||
*/
|
||||
public ValueHolder<T> getAggregateState(sqlite3_context cx, T initialValue){
|
||||
ValueHolder<T> rc = map.get(cx.getAggregateContext());
|
||||
if(null == rc){
|
||||
map.put(cx.getAggregateContext(), rc = new ValueHolder<>(initialValue));
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
Should be called from a UDF's xFinal() method and passed that
|
||||
method's first argument. This function removes the value
|
||||
associated with cx.getAggregateContext() from the map and
|
||||
returns it, returning null if no other UDF method has been
|
||||
called to set up such a mapping. The latter condition will be
|
||||
the case if a UDF is used in a statement which has no result
|
||||
rows.
|
||||
*/
|
||||
public T takeAggregateState(sqlite3_context cx){
|
||||
final ValueHolder<T> h = map.remove(cx.getAggregateContext());
|
||||
return null==h ? null : h.value;
|
||||
}
|
||||
}
|
||||
|
||||
//! Subclass for creating scalar functions.
|
||||
public static abstract class Scalar extends SQLFunction {
|
||||
|
||||
//! As for the xFunc() argument of the C API's sqlite3_create_function()
|
||||
public abstract void xFunc(sqlite3_context cx, sqlite3_value[] args);
|
||||
|
||||
/**
|
||||
Optionally override to be notified when the UDF is finalized by
|
||||
SQLite.
|
||||
*/
|
||||
public void xDestroy() {}
|
||||
}
|
||||
|
||||
/**
|
||||
SQLFunction Subclass for creating aggregate functions. Its T is
|
||||
the data type of its "accumulator" state, an instance of which is
|
||||
intended to be be managed using the getAggregateState() and
|
||||
takeAggregateState() methods.
|
||||
*/
|
||||
public static abstract class Aggregate<T> extends SQLFunction {
|
||||
|
||||
//! As for the xStep() argument of the C API's sqlite3_create_function()
|
||||
public abstract void xStep(sqlite3_context cx, sqlite3_value[] args);
|
||||
|
||||
//! As for the xFinal() argument of the C API's sqlite3_create_function()
|
||||
public abstract void xFinal(sqlite3_context cx);
|
||||
|
||||
/**
|
||||
Optionally override to be notified when the UDF is finalized by
|
||||
SQLite.
|
||||
*/
|
||||
public void xDestroy() {}
|
||||
|
||||
//! Per-invocation state for the UDF.
|
||||
private final PerContextState<T> map = new PerContextState<>();
|
||||
|
||||
/**
|
||||
To be called from the implementation's xStep() method, as well
|
||||
as the xValue() and xInverse() methods of the Window<T>
|
||||
subclass, to fetch the current per-call UDF state. On the
|
||||
first call to this method for any given sqlite3_context
|
||||
argument, the context is set to the given initial value. On all other
|
||||
calls, the 2nd argument is ignored.
|
||||
|
||||
@see PerContextState<T>#takeAggregateState()
|
||||
*/
|
||||
protected final ValueHolder<T> getAggregateState(sqlite3_context cx, T initialValue){
|
||||
return map.getAggregateState(cx, initialValue);
|
||||
}
|
||||
|
||||
/**
|
||||
To be called from the implementation's xFinal() method to fetch
|
||||
the final state of the UDF and remove its mapping.
|
||||
|
||||
@see PerContextState<T>#takeAggregateState()
|
||||
*/
|
||||
protected final T takeAggregateState(sqlite3_context cx){
|
||||
return map.takeAggregateState(cx);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
An SQLFunction subclass for creating window functions. Note that
|
||||
Window<T> inherits from Aggregate<T> and each instance is
|
||||
required to implement the inherited abstract methods from that
|
||||
class. See Aggregate<T> for information on managing the UDF's
|
||||
invocation-specific state.
|
||||
*/
|
||||
public static abstract class Window<T> extends Aggregate<T> {
|
||||
|
||||
//! As for the xInverse() argument of the C API's sqlite3_create_window_function()
|
||||
public abstract void xInverse(sqlite3_context cx, sqlite3_value[] args);
|
||||
|
||||
//! As for the xValue() argument of the C API's sqlite3_create_window_function()
|
||||
public abstract void xValue(sqlite3_context cx);
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,87 @@
|
||||
/*
|
||||
** 2023-08-04
|
||||
**
|
||||
** The author disclaims copyright to this source code. In place of
|
||||
** a legal notice, here is a blessing:
|
||||
**
|
||||
** May you do good and not evil.
|
||||
** May you find forgiveness for yourself and forgive others.
|
||||
** May you share freely, never taking more than you give.
|
||||
**
|
||||
*************************************************************************
|
||||
** This file contains a set of tests for the sqlite3 JNI bindings.
|
||||
*/
|
||||
package org.sqlite.jni;
|
||||
import static org.sqlite.jni.SQLite3Jni.*;
|
||||
import static org.sqlite.jni.Tester1.*;
|
||||
|
||||
public class TesterFts5 {
|
||||
|
||||
private static void test1(){
|
||||
Fts5ExtensionApi fea = Fts5ExtensionApi.getInstance();
|
||||
affirm( null != fea );
|
||||
affirm( fea.getNativePointer() != 0 );
|
||||
affirm( fea == Fts5ExtensionApi.getInstance() )/*singleton*/;
|
||||
|
||||
sqlite3 db = createNewDb();
|
||||
fts5_api fApi = fts5_api.getInstanceForDb(db);
|
||||
affirm( fApi != null );
|
||||
affirm( fApi == fts5_api.getInstanceForDb(db) /* singleton per db */ );
|
||||
|
||||
execSql(db, new String[] {
|
||||
"CREATE VIRTUAL TABLE ft USING fts5(a, b);",
|
||||
"INSERT INTO ft(rowid, a, b) VALUES(1, 'X Y', 'Y Z');",
|
||||
"INSERT INTO ft(rowid, a, b) VALUES(2, 'A Z', 'Y Y');"
|
||||
});
|
||||
|
||||
final String pUserData = "This is pUserData";
|
||||
ValueHolder<Boolean> xDestroyCalled = new ValueHolder<>(false);
|
||||
ValueHolder<Integer> xFuncCount = new ValueHolder<>(0);
|
||||
final fts5_extension_function func = new fts5_extension_function(){
|
||||
public void xFunction(Fts5ExtensionApi ext, Fts5Context fCx,
|
||||
sqlite3_context pCx, sqlite3_value argv[]){
|
||||
int nCols = ext.xColumnCount(fCx);
|
||||
affirm( 2 == nCols );
|
||||
affirm( nCols == argv.length );
|
||||
affirm( ext.xUserData(fCx) == pUserData );
|
||||
if(true){
|
||||
OutputPointer.String op = new OutputPointer.String();
|
||||
for(int i = 0; i < nCols; ++i ){
|
||||
int rc = ext.xColumnText(fCx, i, op);
|
||||
affirm( 0 == rc );
|
||||
final String val = op.value;
|
||||
affirm( val.equals(sqlite3_value_text(argv[i])) );
|
||||
//outln("xFunction col "+i+": "+val);
|
||||
}
|
||||
}
|
||||
++xFuncCount.value;
|
||||
}
|
||||
public void xDestroy(){
|
||||
xDestroyCalled.value = true;
|
||||
}
|
||||
};
|
||||
|
||||
int rc = fApi.xCreateFunction("myaux", pUserData, func);
|
||||
affirm( 0==rc );
|
||||
|
||||
affirm( 0==xFuncCount.value );
|
||||
execSql(db, "select myaux(ft,a,b) from ft;");
|
||||
affirm( 2==xFuncCount.value );
|
||||
affirm( !xDestroyCalled.value );
|
||||
sqlite3_close_v2(db);
|
||||
affirm( xDestroyCalled.value );
|
||||
}
|
||||
|
||||
public TesterFts5(){
|
||||
int oldAffirmCount = Tester1.affirmCount;
|
||||
Tester1.affirmCount = 0;
|
||||
final long timeStart = System.nanoTime();
|
||||
test1();
|
||||
final long timeEnd = System.nanoTime();
|
||||
outln("FTS5 Tests done. Metrics:");
|
||||
outln("\tAssertions checked: "+Tester1.affirmCount);
|
||||
outln("\tTotal time = "
|
||||
+((timeEnd - timeStart)/1000000.0)+"ms");
|
||||
Tester1.affirmCount = oldAffirmCount;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
** 2023-07-22
|
||||
**
|
||||
** The author disclaims copyright to this source code. In place of
|
||||
** a legal notice, here is a blessing:
|
||||
**
|
||||
** May you do good and not evil.
|
||||
** May you find forgiveness for yourself and forgive others.
|
||||
** May you share freely, never taking more than you give.
|
||||
**
|
||||
*************************************************************************
|
||||
** This file is part of the JNI bindings for the sqlite3 C API.
|
||||
*/
|
||||
package org.sqlite.jni;
|
||||
|
||||
/**
|
||||
Callback proxy for use with sqlite3_trace_v2().
|
||||
*/
|
||||
public interface Tracer {
|
||||
/**
|
||||
Achtung: this interface is subject to change because the current
|
||||
approach to mapping the passed-in natives back to Java is
|
||||
uncomfortably quirky.
|
||||
|
||||
Called by sqlite3 for various tracing operations, as per
|
||||
sqlite3_trace_v2(). Note that this interface elides the 2nd
|
||||
argument to the native trace callback, as that role is better
|
||||
filled by instance-local state.
|
||||
|
||||
The 2nd argument to this function, if non-0, will be a native
|
||||
pointer to either an sqlite3 or sqlite3_stmt object, depending on
|
||||
the first argument (see below). Client code can pass it to the
|
||||
sqlite3 resp. sqlite3_stmt constructor to create a wrapping
|
||||
object, if necessary. This API does not do so by default because
|
||||
tracing can be called frequently, creating such a wrapper for
|
||||
each call is comparatively expensive, and the objects are
|
||||
probably only seldom useful.
|
||||
|
||||
The final argument to this function is the "X" argument
|
||||
documented for sqlite3_trace() and sqlite3_trace_v2(). Its type
|
||||
depends on value of the first argument:
|
||||
|
||||
- SQLITE_TRACE_STMT: pNative is a sqlite3_stmt. pX is a string
|
||||
containing the prepared SQL, with one caveat: JNI only provides
|
||||
us with the ability to convert that string to MUTF-8, as
|
||||
opposed to standard UTF-8, and is cannot be ruled out that that
|
||||
difference may be significant for certain inputs. The
|
||||
alternative would be that we first convert it to UTF-16 before
|
||||
passing it on, but there's no readily-available way to do that
|
||||
without calling back into the db to peform the conversion
|
||||
(which would lead to further tracing).
|
||||
|
||||
- SQLITE_TRACE_PROFILE: pNative is a sqlite3_stmt. pX is a Long
|
||||
holding an approximate number of nanoseconds the statement took
|
||||
to run.
|
||||
|
||||
- SQLITE_TRACE_ROW: pNative is a sqlite3_stmt. pX is null.
|
||||
|
||||
- SQLITE_TRACE_CLOSE: pNative is a sqlite3. pX is null.
|
||||
*/
|
||||
int xCallback(int traceFlag, Object pNative, Object pX);
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
** 2023-07-22
|
||||
**
|
||||
** The author disclaims copyright to this source code. In place of
|
||||
** a legal notice, here is a blessing:
|
||||
**
|
||||
** May you do good and not evil.
|
||||
** May you find forgiveness for yourself and forgive others.
|
||||
** May you share freely, never taking more than you give.
|
||||
**
|
||||
*************************************************************************
|
||||
** This file is part of the JNI bindings for the sqlite3 C API.
|
||||
*/
|
||||
package org.sqlite.jni;
|
||||
|
||||
/**
|
||||
Callback proxy for use with sqlite3_update_hook().
|
||||
*/
|
||||
public interface UpdateHook {
|
||||
/**
|
||||
Works as documented for the sqlite3_update_hook() callback.
|
||||
Must not throw.
|
||||
*/
|
||||
void xUpdateHook(int opId, String dbName, String tableName, long rowId);
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
** 2023-07-21
|
||||
**
|
||||
** The author disclaims copyright to this source code. In place of
|
||||
** a legal notice, here is a blessing:
|
||||
**
|
||||
** May you do good and not evil.
|
||||
** May you find forgiveness for yourself and forgive others.
|
||||
** May you share freely, never taking more than you give.
|
||||
**
|
||||
*************************************************************************
|
||||
** This file is part of the JNI bindings for the sqlite3 C API.
|
||||
*/
|
||||
package org.sqlite.jni;
|
||||
|
||||
/**
|
||||
A helper class which simply holds a single value. Its current use
|
||||
is for communicating values out of anonymous classes, as doing so
|
||||
requires a "final" reference.
|
||||
*/
|
||||
public class ValueHolder<T> {
|
||||
public T value;
|
||||
public ValueHolder(){}
|
||||
public ValueHolder(T v){value = v;}
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
/*
|
||||
** 2023-08-05
|
||||
**
|
||||
** The author disclaims copyright to this source code. In place of
|
||||
** a legal notice, here is a blessing:
|
||||
**
|
||||
** May you do good and not evil.
|
||||
** May you find forgiveness for yourself and forgive others.
|
||||
** May you share freely, never taking more than you give.
|
||||
**
|
||||
*************************************************************************
|
||||
** This file is part of the JNI bindings for the sqlite3 C API.
|
||||
*/
|
||||
package org.sqlite.jni;
|
||||
|
||||
/**
|
||||
INCOMPLETE AND COMPLETELY UNTESTED.
|
||||
|
||||
A wrapper for communicating C-level (fts5_api*) instances with
|
||||
Java. These wrappers do not own their associated pointer, they
|
||||
simply provide a type-safe way to communicate it between Java and C
|
||||
via JNI.
|
||||
*/
|
||||
public final class fts5_api extends NativePointerHolder<fts5_api> {
|
||||
/* Only invoked from JNI */
|
||||
private fts5_api(){}
|
||||
public final int iVersion = 2;
|
||||
|
||||
/**
|
||||
Returns the fts5_api instance associated with the given db, or
|
||||
null if something goes horribly wrong.
|
||||
*/
|
||||
public static synchronized native fts5_api getInstanceForDb(@NotNull sqlite3 db);
|
||||
|
||||
// int (*xCreateTokenizer)(
|
||||
// fts5_api *pApi,
|
||||
// const char *zName,
|
||||
// void *pContext,
|
||||
// fts5_tokenizer *pTokenizer,
|
||||
// void (*xDestroy)(void*)
|
||||
// );
|
||||
|
||||
// /* Find an existing tokenizer */
|
||||
// int (*xFindTokenizer)(
|
||||
// fts5_api *pApi,
|
||||
// const char *zName,
|
||||
// void **ppContext,
|
||||
// fts5_tokenizer *pTokenizer
|
||||
// );
|
||||
|
||||
// /* Create a new auxiliary function */
|
||||
// int (*xCreateFunction)(
|
||||
// fts5_api *pApi,
|
||||
// const char *zName,
|
||||
// void *pContext,
|
||||
// fts5_extension_function xFunction,
|
||||
// void (*xDestroy)(void*)
|
||||
// );
|
||||
|
||||
public synchronized native int xCreateFunction(@NotNull String name,
|
||||
@Nullable Object userData,
|
||||
@NotNull fts5_extension_function xFunction);
|
||||
|
||||
public int xCreateFunction(@NotNull String name,
|
||||
@NotNull fts5_extension_function xFunction){
|
||||
return xCreateFunction(name, null, xFunction);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
** 2023-08-05
|
||||
**
|
||||
** The author disclaims copyright to this source code. In place of
|
||||
** a legal notice, here is a blessing:
|
||||
**
|
||||
** May you do good and not evil.
|
||||
** May you find forgiveness for yourself and forgive others.
|
||||
** May you share freely, never taking more than you give.
|
||||
**
|
||||
*************************************************************************
|
||||
** This file is part of the JNI bindings for the sqlite3 C API.
|
||||
*/
|
||||
package org.sqlite.jni;
|
||||
|
||||
/**
|
||||
JNI-level wrapper for C's fts5_extension_function type.
|
||||
|
||||
*/
|
||||
public abstract class fts5_extension_function {
|
||||
// typedef void (*fts5_extension_function)(
|
||||
// const Fts5ExtensionApi *pApi, /* API offered by current FTS version */
|
||||
// Fts5Context *pFts, /* First arg to pass to pApi functions */
|
||||
// sqlite3_context *pCtx, /* Context for returning result/error */
|
||||
// int nVal, /* Number of values in apVal[] array */
|
||||
// sqlite3_value **apVal /* Array of trailing arguments */
|
||||
// );
|
||||
|
||||
/**
|
||||
The callback implementation, corresponding to the xFunction
|
||||
argument of C's fts5_api::xCreateFunction().
|
||||
*/
|
||||
public abstract void xFunction(Fts5ExtensionApi ext, Fts5Context fCx,
|
||||
sqlite3_context pCx, sqlite3_value argv[]);
|
||||
//! Optionally override
|
||||
public void xDestroy(){}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
** 2023-08-05
|
||||
**
|
||||
** The author disclaims copyright to this source code. In place of
|
||||
** a legal notice, here is a blessing:
|
||||
**
|
||||
** May you do good and not evil.
|
||||
** May you find forgiveness for yourself and forgive others.
|
||||
** May you share freely, never taking more than you give.
|
||||
**
|
||||
*************************************************************************
|
||||
** This file is part of the JNI bindings for the sqlite3 C API.
|
||||
*/
|
||||
package org.sqlite.jni;
|
||||
|
||||
/**
|
||||
INCOMPLETE AND COMPLETELY UNTESTED.
|
||||
|
||||
A wrapper for communicating C-level (fts5_tokenizer*) instances with
|
||||
Java. These wrappers do not own their associated pointer, they
|
||||
simply provide a type-safe way to communicate it between Java and C
|
||||
via JNI.
|
||||
*/
|
||||
public final class fts5_tokenizer extends NativePointerHolder<fts5_tokenizer> {
|
||||
/* Only invoked by JNI */
|
||||
private fts5_tokenizer(){}
|
||||
|
||||
// int (*xCreate)(void*, const char **azArg, int nArg, Fts5Tokenizer **ppOut);
|
||||
// void (*xDelete)(Fts5Tokenizer*);
|
||||
|
||||
public native int xTokenize(@NotNull Fts5Tokenizer t, int tokFlags,
|
||||
@NotNull byte pText[],
|
||||
@NotNull Fts5.xTokenizeCallback callback);
|
||||
|
||||
|
||||
// int (*xTokenize)(Fts5Tokenizer*,
|
||||
// void *pCtx,
|
||||
// int flags, /* Mask of FTS5_TOKENIZE_* flags */
|
||||
// const char *pText, int nText,
|
||||
// int (*xToken)(
|
||||
// void *pCtx, /* Copy of 2nd argument to xTokenize() */
|
||||
// int tflags, /* Mask of FTS5_TOKEN_* flags */
|
||||
// const char *pToken, /* Pointer to buffer containing token */
|
||||
// int nToken, /* Size of token in bytes */
|
||||
// int iStart, /* Byte offset of token within input text */
|
||||
// int iEnd /* Byte offset of end of token within input text */
|
||||
// )
|
||||
// );
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
** 2023-07-21
|
||||
**
|
||||
** The author disclaims copyright to this source code. In place of
|
||||
** a legal notice, here is a blessing:
|
||||
**
|
||||
** May you do good and not evil.
|
||||
** May you find forgiveness for yourself and forgive others.
|
||||
** May you share freely, never taking more than you give.
|
||||
**
|
||||
*************************************************************************
|
||||
** This file is part of the JNI bindings for the sqlite3 C API.
|
||||
*/
|
||||
package org.sqlite.jni;
|
||||
|
||||
/**
|
||||
A wrapper for communicating C-level (sqlite3*) instances with
|
||||
Java. These wrappers do not own their associated pointer, they
|
||||
simply provide a type-safe way to communicate it between Java
|
||||
and C via JNI.
|
||||
*/
|
||||
public final class sqlite3 extends NativePointerHolder<sqlite3> {
|
||||
// Only invoked from JNI
|
||||
private sqlite3(){}
|
||||
|
||||
public String toString(){
|
||||
long ptr = getNativePointer();
|
||||
if( 0==ptr ){
|
||||
return sqlite3.class.getSimpleName()+"@null";
|
||||
}
|
||||
String fn = SQLite3Jni.sqlite3_db_filename(this, "main");
|
||||
return sqlite3.class.getSimpleName()
|
||||
+"@"+String.format("0x%08x",ptr)
|
||||
+"["+((null == fn) ? "<unnamed>" : fn)+"]"
|
||||
;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
/*
|
||||
** 2023-07-21
|
||||
**
|
||||
** The author disclaims copyright to this source code. In place of
|
||||
** a legal notice, here is a blessing:
|
||||
**
|
||||
** May you do good and not evil.
|
||||
** May you find forgiveness for yourself and forgive others.
|
||||
** May you share freely, never taking more than you give.
|
||||
**
|
||||
*************************************************************************
|
||||
** This file is part of the JNI bindings for the sqlite3 C API.
|
||||
*/
|
||||
package org.sqlite.jni;
|
||||
|
||||
/**
|
||||
sqlite3_context instances are used in conjunction with user-defined
|
||||
SQL functions (a.k.a. UDFs).
|
||||
*/
|
||||
public final class sqlite3_context extends NativePointerHolder<sqlite3_context> {
|
||||
/**
|
||||
For use only by the JNI layer. It's permitted to set this even
|
||||
though it's private.
|
||||
*/
|
||||
private long aggregateContext = 0;
|
||||
|
||||
/**
|
||||
getAggregateContext() corresponds to C's
|
||||
sqlite3_aggregate_context(), with a slightly different interface
|
||||
to account for cross-language differences. It serves the same
|
||||
purposes in a slightly different way: it provides a key which is
|
||||
stable across invocations of "matching sets" of a UDF's callbacks,
|
||||
such that all calls into those callbacks can determine which "set"
|
||||
of those calls they belong to.
|
||||
|
||||
If this object is being used in the context of an aggregate or
|
||||
window UDF, this function returns a non-0 value which is distinct
|
||||
for each set of UDF callbacks from a single invocation of the
|
||||
UDF, otherwise it returns 0. The returned value is only only
|
||||
valid within the context of execution of a single SQL statement,
|
||||
and may be re-used by future invocations of the UDF in different
|
||||
SQL statements.
|
||||
|
||||
Consider this SQL, where MYFUNC is a user-defined aggregate function:
|
||||
|
||||
SELECT MYFUNC(A), MYFUNC(B) FROM T;
|
||||
|
||||
The xStep() and xFinal() methods of the callback need to be able
|
||||
to differentiate between those two invocations in order to
|
||||
perform their work properly. The value returned by
|
||||
getAggregateContext() will be distinct for each of those
|
||||
invocations of MYFUNC() and is intended to be used as a lookup
|
||||
key for mapping callback invocations to whatever client-defined
|
||||
state is needed by the UDF.
|
||||
|
||||
There is one case where this will return 0 in the context of an
|
||||
aggregate or window function: if the result set has no rows,
|
||||
the UDF's xFinal() will be called without any other x...() members
|
||||
having been called. In that one case, no aggregate context key will
|
||||
have been generated. xFinal() implementations need to be prepared to
|
||||
accept that condition as legal.
|
||||
*/
|
||||
public long getAggregateContext(){
|
||||
return aggregateContext;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
** 2023-07-21
|
||||
**
|
||||
** The author disclaims copyright to this source code. In place of
|
||||
** a legal notice, here is a blessing:
|
||||
**
|
||||
** May you do good and not evil.
|
||||
** May you find forgiveness for yourself and forgive others.
|
||||
** May you share freely, never taking more than you give.
|
||||
**
|
||||
*************************************************************************
|
||||
** This file is part of the JNI bindings for the sqlite3 C API.
|
||||
*/
|
||||
package org.sqlite.jni;
|
||||
|
||||
/**
|
||||
A wrapper for communicating C-level (sqlite3_stmt*) instances with
|
||||
Java. These wrappers do not own their associated pointer, they
|
||||
simply provide a type-safe way to communicate it between Java and C
|
||||
via JNI.
|
||||
*/
|
||||
public final class sqlite3_stmt extends NativePointerHolder<sqlite3_stmt> {
|
||||
// Only invoked from JNI.
|
||||
private sqlite3_stmt(){}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
/*
|
||||
** 2023-07-21
|
||||
**
|
||||
** The author disclaims copyright to this source code. In place of
|
||||
** a legal notice, here is a blessing:
|
||||
**
|
||||
** May you do good and not evil.
|
||||
** May you find forgiveness for yourself and forgive others.
|
||||
** May you share freely, never taking more than you give.
|
||||
**
|
||||
*************************************************************************
|
||||
** This file is part of the JNI bindings for the sqlite3 C API.
|
||||
*/
|
||||
package org.sqlite.jni;
|
||||
|
||||
public final class sqlite3_value extends NativePointerHolder<sqlite3_value> {
|
||||
//! Invoked only from JNI.
|
||||
private sqlite3_value(){}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,269 @@
|
||||
# Specifications For A Rudimentary SQLite Test Script Interpreter
|
||||
|
||||
## Overview
|
||||
|
||||
The purpose of the Test Script Interpreter is to read and interpret
|
||||
script files that contain SQL commands and desired results. The
|
||||
interpreter will check results and report an discrepencies found.
|
||||
|
||||
The test script files are ASCII text files. The filename always ends with
|
||||
".test". Each script is evaluated independently; context does not carry
|
||||
forward from one script to the next. So, for example, the --null command
|
||||
run in one test script does not cause any changes in the behavior of
|
||||
subsequent test scripts. All open database connections are closed
|
||||
at the end of each test script. All database files created by a test
|
||||
script are deleted when the script finishes.
|
||||
|
||||
## Parsing Rules:
|
||||
|
||||
1. The test script is read line by line, where a line is a sequence of
|
||||
characters that runs up to the next '\\n' (0x0a) character or until
|
||||
the end of the file. There is never a need to read ahead past the
|
||||
end of the current line.
|
||||
|
||||
2. If any line contains the string " MODULE_NAME:" (with a space before
|
||||
the initial "M") or "MIXED_MODULE_NAME:" then that test script is
|
||||
incompatible with this spec. Processing of the test script should
|
||||
end immediately. There is no need to read any more of the file.
|
||||
In verbose mode, the interpreter might choose to emit an informational
|
||||
messages saying that the test script was abandoned due to an
|
||||
incompatible module type.
|
||||
|
||||
3. If any line contains the string "SCRIPT_MODULE_NAME:" then the input
|
||||
script is known to be of the correct type for this specification and
|
||||
processing may continue. The "MODULE_NAME" checking in steps 2 and 3
|
||||
may optionally be discontinued after sighting a "SCRIPT_MODULE_NAME".
|
||||
|
||||
4. If any line contains "REQUIRED_PROPERTIES:" and that substring is followed
|
||||
by any non-whitespace text, then the script is not compatible with this
|
||||
spec. Processing should stop immediately. In verbose mode, the
|
||||
interpreter might choose to emit an information message saying that the
|
||||
test script was abandoned due to unsupported requirement properties.
|
||||
|
||||
5. If any line begins with the "\|" (0x7c) character, that indicates that
|
||||
the input script is not compatible with this specification. Processing
|
||||
of the script should stop immediately. In verbose mode, the interpreter
|
||||
might choose to emit an informational message indicating that the
|
||||
test script was abandoned because it contained "a dbtotxt format database
|
||||
specification".
|
||||
|
||||
6. Any line that begins with "#" is a C-preprocessor line. The interpreter
|
||||
described by this spec does not know how to deal with C-preprocessor lines.
|
||||
Hence, processing should be abandoned. In verbose mode, the interpreter
|
||||
might emit an informational message similar to
|
||||
"script NAME abandoned due to C-preprocessor line: ..."
|
||||
|
||||
7. If a line begins with exactly two minus signs followed by a
|
||||
lowercase letter, that is a command. Process commands as described
|
||||
below.
|
||||
|
||||
8. All other lines should be accumulated into the "input buffer".
|
||||
The various commands will have access to this input buffer.
|
||||
Some commands will reset the buffer.
|
||||
|
||||
## Initialization
|
||||
|
||||
The initial state of the interpreter at the start of processing each script
|
||||
is as if the following command sequence had been run:
|
||||
|
||||
> ~~~
|
||||
--close all
|
||||
--db 0
|
||||
--new test.db
|
||||
--null nil
|
||||
~~~
|
||||
|
||||
In words, all database connections are closed except for connection 0 (the
|
||||
default) which is open on an empty database named "test.db". The string
|
||||
"nil" is displayed for NULL column values.
|
||||
|
||||
The only context carried forward after the evaluation of one test script
|
||||
into the evaluation of the next test script is the count of the number of
|
||||
tests run and the number of failures seen.
|
||||
|
||||
## Commands:
|
||||
|
||||
Each command looks like an SQL comment. The command begins at the left
|
||||
margin (no leading space) and starts with exactly 2 minus signs ("-").
|
||||
The command name consists of lowercase letters and maybe a "-" or two.
|
||||
Some commands have arguments.
|
||||
The arguments are separated from the command name by one or more spaces.
|
||||
|
||||
Commands have access to the input buffer and might reset the input buffer.
|
||||
The command can also optionally read (and consume) additional text from
|
||||
script that comes after the command.
|
||||
|
||||
Unknown or unrecognized commands indicate that the script contains features
|
||||
that are not (yet) supported by this specification. Processing of the
|
||||
script should terminate immediately. When this happens and when the
|
||||
interpreter is in a "verbose" mode, the interpreter might choose to emit
|
||||
an informational message along the lines of "test script NAME abandoned
|
||||
due to unsupported command: --whatever".
|
||||
|
||||
The initial implemention will only recognize a few commands. Other
|
||||
commands may be added later. The following is the initial set of
|
||||
commands:
|
||||
|
||||
### The --testcase command
|
||||
|
||||
Every test case starts with a --testcase command. The --testcase
|
||||
command resets both the "input buffer" and the "result buffer". The
|
||||
argument to the --testcase command is the name of the test case. That
|
||||
test case name is used for logging and debugging and when printing
|
||||
errors. The input buffer is set to the body of the test case.
|
||||
|
||||
### The --result command
|
||||
|
||||
The --result command tries to execute the text in the input buffer as SQL.
|
||||
For each row of result coming out of this SQL, the text of that result is
|
||||
appended to the "result buffer". If a result row contains multiple columns,
|
||||
the columns are processed from left to right. For each column, text is
|
||||
appended to the result buffer according to the following rules:
|
||||
|
||||
* If the result buffer already contains some text, append a space.
|
||||
(In this way, all column values and all row values are separated from
|
||||
each other by a single space.)
|
||||
|
||||
* If sqlite3_column_text() returns NULL, then append "nil" - or
|
||||
some other text that is specified by the --null command - and skip
|
||||
all subsequent rules.
|
||||
|
||||
* If sqlite3_column_text() is an empty string, append `{}` to the
|
||||
result buffer and skip all subsequent rules.
|
||||
|
||||
* If sqlite3_column_text() does not contain any special
|
||||
characters, append it to the result buffer without any
|
||||
formatting and skip all subsequent rules. Special characters are:
|
||||
0x00 to 0x20 (inclusive), double-quote (0x22), backslash (0x5c),
|
||||
curly braces (0x7b and 0x7d).
|
||||
|
||||
* If sqlite3_column_text() does not contains curly braces, then put
|
||||
the text inside of `{...}` and append it and skip all subsequent rules.
|
||||
|
||||
* Append the text within double-quotes (`"..."`) and within the text
|
||||
escape '"' and '\\' by prepending a single '\\' and escape any
|
||||
control characters (characters less than 0x20) using octal notation:
|
||||
'\\NNN'.
|
||||
|
||||
If an error is encountered while running the SQL, then append the
|
||||
symbolic C-preprocessor name for the error
|
||||
code (ex: "SQLITE_CONSTRAINT") as if it were a column value. Then append
|
||||
the error message text as if it where a column value. Then stop processing.
|
||||
|
||||
After the SQL text has been run, compare the content of the result buffer
|
||||
against the argument to the --result command and report a testing error if
|
||||
there are any differences.
|
||||
|
||||
The --result command resets the input buffer, but it does not reset
|
||||
the result buffer. This distinction does not matter for the --result
|
||||
command itself, but it is important for related commands like --glob
|
||||
and --notglob. Sometimes test cases will contains a bunch of SQL
|
||||
followed by multiple --glob and/or --notglob statements. All of the
|
||||
globs should be evaluted agains the result buffer correct, but the SQL
|
||||
should only be run once. This is accomplished by resetting the input
|
||||
buffer but not the result buffer.
|
||||
|
||||
### The --glob command
|
||||
|
||||
The --glob command works just like --result except that the argument to
|
||||
--glob is interpreted as a TEST-GLOB pattern and the results are compared
|
||||
using that glob pattern rather than using strcmp(). Other than that,
|
||||
the two operate the same.
|
||||
|
||||
The TEST-GLOB pattern is slightly different for a standard GLOB:
|
||||
|
||||
* The '*' character matches zero or more characters.
|
||||
|
||||
* The '?' character matches any single character
|
||||
|
||||
* The '[...]' character sequence machines a single character
|
||||
in between the brackets.
|
||||
|
||||
* The '#' character matches one or more digits (This is the main
|
||||
difference between standard unix-glob and TEST-GLOB. unix-glob
|
||||
does not have this feature. It was added to because it comes
|
||||
up a lot during SQLite testing.)
|
||||
|
||||
### The --notglob command
|
||||
|
||||
The --notglob command works just like --glob except that it reports an
|
||||
error if the GLOB does match, rather than if the GLOB does not matches.
|
||||
|
||||
### The --oom command
|
||||
|
||||
This command is to be used for out-of-memory testing. It means that
|
||||
OOM errors should be simulated to ensure that SQLite is able to deal with
|
||||
them. This command can be silently ignored for now. We might add support
|
||||
for this later.
|
||||
|
||||
### The --tableresult command
|
||||
|
||||
The --tableresult command works like --glob except that the GLOB pattern
|
||||
to be matched is taken from subsequent lines of the input script up to
|
||||
the next --end. Every span of one or more whitespace characters in this
|
||||
pattern text is collapsed into a single space (0x20).
|
||||
Leading and trailing whitespace are removed from the pattern.
|
||||
The --end that ends the GLOB pattern is not part of the GLOB pattern, but
|
||||
the --end is consumed from the script input.
|
||||
|
||||
### The --new and --open commands
|
||||
|
||||
The --new and --open commands cause a database file to be opened.
|
||||
The name of the file is the argument to the command. The --new command
|
||||
opens an initially empty database (it deletes the file before opening it)
|
||||
whereas the --open command opens an existing database if it already
|
||||
exists.
|
||||
|
||||
### The --db command
|
||||
|
||||
The script interpreter can have up to 7 different SQLite database
|
||||
connections open at a time. The --db command is used to switch between
|
||||
them. The argument to --db is an integer between 0 and 6 that selects
|
||||
which database connection to use moving forward.
|
||||
|
||||
### The --close command
|
||||
|
||||
The --close command causes an existing database connection to close.
|
||||
This command is a no-op if the database connection is not currently
|
||||
open. There can be up to 7 different database connections, numbered 0
|
||||
through 6. The number of the database connection to close is an
|
||||
argument to the --close command, which will fail if an out-of-range
|
||||
value is provided. Or if the argument to --close is "all" then all
|
||||
open database connections are closed. If passed no argument, the
|
||||
currently-active database is assumed.
|
||||
|
||||
### The --null command
|
||||
|
||||
The NULL command changes the text that is used to represent SQL NULL
|
||||
values in the result buffer.
|
||||
|
||||
### The --run command
|
||||
|
||||
The --run command executes text in the input buffer as if it where SQL.
|
||||
However, nothing is added to the result buffer. Any output from the SQL
|
||||
is silently ignored. Errors in the SQL are silently ignored.
|
||||
|
||||
The --run command normally executes the SQL in the current database
|
||||
connection. However, if --run has an argument that is an integer between
|
||||
0 and 6 then the SQL is run in the alternative database connection specified
|
||||
by that argument.
|
||||
|
||||
### The --json and --json-block commands
|
||||
|
||||
The --json and --json-block commands work like --result and --tableresult,
|
||||
respectively. The difference is that column values are appended to the
|
||||
result buffer literally, without ever enclosing the values in `{...}` or
|
||||
`"..."` and without escaping any characters in the column value and comparison
|
||||
is always an exact strcmp() not a GLOB.
|
||||
|
||||
### The --print command
|
||||
|
||||
The --print command emits both its arguments and its body (if any) to
|
||||
stdout, indenting each line of output.
|
||||
|
||||
### The --column-names command
|
||||
|
||||
The --column-names command requires 0 or 1 as an argument, to disable
|
||||
resp. enable it, and modifies SQL execution to include column names
|
||||
in output. When this option is on, each column value emitted gets
|
||||
prefixed by its column name, with a single space between them.
|
||||
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
** This is a comment. There are many like it but this one is mine.
|
||||
**
|
||||
** SCRIPT_MODULE_NAME: sanity-check
|
||||
** xMIXED_MODULE_NAME: mixed-module
|
||||
** xMODULE_NAME: module-name
|
||||
** xREQUIRED_PROPERTIES: small fast reliable
|
||||
** xREQUIRED_PROPERTIES: RECURSIVE_TRIGGERS
|
||||
** xREQUIRED_PROPERTIES: TEMPSTORE_MEM TEMPSTORE_FILE
|
||||
**
|
||||
*/
|
||||
--print starting up 😃
|
||||
--close all
|
||||
--oom
|
||||
--db 0
|
||||
--new my.db
|
||||
--null zilch
|
||||
--testcase 1.0
|
||||
SELECT 1, null;
|
||||
--result 1 zilch
|
||||
--glob *zil*
|
||||
--notglob *ZIL*
|
||||
SELECT 1, 2;
|
||||
intentional error;
|
||||
--run
|
||||
--testcase json-1
|
||||
SELECT json_array(1,2,3)
|
||||
--json [1,2,3]
|
||||
--testcase tableresult-1
|
||||
select 1, 'a';
|
||||
select 2, 'b';
|
||||
--tableresult
|
||||
# [a-z]
|
||||
2 b
|
||||
--end
|
||||
--testcase json-block-1
|
||||
select json_array(1,2,3);
|
||||
select json_object('a',1,'b',2);
|
||||
--json-block
|
||||
[1,2,3]
|
||||
{"a":1,"b":2}
|
||||
--end
|
||||
--testcase col-names-on
|
||||
--column-names 1
|
||||
select 1 as 'a', 2 as 'b';
|
||||
--result a 1 b 2
|
||||
--testcase col-names-off
|
||||
--column-names 0
|
||||
select 1 as 'a', 2 as 'b';
|
||||
--result 1 2
|
||||
--close
|
||||
--print reached the end 😃
|
||||
@@ -0,0 +1,9 @@
|
||||
/*
|
||||
** This script must be marked as ignored because it contains
|
||||
** content which triggers that condition.
|
||||
**
|
||||
** SCRIPT_MODULE_NAME: ignored
|
||||
**
|
||||
*/
|
||||
|
||||
|
|
||||
+5
-2
@@ -69,9 +69,12 @@ __declspec(dllexport)
|
||||
#endif
|
||||
int sqlite3_basexx_init(sqlite3 *db, char **pzErr,
|
||||
const sqlite3_api_routines *pApi){
|
||||
int rc1;
|
||||
int rc2;
|
||||
|
||||
init_api_ptr(pApi);
|
||||
int rc1 = BASE64_INIT(db);
|
||||
int rc2 = BASE85_INIT(db);
|
||||
rc1 = BASE64_INIT(db);
|
||||
rc2 = BASE85_INIT(db);
|
||||
|
||||
if( rc1==SQLITE_OK && rc2==SQLITE_OK ){
|
||||
BASE64_EXPOSE(db, pzErr);
|
||||
|
||||
+345
-96
@@ -58,41 +58,24 @@ static void decimal_free(Decimal *p){
|
||||
}
|
||||
|
||||
/*
|
||||
** Allocate a new Decimal object. Initialize it to the number given
|
||||
** by the input string.
|
||||
** Allocate a new Decimal object initialized to the text in zIn[].
|
||||
** Return NULL if any kind of error occurs.
|
||||
*/
|
||||
static Decimal *decimal_new(
|
||||
sqlite3_context *pCtx,
|
||||
sqlite3_value *pIn,
|
||||
int nAlt,
|
||||
const unsigned char *zAlt
|
||||
){
|
||||
Decimal *p;
|
||||
int n, i;
|
||||
const unsigned char *zIn;
|
||||
static Decimal *decimalNewFromText(const char *zIn, int n){
|
||||
Decimal *p = 0;
|
||||
int i;
|
||||
int iExp = 0;
|
||||
|
||||
p = sqlite3_malloc( sizeof(*p) );
|
||||
if( p==0 ) goto new_no_mem;
|
||||
if( p==0 ) goto new_from_text_failed;
|
||||
p->sign = 0;
|
||||
p->oom = 0;
|
||||
p->isInit = 1;
|
||||
p->isNull = 0;
|
||||
p->nDigit = 0;
|
||||
p->nFrac = 0;
|
||||
if( zAlt ){
|
||||
n = nAlt,
|
||||
zIn = zAlt;
|
||||
}else{
|
||||
if( sqlite3_value_type(pIn)==SQLITE_NULL ){
|
||||
p->a = 0;
|
||||
p->isNull = 1;
|
||||
return p;
|
||||
}
|
||||
n = sqlite3_value_bytes(pIn);
|
||||
zIn = sqlite3_value_text(pIn);
|
||||
}
|
||||
p->a = sqlite3_malloc64( n+1 );
|
||||
if( p->a==0 ) goto new_no_mem;
|
||||
if( p->a==0 ) goto new_from_text_failed;
|
||||
for(i=0; isspace(zIn[i]); i++){}
|
||||
if( zIn[i]=='-' ){
|
||||
p->sign = 1;
|
||||
@@ -143,7 +126,7 @@ static Decimal *decimal_new(
|
||||
}
|
||||
if( iExp>0 ){
|
||||
p->a = sqlite3_realloc64(p->a, p->nDigit + iExp + 1 );
|
||||
if( p->a==0 ) goto new_no_mem;
|
||||
if( p->a==0 ) goto new_from_text_failed;
|
||||
memset(p->a+p->nDigit, 0, iExp);
|
||||
p->nDigit += iExp;
|
||||
}
|
||||
@@ -162,7 +145,7 @@ static Decimal *decimal_new(
|
||||
}
|
||||
if( iExp>0 ){
|
||||
p->a = sqlite3_realloc64(p->a, p->nDigit + iExp + 1 );
|
||||
if( p->a==0 ) goto new_no_mem;
|
||||
if( p->a==0 ) goto new_from_text_failed;
|
||||
memmove(p->a+iExp, p->a, p->nDigit);
|
||||
memset(p->a, 0, iExp);
|
||||
p->nDigit += iExp;
|
||||
@@ -171,7 +154,76 @@ static Decimal *decimal_new(
|
||||
}
|
||||
return p;
|
||||
|
||||
new_no_mem:
|
||||
new_from_text_failed:
|
||||
if( p ){
|
||||
if( p->a ) sqlite3_free(p->a);
|
||||
sqlite3_free(p);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Forward reference */
|
||||
static Decimal *decimalFromDouble(double);
|
||||
|
||||
/*
|
||||
** Allocate a new Decimal object from an sqlite3_value. Return a pointer
|
||||
** to the new object, or NULL if there is an error. If the pCtx argument
|
||||
** is not NULL, then errors are reported on it as well.
|
||||
**
|
||||
** If the pIn argument is SQLITE_TEXT or SQLITE_INTEGER, it is converted
|
||||
** directly into a Decimal. For SQLITE_FLOAT or for SQLITE_BLOB of length
|
||||
** 8 bytes, the resulting double value is expanded into its decimal equivalent.
|
||||
** If pIn is NULL or if it is a BLOB that is not exactly 8 bytes in length,
|
||||
** then NULL is returned.
|
||||
*/
|
||||
static Decimal *decimal_new(
|
||||
sqlite3_context *pCtx, /* Report error here, if not null */
|
||||
sqlite3_value *pIn, /* Construct the decimal object from this */
|
||||
int bTextOnly /* Always interpret pIn as text if true */
|
||||
){
|
||||
Decimal *p = 0;
|
||||
int eType = sqlite3_value_type(pIn);
|
||||
if( bTextOnly && (eType==SQLITE_FLOAT || eType==SQLITE_BLOB) ){
|
||||
eType = SQLITE_TEXT;
|
||||
}
|
||||
switch( eType ){
|
||||
case SQLITE_TEXT:
|
||||
case SQLITE_INTEGER: {
|
||||
const char *zIn = (const char*)sqlite3_value_text(pIn);
|
||||
int n = sqlite3_value_bytes(pIn);
|
||||
p = decimalNewFromText(zIn, n);
|
||||
if( p==0 ) goto new_failed;
|
||||
break;
|
||||
}
|
||||
|
||||
case SQLITE_FLOAT: {
|
||||
p = decimalFromDouble(sqlite3_value_double(pIn));
|
||||
break;
|
||||
}
|
||||
|
||||
case SQLITE_BLOB: {
|
||||
const unsigned char *x;
|
||||
unsigned int i;
|
||||
sqlite3_uint64 v = 0;
|
||||
double r;
|
||||
|
||||
if( sqlite3_value_bytes(pIn)!=sizeof(r) ) break;
|
||||
x = sqlite3_value_blob(pIn);
|
||||
for(i=0; i<sizeof(r); i++){
|
||||
v = (v<<8) | x[i];
|
||||
}
|
||||
memcpy(&r, &v, sizeof(r));
|
||||
p = decimalFromDouble(r);
|
||||
break;
|
||||
}
|
||||
|
||||
case SQLITE_NULL: {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return p;
|
||||
|
||||
new_failed:
|
||||
if( pCtx ) sqlite3_result_error_nomem(pCtx);
|
||||
sqlite3_free(p);
|
||||
return 0;
|
||||
@@ -231,19 +283,64 @@ static void decimal_result(sqlite3_context *pCtx, Decimal *p){
|
||||
}
|
||||
|
||||
/*
|
||||
** SQL Function: decimal(X)
|
||||
**
|
||||
** Convert input X into decimal and then back into text
|
||||
** Make the given Decimal the result in an format similar to '%+#e'.
|
||||
** In other words, show exponential notation with leading and trailing
|
||||
** zeros omitted.
|
||||
*/
|
||||
static void decimalFunc(
|
||||
sqlite3_context *context,
|
||||
int argc,
|
||||
sqlite3_value **argv
|
||||
){
|
||||
Decimal *p = decimal_new(context, argv[0], 0, 0);
|
||||
UNUSED_PARAMETER(argc);
|
||||
decimal_result(context, p);
|
||||
decimal_free(p);
|
||||
static void decimal_result_sci(sqlite3_context *pCtx, Decimal *p){
|
||||
char *z; /* The output buffer */
|
||||
int i; /* Loop counter */
|
||||
int nZero; /* Number of leading zeros */
|
||||
int nDigit; /* Number of digits not counting trailing zeros */
|
||||
int nFrac; /* Digits to the right of the decimal point */
|
||||
int exp; /* Exponent value */
|
||||
signed char zero; /* Zero value */
|
||||
signed char *a; /* Array of digits */
|
||||
|
||||
if( p==0 || p->oom ){
|
||||
sqlite3_result_error_nomem(pCtx);
|
||||
return;
|
||||
}
|
||||
if( p->isNull ){
|
||||
sqlite3_result_null(pCtx);
|
||||
return;
|
||||
}
|
||||
for(nDigit=p->nDigit; nDigit>0 && p->a[nDigit-1]==0; nDigit--){}
|
||||
for(nZero=0; nZero<nDigit && p->a[nZero]==0; nZero++){}
|
||||
nFrac = p->nFrac + (nDigit - p->nDigit);
|
||||
nDigit -= nZero;
|
||||
z = sqlite3_malloc( nDigit+20 );
|
||||
if( z==0 ){
|
||||
sqlite3_result_error_nomem(pCtx);
|
||||
return;
|
||||
}
|
||||
if( nDigit==0 ){
|
||||
zero = 0;
|
||||
a = &zero;
|
||||
nDigit = 1;
|
||||
nFrac = 0;
|
||||
}else{
|
||||
a = &p->a[nZero];
|
||||
}
|
||||
if( p->sign && nDigit>0 ){
|
||||
z[0] = '-';
|
||||
}else{
|
||||
z[0] = '+';
|
||||
}
|
||||
z[1] = a[0]+'0';
|
||||
z[2] = '.';
|
||||
if( nDigit==1 ){
|
||||
z[3] = '0';
|
||||
i = 4;
|
||||
}else{
|
||||
for(i=1; i<nDigit; i++){
|
||||
z[2+i] = a[i]+'0';
|
||||
}
|
||||
i = nDigit+2;
|
||||
}
|
||||
exp = nDigit - nFrac - 1;
|
||||
sqlite3_snprintf(nDigit+20-i, &z[i], "e%+03d", exp);
|
||||
sqlite3_result_text(pCtx, z, -1, sqlite3_free);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -296,9 +393,9 @@ static void decimalCmpFunc(
|
||||
int rc;
|
||||
|
||||
UNUSED_PARAMETER(argc);
|
||||
pA = decimal_new(context, argv[0], 0, 0);
|
||||
pA = decimal_new(context, argv[0], 1);
|
||||
if( pA==0 || pA->isNull ) goto cmp_done;
|
||||
pB = decimal_new(context, argv[1], 0, 0);
|
||||
pB = decimal_new(context, argv[1], 1);
|
||||
if( pB==0 || pB->isNull ) goto cmp_done;
|
||||
rc = decimal_cmp(pA, pB);
|
||||
if( rc<0 ) rc = -1;
|
||||
@@ -338,7 +435,7 @@ static void decimal_expand(Decimal *p, int nDigit, int nFrac){
|
||||
}
|
||||
|
||||
/*
|
||||
** Add the value pB into pA.
|
||||
** Add the value pB into pA. A := A + B.
|
||||
**
|
||||
** Both pA and pB might become denormalized by this routine.
|
||||
*/
|
||||
@@ -407,6 +504,172 @@ static void decimal_add(Decimal *pA, Decimal *pB){
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** Multiply A by B. A := A * B
|
||||
**
|
||||
** All significant digits after the decimal point are retained.
|
||||
** Trailing zeros after the decimal point are omitted as long as
|
||||
** the number of digits after the decimal point is no less than
|
||||
** either the number of digits in either input.
|
||||
*/
|
||||
static void decimalMul(Decimal *pA, Decimal *pB){
|
||||
signed char *acc = 0;
|
||||
int i, j, k;
|
||||
int minFrac;
|
||||
|
||||
if( pA==0 || pA->oom || pA->isNull
|
||||
|| pB==0 || pB->oom || pB->isNull
|
||||
){
|
||||
goto mul_end;
|
||||
}
|
||||
acc = sqlite3_malloc64( pA->nDigit + pB->nDigit + 2 );
|
||||
if( acc==0 ){
|
||||
pA->oom = 1;
|
||||
goto mul_end;
|
||||
}
|
||||
memset(acc, 0, pA->nDigit + pB->nDigit + 2);
|
||||
minFrac = pA->nFrac;
|
||||
if( pB->nFrac<minFrac ) minFrac = pB->nFrac;
|
||||
for(i=pA->nDigit-1; i>=0; i--){
|
||||
signed char f = pA->a[i];
|
||||
int carry = 0, x;
|
||||
for(j=pB->nDigit-1, k=i+j+3; j>=0; j--, k--){
|
||||
x = acc[k] + f*pB->a[j] + carry;
|
||||
acc[k] = x%10;
|
||||
carry = x/10;
|
||||
}
|
||||
x = acc[k] + carry;
|
||||
acc[k] = x%10;
|
||||
acc[k-1] += x/10;
|
||||
}
|
||||
sqlite3_free(pA->a);
|
||||
pA->a = acc;
|
||||
acc = 0;
|
||||
pA->nDigit += pB->nDigit + 2;
|
||||
pA->nFrac += pB->nFrac;
|
||||
pA->sign ^= pB->sign;
|
||||
while( pA->nFrac>minFrac && pA->a[pA->nDigit-1]==0 ){
|
||||
pA->nFrac--;
|
||||
pA->nDigit--;
|
||||
}
|
||||
|
||||
mul_end:
|
||||
sqlite3_free(acc);
|
||||
}
|
||||
|
||||
/*
|
||||
** Create a new Decimal object that contains an integer power of 2.
|
||||
*/
|
||||
static Decimal *decimalPow2(int N){
|
||||
Decimal *pA = 0; /* The result to be returned */
|
||||
Decimal *pX = 0; /* Multiplier */
|
||||
if( N<-20000 || N>20000 ) goto pow2_fault;
|
||||
pA = decimalNewFromText("1.0", 3);
|
||||
if( pA==0 || pA->oom ) goto pow2_fault;
|
||||
if( N==0 ) return pA;
|
||||
if( N>0 ){
|
||||
pX = decimalNewFromText("2.0", 3);
|
||||
}else{
|
||||
N = -N;
|
||||
pX = decimalNewFromText("0.5", 3);
|
||||
}
|
||||
if( pX==0 || pX->oom ) goto pow2_fault;
|
||||
while( 1 /* Exit by break */ ){
|
||||
if( N & 1 ){
|
||||
decimalMul(pA, pX);
|
||||
if( pA->oom ) goto pow2_fault;
|
||||
}
|
||||
N >>= 1;
|
||||
if( N==0 ) break;
|
||||
decimalMul(pX, pX);
|
||||
}
|
||||
decimal_free(pX);
|
||||
return pA;
|
||||
|
||||
pow2_fault:
|
||||
decimal_free(pA);
|
||||
decimal_free(pX);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
** Use an IEEE754 binary64 ("double") to generate a new Decimal object.
|
||||
*/
|
||||
static Decimal *decimalFromDouble(double r){
|
||||
sqlite3_int64 m, a;
|
||||
int e;
|
||||
int isNeg;
|
||||
Decimal *pA;
|
||||
Decimal *pX;
|
||||
char zNum[100];
|
||||
if( r<0.0 ){
|
||||
isNeg = 1;
|
||||
r = -r;
|
||||
}else{
|
||||
isNeg = 0;
|
||||
}
|
||||
memcpy(&a,&r,sizeof(a));
|
||||
if( a==0 ){
|
||||
e = 0;
|
||||
m = 0;
|
||||
}else{
|
||||
e = a>>52;
|
||||
m = a & ((((sqlite3_int64)1)<<52)-1);
|
||||
if( e==0 ){
|
||||
m <<= 1;
|
||||
}else{
|
||||
m |= ((sqlite3_int64)1)<<52;
|
||||
}
|
||||
while( e<1075 && m>0 && (m&1)==0 ){
|
||||
m >>= 1;
|
||||
e++;
|
||||
}
|
||||
if( isNeg ) m = -m;
|
||||
e = e - 1075;
|
||||
if( e>971 ){
|
||||
return 0; /* A NaN or an Infinity */
|
||||
}
|
||||
}
|
||||
|
||||
/* At this point m is the integer significand and e is the exponent */
|
||||
sqlite3_snprintf(sizeof(zNum), zNum, "%lld", m);
|
||||
pA = decimalNewFromText(zNum, (int)strlen(zNum));
|
||||
pX = decimalPow2(e);
|
||||
decimalMul(pA, pX);
|
||||
decimal_free(pX);
|
||||
return pA;
|
||||
}
|
||||
|
||||
/*
|
||||
** SQL Function: decimal(X)
|
||||
** OR: decimal_sci(X)
|
||||
**
|
||||
** Convert input X into decimal and then back into text.
|
||||
**
|
||||
** If X is originally a float, then a full decimal expansion of that floating
|
||||
** point value is done. Or if X is an 8-byte blob, it is interpreted
|
||||
** as a float and similarly expanded.
|
||||
**
|
||||
** The decimal_sci(X) function returns the result in scientific notation.
|
||||
** decimal(X) returns a complete decimal, without the e+NNN at the end.
|
||||
*/
|
||||
static void decimalFunc(
|
||||
sqlite3_context *context,
|
||||
int argc,
|
||||
sqlite3_value **argv
|
||||
){
|
||||
Decimal *p = decimal_new(context, argv[0], 0);
|
||||
UNUSED_PARAMETER(argc);
|
||||
if( p ){
|
||||
if( sqlite3_user_data(context)!=0 ){
|
||||
decimal_result_sci(context, p);
|
||||
}else{
|
||||
decimal_result(context, p);
|
||||
}
|
||||
decimal_free(p);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** Compare text in decimal order.
|
||||
*/
|
||||
@@ -417,8 +680,8 @@ static int decimalCollFunc(
|
||||
){
|
||||
const unsigned char *zA = (const unsigned char*)pKey1;
|
||||
const unsigned char *zB = (const unsigned char*)pKey2;
|
||||
Decimal *pA = decimal_new(0, 0, nKey1, zA);
|
||||
Decimal *pB = decimal_new(0, 0, nKey2, zB);
|
||||
Decimal *pA = decimalNewFromText((const char*)zA, nKey1);
|
||||
Decimal *pB = decimalNewFromText((const char*)zB, nKey2);
|
||||
int rc;
|
||||
UNUSED_PARAMETER(notUsed);
|
||||
if( pA==0 || pB==0 ){
|
||||
@@ -443,8 +706,8 @@ static void decimalAddFunc(
|
||||
int argc,
|
||||
sqlite3_value **argv
|
||||
){
|
||||
Decimal *pA = decimal_new(context, argv[0], 0, 0);
|
||||
Decimal *pB = decimal_new(context, argv[1], 0, 0);
|
||||
Decimal *pA = decimal_new(context, argv[0], 1);
|
||||
Decimal *pB = decimal_new(context, argv[1], 1);
|
||||
UNUSED_PARAMETER(argc);
|
||||
decimal_add(pA, pB);
|
||||
decimal_result(context, pA);
|
||||
@@ -456,8 +719,8 @@ static void decimalSubFunc(
|
||||
int argc,
|
||||
sqlite3_value **argv
|
||||
){
|
||||
Decimal *pA = decimal_new(context, argv[0], 0, 0);
|
||||
Decimal *pB = decimal_new(context, argv[1], 0, 0);
|
||||
Decimal *pA = decimal_new(context, argv[0], 1);
|
||||
Decimal *pB = decimal_new(context, argv[1], 1);
|
||||
UNUSED_PARAMETER(argc);
|
||||
if( pB ){
|
||||
pB->sign = !pB->sign;
|
||||
@@ -495,7 +758,7 @@ static void decimalSumStep(
|
||||
p->nFrac = 0;
|
||||
}
|
||||
if( sqlite3_value_type(argv[0])==SQLITE_NULL ) return;
|
||||
pArg = decimal_new(context, argv[0], 0, 0);
|
||||
pArg = decimal_new(context, argv[0], 1);
|
||||
decimal_add(p, pArg);
|
||||
decimal_free(pArg);
|
||||
}
|
||||
@@ -510,7 +773,7 @@ static void decimalSumInverse(
|
||||
p = sqlite3_aggregate_context(context, sizeof(*p));
|
||||
if( p==0 ) return;
|
||||
if( sqlite3_value_type(argv[0])==SQLITE_NULL ) return;
|
||||
pArg = decimal_new(context, argv[0], 0, 0);
|
||||
pArg = decimal_new(context, argv[0], 1);
|
||||
if( pArg ) pArg->sign = !pArg->sign;
|
||||
decimal_add(p, pArg);
|
||||
decimal_free(pArg);
|
||||
@@ -531,66 +794,49 @@ static void decimalSumFinalize(sqlite3_context *context){
|
||||
** SQL Function: decimal_mul(X, Y)
|
||||
**
|
||||
** Return the product of X and Y.
|
||||
**
|
||||
** All significant digits after the decimal point are retained.
|
||||
** Trailing zeros after the decimal point are omitted as long as
|
||||
** the number of digits after the decimal point is no less than
|
||||
** either the number of digits in either input.
|
||||
*/
|
||||
static void decimalMulFunc(
|
||||
sqlite3_context *context,
|
||||
int argc,
|
||||
sqlite3_value **argv
|
||||
){
|
||||
Decimal *pA = decimal_new(context, argv[0], 0, 0);
|
||||
Decimal *pB = decimal_new(context, argv[1], 0, 0);
|
||||
signed char *acc = 0;
|
||||
int i, j, k;
|
||||
int minFrac;
|
||||
Decimal *pA = decimal_new(context, argv[0], 1);
|
||||
Decimal *pB = decimal_new(context, argv[1], 1);
|
||||
UNUSED_PARAMETER(argc);
|
||||
if( pA==0 || pA->oom || pA->isNull
|
||||
|| pB==0 || pB->oom || pB->isNull
|
||||
){
|
||||
goto mul_end;
|
||||
}
|
||||
acc = sqlite3_malloc64( pA->nDigit + pB->nDigit + 2 );
|
||||
if( acc==0 ){
|
||||
sqlite3_result_error_nomem(context);
|
||||
decimalMul(pA, pB);
|
||||
if( pA->oom ){
|
||||
goto mul_end;
|
||||
}
|
||||
memset(acc, 0, pA->nDigit + pB->nDigit + 2);
|
||||
minFrac = pA->nFrac;
|
||||
if( pB->nFrac<minFrac ) minFrac = pB->nFrac;
|
||||
for(i=pA->nDigit-1; i>=0; i--){
|
||||
signed char f = pA->a[i];
|
||||
int carry = 0, x;
|
||||
for(j=pB->nDigit-1, k=i+j+3; j>=0; j--, k--){
|
||||
x = acc[k] + f*pB->a[j] + carry;
|
||||
acc[k] = x%10;
|
||||
carry = x/10;
|
||||
}
|
||||
x = acc[k] + carry;
|
||||
acc[k] = x%10;
|
||||
acc[k-1] += x/10;
|
||||
}
|
||||
sqlite3_free(pA->a);
|
||||
pA->a = acc;
|
||||
acc = 0;
|
||||
pA->nDigit += pB->nDigit + 2;
|
||||
pA->nFrac += pB->nFrac;
|
||||
pA->sign ^= pB->sign;
|
||||
while( pA->nFrac>minFrac && pA->a[pA->nDigit-1]==0 ){
|
||||
pA->nFrac--;
|
||||
pA->nDigit--;
|
||||
}
|
||||
decimal_result(context, pA);
|
||||
|
||||
mul_end:
|
||||
sqlite3_free(acc);
|
||||
decimal_free(pA);
|
||||
decimal_free(pB);
|
||||
}
|
||||
|
||||
/*
|
||||
** SQL Function: decimal_pow2(N)
|
||||
**
|
||||
** Return the N-th power of 2. N must be an integer.
|
||||
*/
|
||||
static void decimalPow2Func(
|
||||
sqlite3_context *context,
|
||||
int argc,
|
||||
sqlite3_value **argv
|
||||
){
|
||||
UNUSED_PARAMETER(argc);
|
||||
if( sqlite3_value_type(argv[0])==SQLITE_INTEGER ){
|
||||
Decimal *pA = decimalPow2(sqlite3_value_int(argv[0]));
|
||||
decimal_result_sci(context, pA);
|
||||
decimal_free(pA);
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
__declspec(dllexport)
|
||||
#endif
|
||||
@@ -603,13 +849,16 @@ int sqlite3_decimal_init(
|
||||
static const struct {
|
||||
const char *zFuncName;
|
||||
int nArg;
|
||||
int iArg;
|
||||
void (*xFunc)(sqlite3_context*,int,sqlite3_value**);
|
||||
} aFunc[] = {
|
||||
{ "decimal", 1, decimalFunc },
|
||||
{ "decimal_cmp", 2, decimalCmpFunc },
|
||||
{ "decimal_add", 2, decimalAddFunc },
|
||||
{ "decimal_sub", 2, decimalSubFunc },
|
||||
{ "decimal_mul", 2, decimalMulFunc },
|
||||
{ "decimal", 1, 0, decimalFunc },
|
||||
{ "decimal_sci", 1, 1, decimalFunc },
|
||||
{ "decimal_cmp", 2, 0, decimalCmpFunc },
|
||||
{ "decimal_add", 2, 0, decimalAddFunc },
|
||||
{ "decimal_sub", 2, 0, decimalSubFunc },
|
||||
{ "decimal_mul", 2, 0, decimalMulFunc },
|
||||
{ "decimal_pow2", 1, 0, decimalPow2Func },
|
||||
};
|
||||
unsigned int i;
|
||||
(void)pzErrMsg; /* Unused parameter */
|
||||
@@ -619,7 +868,7 @@ int sqlite3_decimal_init(
|
||||
for(i=0; i<(int)(sizeof(aFunc)/sizeof(aFunc[0])) && rc==SQLITE_OK; i++){
|
||||
rc = sqlite3_create_function(db, aFunc[i].zFuncName, aFunc[i].nArg,
|
||||
SQLITE_UTF8|SQLITE_INNOCUOUS|SQLITE_DETERMINISTIC,
|
||||
0, aFunc[i].xFunc, 0, 0);
|
||||
aFunc[i].iArg ? db : 0, aFunc[i].xFunc, 0, 0);
|
||||
}
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = sqlite3_create_window_function(db, "decimal_sum", 1,
|
||||
|
||||
+32
-1
@@ -256,6 +256,37 @@ static void ieee754func_to_blob(
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** SQL Function: ieee754_inc(r,N)
|
||||
**
|
||||
** Move the floating point value r by N quantums and return the new
|
||||
** values.
|
||||
**
|
||||
** Behind the scenes: this routine merely casts r into a 64-bit unsigned
|
||||
** integer, adds N, then casts the value back into float.
|
||||
**
|
||||
** Example: To find the smallest positive number:
|
||||
**
|
||||
** SELECT ieee754_inc(0.0,+1);
|
||||
*/
|
||||
static void ieee754inc(
|
||||
sqlite3_context *context,
|
||||
int argc,
|
||||
sqlite3_value **argv
|
||||
){
|
||||
double r;
|
||||
sqlite3_int64 N;
|
||||
sqlite3_uint64 m1, m2;
|
||||
double r2;
|
||||
UNUSED_PARAMETER(argc);
|
||||
r = sqlite3_value_double(argv[0]);
|
||||
N = sqlite3_value_int64(argv[1]);
|
||||
memcpy(&m1, &r, 8);
|
||||
m2 = m1 + N;
|
||||
memcpy(&r2, &m2, 8);
|
||||
sqlite3_result_double(context, r2);
|
||||
}
|
||||
|
||||
|
||||
#ifdef _WIN32
|
||||
__declspec(dllexport)
|
||||
@@ -277,7 +308,7 @@ int sqlite3_ieee_init(
|
||||
{ "ieee754_exponent", 1, 2, ieee754func },
|
||||
{ "ieee754_to_blob", 1, 0, ieee754func_to_blob },
|
||||
{ "ieee754_from_blob", 1, 0, ieee754func_from_blob },
|
||||
|
||||
{ "ieee754_inc", 2, 0, ieee754inc },
|
||||
};
|
||||
unsigned int i;
|
||||
int rc = SQLITE_OK;
|
||||
|
||||
@@ -0,0 +1,179 @@
|
||||
/*
|
||||
** 2023-06-21
|
||||
**
|
||||
** The author disclaims copyright to this source code. In place of
|
||||
** a legal notice, here is a blessing:
|
||||
**
|
||||
** May you do good and not evil.
|
||||
** May you find forgiveness for yourself and forgive others.
|
||||
** May you share freely, never taking more than you give.
|
||||
**
|
||||
*************************************************************************
|
||||
**
|
||||
** This file implements an extension that uses the SQLITE_CONFIG_PCACHE2
|
||||
** mechanism to add a tracing layer on top of pluggable page cache of
|
||||
** SQLite. If this extension is registered prior to sqlite3_initialize(),
|
||||
** it will cause all page cache activities to be logged on standard output,
|
||||
** or to some other FILE specified by the initializer.
|
||||
**
|
||||
** This file needs to be compiled into the application that uses it.
|
||||
**
|
||||
** This extension is used to implement the --pcachetrace option of the
|
||||
** command-line shell.
|
||||
*/
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
|
||||
/* The original page cache routines */
|
||||
static sqlite3_pcache_methods2 pcacheBase;
|
||||
static FILE *pcachetraceOut;
|
||||
|
||||
/* Methods that trace pcache activity */
|
||||
static int pcachetraceInit(void *pArg){
|
||||
int nRes;
|
||||
if( pcachetraceOut ){
|
||||
fprintf(pcachetraceOut, "PCACHETRACE: xInit(%p)\n", pArg);
|
||||
}
|
||||
nRes = pcacheBase.xInit(pArg);
|
||||
if( pcachetraceOut ){
|
||||
fprintf(pcachetraceOut, "PCACHETRACE: xInit(%p) -> %d\n", pArg, nRes);
|
||||
}
|
||||
return nRes;
|
||||
}
|
||||
static void pcachetraceShutdown(void *pArg){
|
||||
if( pcachetraceOut ){
|
||||
fprintf(pcachetraceOut, "PCACHETRACE: xShutdown(%p)\n", pArg);
|
||||
}
|
||||
pcacheBase.xShutdown(pArg);
|
||||
}
|
||||
static sqlite3_pcache *pcachetraceCreate(int szPage, int szExtra, int bPurge){
|
||||
sqlite3_pcache *pRes;
|
||||
if( pcachetraceOut ){
|
||||
fprintf(pcachetraceOut, "PCACHETRACE: xCreate(%d,%d,%d)\n",
|
||||
szPage, szExtra, bPurge);
|
||||
}
|
||||
pRes = pcacheBase.xCreate(szPage, szExtra, bPurge);
|
||||
if( pcachetraceOut ){
|
||||
fprintf(pcachetraceOut, "PCACHETRACE: xCreate(%d,%d,%d) -> %p\n",
|
||||
szPage, szExtra, bPurge, pRes);
|
||||
}
|
||||
return pRes;
|
||||
}
|
||||
static void pcachetraceCachesize(sqlite3_pcache *p, int nCachesize){
|
||||
if( pcachetraceOut ){
|
||||
fprintf(pcachetraceOut, "PCACHETRACE: xCachesize(%p, %d)\n", p, nCachesize);
|
||||
}
|
||||
pcacheBase.xCachesize(p, nCachesize);
|
||||
}
|
||||
static int pcachetracePagecount(sqlite3_pcache *p){
|
||||
int nRes;
|
||||
if( pcachetraceOut ){
|
||||
fprintf(pcachetraceOut, "PCACHETRACE: xPagecount(%p)\n", p);
|
||||
}
|
||||
nRes = pcacheBase.xPagecount(p);
|
||||
if( pcachetraceOut ){
|
||||
fprintf(pcachetraceOut, "PCACHETRACE: xPagecount(%p) -> %d\n", p, nRes);
|
||||
}
|
||||
return nRes;
|
||||
}
|
||||
static sqlite3_pcache_page *pcachetraceFetch(
|
||||
sqlite3_pcache *p,
|
||||
unsigned key,
|
||||
int crFg
|
||||
){
|
||||
sqlite3_pcache_page *pRes;
|
||||
if( pcachetraceOut ){
|
||||
fprintf(pcachetraceOut, "PCACHETRACE: xFetch(%p,%u,%d)\n", p, key, crFg);
|
||||
}
|
||||
pRes = pcacheBase.xFetch(p, key, crFg);
|
||||
if( pcachetraceOut ){
|
||||
fprintf(pcachetraceOut, "PCACHETRACE: xFetch(%p,%u,%d) -> %p\n",
|
||||
p, key, crFg, pRes);
|
||||
}
|
||||
return pRes;
|
||||
}
|
||||
static void pcachetraceUnpin(
|
||||
sqlite3_pcache *p,
|
||||
sqlite3_pcache_page *pPg,
|
||||
int bDiscard
|
||||
){
|
||||
if( pcachetraceOut ){
|
||||
fprintf(pcachetraceOut, "PCACHETRACE: xUnpin(%p, %p, %d)\n",
|
||||
p, pPg, bDiscard);
|
||||
}
|
||||
pcacheBase.xUnpin(p, pPg, bDiscard);
|
||||
}
|
||||
static void pcachetraceRekey(
|
||||
sqlite3_pcache *p,
|
||||
sqlite3_pcache_page *pPg,
|
||||
unsigned oldKey,
|
||||
unsigned newKey
|
||||
){
|
||||
if( pcachetraceOut ){
|
||||
fprintf(pcachetraceOut, "PCACHETRACE: xRekey(%p, %p, %u, %u)\n",
|
||||
p, pPg, oldKey, newKey);
|
||||
}
|
||||
pcacheBase.xRekey(p, pPg, oldKey, newKey);
|
||||
}
|
||||
static void pcachetraceTruncate(sqlite3_pcache *p, unsigned n){
|
||||
if( pcachetraceOut ){
|
||||
fprintf(pcachetraceOut, "PCACHETRACE: xTruncate(%p, %u)\n", p, n);
|
||||
}
|
||||
pcacheBase.xTruncate(p, n);
|
||||
}
|
||||
static void pcachetraceDestroy(sqlite3_pcache *p){
|
||||
if( pcachetraceOut ){
|
||||
fprintf(pcachetraceOut, "PCACHETRACE: xDestroy(%p)\n", p);
|
||||
}
|
||||
pcacheBase.xDestroy(p);
|
||||
}
|
||||
static void pcachetraceShrink(sqlite3_pcache *p){
|
||||
if( pcachetraceOut ){
|
||||
fprintf(pcachetraceOut, "PCACHETRACE: xShrink(%p)\n", p);
|
||||
}
|
||||
pcacheBase.xShrink(p);
|
||||
}
|
||||
|
||||
/* The substitute pcache methods */
|
||||
static sqlite3_pcache_methods2 ersaztPcacheMethods = {
|
||||
0,
|
||||
0,
|
||||
pcachetraceInit,
|
||||
pcachetraceShutdown,
|
||||
pcachetraceCreate,
|
||||
pcachetraceCachesize,
|
||||
pcachetracePagecount,
|
||||
pcachetraceFetch,
|
||||
pcachetraceUnpin,
|
||||
pcachetraceRekey,
|
||||
pcachetraceTruncate,
|
||||
pcachetraceDestroy,
|
||||
pcachetraceShrink
|
||||
};
|
||||
|
||||
/* Begin tracing memory allocations to out. */
|
||||
int sqlite3PcacheTraceActivate(FILE *out){
|
||||
int rc = SQLITE_OK;
|
||||
if( pcacheBase.xFetch==0 ){
|
||||
rc = sqlite3_config(SQLITE_CONFIG_GETPCACHE2, &pcacheBase);
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = sqlite3_config(SQLITE_CONFIG_PCACHE2, &ersaztPcacheMethods);
|
||||
}
|
||||
}
|
||||
pcachetraceOut = out;
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* Deactivate memory tracing */
|
||||
int sqlite3PcacheTraceDeactivate(void){
|
||||
int rc = SQLITE_OK;
|
||||
if( pcacheBase.xFetch!=0 ){
|
||||
rc = sqlite3_config(SQLITE_CONFIG_PCACHE2, &pcacheBase);
|
||||
if( rc==SQLITE_OK ){
|
||||
memset(&pcacheBase, 0, sizeof(pcacheBase));
|
||||
}
|
||||
}
|
||||
pcachetraceOut = 0;
|
||||
return rc;
|
||||
}
|
||||
+2
-2
@@ -595,7 +595,7 @@ static const char *re_subcompile_string(ReCompiled *p){
|
||||
break;
|
||||
}
|
||||
case '[': {
|
||||
int iFirst = p->nState;
|
||||
unsigned int iFirst = p->nState;
|
||||
if( rePeek(p)=='^' ){
|
||||
re_append(p, RE_OP_CC_EXC, 0);
|
||||
p->sIn.i++;
|
||||
@@ -619,7 +619,7 @@ static const char *re_subcompile_string(ReCompiled *p){
|
||||
if( rePeek(p)==']' ){ p->sIn.i++; break; }
|
||||
}
|
||||
if( c==0 ) return "unclosed '['";
|
||||
p->aArg[iFirst] = p->nState - iFirst;
|
||||
if( p->nState>iFirst ) p->aArg[iFirst] = p->nState - iFirst;
|
||||
break;
|
||||
}
|
||||
case '\\': {
|
||||
|
||||
+43
-9
@@ -109,7 +109,17 @@ static sqlite3_int64 genSeqMember(sqlite3_int64 smBase,
|
||||
if( ix>=(sqlite3_uint64)LLONG_MAX ){
|
||||
/* Get ix into signed i64 range. */
|
||||
ix -= (sqlite3_uint64)LLONG_MAX;
|
||||
smBase += LLONG_MAX * smStep;
|
||||
/* With 2's complement ALU, this next can be 1 step, but is split into
|
||||
* 2 for UBSAN's satisfaction (and hypothetical 1's complement ALUs.) */
|
||||
smBase += (LLONG_MAX/2) * smStep;
|
||||
smBase += (LLONG_MAX - LLONG_MAX/2) * smStep;
|
||||
}
|
||||
/* Under UBSAN (or on 1's complement machines), must do this last term
|
||||
* in steps to avoid the dreaded (and harmless) signed multiply overlow. */
|
||||
if( ix>=2 ){
|
||||
sqlite3_int64 ix2 = (sqlite3_int64)ix/2;
|
||||
smBase += ix2*smStep;
|
||||
ix -= ix2;
|
||||
}
|
||||
return smBase + ((sqlite3_int64)ix)*smStep;
|
||||
}
|
||||
@@ -132,11 +142,22 @@ typedef struct SequenceSpec {
|
||||
** given initialized iBase, iTerm and iStep values. Sequence is
|
||||
** initialized per given isReversing. Other members are computed.
|
||||
*/
|
||||
void setupSequence( SequenceSpec *pss ){
|
||||
static void setupSequence( SequenceSpec *pss ){
|
||||
int bSameSigns;
|
||||
pss->uSeqIndexMax = 0;
|
||||
pss->isNotEOF = 0;
|
||||
bSameSigns = (pss->iBase < 0)==(pss->iTerm < 0);
|
||||
if( pss->iTerm < pss->iBase ){
|
||||
sqlite3_uint64 nuspan = (sqlite3_uint64)(pss->iBase-pss->iTerm);
|
||||
sqlite3_uint64 nuspan = 0;
|
||||
if( bSameSigns ){
|
||||
nuspan = (sqlite3_uint64)(pss->iBase - pss->iTerm);
|
||||
}else{
|
||||
/* Under UBSAN (or on 1's complement machines), must do this in steps.
|
||||
* In this clause, iBase>=0 and iTerm<0 . */
|
||||
nuspan = 1;
|
||||
nuspan += pss->iBase;
|
||||
nuspan += -(pss->iTerm+1);
|
||||
}
|
||||
if( pss->iStep<0 ){
|
||||
pss->isNotEOF = 1;
|
||||
if( nuspan==ULONG_MAX ){
|
||||
@@ -146,7 +167,16 @@ void setupSequence( SequenceSpec *pss ){
|
||||
}
|
||||
}
|
||||
}else if( pss->iTerm > pss->iBase ){
|
||||
sqlite3_uint64 puspan = (sqlite3_uint64)(pss->iTerm-pss->iBase);
|
||||
sqlite3_uint64 puspan = 0;
|
||||
if( bSameSigns ){
|
||||
puspan = (sqlite3_uint64)(pss->iTerm - pss->iBase);
|
||||
}else{
|
||||
/* Under UBSAN (or on 1's complement machines), must do this in steps.
|
||||
* In this clause, iTerm>=0 and iBase<0 . */
|
||||
puspan = 1;
|
||||
puspan += pss->iTerm;
|
||||
puspan += -(pss->iBase+1);
|
||||
}
|
||||
if( pss->iStep>0 ){
|
||||
pss->isNotEOF = 1;
|
||||
pss->uSeqIndexMax = puspan/pss->iStep;
|
||||
@@ -166,7 +196,7 @@ void setupSequence( SequenceSpec *pss ){
|
||||
** Leave its state to either yield next value or be at EOF.
|
||||
** Return whether there is a next value, or 0 at EOF.
|
||||
*/
|
||||
int progressSequence( SequenceSpec *pss ){
|
||||
static int progressSequence( SequenceSpec *pss ){
|
||||
if( !pss->isNotEOF ) return 0;
|
||||
if( pss->isReversing ){
|
||||
if( pss->uSeqIndexNow > 0 ){
|
||||
@@ -300,14 +330,18 @@ static int seriesColumn(
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
#ifndef LARGEST_UINT64
|
||||
#define LARGEST_UINT64 (0xffffffff|(((sqlite3_uint64)0xffffffff)<<32))
|
||||
#endif
|
||||
|
||||
/*
|
||||
** Return the rowid for the current row. In this implementation, the
|
||||
** first row returned is assigned rowid value 1, and each subsequent
|
||||
** row a value 1 more than that of the previous.
|
||||
** Return the rowid for the current row, logically equivalent to n+1 where
|
||||
** "n" is the ascending integer in the aforesaid production definition.
|
||||
*/
|
||||
static int seriesRowid(sqlite3_vtab_cursor *cur, sqlite_int64 *pRowid){
|
||||
series_cursor *pCur = (series_cursor*)cur;
|
||||
*pRowid = ((sqlite3_int64)pCur->ss.uSeqIndexNow + 1);
|
||||
sqlite3_uint64 n = pCur->ss.uSeqIndexNow;
|
||||
*pRowid = (sqlite3_int64)((n<LARGEST_UINT64)? n+1 : 0);
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
|
||||
@@ -3156,11 +3156,18 @@ static int rbuLockDatabase(sqlite3 *db){
|
||||
sqlite3_file *fd = 0;
|
||||
|
||||
sqlite3_file_control(db, "main", RBU_ZIPVFS_CTRL_FILE_POINTER, &fd);
|
||||
if( fd==0 ){
|
||||
if( fd ){
|
||||
sqlite3_file_control(db, "main", SQLITE_FCNTL_FILE_POINTER, &fd);
|
||||
rc = fd->pMethods->xLock(fd, SQLITE_LOCK_SHARED);
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = fd->pMethods->xUnlock(fd, SQLITE_LOCK_NONE);
|
||||
}
|
||||
sqlite3_file_control(db, "main", RBU_ZIPVFS_CTRL_FILE_POINTER, &fd);
|
||||
}else{
|
||||
sqlite3_file_control(db, "main", SQLITE_FCNTL_FILE_POINTER, &fd);
|
||||
}
|
||||
|
||||
if( fd->pMethods ){
|
||||
if( rc==SQLITE_OK && fd->pMethods ){
|
||||
rc = fd->pMethods->xLock(fd, SQLITE_LOCK_SHARED);
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = fd->pMethods->xLock(fd, SQLITE_LOCK_EXCLUSIVE);
|
||||
|
||||
@@ -73,13 +73,12 @@
|
||||
*/
|
||||
|
||||
#if !defined(SQLITEINT_H)
|
||||
#include "sqlite3ext.h"
|
||||
#include "sqlite3.h"
|
||||
|
||||
typedef unsigned char u8;
|
||||
typedef unsigned int u32;
|
||||
|
||||
#endif
|
||||
SQLITE_EXTENSION_INIT1
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
|
||||
@@ -664,8 +663,14 @@ static int dbdataNext(sqlite3_vtab_cursor *pCursor){
|
||||
if( pCsr->pHdrPtr>&pCsr->pRec[pCsr->nRec] ){
|
||||
bNextPage = 1;
|
||||
}else{
|
||||
int szField = 0;
|
||||
pCsr->pHdrPtr += dbdataGetVarintU32(pCsr->pHdrPtr, &iType);
|
||||
pCsr->pPtr += dbdataValueBytes(iType);
|
||||
szField = dbdataValueBytes(iType);
|
||||
if( (pCsr->nRec - (pCsr->pPtr - pCsr->pRec))<szField ){
|
||||
pCsr->pPtr = &pCsr->pRec[pCsr->nRec];
|
||||
}else{
|
||||
pCsr->pPtr += szField;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -938,15 +943,11 @@ static int sqlite3DbdataRegister(sqlite3 *db){
|
||||
return rc;
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
__declspec(dllexport)
|
||||
#endif
|
||||
int sqlite3_dbdata_init(
|
||||
sqlite3 *db,
|
||||
char **pzErrMsg,
|
||||
const sqlite3_api_routines *pApi
|
||||
){
|
||||
SQLITE_EXTENSION_INIT2(pApi);
|
||||
(void)pzErrMsg;
|
||||
return sqlite3DbdataRegister(db);
|
||||
}
|
||||
|
||||
@@ -329,5 +329,38 @@ do_test 17.2 {
|
||||
list [catch { $R finish } msg] $msg
|
||||
} {0 {}}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
foreach enc {utf8 utf16 utf16le utf16be} {
|
||||
reset_db
|
||||
do_execsql_test 18.$enc.1 {
|
||||
PRAGMA auto_vacuum = 0;
|
||||
PRAGMA encoding='utf16';
|
||||
CREATE TABLE t1(a, b);
|
||||
CREATE TABLE t2(a, b);
|
||||
INSERT INTO t1 VALUES('abc', 'def');
|
||||
PRAGMA writable_schema = 1;
|
||||
DELETE FROM sqlite_schema WHERE name='t1';
|
||||
}
|
||||
|
||||
proc my_sql_hook {sql} {
|
||||
if {[string match "INSERT INTO lostandfound*" $sql]} {
|
||||
lappend ::script $sql
|
||||
}
|
||||
return 0
|
||||
}
|
||||
do_test 18.$enc.2 {
|
||||
set ::script [list]
|
||||
set R [sqlite3_recover_init_sql db main my_sql_hook]
|
||||
$R config lostandfound lostandfound
|
||||
$R run
|
||||
$R finish
|
||||
set ::script
|
||||
} {{INSERT INTO lostandfound VALUES(2, 2, 2, 1, 'abc', 'def')}}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
finish_test
|
||||
|
||||
|
||||
@@ -285,5 +285,244 @@ do_test 5.1 {
|
||||
list [catch { $R finish } msg] $msg
|
||||
} {0 {}}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
reset_db
|
||||
do_test 6.0 {
|
||||
sqlite3 db {}
|
||||
db deserialize [decode_hexdb {
|
||||
| size 8192 pagesize 4096 filename abc.db
|
||||
| page 1 offset 0
|
||||
| 0: 53 51 4c 69 74 65 20 66 6f 72 6d 61 74 20 33 00 SQLite format 3.
|
||||
| 16: 10 00 01 01 00 40 20 20 00 00 00 02 00 00 00 02 .....@ ........
|
||||
| 32: 00 00 00 00 00 00 00 00 00 00 00 01 00 00 00 04 ................
|
||||
| 48: 00 00 00 00 00 00 00 00 00 00 00 01 00 00 00 00 ................
|
||||
| 80: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 02 ................
|
||||
| 96: 00 2e 6e b8 0d 00 00 00 01 0f dc 00 0f dc 00 00 ..n.............
|
||||
| 4048: 00 00 00 00 00 00 00 00 00 00 00 00 22 01 06 17 ................
|
||||
| 4064: 11 11 01 31 74 61 62 6c 65 74 31 74 31 02 43 52 ...1tablet1t1.CR
|
||||
| 4080: 45 41 54 45 20 54 41 42 4c 45 20 74 31 28 78 29 EATE TABLE t1(x)
|
||||
| page 2 offset 4096
|
||||
| 0: 0d 00 00 00 01 0f e2 00 0f e2 00 00 00 00 00 00 ................
|
||||
| 4064: 00 00 1c 01 02 41 61 62 63 64 65 66 67 68 69 6a .....Aabcdefghij
|
||||
| 4080: 6b 6c 6d 6e 6f 70 71 72 73 74 75 76 77 78 79 7a klmnopqrstuvwxyz
|
||||
| end abc.db
|
||||
}]} {}
|
||||
do_test 6.1 {
|
||||
set R [sqlite3_recover_init db main test.db2]
|
||||
catch { $R run }
|
||||
list [catch { $R finish } msg] $msg
|
||||
} {0 {}}
|
||||
|
||||
reset_db
|
||||
breakpoint
|
||||
do_test 6.2 {
|
||||
sqlite3 db {}
|
||||
db deserialize [decode_hexdb {
|
||||
| size 8192 pagesize 4096 filename abc.db
|
||||
| page 1 offset 0
|
||||
| 0: 53 51 4c 69 74 65 20 66 6f 72 6d 61 74 20 33 00 SQLite format 3.
|
||||
| 16: 10 00 01 01 00 40 20 20 00 00 00 02 00 00 00 02 .....@ ........
|
||||
| 32: 00 00 00 00 00 00 00 00 00 00 00 01 00 00 00 04 ................
|
||||
| 48: 00 00 00 00 00 00 00 00 00 00 00 01 00 00 00 00 ................
|
||||
| 80: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 02 ................
|
||||
| 96: 00 2e 6e b8 0d 00 00 00 01 0f dc 00 0f dc 00 00 ..n.............
|
||||
| 4048: 00 00 00 00 00 00 00 00 00 00 00 00 22 01 06 17 ................
|
||||
| 4064: 11 11 01 31 74 61 62 6c 65 74 31 74 31 02 43 52 ...1tablet1t1.CR
|
||||
| 4080: 45 41 54 45 20 54 41 42 4c 45 20 74 31 28 78 29 EATE TABLE t1(x)
|
||||
| page 2 offset 4096
|
||||
| 0: 0d 00 00 00 01 0f e2 00 0f e2 00 00 00 00 00 00 ................
|
||||
| 4064: 00 00 1c 01 02 8F FF FF FF 7E 65 66 67 68 69 6a .....Aabcdefghij
|
||||
| 4080: 6b 6c 6d 6e 6f 70 71 72 73 74 75 76 77 78 79 7a klmnopqrstuvwxyz
|
||||
| end abc.db
|
||||
}]} {}
|
||||
do_test 6.3 {
|
||||
set R [sqlite3_recover_init db main test.db2]
|
||||
catch { $R run }
|
||||
list [catch { $R finish } msg] $msg
|
||||
} {0 {}}
|
||||
|
||||
reset_db
|
||||
breakpoint
|
||||
do_test 7.0 {
|
||||
sqlite3 db {}
|
||||
db deserialize [decode_hexdb {
|
||||
| size 4108 pagesize 4096 filename x1.db
|
||||
| page 1 offset 0
|
||||
| 0: 02 01 00 00 00 00 14 15 40 00 00 00 00 00 00 00 ........@.......
|
||||
| 16: 33 3a 6d 65 6d 6f 72 79 3a 02 02 02 02 02 02 02 3:memory:.......
|
||||
| 32: 02 02 02 02 02 02 12 02 02 02 63 6f 6c 6f 72 20 ..........color
|
||||
| 48: 73 70 61 63 00 f3 a0 81 a1 00 00 a0 02 02 02 02 spac............
|
||||
| 64: 69 95 73 6f 36 00 ff 0d 00 97 8c 90 3f 0a 70 02 i.so6.......?.p.
|
||||
| 80: 02 02 02 02 02 02 02 02 02 02 02 02 02 01 00 00 ................
|
||||
| 96: 06 02 02 02 02 5f 02 02 02 2c 02 02 02 02 02 02 ....._...,......
|
||||
| 112: 02 02 02 02 02 02 02 02 02 12 02 02 02 63 6f 6c .............col
|
||||
| 128: 6f 72 20 73 70 61 63 00 f3 a0 81 a1 00 00 a0 02 or spac.........
|
||||
| 144: 02 02 02 69 95 73 6f 36 00 ff 0d 00 97 8c 90 3f ...i.so6.......?
|
||||
| 160: 0a 70 02 02 02 02 02 02 02 02 02 02 02 02 02 02 .p..............
|
||||
| 176: 01 00 00 06 02 02 02 02 5f 02 02 02 2c 02 02 00 ........_...,...
|
||||
| 192: 00 01 00 01 00 00 00 01 00 02 fe 00 00 03 00 01 ................
|
||||
| 208: 00 00 00 01 c5 04 00 00 00 01 00 01 00 00 00 01 ................
|
||||
| 224: 00 fa 02 00 00 00 03 00 01 00 00 00 81 00 04 00 ................
|
||||
| 240: 00 00 01 00 01 00 00 00 01 00 02 00 fe 00 03 00 ................
|
||||
| 256: 01 00 00 00 01 00 04 00 00 00 01 00 01 00 00 00 ................
|
||||
| 272: 01 00 02 00 00 00 03 00 01 00 00 00 01 00 04 00 ................
|
||||
| 288: 00 00 01 00 01 00 00 00 01 00 02 00 00 00 03 00 ................
|
||||
| 304: 01 00 00 00 01 00 04 00 00 00 01 00 01 00 00 00 ................
|
||||
| 320: 01 00 02 00 00 00 03 00 01 00 00 00 40 00 84 00 ............@...
|
||||
| 336: 84 00 84 00 01 00 00 00 09 00 06 00 f5 00 01 00 ................
|
||||
| 352: 08 01 03 00 03 00 62 00 62 00 23 00 01 00 62 00 ......b.b.#...b.
|
||||
| 368: 04 00 1e 00 62 00 62 00 62 00 01 00 00 00 0a 00 ....b.b.b.......
|
||||
| 384: 01 00 03 00 01 00 03 00 04 00 02 00 01 00 01 00 ................
|
||||
| 400: 08 00 01 00 31 c6 00 03 00 0c 00 12 00 18 00 02 ....1...........
|
||||
| 416: 00 05 00 08 00 02 00 06 00 08 00 02 00 07 00 08 ................
|
||||
| 432: 00 02 00 01 00 01 00 08 00 01 00 0c 00 03 00 16 ................
|
||||
| 448: 00 1c 00 22 00 01 00 03 00 05 00 06 00 07 00 02 ................
|
||||
| 464: 00 05 00 09 00 02 00 06 00 09 00 02 00 07 00 09 ................
|
||||
| 480: 00 00 00 00 01 00 05 00 00 00 01 00 01 00 00 00 ................
|
||||
| 496: 01 00 02 00 00 00 03 00 01 00 00 00 01 00 04 00 ................
|
||||
| 512: 00 00 01 00 01 00 00 00 01 00 02 00 00 00 03 00 ................
|
||||
| 528: 01 00 00 00 01 00 04 00 00 00 01 00 01 00 00 00 ................
|
||||
| 544: 01 00 02 00 00 f6 03 00 00 02 00 00 01 00 04 00 ................
|
||||
| 560: 00 00 01 00 01 00 00 00 01 00 02 00 00 00 03 00 ................
|
||||
| 576: 01 00 00 00 01 00 04 00 00 00 01 00 01 00 00 00 ................
|
||||
| 592: 01 00 02 00 00 00 03 00 01 00 00 00 01 00 04 00 ................
|
||||
| 608: 00 00 01 00 01 00 00 00 01 00 02 00 00 00 03 00 ................
|
||||
| 624: 01 00 00 00 01 00 04 00 00 00 01 00 01 00 00 00 ................
|
||||
| 640: 01 3d 02 00 00 00 03 00 06 00 00 00 01 00 01 00 .=..............
|
||||
| 656: 00 00 01 00 01 00 00 00 01 00 02 00 00 00 03 00 ................
|
||||
| 672: 01 00 00 00 01 00 04 00 00 00 01 00 01 00 00 00 ................
|
||||
| 688: 01 00 02 00 00 00 55 52 4c 52 65 71 75 65 73 74 ......URLRequest
|
||||
| 704: 43 6f 6e 00 00 00 01 01 0e d4 00 04 00 00 00 01 Con.............
|
||||
| 720: 0e f8 00 04 00 00 00 01 0f 1c 00 04 00 00 00 01 ................
|
||||
| 736: 0f 00 00 01 00 00 00 01 0f 86 00 01 00 00 00 01 ................
|
||||
| 752: 0f 84 00 01 00 00 00 01 00 00 01 0f c0 00 01 00 ................
|
||||
| 768: 00 00 01 0f e8 00 d6 0f 00 01 6f 00 02 0f d6 00 ..........o.....
|
||||
| 784: 02 34 03 03 03 00 01 00 00 00 01 00 05 00 00 00 .4..............
|
||||
| 800: 01 00 01 00 00 00 01 00 02 00 00 00 03 00 01 00 ................
|
||||
| 816: 00 00 01 00 04 00 00 00 01 00 01 00 00 00 01 00 ................
|
||||
| 832: 02 00 00 00 03 00 01 00 10 00 01 00 04 00 00 00 ................
|
||||
| 848: 01 00 01 00 00 00 01 00 02 00 00 00 03 00 00 02 ................
|
||||
| 864: 00 00 01 40 04 00 00 03 01 00 01 00 00 00 01 00 ...@............
|
||||
| 880: 02 00 00 00 03 00 01 00 00 00 00 00 01 0e f8 00 ................
|
||||
| 896: 04 77 4f 46 32 73 40 23 70 00 00 00 70 00 1f 00 .wOF2s@#p...p...
|
||||
| 912: 00 00 d8 00 00 00 ff ff 00 00 00 00 43 00 00 00 ............C...
|
||||
| 928: 00 00 ff ff ff ff ff 00 00 a8 00 00 0c 00 00 00 ................
|
||||
| 1024: 00 00 00 00 00 00 00 00 00 00 10 22 00 22 0f 00 ................
|
||||
| 1040: 00 00 00 00 00 00 10 22 00 00 70 00 1f 00 00 0f ..........p.....
|
||||
| 1056: d8 00 00 00 00 00 00 00 00 00 03 00 00 00 00 00 ................
|
||||
| 1072: 00 01 00 00 00 3f 23 70 00 00 00 01 0f 1c 00 04 .....?#p........
|
||||
| 1088: 00 00 00 01 0f 40 00 01 00 00 00 01 0f 86 00 01 .....@..........
|
||||
| 1104: 00 00 00 01 0f 84 00 01 00 00 00 01 00 00 01 0f ................
|
||||
| 1120: c0 00 01 00 00 00 01 0f e8 00 01 0f d6 00 6f 00 ..............o.
|
||||
| 1136: 02 0f d6 00 03 02 31 03 2b 03 2a f2 00 0f d4 00 ......1.+.*.....
|
||||
| 1152: 01 00 08 00 01 00 04 03 2b 00 02 02 32 00 01 0f ........+...2...
|
||||
| 1168: c8 01 15 00 02 20 c8 00 02 12 ad 02 00 24 06 c0 ..... .......$..
|
||||
| 1184: 00 00 00 03 00 00 01 24 00 2a 06 e4 00 00 00 03 .......$.*......
|
||||
| 1200: 00 00 01 25 00 38 07 0e 00 00 00 03 00 00 01 26 ...%.8.........&
|
||||
| 1216: 00 34 07 46 00 00 00 03 00 00 01 27 00 1c 07 7a .4.F.......'...z
|
||||
| 1232: 00 00 00 03 00 00 01 28 00 2a 07 96 00 00 00 03 .......(.*......
|
||||
| 1248: 00 e5 01 29 00 34 07 c0 00 00 00 03 00 00 01 2a ...).4.........*
|
||||
| 1264: 67 34 07 f4 00 00 00 03 00 00 01 2b 00 22 08 28 g4.........+...(
|
||||
| 1280: 00 00 00 00 01 00 01 00 00 00 01 00 02 00 00 00 ................
|
||||
| 1296: 03 00 01 00 00 00 01 00 00 00 01 00 00 00 01 00 ................
|
||||
| 1312: 01 00 00 00 01 00 02 00 00 00 03 00 00 02 00 00 ................
|
||||
| 1328: 01 00 04 00 00 00 01 00 01 00 00 00 01 00 02 00 ................
|
||||
| 1344: 00 00 03 00 01 00 00 00 01 00 04 00 00 00 01 00 ................
|
||||
| 1360: 01 00 00 00 01 00 02 00 00 00 03 00 01 00 00 21 ...............!
|
||||
| 1376: 04 00 01 00 00 00 00 00 01 00 00 00 01 00 02 00 ................
|
||||
| 1392: 00 00 03 00 01 00 00 00 01 00 04 00 00 00 01 00 ................
|
||||
| 1408: 01 00 00 00 01 00 02 00 00 00 03 00 01 00 00 00 ................
|
||||
| 1424: 01 00 05 00 00 00 01 00 01 00 00 01 00 02 02 02 ................
|
||||
| 1440: 12 02 02 02 63 6f 6c 6f 72 20 73 70 61 63 00 f3 ....color spac..
|
||||
| 1456: a0 81 a1 00 00 a0 02 02 02 02 69 95 73 6f 36 00 ..........i.so6.
|
||||
| 1472: ff 0d 00 97 8c 90 3f 0a 70 02 02 02 02 02 02 02 ......?.p.......
|
||||
| 1488: 02 02 02 02 02 02 02 01 00 00 06 02 02 02 02 5f ..............._
|
||||
| 1504: 02 02 02 2c 02 02 00 00 01 00 01 00 00 00 01 00 ...,............
|
||||
| 1520: 02 fe 00 00 03 00 01 00 00 00 01 c5 04 00 00 00 ................
|
||||
| 1536: 01 00 01 00 00 00 01 00 02 00 00 00 03 00 01 00 ................
|
||||
| 1552: 00 00 81 00 04 00 00 00 01 00 01 00 00 00 01 00 ................
|
||||
| 1568: 02 00 fe 00 03 00 01 00 00 00 01 00 04 00 00 00 ................
|
||||
| 1584: 01 00 01 00 00 00 01 00 02 00 00 00 03 00 01 00 ................
|
||||
| 1600: 00 00 01 00 04 00 00 00 01 00 01 00 00 00 01 00 ................
|
||||
| 1616: 02 00 00 00 03 00 01 00 00 00 01 00 04 00 00 00 ................
|
||||
| 1632: 01 00 01 00 00 00 01 00 02 00 00 00 03 00 01 00 ................
|
||||
| 1648: 00 00 40 00 84 00 84 00 84 00 01 00 00 00 09 00 ..@.............
|
||||
| 1664: 06 00 f5 00 01 00 08 01 03 15 15 15 15 15 15 15 ................
|
||||
| 1680: 15 15 15 15 15 15 15 15 15 15 15 15 15 15 15 15 ................
|
||||
| 1696: 15 15 15 15 15 15 15 15 15 15 15 15 15 15 15 15 ................
|
||||
| 1712: 15 15 15 15 15 15 15 15 15 15 15 15 15 15 15 15 ................
|
||||
| 1728: 15 15 15 15 15 15 15 15 15 15 15 15 15 15 15 15 ................
|
||||
| 1744: 15 15 15 15 15 15 15 15 15 15 15 15 15 15 15 15 ................
|
||||
| 1760: 15 15 15 15 15 15 15 15 15 15 15 00 03 00 62 00 ..............b.
|
||||
| 1776: 62 00 23 00 01 00 62 00 04 00 1e 00 62 00 62 00 b.#...b.....b.b.
|
||||
| 1792: 62 00 01 00 00 00 0a 00 01 00 03 00 01 00 03 00 b...............
|
||||
| 1808: 04 00 02 00 01 00 01 00 08 00 01 00 31 c6 00 03 ............1...
|
||||
| 1824: 00 0c 00 12 00 18 00 02 00 05 00 08 00 02 00 06 ................
|
||||
| 1840: 00 08 00 02 00 07 00 08 00 02 00 01 00 01 00 08 ................
|
||||
| 1856: 00 01 00 0c 00 03 00 16 00 1c 00 22 00 01 00 03 ................
|
||||
| 1872: 00 05 00 06 00 07 00 02 00 05 00 09 00 02 00 06 ................
|
||||
| 1888: 00 09 00 02 00 07 00 09 00 00 00 00 01 00 05 00 ................
|
||||
| 1904: 00 00 01 00 01 00 00 00 01 00 02 00 00 00 03 00 ................
|
||||
| 1920: 01 00 00 00 01 00 04 00 00 00 01 00 01 00 00 00 ................
|
||||
| 1936: 01 00 02 00 00 00 03 00 01 00 00 00 01 00 04 00 ................
|
||||
| 1952: 00 00 01 0f d6 00 02 34 03 03 03 00 01 00 00 00 .......4........
|
||||
| 1968: 01 00 05 00 00 00 01 00 01 00 00 00 01 00 02 00 ................
|
||||
| 1984: 00 00 03 00 01 00 00 00 01 00 04 00 00 00 01 00 ................
|
||||
| 2000: 01 00 00 00 01 00 02 fc 42 dc 19 5c 74 23 18 cd ........B...t#..
|
||||
| 2016: b3 a5 a8 7a 90 40 1d 66 12 5d e5 4f 85 00 68 f4 ...z.@.f.].O..h.
|
||||
| 2032: 05 98 86 25 24 dd bc c2 f6 f6 4e a3 e2 61 d2 c6 ...%$.....N..a..
|
||||
| 2048: aa c1 56 50 d4 80 82 35 f1 e2 59 41 50 a6 da 51 ..VP...5..YAP..Q
|
||||
| 2064: d4 62 9c 19 94 58 aa 31 30 8a 22 c2 5f 33 2b c9 .b...X.10..._3+.
|
||||
| 2080: b6 e6 b4 11 4e 51 82 c4 d8 b6 d8 b4 06 04 fb 68 ....NQ.........h
|
||||
| 2096: f4 d2 6f e7 cb 8a a8 82 d5 74 00 00 00 00 00 00 ..o......t......
|
||||
| 2368: 00 00 00 00 00 00 00 00 00 08 00 00 00 00 00 00 ................
|
||||
| 2432: 00 00 00 00 00 03 00 01 00 10 00 01 00 04 00 00 ................
|
||||
| 2448: 00 01 00 01 00 00 00 01 00 02 00 00 00 03 00 00 ................
|
||||
| 2464: 02 00 00 01 40 04 00 00 03 01 00 01 00 00 00 01 ....@...........
|
||||
| 2480: 00 02 00 00 00 03 00 01 00 00 00 00 00 01 0e f8 ................
|
||||
| 2496: 00 04 77 4f 46 32 73 40 23 70 00 00 00 70 00 1f ..wOF2s@#p...p..
|
||||
| 2512: 00 00 00 d8 00 00 00 ff ff 00 00 00 00 43 00 00 .............C..
|
||||
| 2528: 00 00 00 ff ff ff ff ff 00 00 a8 00 00 0c 00 00 ................
|
||||
| 2624: 00 00 00 00 00 00 00 00 00 00 00 10 22 00 22 0f ................
|
||||
| 2640: 00 00 00 00 00 00 00 10 22 00 00 70 00 1f 00 00 ...........p....
|
||||
| 2656: 0f d8 00 00 00 00 00 00 00 00 00 03 00 00 00 00 ................
|
||||
| 2672: 00 00 01 00 00 00 3f 23 70 00 00 00 01 0f 1c 00 ......?#p.......
|
||||
| 2688: 04 00 00 00 01 0f 40 00 01 00 00 00 01 0f 86 00 ......@.........
|
||||
| 2704: 01 00 00 00 01 0f 84 00 01 00 00 00 01 00 00 01 ................
|
||||
| 2720: 0f c0 00 01 00 00 00 01 0f e8 00 01 0f d6 00 6f ...............o
|
||||
| 2736: 00 02 0f d6 00 03 02 31 03 2b 03 2a f2 00 0f d4 .......1.+.*....
|
||||
| 2752: 00 01 00 08 00 01 00 04 03 2b 00 02 02 32 00 01 .........+...2..
|
||||
| 2768: 0f c8 01 15 00 02 20 c8 00 02 12 ad 02 00 24 06 ...... .......$.
|
||||
| 2784: c0 00 00 5a 03 00 00 01 24 00 2a 06 e4 00 00 00 ...Z....$.*.....
|
||||
| 2800: 03 00 00 01 25 00 38 07 0e 00 00 00 03 00 00 01 ....%.8.........
|
||||
| 2816: 26 00 34 07 46 00 00 00 03 00 00 01 27 00 1c 07 &.4.F.......'...
|
||||
| 2832: 7a 00 00 00 03 00 00 01 28 00 2a 07 96 00 00 00 z.......(.*.....
|
||||
| 2848: 03 00 e5 01 29 00 34 07 c0 00 00 00 03 00 00 01 ....).4.........
|
||||
| 2864: 2a 67 34 07 f4 00 00 00 03 00 00 01 2b 00 22 08 *g4.........+...
|
||||
| 2880: 28 00 00 00 00 01 00 01 00 00 00 01 00 02 00 00 (...............
|
||||
| 2896: 00 03 00 01 00 00 00 01 00 00 00 01 00 00 00 01 ................
|
||||
| 2912: 00 01 00 00 00 01 00 02 00 00 00 03 00 00 02 00 ................
|
||||
| 2928: 00 01 00 04 00 00 00 01 00 01 00 00 00 00 00 00 ................
|
||||
| 2992: 00 48 00 00 00 00 00 00 00 00 00 00 00 00 00 00 .H..............
|
||||
| 3504: 00 00 00 00 00 00 00 00 00 00 00 97 00 00 00 00 ................
|
||||
| 3904: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 09 ................
|
||||
| 3920: 03 fe 00 00 01 36 00 3c 0a 38 00 00 00 03 00 00 .....6.<.8......
|
||||
| 3936: 01 37 00 20 0a 74 00 00 00 fb ff ff 00 38 00 2a .7. .t.......8.*
|
||||
| 3952: 0a 94 00 00 00 03 00 00 01 39 4f 54 54 4f 00 0e .........9OTTO..
|
||||
| 3968: 00 80 00 03 00 60 43 46 46 20 e3 ae 89 2a 00 00 .....`CFF ...*..
|
||||
| 3984: 02 b0 00 00 02 76 42 50 4f 53 00 15 00 0a 00 00 .....vBPOS......
|
||||
| 4000: 05 28 00 00 00 0c 54 53 55 42 c9 70 c3 06 00 00 .(....TSUB.p....
|
||||
| 4016: 05 34 1f 00 40 00 48 00 00 00 00 00 00 00 00 00 .4..@.H.........
|
||||
| 4064: 00 00 00 00 00 08 00 01 00 01 00 01 00 01 00 06 ................
|
||||
| 4080: 00 02 00 08 00 01 00 01 00 01 00 01 00 00 00 00 ................
|
||||
| end x1.db
|
||||
}]} {}
|
||||
do_test 7.1 {
|
||||
set R [sqlite3_recover_init db main test.db2]
|
||||
catch { $R run }
|
||||
list [catch { $R finish } msg] $msg
|
||||
} {1 {file is not a database}}
|
||||
|
||||
finish_test
|
||||
|
||||
|
||||
@@ -2103,7 +2103,7 @@ static int recoverIsValidPage(u8 *aTmp, const u8 *a, int n){
|
||||
if( iFree>(n-4) ) return 0;
|
||||
iNext = recoverGetU16(&a[iFree]);
|
||||
nByte = recoverGetU16(&a[iFree+2]);
|
||||
if( iFree+nByte>n ) return 0;
|
||||
if( iFree+nByte>n || nByte<4 ) return 0;
|
||||
if( iNext && iNext<iFree+nByte ) return 0;
|
||||
memset(&aUsed[iFree], 0xFF, nByte);
|
||||
iFree = iNext;
|
||||
|
||||
+28
-9
@@ -96,6 +96,11 @@ typedef unsigned int u32;
|
||||
#endif
|
||||
#endif /* !defined(SQLITE_AMALGAMATION) */
|
||||
|
||||
/* Macro to check for 4-byte alignment. Only used inside of assert() */
|
||||
#ifdef SQLITE_DEBUG
|
||||
# define FOUR_BYTE_ALIGNED(X) ((((char*)(X) - (char*)0) & 3)==0)
|
||||
#endif
|
||||
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <assert.h>
|
||||
@@ -502,7 +507,7 @@ static int readInt16(u8 *p){
|
||||
return (p[0]<<8) + p[1];
|
||||
}
|
||||
static void readCoord(u8 *p, RtreeCoord *pCoord){
|
||||
assert( (((sqlite3_uint64)p)&3)==0 ); /* p is always 4-byte aligned */
|
||||
assert( FOUR_BYTE_ALIGNED(p) );
|
||||
#if SQLITE_BYTEORDER==1234 && MSVC_VERSION>=1300
|
||||
pCoord->u = _byteswap_ulong(*(u32*)p);
|
||||
#elif SQLITE_BYTEORDER==1234 && GCC_VERSION>=4003000
|
||||
@@ -556,7 +561,7 @@ static void writeInt16(u8 *p, int i){
|
||||
}
|
||||
static int writeCoord(u8 *p, RtreeCoord *pCoord){
|
||||
u32 i;
|
||||
assert( (((sqlite3_uint64)p)&3)==0 ); /* p is always 4-byte aligned */
|
||||
assert( FOUR_BYTE_ALIGNED(p) );
|
||||
assert( sizeof(RtreeCoord)==4 );
|
||||
assert( sizeof(u32)==4 );
|
||||
#if SQLITE_BYTEORDER==1234 && GCC_VERSION>=4003000
|
||||
@@ -1284,7 +1289,7 @@ static void rtreeNonleafConstraint(
|
||||
assert(p->op==RTREE_LE || p->op==RTREE_LT || p->op==RTREE_GE
|
||||
|| p->op==RTREE_GT || p->op==RTREE_EQ || p->op==RTREE_TRUE
|
||||
|| p->op==RTREE_FALSE );
|
||||
assert( (((sqlite3_uint64)pCellData)&3)==0 ); /* 4-byte aligned */
|
||||
assert( FOUR_BYTE_ALIGNED(pCellData) );
|
||||
switch( p->op ){
|
||||
case RTREE_TRUE: return; /* Always satisfied */
|
||||
case RTREE_FALSE: break; /* Never satisfied */
|
||||
@@ -1337,7 +1342,7 @@ static void rtreeLeafConstraint(
|
||||
|| p->op==RTREE_GT || p->op==RTREE_EQ || p->op==RTREE_TRUE
|
||||
|| p->op==RTREE_FALSE );
|
||||
pCellData += 8 + p->iCoord*4;
|
||||
assert( (((sqlite3_uint64)pCellData)&3)==0 ); /* 4-byte aligned */
|
||||
assert( FOUR_BYTE_ALIGNED(pCellData) );
|
||||
RTREE_DECODE_COORD(eInt, pCellData, xN);
|
||||
switch( p->op ){
|
||||
case RTREE_TRUE: return; /* Always satisfied */
|
||||
@@ -1907,7 +1912,20 @@ static int rtreeFilter(
|
||||
p->pInfo->nCoord = pRtree->nDim2;
|
||||
p->pInfo->anQueue = pCsr->anQueue;
|
||||
p->pInfo->mxLevel = pRtree->iDepth + 1;
|
||||
}else if( eType==SQLITE_INTEGER || eType==SQLITE_FLOAT ){
|
||||
}else if( eType==SQLITE_INTEGER ){
|
||||
sqlite3_int64 iVal = sqlite3_value_int64(argv[ii]);
|
||||
#ifdef SQLITE_RTREE_INT_ONLY
|
||||
p->u.rValue = iVal;
|
||||
#else
|
||||
p->u.rValue = (double)iVal;
|
||||
if( iVal>=((sqlite3_int64)1)<<48
|
||||
|| -iVal>=((sqlite3_int64)1)<<48
|
||||
){
|
||||
if( p->op==RTREE_LT ) p->op = RTREE_LE;
|
||||
if( p->op==RTREE_GT ) p->op = RTREE_GE;
|
||||
}
|
||||
#endif
|
||||
}else if( eType==SQLITE_FLOAT ){
|
||||
#ifdef SQLITE_RTREE_INT_ONLY
|
||||
p->u.rValue = sqlite3_value_int64(argv[ii]);
|
||||
#else
|
||||
@@ -2038,11 +2056,12 @@ static int rtreeBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){
|
||||
|| p->op==SQLITE_INDEX_CONSTRAINT_MATCH)
|
||||
){
|
||||
u8 op;
|
||||
u8 doOmit = 1;
|
||||
switch( p->op ){
|
||||
case SQLITE_INDEX_CONSTRAINT_EQ: op = RTREE_EQ; break;
|
||||
case SQLITE_INDEX_CONSTRAINT_GT: op = RTREE_GT; break;
|
||||
case SQLITE_INDEX_CONSTRAINT_EQ: op = RTREE_EQ; doOmit = 0; break;
|
||||
case SQLITE_INDEX_CONSTRAINT_GT: op = RTREE_GT; doOmit = 0; break;
|
||||
case SQLITE_INDEX_CONSTRAINT_LE: op = RTREE_LE; break;
|
||||
case SQLITE_INDEX_CONSTRAINT_LT: op = RTREE_LT; break;
|
||||
case SQLITE_INDEX_CONSTRAINT_LT: op = RTREE_LT; doOmit = 0; break;
|
||||
case SQLITE_INDEX_CONSTRAINT_GE: op = RTREE_GE; break;
|
||||
case SQLITE_INDEX_CONSTRAINT_MATCH: op = RTREE_MATCH; break;
|
||||
default: op = 0; break;
|
||||
@@ -2051,7 +2070,7 @@ static int rtreeBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){
|
||||
zIdxStr[iIdx++] = op;
|
||||
zIdxStr[iIdx++] = (char)(p->iColumn - 1 + '0');
|
||||
pIdxInfo->aConstraintUsage[ii].argvIndex = (iIdx/2);
|
||||
pIdxInfo->aConstraintUsage[ii].omit = 1;
|
||||
pIdxInfo->aConstraintUsage[ii].omit = doOmit;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -756,4 +756,32 @@ do_execsql_test 20.4 {
|
||||
SELECT * FROM t1 JOIN t0 ON true RIGHT JOIN rt0 ON x0>a WHERE x0 = 0;
|
||||
} {- - 0 0.0 0.0}
|
||||
|
||||
# 2023-05-19 https://sqlite.org/forum/forumpost/da61c4a1b5b4af19
|
||||
# Do not omit constraints that involve equality comparisons of
|
||||
# floating-point values.
|
||||
#
|
||||
reset_db
|
||||
do_execsql_test 21.0 {
|
||||
CREATE VIRTUAL TABLE t1 USING rtree(id, x0, x1);
|
||||
INSERT INTO t1 VALUES(0, 1, 9223372036854775807);
|
||||
SELECT count(*) FROM t1 WHERE x1=9223372036854775807;
|
||||
} {0}
|
||||
do_execsql_test 21.1 {
|
||||
SELECT x1=9223372036854775807 FROM t1;
|
||||
} {0}
|
||||
|
||||
# 2023-05-22 https://sqlite.org/forum/forumpost/da70ee0d0d
|
||||
# Round-off error associated with using large integer constraints on
|
||||
# a rtree search.
|
||||
#
|
||||
reset_db
|
||||
do_execsql_test 22.0 {
|
||||
CREATE VIRTUAL TABLE t1 USING rtree ( id, x0, x1 );
|
||||
INSERT INTO t1 VALUES (123, 9223372036854775799, 9223372036854775800);
|
||||
SELECT id FROM t1 WHERE x0 > 9223372036854775807;
|
||||
} {123}
|
||||
do_execsql_test 22.1 {
|
||||
SELECT id, x0 > 9223372036854775807 AS 'a0' FROM t1;
|
||||
} {123 1}
|
||||
|
||||
finish_test
|
||||
|
||||
@@ -18,7 +18,7 @@ source [file join [file dirname [info script]] session_common.tcl]
|
||||
source $testdir/tester.tcl
|
||||
ifcapable !session {finish_test; return}
|
||||
|
||||
set testprefix sessionnoop
|
||||
set testprefix sessionnoop2
|
||||
|
||||
foreach {tn wo} {
|
||||
1 ""
|
||||
@@ -120,11 +120,11 @@ do_test $tn.2.2 {
|
||||
}
|
||||
} {}
|
||||
|
||||
db2 close
|
||||
|
||||
}]
|
||||
}
|
||||
|
||||
db2 close
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
reset_db
|
||||
|
||||
@@ -255,6 +255,7 @@ do_conflict_test 3.20 -sql {
|
||||
do_execsql_test -db db2 3.21 {
|
||||
SELECT * FROM t4
|
||||
} {one blip}
|
||||
db2 close
|
||||
|
||||
#--------------------------------------------------------------------------
|
||||
breakpoint
|
||||
|
||||
@@ -891,6 +891,7 @@ static int sessionPreupdateEqual(
|
||||
rc = pSession->hook.xOld(pSession->hook.pCtx, iCol, &pVal);
|
||||
}
|
||||
assert( rc==SQLITE_OK );
|
||||
(void)rc; /* Suppress warning about unused variable */
|
||||
if( sqlite3_value_type(pVal)!=eType ) return 0;
|
||||
|
||||
/* A SessionChange object never has a NULL value in a PK column */
|
||||
|
||||
+145
-111
@@ -18,8 +18,6 @@
|
||||
# quick, q = do just a minimal build (sqlite3.js/wasm, tester1) for
|
||||
# faster development-mode turnaround.
|
||||
#
|
||||
# qo2, qoz = a combination of quick+o2/oz.
|
||||
#
|
||||
# dist = create end user deliverables. Add dist.build=oX to build
|
||||
# with a specific optimization level, where oX is one of the
|
||||
# above-listed o? or qo? target names.
|
||||
@@ -46,11 +44,12 @@
|
||||
# $(eval), or at least centralize the setup of the numerous vars
|
||||
# related to each build variant $(JS_BUILD_MODES).
|
||||
#
|
||||
default: all
|
||||
#default: quick
|
||||
SHELL := $(shell which bash 2>/dev/null)
|
||||
MAKEFILE := $(lastword $(MAKEFILE_LIST))
|
||||
CLEAN_FILES :=
|
||||
DISTCLEAN_FILES := ./--dummy--
|
||||
default: all
|
||||
release: oz
|
||||
# JS_BUILD_MODES exists solely to reduce repetition in documentation
|
||||
# below.
|
||||
@@ -68,13 +67,6 @@ ifeq (,$(emcc.version))
|
||||
else
|
||||
$(info using emcc version [$(emcc.version)])
|
||||
endif
|
||||
emcc.version := $(shell "$(emcc.bin)" --version | sed -n 1p \
|
||||
| sed -e 's/^.* \([3-9][^ ]*\) .*$$/\1/;')
|
||||
ifeq (,$(emcc.version))
|
||||
$(warning Cannot determine emcc version. This might unduly impact build flags.)
|
||||
else
|
||||
$(info using emcc version [$(emcc.version)])
|
||||
endif
|
||||
|
||||
wasm-strip ?= $(shell which wasm-strip 2>/dev/null)
|
||||
ifeq (,$(filter clean,$(MAKECMDGOALS)))
|
||||
@@ -159,6 +151,9 @@ endif
|
||||
# bundle.
|
||||
#
|
||||
# A custom sqlite3.c must not have any spaces in its name.
|
||||
# $(sqlite3.canonical.c) must point to the sqlite3.c in
|
||||
# the sqlite3 canonical source tree, as that source file
|
||||
# is required for certain utility and test code.
|
||||
sqlite3.canonical.c := $(dir.top)/sqlite3.c
|
||||
sqlite3.c ?= $(firstword $(wildcard $(dir.top)/sqlite3-see.c) $(sqlite3.canonical.c))
|
||||
sqlite3.h := $(dir.top)/sqlite3.h
|
||||
@@ -184,14 +179,19 @@ SQLITE_OPT = \
|
||||
-DSQLITE_OMIT_DEPRECATED \
|
||||
-DSQLITE_OMIT_UTF16 \
|
||||
-DSQLITE_OMIT_SHARED_CACHE \
|
||||
-DSQLITE_OMIT_WAL \
|
||||
-DSQLITE_THREADSAFE=0 \
|
||||
-DSQLITE_TEMP_STORE=3 \
|
||||
-DSQLITE_TEMP_STORE=2 \
|
||||
-DSQLITE_OS_KV_OPTIONAL=1 \
|
||||
'-DSQLITE_DEFAULT_UNIX_VFS="unix-none"' \
|
||||
-DSQLITE_USE_URI=1 \
|
||||
-DSQLITE_WASM_ENABLE_C_TESTS \
|
||||
-DSQLITE_C=$(sqlite3.c)
|
||||
#SQLITE_OPT += -DSQLITE_DEBUG
|
||||
# Enabling SQLITE_DEBUG will break sqlite3_wasm_vfs_create_file()
|
||||
# (and thus sqlite3_js_vfs_create_file()). Those functions are
|
||||
# deprecated and alternatives are in place, but this crash behavior
|
||||
# can be used to find errant uses of sqlite3_js_vfs_create_file()
|
||||
# in client code.
|
||||
|
||||
.NOTPARALLEL: $(sqlite3.h)
|
||||
$(sqlite3.h):
|
||||
@@ -246,10 +246,10 @@ endif
|
||||
# embedding in the JS files and in building the distribution zip file.
|
||||
# It must NOT be in $(dir.tmp) because we need it to survive the
|
||||
# cleanup process for the dist build to work properly.
|
||||
bin.version-info := $(dir.wasm)/version-info
|
||||
$(bin.version-info): $(dir.wasm)/version-info.c $(sqlite3.h) $(MAKEFILE)
|
||||
$(CC) -O0 -I$(dir $(sqlite3.c)) -o $@ $<
|
||||
DISTCLEAN_FILES += $(bin.version-info)
|
||||
bin.version-info := $(dir.top)/version-info
|
||||
.NOTPARALLEL: $(bin.version-info)
|
||||
$(bin.version-info): $(dir.tool)/version-info.c $(sqlite3.h) $(dir.top)/Makefile
|
||||
$(MAKE) -C $(dir.top) version-info
|
||||
|
||||
# bin.stripcomments is used for stripping C/C++-style comments from JS
|
||||
# files. The JS files contain large chunks of documentation which we
|
||||
@@ -379,20 +379,17 @@ sqlite3-api.jses += $(dir.api)/sqlite3-api-oo1.js
|
||||
sqlite3-api.jses += $(dir.api)/sqlite3-api-worker1.js
|
||||
sqlite3-api.jses += $(dir.api)/sqlite3-v-helper.js
|
||||
sqlite3-api.jses += $(dir.api)/sqlite3-vfs-opfs.c-pp.js
|
||||
sqlite3-api.jses += $(dir.api)/sqlite3-vfs-opfs-sahpool.c-pp.js
|
||||
sqlite3-api.jses += $(dir.api)/sqlite3-api-cleanup.js
|
||||
|
||||
# "External" API files which are part of our distribution
|
||||
# SOAP.js is an external API file which is part of our distribution
|
||||
# but not part of the sqlite3-api.js amalgamation.
|
||||
SOAP.js := $(dir.api)/sqlite3-opfs-async-proxy.js
|
||||
# COPY_XAPI = a $(call)able function to copy $1 to $(dir.dout), where
|
||||
# $1 must be one of the "external" JS API files.
|
||||
define COPY_XAPI
|
||||
sqlite3-api.ext.jses += $$(dir.dout)/$$(notdir $(1))
|
||||
$$(dir.dout)/$$(notdir $(1)): $(1) $$(MAKEFILE)
|
||||
cp $$< $$@
|
||||
endef
|
||||
$(foreach X,$(SOAP.js),\
|
||||
$(eval $(call COPY_XAPI,$(X))))
|
||||
SOAP.js.bld := $(dir.dout)/$(notdir $(SOAP.js))
|
||||
sqlite3-api.ext.jses += $(SOAP.js.bld)
|
||||
$(SOAP.js.bld): $(SOAP.js)
|
||||
cp $< $@
|
||||
|
||||
all quick: $(sqlite3-api.ext.jses)
|
||||
q: quick
|
||||
|
||||
@@ -459,6 +456,13 @@ emcc.exportedRuntimeMethods := \
|
||||
emcc.jsflags += $(emcc.exportedRuntimeMethods)
|
||||
emcc.jsflags += -sUSE_CLOSURE_COMPILER=0
|
||||
emcc.jsflags += -sIMPORTED_MEMORY
|
||||
#emcc.jsflags += -sASYNCIFY=2
|
||||
# ^^^ ASYNCIFY=2 is for experimental JSPI support
|
||||
# (https://v8.dev/blog/jspi), but enabling it causes the lib-level
|
||||
# init code to throw inexplicable complaints about C-level function
|
||||
# signatures not matching what we expect them to be. JSPI requires, as of
|
||||
# this writing, requires an experimental Chrome flag:
|
||||
# chrome://flags/#enable-experimental-webassembly-stack-switching
|
||||
emcc.jsflags += -sSTRICT_JS=0
|
||||
# STRICT_JS disabled due to:
|
||||
# https://github.com/emscripten-core/emscripten/issues/18610
|
||||
@@ -615,26 +619,40 @@ $(post-js.js.in): $(post-jses.js) $(MAKEFILE)
|
||||
|
||||
########################################################################
|
||||
# call-make-pre-post is a $(call)able which creates rules for
|
||||
# pre-js-$(1).js. $1 = the base name of the JS file on whose behalf
|
||||
# this pre-js is for (one of: sqlite3, sqlite3-wasmfs). $2 is the
|
||||
# build mode: one of $(JS_BUILD_MODES). This
|
||||
# sets up --[extern-][pre/post]-js flags in
|
||||
# $(pre-post-$(1).flags.$(2)) and dependencies in
|
||||
# $(pre-post-$(1).deps.$(2)).
|
||||
# pre-js-$(1)-$(2).js. $1 = the base name of the JS file on whose
|
||||
# behalf this pre-js is for (one of: sqlite3, sqlite3-wasmfs). $2 is
|
||||
# the build mode: one of $(JS_BUILD_MODES). This sets up
|
||||
# --[extern-][pre/post]-js flags in $(pre-post-$(1)-$(2).flags) and
|
||||
# dependencies in $(pre-post-$(1)-$(2).deps). The resulting files get
|
||||
# filtered using $(C-PP.FILTER). Any flags necessary for such
|
||||
# filtering need to be set in $(c-pp.D.$(1)-$(2)) before $(call)ing
|
||||
# this.
|
||||
define call-make-pre-post
|
||||
pre-post-$(1).flags.$(2) ?=
|
||||
$$(dir.tmp)/pre-js-$(1)-$(2).js: $$(pre-js.js.$(2)) $$(MAKEFILE)
|
||||
cp $$(pre-js.js.$(2)) $$@
|
||||
pre-post-$(1)-$(2).flags ?=
|
||||
pre-js.js.$(1)-$(2) := $$(dir.tmp)/pre-js.$(1)-$(2).intermediary.js
|
||||
$$(eval $$(call C-PP.FILTER,$$(pre-js.js.in),$$(pre-js.js.$(1)-$(2)),$$(c-pp.D.$(1)-$(2))))
|
||||
post-js.js.$(1)-$(2) := $$(dir.tmp)/post-js.$(1)-$(2).js
|
||||
$$(eval $$(call C-PP.FILTER,$$(post-js.js.in),$$(post-js.js.$(1)-$(2)),$$(c-pp.D.$(1)-$(2))))
|
||||
extern-post-js.js.$(1)-$(2) := $$(dir.tmp)/extern-post-js.$(1)-$(2).js
|
||||
$$(eval $$(call C-PP.FILTER,$$(extern-post-js.js.in),$$(extern-post-js.js.$(1)-$(2)),$$(c-pp.D.$(1)-$(2))))
|
||||
pre-post-common.flags.$(1)-$(2) := \
|
||||
$$(pre-post-common.flags) \
|
||||
--post-js=$$(post-js.js.$(1)-$(2)) \
|
||||
--extern-post-js=$$(extern-post-js.js.$(1)-$(2))
|
||||
pre-post-jses.$(1)-$(2).deps := $$(pre-post-jses.deps.common) \
|
||||
$$(post-js.js.$(1)-$(2)) $$(extern-post-js.js.$(1)-$(2))
|
||||
$$(dir.tmp)/pre-js-$(1)-$(2).js: $$(pre-js.js.$(1)-$(2)) $$(MAKEFILE)
|
||||
cp $$(pre-js.js.$(1)-$(2)) $$@
|
||||
@if [ sqlite3-wasmfs = $(1) ]; then \
|
||||
echo "delete Module[xNameOfInstantiateWasm] /*for WASMFS build*/;"; \
|
||||
elif [ sqlite3 != $(1) ]; then \
|
||||
echo "Module[xNameOfInstantiateWasm].uri = '$(1).wasm';"; \
|
||||
fi >> $$@
|
||||
pre-post-$(1).deps.$(2) := \
|
||||
$$(pre-post-jses.deps.$(2)) \
|
||||
pre-post-$(1)-$(2).deps := \
|
||||
$$(pre-post-jses.$(1)-$(2).deps) \
|
||||
$$(dir.tmp)/pre-js-$(1)-$(2).js
|
||||
pre-post-$(1).flags.$(2) += \
|
||||
$$(pre-post-common.flags.$(2)) \
|
||||
pre-post-$(1)-$(2).flags += \
|
||||
$$(pre-post-common.flags.$(1)-$(2)) \
|
||||
--pre-js=$$(dir.tmp)/pre-js-$(1)-$(2).js
|
||||
endef
|
||||
# /post-js and pre-js
|
||||
@@ -645,7 +663,8 @@ endef
|
||||
# https://github.com/emscripten-core/emscripten/issues/14383
|
||||
sqlite3.wasm := $(dir.dout)/sqlite3.wasm
|
||||
sqlite3-wasm.c := $(dir.api)/sqlite3-wasm.c
|
||||
sqlite3-wasm.cses := $(sqlite3-wasm.c) $(sqlite3_wasm_extra_init.c)
|
||||
sqlite3-wasm.cfiles := $(sqlite3-wasm.c) $(sqlite3_wasm_extra_init.c)
|
||||
sqlite3-wasmfs.cfiles := $(sqlite3-wasm.cfiles)
|
||||
# sqlite3-wasm.o vs sqlite3-wasm.c: building against the latter
|
||||
# (predictably) results in a slightly faster binary. We're close
|
||||
# enough to the target speed requirements that the 500ms makes a
|
||||
@@ -655,7 +674,8 @@ sqlite3-wasm.cses := $(sqlite3-wasm.c) $(sqlite3_wasm_extra_init.c)
|
||||
# SQLITE3.xJS.EXPORT-DEFAULT is part of SQLITE3-WASMFS.xJS.RECIPE and
|
||||
# SETUP_LIB_BUILD_MODE, factored into a separate piece to avoid code
|
||||
# duplication. $1 is 1 if the build mode needs this workaround (esm,
|
||||
# bundler-friendly) and 0 if not (vanilla).
|
||||
# bundler-friendly, node) and 0 if not (vanilla). $2 must be empty for
|
||||
# all builds except sqlite3-wasmfs.mjs, in which case it must be 1.
|
||||
#
|
||||
# Reminder for ESM builds: even if we use -sEXPORT_ES6=0, emcc _still_
|
||||
# adds:
|
||||
@@ -669,13 +689,20 @@ sqlite3-wasm.cses := $(sqlite3-wasm.c) $(sqlite3_wasm_extra_init.c)
|
||||
#
|
||||
# Upstream RFE:
|
||||
# https://github.com/emscripten-core/emscripten/issues/18237
|
||||
#
|
||||
# Maintenance reminder: Mac sed works differently than GNU sed, so
|
||||
# don't use sed for this.
|
||||
define SQLITE3.xJS.ESM-EXPORT-DEFAULT
|
||||
if [ x1 = x$(1) ]; then \
|
||||
echo "Fragile workaround for an Emscripten annoyance. See SQLITE3.xJS.RECIPE."; \
|
||||
sed -i -e '0,/^export default/{/^export default/d;}' $@ || exit $$?; \
|
||||
if ! grep -q '^export default' $@; then \
|
||||
echo "Cannot find export default." 1>&2; \
|
||||
exit 1; \
|
||||
echo "Fragile workaround for emscripten/issues/18237. See SQLITE3.xJS.RECIPE."; \
|
||||
{\
|
||||
awk '/^export default/ && !f{f=1; next} 1' $@ > $@.tmp && mv $@.tmp $@; \
|
||||
} || exit $$?; \
|
||||
if [ x != x$(2) ]; then \
|
||||
if ! grep -q '^export default' $@; then \
|
||||
echo "Cannot find export default." 1>&2; \
|
||||
exit 1; \
|
||||
fi; \
|
||||
fi; \
|
||||
fi
|
||||
endef
|
||||
@@ -695,53 +722,57 @@ pre-post-jses.deps.common := $(extern-pre-js.js) $(sqlite3-license-version.js)
|
||||
# SETUP_LIB_BUILD_MODE is a $(call)'able which sets up numerous pieces
|
||||
# for one of the build modes.
|
||||
#
|
||||
# $1 = build mode name: one of $(JS_BUILD_MODES)
|
||||
# $2 = 1 for ESM build mode, else 0
|
||||
# $3 = resulting sqlite-api JS/MJS file
|
||||
# $4 = resulting JS/MJS file
|
||||
# $5 = -D... flags for $(bin.c-pp)
|
||||
# $6 = emcc -sXYZ flags (CURRENTLY UNUSED - was factored out)
|
||||
# $1 = one of: sqlite3, sqlite3-wasmfs
|
||||
# $2 = build mode name: one of $(JS_BUILD_MODES)
|
||||
# $3 = 1 for ESM build mode, else 0
|
||||
# $4 = resulting sqlite-api JS/MJS file
|
||||
# $5 = resulting JS/MJS file
|
||||
# $6 = -D... flags for $(bin.c-pp)
|
||||
# $7 = emcc -sXYZ flags (CURRENTLY UNUSED - was factored out)
|
||||
#
|
||||
# emcc.environment.$(1) must be set to a value for the -sENVIRONMENT flag.
|
||||
# Maintenance reminder: be careful not to introduce spaces around args
|
||||
# ($1, $2), otherwise string concatenation will malfunction.
|
||||
#
|
||||
# emcc.environment.$(2) must be set to a value for the -sENVIRONMENT flag.
|
||||
#
|
||||
# $(cflags.$(1)) and $(cflags.$(1).$(2)) may be defined to append
|
||||
# CFLAGS to a given build mode.
|
||||
#
|
||||
# $(emcc.flags.$(1)) and $(emcc.flags.$(1).$(2)) may be defined to
|
||||
# append emcc-specific flags to a given build mode.
|
||||
define SETUP_LIB_BUILD_MODE
|
||||
$(info Setting up build [$(1)]: $(4))
|
||||
c-pp.D.$(1) := $(5)
|
||||
pre-js.js.$(1) := $$(dir.tmp)/pre-js.$(1).js
|
||||
$$(eval $$(call C-PP.FILTER,$$(pre-js.js.in),$$(pre-js.js.$(1)),$$(c-pp.D.$(1))))
|
||||
post-js.js.$(1) := $$(dir.tmp)/post-js.$(1).js
|
||||
$$(eval $$(call C-PP.FILTER,$$(post-js.js.in),$$(post-js.js.$(1)),$$(c-pp.D.$(1))))
|
||||
extern-post-js.js.$(1) := $$(dir.tmp)/extern-post-js.$(1).js
|
||||
$$(eval $$(call C-PP.FILTER,$$(extern-post-js.js.in),$$(extern-post-js.js.$(1)),$$(c-pp.D.$(1))))
|
||||
pre-post-common.flags.$(1) := \
|
||||
$$(pre-post-common.flags) \
|
||||
--post-js=$$(post-js.js.$(1)) \
|
||||
--extern-post-js=$$(extern-post-js.js.$(1))
|
||||
pre-post-jses.deps.$(1) := $$(pre-post-jses.deps.common) \
|
||||
$$(post-js.js.$(1)) $$(extern-post-js.js.$(1))
|
||||
$$(eval $$(call call-make-pre-post,sqlite3,$(1)))
|
||||
emcc.flags.sqlite3.$(1) := $(6)
|
||||
$$(eval $$(call C-PP.FILTER, $$(sqlite3-api.js.in), $(3), $(5)))
|
||||
$(4): $(3) $$(MAKEFILE) $$(sqlite3-wasm.cses) $$(EXPORTED_FUNCTIONS.api) $$(pre-post-sqlite3.deps.$(1))
|
||||
$(info Setting up build [$(1)-$(2)]: $(5))
|
||||
c-pp.D.$(1)-$(2) := $(6)
|
||||
$$(eval $$(call call-make-pre-post,$(1),$(2)))
|
||||
emcc.flags.$(1).$(2) ?=
|
||||
emcc.flags.$(1).$(2) += $(7)
|
||||
$$(eval $$(call C-PP.FILTER, $$(sqlite3-api.js.in), $(4), $(6)))
|
||||
$(5): $(4) $$(MAKEFILE) $$(sqlite3-wasm.cfiles) $$(EXPORTED_FUNCTIONS.api) $$(pre-post-$(1)-$(2).deps)
|
||||
@echo "Building $$@ ..."
|
||||
$$(emcc.bin) -o $$@ $$(emcc_opt_full) $$(emcc.flags) \
|
||||
$$(emcc.jsflags) \
|
||||
-sENVIRONMENT=$$(emcc.environment.$(1)) \
|
||||
$$(pre-post-sqlite3.flags.$(1)) $$(emcc.flags.sqlite3.$(1)) \
|
||||
$$(cflags.common) $$(SQLITE_OPT) $$(cflags.wasm_extra_init) $$(sqlite3-wasm.cses)
|
||||
@$$(call SQLITE3.xJS.ESM-EXPORT-DEFAULT,$(2))
|
||||
@case $(1) in \
|
||||
-sENVIRONMENT=$$(emcc.environment.$(2)) \
|
||||
$$(pre-post-$(1)-$(2).flags) \
|
||||
$$(emcc.flags.$(1)) $$(emcc.flags.$(1).$(2)) \
|
||||
$$(cflags.common) $$(SQLITE_OPT) \
|
||||
$$(cflags.$(1)) $$(cflags.$(1).$(2)) \
|
||||
$$(cflags.wasm_extra_init) $$(sqlite3-wasm.cfiles)
|
||||
@$$(call SQLITE3.xJS.ESM-EXPORT-DEFAULT,$(3))
|
||||
@dotwasm=$$(basename $$@).wasm; \
|
||||
chmod -x $$$$dotwasm; \
|
||||
$(maybe-wasm-strip) $$$$dotwasm; \
|
||||
case $(2) in \
|
||||
bundler-friendly|node) \
|
||||
echo "Patching $(3) for sqlite3.wasm..."; \
|
||||
rm -f $$(dir.dout)/sqlite3-$(1).wasm; \
|
||||
sed -i -e 's/sqlite3-$(1).wasm/sqlite3.wasm/g' $$@ || exit $$$$?; \
|
||||
echo "Patching $$@ for $(1).wasm..."; \
|
||||
rm -f $$$$dotwasm; \
|
||||
dotwasm=; \
|
||||
sed -i -e 's/$(1)-$(2).wasm/$(1).wasm/g' $$@ || exit $$$$?; \
|
||||
;; \
|
||||
esac
|
||||
chmod -x $$(sqlite3.wasm)
|
||||
$$(maybe-wasm-strip) $$(sqlite3.wasm)
|
||||
@ls -la $@ $$(sqlite3.wasm)
|
||||
all: $(4)
|
||||
quick: $(4)
|
||||
CLEAN_FILES += $(3) $(4)
|
||||
esac; \
|
||||
ls -la $$$$dotwasm $$@
|
||||
all: $(5)
|
||||
#quick: $(5)
|
||||
CLEAN_FILES += $(4) $(5)
|
||||
endef
|
||||
# ^^^ /SETUP_LIB_BUILD_MODE
|
||||
########################################################################
|
||||
@@ -753,21 +784,24 @@ sqlite3-api-bundler-friendly.mjs := $(dir.dout)/sqlite3-api-bundler-friendly.mjs
|
||||
sqlite3-bundler-friendly.mjs := $(dir.dout)/sqlite3-bundler-friendly.mjs
|
||||
sqlite3-api-node.mjs := $(dir.dout)/sqlite3-api-node.mjs
|
||||
sqlite3-node.mjs := $(dir.dout)/sqlite3-node.mjs
|
||||
# Maintenance reminder: careful not to introduce spaces around args $1, $2
|
||||
#$(info $(call SETUP_LIB_BUILD_MODE,vanilla,0, $(sqlite3-api.js), $(sqlite3.js)))
|
||||
$(eval $(call SETUP_LIB_BUILD_MODE,vanilla,0, $(sqlite3-api.js), $(sqlite3.js)))
|
||||
$(eval $(call SETUP_LIB_BUILD_MODE,esm,1, $(sqlite3-api.mjs), $(sqlite3.mjs), \
|
||||
#$(info $(call SETUP_LIB_BUILD_MODE,sqlite3,vanilla,0, $(sqlite3-api.js), $(sqlite3.js)))
|
||||
$(eval $(call SETUP_LIB_BUILD_MODE,sqlite3,vanilla,0,\
|
||||
$(sqlite3-api.js), $(sqlite3.js)))
|
||||
$(eval $(call SETUP_LIB_BUILD_MODE,sqlite3,esm,1,\
|
||||
$(sqlite3-api.mjs), $(sqlite3.mjs), \
|
||||
-Dtarget=es6-module, -sEXPORT_ES6 -sUSE_ES6_IMPORT_META))
|
||||
$(eval $(call SETUP_LIB_BUILD_MODE,bundler-friendly,1,\
|
||||
$(eval $(call SETUP_LIB_BUILD_MODE,sqlite3,bundler-friendly,1,\
|
||||
$(sqlite3-api-bundler-friendly.mjs),$(sqlite3-bundler-friendly.mjs),\
|
||||
$(c-pp.D.esm) -Dtarget=es6-bundler-friendly))
|
||||
$(eval $(call SETUP_LIB_BUILD_MODE,node,1,\
|
||||
$(c-pp.D.sqlite3-esm) -Dtarget=es6-bundler-friendly))
|
||||
$(eval $(call SETUP_LIB_BUILD_MODE,sqlite3,node,1,\
|
||||
$(sqlite3-api-node.mjs),$(sqlite3-node.mjs),\
|
||||
$(c-pp.D.bundler-friendly) -Dtarget=node))
|
||||
$(c-pp.D.sqlite3-bundler-friendly) -Dtarget=node))
|
||||
# The various -D... values used by *.c-pp.js include:
|
||||
#
|
||||
# -Dtarget=es6-module: for all ESM module builds
|
||||
#
|
||||
# -Dtarget=node: for node.js builds
|
||||
#
|
||||
# -Dtarget=es6-module -Dtarget=es6-bundler-friendly: intended for
|
||||
# "bundler-friendly" ESM module build. These have some restrictions
|
||||
# on how URL() objects are constructed in some contexts: URLs which
|
||||
@@ -841,7 +875,7 @@ clean-batch:
|
||||
# a regular basis with different -Ox flags and rebuilding the batch
|
||||
# pieces each time is an unnecessary time sink.
|
||||
batch: batch-runner.list
|
||||
all: batch
|
||||
#all: batch
|
||||
# end batch-runner.js
|
||||
########################################################################
|
||||
# Wasmified speedtest1 is our primary benchmarking tool.
|
||||
@@ -849,7 +883,7 @@ all: batch
|
||||
# emcc.speedtest1.common = emcc flags used by multiple builds of speedtest1
|
||||
# emcc.speedtest1 = emcc flags used by main build of speedtest1
|
||||
emcc.speedtest1.common := $(emcc_opt_full)
|
||||
emcc.speedtest1 :=
|
||||
emcc.speedtest1 := -I. -I$(dir $(sqlite3.canonical.c))
|
||||
emcc.speedtest1 += -sENVIRONMENT=web
|
||||
emcc.speedtest1 += -sALLOW_MEMORY_GROWTH
|
||||
emcc.speedtest1 += -sINITIAL_MEMORY=$(emcc.INITIAL_MEMORY.$(emcc.INITIAL_MEMORY))
|
||||
@@ -892,22 +926,24 @@ $(EXPORTED_FUNCTIONS.speedtest1): $(EXPORTED_FUNCTIONS.api.main)
|
||||
@{ echo _wasm_main; cat $(EXPORTED_FUNCTIONS.api.main); } > $@
|
||||
speedtest1.js := $(dir.dout)/speedtest1.js
|
||||
speedtest1.wasm := $(dir.dout)/speedtest1.wasm
|
||||
cflags.speedtest1 := $(cflags.common) -DSQLITE_SPEEDTEST1_WASM
|
||||
speedtest1.cses := $(speedtest1.c) $(sqlite3-wasm.c)
|
||||
emcc.flags.speedtest1-vanilla := $(cflags.common) -DSQLITE_SPEEDTEST1_WASM
|
||||
|
||||
speedtest1.cfiles := $(speedtest1.c) $(sqlite3-wasm.c)
|
||||
$(eval $(call call-make-pre-post,speedtest1,vanilla))
|
||||
$(speedtest1.js): $(MAKEFILE) $(speedtest1.cses) \
|
||||
$(pre-post-speedtest1.deps.vanilla) \
|
||||
$(speedtest1.js): $(MAKEFILE) $(speedtest1.cfiles) \
|
||||
$(pre-post-speedtest1-vanilla.deps) \
|
||||
$(EXPORTED_FUNCTIONS.speedtest1)
|
||||
@echo "Building $@ ..."
|
||||
$(emcc.bin) \
|
||||
$(emcc.speedtest1) -I$(dir $(sqlite3.canonical.c)) \
|
||||
$(emcc.speedtest1) \
|
||||
$(emcc.speedtest1.common) \
|
||||
$(cflags.speedtest1) $(pre-post-speedtest1.flags.vanilla) \
|
||||
$(emcc.flags.speedtest1-vanilla) $(pre-post-speedtest1-vanilla.flags) \
|
||||
$(SQLITE_OPT) \
|
||||
-USQLITE_C -DSQLITE_C=$(sqlite3.canonical.c) \
|
||||
$(speedtest1.exit-runtime0) \
|
||||
-o $@ $(speedtest1.cses) -lm
|
||||
-o $@ $(speedtest1.cfiles) -lm
|
||||
$(maybe-wasm-strip) $(speedtest1.wasm)
|
||||
chmod -x $(speedtest1.wasm)
|
||||
ls -la $@ $(speedtest1.wasm)
|
||||
|
||||
speedtest1: $(speedtest1.js)
|
||||
@@ -933,13 +969,14 @@ CLEAN_FILES += $(speedtest1.js) $(speedtest1.wasm)
|
||||
#
|
||||
# To create those, we filter tester1.c-pp.js with $(bin.c-pp)...
|
||||
$(eval $(call C-PP.FILTER,tester1.c-pp.js,tester1.js))
|
||||
$(eval $(call C-PP.FILTER,tester1.c-pp.js,tester1.mjs,$(c-pp.D.esm)))
|
||||
$(eval $(call C-PP.FILTER,tester1.c-pp.js,tester1.mjs,$(c-pp.D.sqlite3-esm)))
|
||||
$(eval $(call C-PP.FILTER,tester1.c-pp.html,tester1.html))
|
||||
$(eval $(call C-PP.FILTER,tester1.c-pp.html,tester1-esm.html,$(c-pp.D.esm)))
|
||||
$(eval $(call C-PP.FILTER,tester1.c-pp.html,tester1-esm.html,$(c-pp.D.sqlite3-esm)))
|
||||
tester1: tester1.js tester1.mjs tester1.html tester1-esm.html
|
||||
# Note that we do not include $(sqlite3-bundler-friendly.mjs) in this
|
||||
# because bundlers are client-specific.
|
||||
all quick: tester1
|
||||
quick: $(sqlite3.js)
|
||||
|
||||
########################################################################
|
||||
# Convenience rules to rebuild with various -Ox levels. Much
|
||||
@@ -959,8 +996,6 @@ o1: clean
|
||||
$(MAKE) -e "emcc_opt=-O1 $(o-xtra)"
|
||||
o2: clean
|
||||
$(MAKE) -j2 -e "emcc_opt=-O2 $(o-xtra)"
|
||||
qo2: clean
|
||||
$(MAKE) -j2 -e "emcc_opt=-O2 $(o-xtra)" quick
|
||||
o3: clean
|
||||
$(MAKE) -e "emcc_opt=-O3 $(o-xtra)"
|
||||
os: clean
|
||||
@@ -968,8 +1003,6 @@ os: clean
|
||||
$(MAKE) -e "emcc_opt=-Os $(o-xtra)"
|
||||
oz: clean
|
||||
$(MAKE) -j2 -e "emcc_opt=-Oz $(o-xtra)"
|
||||
qoz: clean
|
||||
$(MAKE) -j2 -e "emcc_opt=-Oz $(o-xtra)" quick
|
||||
|
||||
########################################################################
|
||||
# Sub-makes...
|
||||
@@ -981,6 +1014,8 @@ include fiddle.make
|
||||
ifneq (,$(filter wasmfs,$(MAKECMDGOALS)))
|
||||
wasmfs.enable ?= 1
|
||||
else
|
||||
# Unconditionally enable wasmfs for [dist]clean so that the wasmfs
|
||||
# sub-make can clean up.
|
||||
wasmfs.enable ?= $(if $(filter %clean,$(MAKECMDGOALS)),1,0)
|
||||
endif
|
||||
ifeq (1,$(wasmfs.enable))
|
||||
@@ -1011,8 +1046,7 @@ endif
|
||||
# Push files to public wasm-testing.sqlite.org server
|
||||
wasm-testing.include = *.js *.mjs *.html \
|
||||
./tests \
|
||||
batch-runner.list \
|
||||
$(dir.dout) $(dir.sql) $(dir.common) $(dir.fiddle) $(dir.jacc)
|
||||
$(dir.dout) $(dir.common) $(dir.fiddle) $(dir.jacc)
|
||||
wasm-testing.exclude = sql/speedtest1.sql
|
||||
wasm-testing.dir = /jail/sites/wasm-testing
|
||||
wasm-testing.dest ?= wasm-testing:$(wasm-testing.dir)
|
||||
|
||||
+8
-7
@@ -82,7 +82,7 @@ features in the apps which use them.
|
||||
|
||||
# Testing on a remote machine that is accessed via SSH
|
||||
|
||||
*NB: The following are developer notes, last validated on 2022-08-18*
|
||||
*NB: The following are developer notes, last validated on 2023-07-19*
|
||||
|
||||
* Remote: Install git, emsdk, and althttpd
|
||||
* Use a [version of althttpd][althttpd] from
|
||||
@@ -93,13 +93,14 @@ features in the apps which use them.
|
||||
* Local: `ssh -L 8180:localhost:8080 remote`
|
||||
* Local: Point your web-browser at http://localhost:8180/index.html
|
||||
|
||||
In order to enable [SharedArrayBuffers](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/SharedArrayBuffer),
|
||||
the web-browser requires that the two extra Cross-Origin lines be present
|
||||
in HTTP reply headers and that the request must come from "localhost".
|
||||
Since the web-server is on a different machine from
|
||||
the web-broser, the localhost requirement means that the connection must be tunneled
|
||||
using SSH.
|
||||
In order to enable [SharedArrayBuffer][], the web-browser requires
|
||||
that the two extra Cross-Origin lines be present in HTTP reply headers
|
||||
and that the request must come from "localhost" (_or_ over an SSL
|
||||
connection). Since the web-server is on a different machine from the
|
||||
web-broser, the localhost requirement means that the connection must
|
||||
be tunneled using SSH.
|
||||
|
||||
|
||||
[emscripten]: https://emscripten.org
|
||||
[althttpd]: https://sqlite.org/althttpd
|
||||
[SharedArrayBuffer]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/SharedArrayBuffer
|
||||
|
||||
+16
-11
@@ -83,15 +83,18 @@ browser client:
|
||||
helpers for use by downstream code which creates `sqlite3_vfs`
|
||||
and `sqlite3_module` implementations.
|
||||
- **`sqlite3-vfs-opfs.c-pp.js`**\
|
||||
is an sqlite3 VFS implementation which supports Google Chrome's
|
||||
Origin-Private FileSystem (OPFS) as a storage layer to provide
|
||||
persistent storage for database files in a browser. It requires...
|
||||
is an sqlite3 VFS implementation which supports the Origin-Private
|
||||
FileSystem (OPFS) as a storage layer to provide persistent storage
|
||||
for database files in a browser. It requires...
|
||||
- **`sqlite3-opfs-async-proxy.js`**\
|
||||
is the asynchronous backend part of the OPFS proxy. It speaks
|
||||
directly to the (async) OPFS API and channels those results back
|
||||
to its synchronous counterpart. This file, because it must be
|
||||
started in its own Worker, is not part of the amalgamation.
|
||||
- **`api/sqlite3-api-cleanup.js`**\
|
||||
- **`sqlite3-vfs-opfs-sahpool.c-pp.js`**\
|
||||
is another sqlite3 VFS supporting the OPFS, but uses a completely
|
||||
different approach that the above-listed one.
|
||||
- **`sqlite3-api-cleanup.js`**\
|
||||
The previous files do not immediately extend the library. Instead
|
||||
they add callback functions to be called during its
|
||||
bootstrapping. Some also temporarily create global objects in order
|
||||
@@ -108,13 +111,15 @@ browser client:
|
||||
with `c-pp`](#c-pp), noting that such preprocessing may be applied
|
||||
after all of the relevant files are concatenated. That extension is
|
||||
used primarily to keep the code maintainers cognisant of the fact that
|
||||
those files contain constructs which will not run as-is in JavaScript.
|
||||
those files contain constructs which may not run as-is in any given
|
||||
JavaScript environment.
|
||||
|
||||
The build process glues those files together, resulting in
|
||||
`sqlite3-api.js`, which is everything except for the `post-js-*.js`
|
||||
files, and `sqlite3.js`, which is the Emscripten-generated amalgamated
|
||||
output and includes the `post-js-*.js` parts, as well as the
|
||||
Emscripten-provided module loading pieces.
|
||||
`sqlite3-api.js`, which is everything except for the
|
||||
`pre/post-js-*.js` files, and `sqlite3.js`, which is the
|
||||
Emscripten-generated amalgamated output and includes the
|
||||
`pre/post-js-*.js` parts, as well as the Emscripten-provided module
|
||||
loading pieces.
|
||||
|
||||
The non-JS outlier file is `sqlite3-wasm.c`: it is a proxy for
|
||||
`sqlite3.c` which `#include`'s that file and adds a couple more
|
||||
@@ -152,8 +157,8 @@ Preprocessing of Source Files
|
||||
------------------------------------------------------------------------
|
||||
|
||||
Certain files in the build require preprocessing to filter in/out
|
||||
parts which differ between vanilla JS builds and ES6 Module
|
||||
(a.k.a. esm) builds. The preprocessor application itself is in
|
||||
parts which differ between vanilla JS, ES6 Modules, and node.js
|
||||
builds. The preprocessor application itself is in
|
||||
[`c-pp.c`](/file/ext/wasm/c-pp.c) and the complete technical details
|
||||
of such preprocessing are maintained in
|
||||
[`GNUMakefile`](/file/ext/wasm/GNUmakefile).
|
||||
|
||||
@@ -23,10 +23,7 @@ const toExportForESM =
|
||||
impls which Emscripten installs at some point in the file above
|
||||
this.
|
||||
*/
|
||||
const originalInit =
|
||||
/* Maintenance reminder: DO NOT use `self.` here. It's correct
|
||||
for non-ES6 Module cases but wrong for ES6 modules because those
|
||||
resolve this symbol differently. */ sqlite3InitModule;
|
||||
const originalInit = sqlite3InitModule;
|
||||
if(!originalInit){
|
||||
throw new Error("Expecting globalThis.sqlite3InitModule to be defined by the Emscripten build.");
|
||||
}
|
||||
@@ -65,19 +62,19 @@ const toExportForESM =
|
||||
globalThis.sqlite3InitModule = function ff(...args){
|
||||
//console.warn("Using replaced sqlite3InitModule()",globalThis.location);
|
||||
return originalInit(...args).then((EmscriptenModule)=>{
|
||||
//#if wasmfs
|
||||
if('undefined'!==typeof WorkerGlobalScope &&
|
||||
(EmscriptenModule['ENVIRONMENT_IS_PTHREAD']
|
||||
|| EmscriptenModule['_pthread_self']
|
||||
|| 'function'===typeof threadAlert
|
||||
|| globalThis?.location?.pathname?.endsWith?.('.worker.js')
|
||||
)){
|
||||
EmscriptenModule['ENVIRONMENT_IS_PTHREAD']){
|
||||
/** Workaround for wasmfs-generated worker, which calls this
|
||||
routine from each individual thread and requires that its
|
||||
argument be returned. All of the criteria above are fragile,
|
||||
based solely on inspection of the offending code, not public
|
||||
Emscripten details. */
|
||||
argument be returned. The conditional criteria above are
|
||||
fragile, based solely on inspection of the offending code,
|
||||
not public Emscripten details. */
|
||||
//console.warn("sqlite3InitModule() returning E-module.",EmscriptenModule);
|
||||
return EmscriptenModule;
|
||||
}
|
||||
//#endif
|
||||
//console.warn("sqlite3InitModule() returning sqlite3 object.");
|
||||
const s = EmscriptenModule.sqlite3;
|
||||
s.scriptInfo = initModuleState;
|
||||
//console.warn("sqlite3.scriptInfo =",s.scriptInfo);
|
||||
@@ -124,5 +121,6 @@ const toExportForESM =
|
||||
return globalThis.sqlite3InitModule /* required for ESM */;
|
||||
})();
|
||||
//#if target=es6-module
|
||||
export default toExportForESM;
|
||||
sqlite3InitModule = toExportForESM;
|
||||
export default sqlite3InitModule;
|
||||
//#endif
|
||||
|
||||
@@ -22,8 +22,10 @@ if('undefined' !== typeof Module){ // presumably an Emscripten build
|
||||
*/
|
||||
const SABC = Object.assign(
|
||||
Object.create(null), {
|
||||
exports: Module['asm'],
|
||||
memory: Module.wasmMemory /* gets set if built with -sIMPORT_MEMORY */
|
||||
exports: ('undefined'===typeof wasmExports)
|
||||
? Module['asm']/* emscripten <=3.1.43 */
|
||||
: wasmExports /* emscripten >=3.1.44 */,
|
||||
memory: Module.wasmMemory /* gets set if built with -sIMPORTED_MEMORY */
|
||||
},
|
||||
globalThis.sqlite3ApiConfig || {}
|
||||
);
|
||||
|
||||
@@ -608,6 +608,7 @@ globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
|
||||
["sqlite3_wasm_db_vfs", "sqlite3_vfs*", "sqlite3*","string"],
|
||||
["sqlite3_wasm_vfs_create_file", "int",
|
||||
"sqlite3_vfs*","string","*", "int"],
|
||||
["sqlite3_wasm_posix_create_file", "int", "string","*", "int"],
|
||||
["sqlite3_wasm_vfs_unlink", "int", "sqlite3_vfs*","string"]
|
||||
];
|
||||
|
||||
@@ -728,6 +729,15 @@ globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
|
||||
Populate api object with sqlite3_...() by binding the "raw" wasm
|
||||
exports into type-converting proxies using wasm.xWrap().
|
||||
*/
|
||||
if(0 === wasm.exports.sqlite3_step.length){
|
||||
/* This environment wraps exports in nullary functions, which means
|
||||
we must disable the arg-count validation we otherwise perform
|
||||
on the wrappers. */
|
||||
wasm.xWrap.doArgcCheck = false;
|
||||
sqlite3.config.warn(
|
||||
"Disabling sqlite3.wasm.xWrap.doArgcCheck due to environmental quirks."
|
||||
);
|
||||
}
|
||||
for(const e of wasm.bindingSignatures){
|
||||
capi[e[0]] = wasm.xWrap.apply(null, e);
|
||||
}
|
||||
|
||||
+142
-75
@@ -55,6 +55,7 @@ globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
|
||||
if(sqliteResultCode){
|
||||
if(dbPtr instanceof DB) dbPtr = dbPtr.pointer;
|
||||
toss3(
|
||||
sqliteResultCode,
|
||||
"sqlite3 result code",sqliteResultCode+":",
|
||||
(dbPtr
|
||||
? capi.sqlite3_errmsg(dbPtr)
|
||||
@@ -330,10 +331,15 @@ globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
|
||||
|
||||
- `db`: the DB object which created the statement.
|
||||
|
||||
- `columnCount`: the number of result columns in the query, or 0 for
|
||||
queries which cannot return results.
|
||||
- `columnCount`: the number of result columns in the query, or 0
|
||||
for queries which cannot return results. This property is a proxy
|
||||
for sqlite3_column_count() and its use in loops should be avoided
|
||||
because of the call overhead associated with that. The
|
||||
`columnCount` is not cached when the Stmt is created because a
|
||||
schema change made via a separate db connection between this
|
||||
statement's preparation and when it is stepped may invalidate it.
|
||||
|
||||
- `parameterCount`: the number of bindable paramters in the query.
|
||||
- `parameterCount`: the number of bindable parameters in the query.
|
||||
*/
|
||||
const Stmt = function(){
|
||||
if(BindTypes!==arguments[2]){
|
||||
@@ -341,7 +347,6 @@ globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
|
||||
}
|
||||
this.db = arguments[0];
|
||||
__ptrMap.set(this, arguments[1]);
|
||||
this.columnCount = capi.sqlite3_column_count(this.pointer);
|
||||
this.parameterCount = capi.sqlite3_bind_parameter_count(this.pointer);
|
||||
};
|
||||
|
||||
@@ -473,7 +478,9 @@ globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
|
||||
const __selectFirstRow = (db, sql, bind, ...getArgs)=>{
|
||||
const stmt = db.prepare(sql);
|
||||
try {
|
||||
return stmt.bind(bind).step() ? stmt.get(...getArgs) : undefined;
|
||||
const rc = stmt.bind(bind).step() ? stmt.get(...getArgs) : undefined;
|
||||
stmt.reset(/*for INSERT...RETURNING locking case*/);
|
||||
return rc;
|
||||
}finally{
|
||||
stmt.finalize();
|
||||
}
|
||||
@@ -499,6 +506,9 @@ globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
|
||||
"Not an error." The various non-0 non-error codes need to be
|
||||
checked for in client code where they are expected.
|
||||
|
||||
The thrown exception's `resultCode` property will be the value of
|
||||
the second argument to this function.
|
||||
|
||||
If it does not throw, it returns its first argument.
|
||||
*/
|
||||
DB.checkRc = (db,resultCode)=>checkSqlite3Rc(db,resultCode);
|
||||
@@ -546,7 +556,10 @@ globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
|
||||
}
|
||||
const pDb = this.pointer;
|
||||
Object.keys(__stmtMap.get(this)).forEach((k,s)=>{
|
||||
if(s && s.pointer) s.finalize();
|
||||
if(s && s.pointer){
|
||||
try{s.finalize()}
|
||||
catch(e){/*ignore*/}
|
||||
}
|
||||
});
|
||||
__ptrMap.delete(this);
|
||||
__stmtMap.delete(this);
|
||||
@@ -701,18 +714,18 @@ globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
|
||||
with identical names.
|
||||
|
||||
- `callback` = a function which gets called for each row of the
|
||||
result set, but only if that statement has any result
|
||||
_rows_. The callback's "this" is the options object, noting
|
||||
that this function synthesizes one if the caller does not pass
|
||||
one to exec(). The second argument passed to the callback is
|
||||
always the current Stmt object, as it's needed if the caller
|
||||
wants to fetch the column names or some such (noting that they
|
||||
could also be fetched via `this.columnNames`, if the client
|
||||
provides the `columnNames` option). If the callback returns a
|
||||
literal `false` (as opposed to any other falsy value, e.g. an
|
||||
implicit `undefined` return), any ongoing statement-`step()`
|
||||
iteration stops without an error. The return value of the
|
||||
callback is otherwise ignored.
|
||||
result set, but only if that statement has any result rows. The
|
||||
callback's "this" is the options object, noting that this
|
||||
function synthesizes one if the caller does not pass one to
|
||||
exec(). The second argument passed to the callback is always
|
||||
the current Stmt object, as it's needed if the caller wants to
|
||||
fetch the column names or some such (noting that they could
|
||||
also be fetched via `this.columnNames`, if the client provides
|
||||
the `columnNames` option). If the callback returns a literal
|
||||
`false` (as opposed to any other falsy value, e.g. an implicit
|
||||
`undefined` return), any ongoing statement-`step()` iteration
|
||||
stops without an error. The return value of the callback is
|
||||
otherwise ignored.
|
||||
|
||||
ACHTUNG: The callback MUST NOT modify the Stmt object. Calling
|
||||
any of the Stmt.get() variants, Stmt.getColumnName(), or
|
||||
@@ -733,7 +746,7 @@ globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
|
||||
|
||||
A.1) `'array'` (the default) causes the results of
|
||||
`stmt.get([])` to be passed to the `callback` and/or appended
|
||||
to `resultRows`
|
||||
to `resultRows`.
|
||||
|
||||
A.2) `'object'` causes the results of
|
||||
`stmt.get(Object.create(null))` to be passed to the
|
||||
@@ -744,8 +757,8 @@ globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
|
||||
|
||||
A.3) `'stmt'` causes the current Stmt to be passed to the
|
||||
callback, but this mode will trigger an exception if
|
||||
`resultRows` is an array because appending the statement to
|
||||
the array would be downright unhelpful.
|
||||
`resultRows` is an array because appending the transient
|
||||
statement to the array would be downright unhelpful.
|
||||
|
||||
B) An integer, indicating a zero-based column in the result
|
||||
row. Only that one single value will be passed on.
|
||||
@@ -775,7 +788,7 @@ globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
|
||||
should return:
|
||||
|
||||
A) The default value is (usually) `"this"`, meaning that the
|
||||
DB object itself should be returned. The exceptions is if
|
||||
DB object itself should be returned. The exception is if
|
||||
the caller passes neither of `callback` nor `returnValue`
|
||||
but does pass an explicit `rowMode` then the default
|
||||
`returnValue` is `"resultRows"`, described below.
|
||||
@@ -857,38 +870,53 @@ globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
|
||||
bind = null;
|
||||
}
|
||||
if(evalFirstResult && stmt.columnCount){
|
||||
/* Only forward SELECT results for the FIRST query
|
||||
/* Only forward SELECT-style results for the FIRST query
|
||||
in the SQL which potentially has them. */
|
||||
let gotColNames = Array.isArray(
|
||||
opt.columnNames
|
||||
/* As reported in
|
||||
https://sqlite.org/forum/forumpost/7774b773937cbe0a
|
||||
we need to delay fetching of the column names until
|
||||
after the first step() (if we step() at all) because
|
||||
a schema change between the prepare() and step(), via
|
||||
another connection, may invalidate the column count
|
||||
and names. */) ? 0 : 1;
|
||||
evalFirstResult = false;
|
||||
if(Array.isArray(opt.columnNames)){
|
||||
stmt.getColumnNames(opt.columnNames);
|
||||
}
|
||||
if(arg.cbArg || resultRows){
|
||||
for(; stmt.step(); stmt._isLocked = false){
|
||||
stmt._isLocked = true;
|
||||
for(; stmt.step(); stmt._lockedByExec = false){
|
||||
if(0===gotColNames++) stmt.getColumnNames(opt.columnNames);
|
||||
stmt._lockedByExec = true;
|
||||
const row = arg.cbArg(stmt);
|
||||
if(resultRows) resultRows.push(row);
|
||||
if(callback && false === callback.call(opt, row, stmt)){
|
||||
break;
|
||||
}
|
||||
}
|
||||
stmt._isLocked = false;
|
||||
stmt._lockedByExec = false;
|
||||
}
|
||||
if(0===gotColNames){
|
||||
/* opt.columnNames was provided but we visited no result rows */
|
||||
stmt.getColumnNames(opt.columnNames);
|
||||
}
|
||||
}else{
|
||||
stmt.step();
|
||||
}
|
||||
stmt.finalize();
|
||||
stmt.reset(
|
||||
/* In order to trigger an exception in the
|
||||
INSERT...RETURNING locking scenario:
|
||||
https://sqlite.org/forum/forumpost/36f7a2e7494897df
|
||||
*/).finalize();
|
||||
stmt = null;
|
||||
}
|
||||
}/*prepare() loop*/
|
||||
}/*catch(e){
|
||||
sqlite3.config.warn("DB.exec() is propagating exception",opt,e);
|
||||
throw e;
|
||||
}*/finally{
|
||||
wasm.scopedAllocPop(stack);
|
||||
if(stmt){
|
||||
delete stmt._isLocked;
|
||||
delete stmt._lockedByExec;
|
||||
stmt.finalize();
|
||||
}
|
||||
wasm.scopedAllocPop(stack);
|
||||
}
|
||||
return arg.returnVal();
|
||||
}/*exec()*/,
|
||||
@@ -1107,6 +1135,7 @@ globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
|
||||
try {
|
||||
stmt.bind(bind);
|
||||
while(stmt.step()) rc.push(stmt.get(0,asType));
|
||||
stmt.reset(/*for INSERT...RETURNING locking case*/);
|
||||
}finally{
|
||||
stmt.finalize();
|
||||
}
|
||||
@@ -1241,7 +1270,7 @@ globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
|
||||
not throw, it returns this object.
|
||||
*/
|
||||
checkRc: function(resultCode){
|
||||
return DB.checkRc(this, resultCode);
|
||||
return checkSqlite3Rc(this, resultCode);
|
||||
}
|
||||
}/*DB.prototype*/;
|
||||
|
||||
@@ -1302,15 +1331,15 @@ globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
|
||||
};
|
||||
|
||||
/**
|
||||
If stmt._isLocked is truthy, this throws an exception
|
||||
If stmt._lockedByExec is truthy, this throws an exception
|
||||
complaining that the 2nd argument (an operation name,
|
||||
e.g. "bind()") is not legal while the statement is "locked".
|
||||
Locking happens before an exec()-like callback is passed a
|
||||
statement, to ensure that the callback does not mutate or
|
||||
finalize the statement. If it does not throw, it returns stmt.
|
||||
*/
|
||||
const affirmUnlocked = function(stmt,currentOpName){
|
||||
if(stmt._isLocked){
|
||||
const affirmNotLockedByExec = function(stmt,currentOpName){
|
||||
if(stmt._lockedByExec){
|
||||
toss3("Operation is illegal when statement is locked:",currentOpName);
|
||||
}
|
||||
return stmt;
|
||||
@@ -1323,14 +1352,11 @@ globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
|
||||
success.
|
||||
*/
|
||||
const bindOne = function f(stmt,ndx,bindType,val){
|
||||
affirmUnlocked(affirmStmtOpen(stmt), 'bind()');
|
||||
affirmNotLockedByExec(affirmStmtOpen(stmt), 'bind()');
|
||||
if(!f._){
|
||||
f._tooBigInt = (v)=>toss3(
|
||||
"BigInt value is too big to store without precision loss:", v
|
||||
);
|
||||
/* Reminder: when not in BigInt mode, it's impossible for
|
||||
JS to represent a number out of the range we can bind,
|
||||
so we have no range checking. */
|
||||
f._ = {
|
||||
string: function(stmt, ndx, val, asBlob){
|
||||
const [pStr, n] = wasm.allocCString(val, true);
|
||||
@@ -1404,46 +1430,67 @@ globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
|
||||
|
||||
Stmt.prototype = {
|
||||
/**
|
||||
"Finalizes" this statement. This is a no-op if the
|
||||
statement has already been finalizes. Returns
|
||||
undefined. Most methods in this class will throw if called
|
||||
after this is.
|
||||
"Finalizes" this statement. This is a no-op if the statement
|
||||
has already been finalized. Returns the result of
|
||||
sqlite3_finalize() (0 on success, non-0 on error), or the
|
||||
undefined value if the statement has already been
|
||||
finalized. Regardless of success or failure, most methods in
|
||||
this class will throw if called after this is.
|
||||
|
||||
This method always throws if called when it is illegal to do
|
||||
so. Namely, when triggered via a per-row callback handler of a
|
||||
DB.exec() call.
|
||||
*/
|
||||
finalize: function(){
|
||||
if(this.pointer){
|
||||
affirmUnlocked(this,'finalize()');
|
||||
affirmNotLockedByExec(this,'finalize()');
|
||||
const rc = capi.sqlite3_finalize(this.pointer);
|
||||
delete __stmtMap.get(this.db)[this.pointer];
|
||||
capi.sqlite3_finalize(this.pointer);
|
||||
__ptrMap.delete(this);
|
||||
delete this._mayGet;
|
||||
delete this.columnCount;
|
||||
delete this.parameterCount;
|
||||
delete this._lockedByExec;
|
||||
delete this.db;
|
||||
delete this._isLocked;
|
||||
return rc;
|
||||
}
|
||||
},
|
||||
/** Clears all bound values. Returns this object.
|
||||
Throws if this statement has been finalized. */
|
||||
/**
|
||||
Clears all bound values. Returns this object. Throws if this
|
||||
statement has been finalized or if modification of the
|
||||
statement is currently illegal (e.g. in the per-row callback of
|
||||
a DB.exec() call).
|
||||
*/
|
||||
clearBindings: function(){
|
||||
affirmUnlocked(affirmStmtOpen(this), 'clearBindings()')
|
||||
affirmNotLockedByExec(affirmStmtOpen(this), 'clearBindings()')
|
||||
capi.sqlite3_clear_bindings(this.pointer);
|
||||
this._mayGet = false;
|
||||
return this;
|
||||
},
|
||||
/**
|
||||
Resets this statement so that it may be step()ed again
|
||||
from the beginning. Returns this object. Throws if this
|
||||
statement has been finalized.
|
||||
Resets this statement so that it may be step()ed again from the
|
||||
beginning. Returns this object. Throws if this statement has
|
||||
been finalized, if it may not legally be reset because it is
|
||||
currently being used from a DB.exec() callback, or if the
|
||||
underlying call to sqlite3_reset() returns non-0.
|
||||
|
||||
If passed a truthy argument then this.clearBindings() is
|
||||
also called, otherwise any existing bindings, along with
|
||||
any memory allocated for them, are retained.
|
||||
|
||||
In versions 3.42.0 and earlier, this function did not throw if
|
||||
sqlite3_reset() returns non-0, but it was discovered that
|
||||
throwing (or significant extra client-side code) is necessary
|
||||
in order to avoid certain silent failure scenarios, as
|
||||
discussed at:
|
||||
|
||||
https://sqlite.org/forum/forumpost/36f7a2e7494897df
|
||||
*/
|
||||
reset: function(alsoClearBinds){
|
||||
affirmUnlocked(this,'reset()');
|
||||
affirmNotLockedByExec(this,'reset()');
|
||||
if(alsoClearBinds) this.clearBindings();
|
||||
capi.sqlite3_reset(affirmStmtOpen(this).pointer);
|
||||
const rc = capi.sqlite3_reset(affirmStmtOpen(this).pointer);
|
||||
this._mayGet = false;
|
||||
checkSqlite3Rc(this.db, rc);
|
||||
return this;
|
||||
},
|
||||
/**
|
||||
@@ -1592,7 +1639,7 @@ globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
|
||||
value is returned. Throws on error.
|
||||
*/
|
||||
step: function(){
|
||||
affirmUnlocked(this, 'step()');
|
||||
affirmNotLockedByExec(this, 'step()');
|
||||
const rc = capi.sqlite3_step(affirmStmtOpen(this).pointer);
|
||||
switch(rc){
|
||||
case capi.SQLITE_DONE: return this._mayGet = false;
|
||||
@@ -1627,11 +1674,9 @@ globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
|
||||
return this.reset();
|
||||
},
|
||||
/**
|
||||
Functions like step() except that it finalizes this statement
|
||||
immediately after stepping unless the step cannot be performed
|
||||
because the statement is locked. Throws on error, but any error
|
||||
other than the statement-is-locked case will also trigger
|
||||
finalization of this statement.
|
||||
Functions like step() except that it calls finalize() on this
|
||||
statement immediately after stepping, even if the step() call
|
||||
throws.
|
||||
|
||||
On success, it returns true if the step indicated that a row of
|
||||
data was available, else it returns false.
|
||||
@@ -1643,9 +1688,14 @@ globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
|
||||
```
|
||||
*/
|
||||
stepFinalize: function(){
|
||||
const rc = this.step();
|
||||
this.finalize();
|
||||
return rc;
|
||||
try{
|
||||
const rc = this.step();
|
||||
this.reset(/*for INSERT...RETURNING locking case*/);
|
||||
return rc;
|
||||
}finally{
|
||||
try{this.finalize()}
|
||||
catch(e){/*ignored*/}
|
||||
}
|
||||
},
|
||||
/**
|
||||
Fetches the value from the given 0-based column index of
|
||||
@@ -1686,13 +1736,15 @@ globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
|
||||
}
|
||||
if(Array.isArray(ndx)){
|
||||
let i = 0;
|
||||
while(i<this.columnCount){
|
||||
const n = this.columnCount;
|
||||
while(i<n){
|
||||
ndx[i] = this.get(i++);
|
||||
}
|
||||
return ndx;
|
||||
}else if(ndx && 'object'===typeof ndx){
|
||||
let i = 0;
|
||||
while(i<this.columnCount){
|
||||
const n = this.columnCount;
|
||||
while(i<n){
|
||||
ndx[capi.sqlite3_column_name(this.pointer,i)] = this.get(i++);
|
||||
}
|
||||
return ndx;
|
||||
@@ -1790,16 +1842,17 @@ globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
|
||||
);
|
||||
},
|
||||
/**
|
||||
If this statement potentially has result columns, this
|
||||
function returns an array of all such names. If passed an
|
||||
array, it is used as the target and all names are appended
|
||||
to it. Returns the target array. Throws if this statement
|
||||
cannot have result columns. This object's columnCount member
|
||||
holds the number of columns.
|
||||
If this statement potentially has result columns, this function
|
||||
returns an array of all such names. If passed an array, it is
|
||||
used as the target and all names are appended to it. Returns
|
||||
the target array. Throws if this statement cannot have result
|
||||
columns. This object's columnCount property holds the number of
|
||||
columns.
|
||||
*/
|
||||
getColumnNames: function(tgt=[]){
|
||||
affirmColIndex(affirmStmtOpen(this),0);
|
||||
for(let i = 0; i < this.columnCount; ++i){
|
||||
const n = this.columnCount;
|
||||
for(let i = 0; i < n; ++i){
|
||||
tgt.push(capi.sqlite3_column_name(this.pointer, i));
|
||||
}
|
||||
return tgt;
|
||||
@@ -1826,6 +1879,20 @@ globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
|
||||
Object.defineProperty(Stmt.prototype, 'pointer', prop);
|
||||
Object.defineProperty(DB.prototype, 'pointer', prop);
|
||||
}
|
||||
/**
|
||||
Stmt.columnCount is an interceptor for sqlite3_column_count().
|
||||
|
||||
This requires an unfortunate performance hit compared to caching
|
||||
columnCount when the Stmt is created/prepared (as was done in
|
||||
SQLite <=3.42.0), but is necessary in order to handle certain
|
||||
corner cases, as described in
|
||||
https://sqlite.org/forum/forumpost/7774b773937cbe0a.
|
||||
*/
|
||||
Object.defineProperty(Stmt.prototype, 'columnCount', {
|
||||
enumerable: false,
|
||||
get: function(){return capi.sqlite3_column_count(this.pointer)},
|
||||
set: ()=>toss3("The columnCount property is read-only.")
|
||||
});
|
||||
|
||||
/** The OO API's public namespace. */
|
||||
sqlite3.oo1 = {
|
||||
|
||||
@@ -53,7 +53,7 @@
|
||||
|
||||
- `memory`[^1]: optional WebAssembly.Memory object, defaulting to
|
||||
`exports.memory`. In Emscripten environments this should be set
|
||||
to `Module.wasmMemory` if the build uses `-sIMPORT_MEMORY`, or be
|
||||
to `Module.wasmMemory` if the build uses `-sIMPORTED_MEMORY`, or be
|
||||
left undefined/falsy to default to `exports.memory` when using
|
||||
WASM-exported memory.
|
||||
|
||||
@@ -88,12 +88,12 @@
|
||||
can be replaced with (e.g.) empty functions to squelch all such
|
||||
output.
|
||||
|
||||
- `wasmfsOpfsDir`[^1]: As of 2022-12-17, this feature does not
|
||||
currently work due to incompatible Emscripten-side changes made
|
||||
in the WASMFS+OPFS combination. This option is currently ignored.
|
||||
- `wasmfsOpfsDir`[^1]: Specifies the "mount point" of the OPFS-backed
|
||||
filesystem in WASMFS-capable builds.
|
||||
|
||||
[^1] = This property may optionally be a function, in which case this
|
||||
function re-assigns calls that function to fetch the value,
|
||||
|
||||
[^1] = This property may optionally be a function, in which case
|
||||
this function calls that function to fetch the value,
|
||||
enabling delayed evaluation.
|
||||
|
||||
The returned object is the top-level sqlite3 namespace object.
|
||||
@@ -125,11 +125,11 @@ globalThis.sqlite3ApiBootstrap = function sqlite3ApiBootstrap(
|
||||
log: console.log.bind(console),
|
||||
wasmfsOpfsDir: '/opfs',
|
||||
/**
|
||||
useStdAlloc is just for testing an allocator discrepancy. The
|
||||
useStdAlloc is just for testing allocator discrepancies. The
|
||||
docs guarantee that this is false in the canonical builds. For
|
||||
99% of purposes it doesn't matter which allocators we use, but
|
||||
it becomes significant with, e.g., sqlite3_deserialize()
|
||||
and certain wasm.xWrap.resultAdapter()s.
|
||||
it becomes significant with, e.g., sqlite3_deserialize() and
|
||||
certain wasm.xWrap.resultAdapter()s.
|
||||
*/
|
||||
useStdAlloc: false
|
||||
}, apiConfig || {});
|
||||
@@ -149,11 +149,6 @@ globalThis.sqlite3ApiBootstrap = function sqlite3ApiBootstrap(
|
||||
config[k] = config[k]();
|
||||
}
|
||||
});
|
||||
config.wasmOpfsDir =
|
||||
/* 2022-12-17: WASMFS+OPFS can no longer be activated from the
|
||||
main thread (aborts via a failed assert() if it's attempted),
|
||||
which eliminates any(?) benefit to supporting it. */ false;
|
||||
|
||||
/**
|
||||
The main sqlite3 binding API gets installed into this object,
|
||||
mimicking the C API as closely as we can. The numerous members
|
||||
@@ -809,7 +804,7 @@ globalThis.sqlite3ApiBootstrap = function sqlite3ApiBootstrap(
|
||||
|| toss3("Missing API config.exports (WASM module exports)."),
|
||||
|
||||
/**
|
||||
When Emscripten compiles with `-sIMPORT_MEMORY`, it
|
||||
When Emscripten compiles with `-sIMPORTED_MEMORY`, it
|
||||
initalizes the heap and imports it into wasm, as opposed to
|
||||
the other way around. In this case, the memory is not
|
||||
available via this.exports.memory.
|
||||
@@ -1177,31 +1172,31 @@ globalThis.sqlite3ApiBootstrap = function sqlite3ApiBootstrap(
|
||||
/** State for sqlite3_wasmfs_opfs_dir(). */
|
||||
let __wasmfsOpfsDir = undefined;
|
||||
/**
|
||||
2022-12-17: incompatible WASMFS changes have made WASMFS+OPFS
|
||||
unavailable from the main thread, which eliminates the most
|
||||
significant benefit of supporting WASMFS. This function is now a
|
||||
no-op which always returns a falsy value. Before that change,
|
||||
this function behaved as documented below (and how it will again
|
||||
if we can find a compelling reason to support it).
|
||||
|
||||
If the wasm environment has a WASMFS/OPFS-backed persistent
|
||||
storage directory, its path is returned by this function. If it
|
||||
does not then it returns "" (noting that "" is a falsy value).
|
||||
|
||||
The first time this is called, this function inspects the current
|
||||
environment to determine whether persistence support is available
|
||||
and, if it is, enables it (if needed).
|
||||
and, if it is, enables it (if needed). After the first call it
|
||||
always returns the cached result.
|
||||
|
||||
This function currently only recognizes the WASMFS/OPFS storage
|
||||
combination and its path refers to storage rooted in the
|
||||
Emscripten-managed virtual filesystem.
|
||||
If the returned string is not empty, any files stored under the
|
||||
given path (recursively) are housed in OPFS storage. If the
|
||||
returned string is empty, this particular persistent storage
|
||||
option is not available on the client.
|
||||
|
||||
Though the mount point name returned by this function is intended
|
||||
to remain stable, clients should not hard-coded it anywhere. Always call this function to get the path.
|
||||
|
||||
Note that this function is a no-op in must builds of this
|
||||
library, as the WASMFS capability requires a custom
|
||||
build.
|
||||
*/
|
||||
capi.sqlite3_wasmfs_opfs_dir = function(){
|
||||
if(undefined !== __wasmfsOpfsDir) return __wasmfsOpfsDir;
|
||||
// If we have no OPFS, there is no persistent dir
|
||||
const pdir = config.wasmfsOpfsDir;
|
||||
console.error("sqlite3_wasmfs_opfs_dir() can no longer work due "+
|
||||
"to incompatible WASMFS changes. It will be removed.");
|
||||
if(!pdir
|
||||
|| !globalThis.FileSystemHandle
|
||||
|| !globalThis.FileSystemDirectoryHandle
|
||||
@@ -1223,8 +1218,6 @@ globalThis.sqlite3ApiBootstrap = function sqlite3ApiBootstrap(
|
||||
};
|
||||
|
||||
/**
|
||||
Experimental and subject to change or removal.
|
||||
|
||||
Returns true if sqlite3.capi.sqlite3_wasmfs_opfs_dir() is a
|
||||
non-empty string and the given name starts with (that string +
|
||||
'/'), else returns false.
|
||||
@@ -1234,13 +1227,6 @@ globalThis.sqlite3ApiBootstrap = function sqlite3ApiBootstrap(
|
||||
return (p && name) ? name.startsWith(p+'/') : false;
|
||||
};
|
||||
|
||||
// This bit is highly arguable and is incompatible with the fiddle shell.
|
||||
if(false && 0===wasm.exports.sqlite3_vfs_find(0)){
|
||||
/* Assume that sqlite3_initialize() has not yet been called.
|
||||
This will be the case in an SQLITE_OS_KV build. */
|
||||
wasm.exports.sqlite3_initialize();
|
||||
}
|
||||
|
||||
/**
|
||||
Given an `sqlite3*`, an sqlite3_vfs name, and an optional db name
|
||||
(defaulting to "main"), returns a truthy value (see below) if
|
||||
@@ -1371,6 +1357,74 @@ globalThis.sqlite3ApiBootstrap = function sqlite3ApiBootstrap(
|
||||
};
|
||||
|
||||
/**
|
||||
If the current environment supports the POSIX file APIs, this routine
|
||||
creates (or overwrites) the given file using those APIs. This is
|
||||
primarily intended for use in Emscripten-based builds where the POSIX
|
||||
APIs are transparently proxied by an in-memory virtual filesystem.
|
||||
It may behave diffrently in other environments.
|
||||
|
||||
The first argument must be either a JS string or WASM C-string
|
||||
holding the filename. Note that this routine does _not_ create
|
||||
intermediary directories if the filename has a directory part.
|
||||
|
||||
The 2nd argument may either a valid WASM memory pointer, an
|
||||
ArrayBuffer, or a Uint8Array. The 3rd must be the length, in
|
||||
bytes, of the data array to copy. If the 2nd argument is an
|
||||
ArrayBuffer or Uint8Array and the 3rd is not a positive integer
|
||||
then the 3rd defaults to the array's byteLength value.
|
||||
|
||||
Results are undefined if data is a WASM pointer and dataLen is
|
||||
exceeds data's bounds.
|
||||
|
||||
Throws if any arguments are invalid or if creating or writing to
|
||||
the file fails.
|
||||
|
||||
Added in 3.43 as an alternative for the deprecated
|
||||
sqlite3_js_vfs_create_file().
|
||||
*/
|
||||
capi.sqlite3_js_posix_create_file = function(filename, data, dataLen){
|
||||
let pData;
|
||||
if(data && wasm.isPtr(data)){
|
||||
pData = data;
|
||||
}else if(data instanceof ArrayBuffer || data instanceof Uint8Array){
|
||||
pData = wasm.allocFromTypedArray(data);
|
||||
if(arguments.length<3 || !util.isInt32(dataLen) || dataLen<0){
|
||||
dataLen = data.byteLength;
|
||||
}
|
||||
}else{
|
||||
SQLite3Error.toss("Invalid 2nd argument for sqlite3_js_posix_create_file().");
|
||||
}
|
||||
try{
|
||||
if(!util.isInt32(dataLen) || dataLen<0){
|
||||
SQLite3Error.toss("Invalid 3rd argument for sqlite3_js_posix_create_file().");
|
||||
}
|
||||
const rc = wasm.sqlite3_wasm_posix_create_file(filename, pData, dataLen);
|
||||
if(rc) SQLite3Error.toss("Creation of file failed with sqlite3 result code",
|
||||
capi.sqlite3_js_rc_str(rc));
|
||||
}finally{
|
||||
wasm.dealloc(pData);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
Deprecation warning: this function does not work properly in
|
||||
debug builds of sqlite3 because its out-of-scope use of the
|
||||
sqlite3_vfs API triggers assertions in the core library. That
|
||||
was unfortunately not discovered until 2023-08-11. This function
|
||||
is now deprecated and should not be used in new code.
|
||||
|
||||
Alternative options:
|
||||
|
||||
- "unix" VFS and its variants can get equivalent functionality
|
||||
with sqlite3_js_posix_create_file().
|
||||
|
||||
- OPFS: use either sqlite3.oo1.OpfsDb.importDb(), for the "opfs"
|
||||
VFS, or the importDb() method of the PoolUtil object provided
|
||||
by the "opfs-sahpool" OPFS (noting that its VFS name may differ
|
||||
depending on client-side configuration). We cannot proxy those
|
||||
from here because the former is necessarily asynchronous and
|
||||
the latter requires information not available to this function.
|
||||
|
||||
Creates a file using the storage appropriate for the given
|
||||
sqlite3_vfs. The first argument may be a VFS name (JS string
|
||||
only, NOT a WASM C-string), WASM-managed `sqlite3_vfs*`, or
|
||||
@@ -1416,9 +1470,13 @@ globalThis.sqlite3ApiBootstrap = function sqlite3ApiBootstrap(
|
||||
VFS nor the WASM environment imposes requirements which break it.
|
||||
|
||||
- "opfs": uses OPFS storage and creates directory parts of the
|
||||
filename.
|
||||
filename. It can only be used to import an SQLite3 database
|
||||
file and will fail if given anything else.
|
||||
*/
|
||||
capi.sqlite3_js_vfs_create_file = function(vfs, filename, data, dataLen){
|
||||
config.warn("sqlite3_js_vfs_create_file() is deprecated and",
|
||||
"should be avoided because it can lead to C-level crashes.",
|
||||
"See its documentation for alternative options.");
|
||||
let pData;
|
||||
if(data){
|
||||
if(wasm.isPtr(data)){
|
||||
@@ -1446,7 +1504,7 @@ globalThis.sqlite3ApiBootstrap = function sqlite3ApiBootstrap(
|
||||
if(rc) SQLite3Error.toss("Creation of file failed with sqlite3 result code",
|
||||
capi.sqlite3_js_rc_str(rc));
|
||||
}finally{
|
||||
wasm.dealloc(pData);
|
||||
wasm.dealloc(pData);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1659,7 +1717,8 @@ globalThis.sqlite3ApiBootstrap = function sqlite3ApiBootstrap(
|
||||
do not.
|
||||
*/
|
||||
tgt.push(capi.sqlite3_value_to_js(
|
||||
wasm.peekPtr(pArgv + (wasm.ptrSizeof * i))
|
||||
wasm.peekPtr(pArgv + (wasm.ptrSizeof * i)),
|
||||
throwIfCannotConvert
|
||||
));
|
||||
}
|
||||
return tgt;
|
||||
@@ -1875,6 +1934,9 @@ globalThis.sqlite3ApiBootstrap = function sqlite3ApiBootstrap(
|
||||
client: undefined,
|
||||
|
||||
/**
|
||||
This function is not part of the public interface, but a
|
||||
piece of internal bootstrapping infrastructure.
|
||||
|
||||
Performs any optional asynchronous library-level initialization
|
||||
which might be required. This function returns a Promise which
|
||||
resolves to the sqlite3 namespace object. Any error in the
|
||||
@@ -1890,27 +1952,19 @@ globalThis.sqlite3ApiBootstrap = function sqlite3ApiBootstrap(
|
||||
then it must be called by client-level code, which must not use
|
||||
the library until the returned promise resolves.
|
||||
|
||||
Bug: if called while a prior call is still resolving, the 2nd
|
||||
call will resolve prematurely, before the 1st call has finished
|
||||
resolving. The current build setup precludes that possibility,
|
||||
so it's only a hypothetical problem if/when this function
|
||||
ever needs to be invoked by clients.
|
||||
If called multiple times it will return the same promise on
|
||||
subsequent calls. The current build setup precludes that
|
||||
possibility, so it's only a hypothetical problem if/when this
|
||||
function ever needs to be invoked by clients.
|
||||
|
||||
In Emscripten-based builds, this function is called
|
||||
automatically and deleted from this object.
|
||||
*/
|
||||
asyncPostInit: async function(){
|
||||
let lip = sqlite3ApiBootstrap.initializersAsync;
|
||||
asyncPostInit: async function ff(){
|
||||
if(ff.isReady instanceof Promise) return ff.isReady;
|
||||
let lia = sqlite3ApiBootstrap.initializersAsync;
|
||||
delete sqlite3ApiBootstrap.initializersAsync;
|
||||
if(!lip || !lip.length) return Promise.resolve(sqlite3);
|
||||
lip = lip.map((f)=>{
|
||||
const p = (f instanceof Promise) ? f : f(sqlite3);
|
||||
return p.catch((e)=>{
|
||||
console.error("an async sqlite3 initializer failed:",e);
|
||||
throw e;
|
||||
});
|
||||
});
|
||||
const postInit = ()=>{
|
||||
const postInit = async ()=>{
|
||||
if(!sqlite3.__isUnderTest){
|
||||
/* Delete references to internal-only APIs which are used by
|
||||
some initializers. Retain them when running in test mode
|
||||
@@ -1919,23 +1973,25 @@ globalThis.sqlite3ApiBootstrap = function sqlite3ApiBootstrap(
|
||||
/* It's conceivable that we might want to expose
|
||||
StructBinder to client-side code, but it's only useful if
|
||||
clients build their own sqlite3.wasm which contains their
|
||||
one C struct types. */
|
||||
own C struct types. */
|
||||
delete sqlite3.StructBinder;
|
||||
}
|
||||
return sqlite3;
|
||||
};
|
||||
if(1){
|
||||
/* Run all initializers in sequence. The advantage is that it
|
||||
allows us to have post-init cleanup defined outside of this
|
||||
routine at the end of the list and have it run at a
|
||||
well-defined time. */
|
||||
let p = lip.shift();
|
||||
while(lip.length) p = p.then(lip.shift());
|
||||
return p.then(postInit);
|
||||
}else{
|
||||
/* Run them in an arbitrary order. */
|
||||
return Promise.all(lip).then(postInit);
|
||||
const catcher = (e)=>{
|
||||
config.error("an async sqlite3 initializer failed:",e);
|
||||
throw e;
|
||||
};
|
||||
if(!lia || !lia.length){
|
||||
return ff.isReady = postInit().catch(catcher);
|
||||
}
|
||||
lia = lia.map((f)=>{
|
||||
return (f instanceof Function) ? async x=>f(sqlite3) : f;
|
||||
});
|
||||
lia.push(postInit);
|
||||
let p = Promise.resolve(sqlite3);
|
||||
while(lia.length) p = p.then(lia.shift());
|
||||
return ff.isReady = p.catch(catcher);
|
||||
},
|
||||
/**
|
||||
scriptInfo ideally gets injected into this object by the
|
||||
@@ -1995,7 +2051,7 @@ globalThis.sqlite3ApiBootstrap.initializers = [];
|
||||
specifically for initializers which are asynchronous. All entries in
|
||||
this list must be either async functions, non-async functions which
|
||||
return a Promise, or a Promise. Each function in the list is called
|
||||
with the sqlite3 ojbect as its only argument.
|
||||
with the sqlite3 object as its only argument.
|
||||
|
||||
The resolved value of any Promise is ignored and rejection will kill
|
||||
the asyncPostInit() process (at an indeterminate point because all
|
||||
|
||||
@@ -278,6 +278,19 @@
|
||||
The arguments are in the same form accepted by oo1.DB.exec(), with
|
||||
the exceptions noted below.
|
||||
|
||||
If the `countChanges` arguments property (added in version 3.43) is
|
||||
truthy then the `result` property contained by the returned object
|
||||
will have a `changeCount` property which holds the number of changes
|
||||
made by the provided SQL. Because the SQL may contain an arbitrary
|
||||
number of statements, the `changeCount` is calculated by calling
|
||||
`sqlite3_total_changes()` before and after the SQL is evaluated. If
|
||||
the value of `countChanges` is 64 then the `changeCount` property
|
||||
will be returned as a 64-bit integer in the form of a BigInt (noting
|
||||
that that will trigger an exception if used in a BigInt-incapable
|
||||
build). In the latter case, the number of changes is calculated by
|
||||
calling `sqlite3_total_changes64()` before and after the SQL is
|
||||
evaluated.
|
||||
|
||||
A function-type args.callback property cannot cross
|
||||
the window/Worker boundary, so is not useful here. If
|
||||
args.callback is a string then it is assumed to be a
|
||||
@@ -523,7 +536,13 @@ sqlite3.initWorker1API = function(){
|
||||
}
|
||||
}
|
||||
try {
|
||||
const changeCount = !!rc.countChanges
|
||||
? db.changes(true,(64===rc.countChanges))
|
||||
: undefined;
|
||||
db.exec(rc);
|
||||
if(undefined !== changeCount){
|
||||
rc.changeCount = db.changes(true,64===rc.countChanges) - changeCount;
|
||||
}
|
||||
if(rc.callback instanceof Function){
|
||||
rc.callback = theCallback;
|
||||
/* Post a sentinel message to tell the client that the end
|
||||
|
||||
@@ -35,6 +35,9 @@
|
||||
|
||||
https://developer.chrome.com/blog/sync-methods-for-accesshandles/
|
||||
|
||||
Firefox v111 and Safari 16.4, both released in March 2023, also
|
||||
include this.
|
||||
|
||||
We cannot change to the sync forms at this point without breaking
|
||||
clients who use Chrome v104-ish or higher. truncate(), getSize(),
|
||||
flush(), and close() are now (as of v108) synchronous. Calling them
|
||||
@@ -818,9 +821,24 @@ const installAsyncProxy = function(self){
|
||||
}
|
||||
while(!flagAsyncShutdown){
|
||||
try {
|
||||
if('timed-out'===Atomics.wait(
|
||||
if('not-equal'!==Atomics.wait(
|
||||
state.sabOPView, state.opIds.whichOp, 0, state.asyncIdleWaitTime
|
||||
)){
|
||||
/* Maintenance note: we compare against 'not-equal' because
|
||||
|
||||
https://github.com/tomayac/sqlite-wasm/issues/12
|
||||
|
||||
is reporting that this occassionally, under high loads,
|
||||
returns 'ok', which leads to the whichOp being 0 (which
|
||||
isn't a valid operation ID and leads to an exception,
|
||||
along with a corresponding ugly console log
|
||||
message). Unfortunately, the conditions for that cannot
|
||||
be reliably reproduced. The only place in our code which
|
||||
writes a 0 to the state.opIds.whichOp SharedArrayBuffer
|
||||
index is a few lines down from here, and that instance
|
||||
is required in order for clear communication between
|
||||
the sync half of this proxy and this half.
|
||||
*/
|
||||
await releaseImplicitLocks();
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -100,7 +100,7 @@ globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
|
||||
|
||||
ACHTUNG: because we cannot generically know how to transform JS
|
||||
exceptions into result codes, the installed functions do no
|
||||
automatic catching of exceptions. It is critical, to avoid
|
||||
automatic catching of exceptions. It is critical, to avoid
|
||||
undefined behavior in the C layer, that methods mapped via
|
||||
this function do not throw. The exception, as it were, to that
|
||||
rule is...
|
||||
@@ -295,7 +295,8 @@ globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
|
||||
|
||||
- If `struct.$zName` is falsy and the entry has a string-type
|
||||
`name` property, `struct.$zName` is set to the C-string form of
|
||||
that `name` value before registerVfs() is called.
|
||||
that `name` value before registerVfs() is called. That string
|
||||
gets added to the on-dispose state of the struct.
|
||||
|
||||
On success returns this object. Throws on error.
|
||||
*/
|
||||
@@ -608,7 +609,7 @@ globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
|
||||
This is to facilitate creation of those methods inline in the
|
||||
passed-in object without requiring the client to explicitly get a
|
||||
reference to one of them in order to assign it to the other
|
||||
one.
|
||||
one.
|
||||
|
||||
The `catchExceptions`-installed handlers will account for
|
||||
identical references to the above functions and will install the
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user