mirror of
https://github.com/sqlite/sqlite.git
synced 2025-08-10 01:02:56 +03:00
Merge trunk enhancements into the begin-concurrent branch.
FossilOrigin-Name: 85054a8691cec593b0490b78715a506f25a8cbe04cbf673129c0e17f7f5fa912
This commit is contained in:
@@ -40,7 +40,7 @@
|
||||
**
|
||||
** Option 1 is the default behavior. 2 is use if there is a usable
|
||||
** constraint on "flags" with an integer right-hand side that where the
|
||||
** value of the right-hand side has its 0x01 bit set.
|
||||
** value of the right-hand side has its 0x001 bit set.
|
||||
**
|
||||
** All constraints on columns "a" through "e" are marked as "omit".
|
||||
**
|
||||
@@ -48,6 +48,12 @@
|
||||
** is an integer and that integer has its 0x02 bit set, then the
|
||||
** orderByConsumed flag is set.
|
||||
**
|
||||
** FLAGS SUMMARY:
|
||||
**
|
||||
** 0x001 Columns 'a' through 'e' have INT values
|
||||
** 0x002 orderByConsumed is set
|
||||
** 0x004 OFFSET and LIMIT have omit set
|
||||
**
|
||||
** COMPILE:
|
||||
**
|
||||
** gcc -Wall -g -shared -fPIC -I. qpvtab.c -o qqvtab.so
|
||||
@@ -103,7 +109,8 @@ static const char *azColname[] = {
|
||||
"ux",
|
||||
"rhs",
|
||||
"a", "b", "c", "d", "e",
|
||||
"flags"
|
||||
"flags",
|
||||
""
|
||||
};
|
||||
|
||||
/*
|
||||
@@ -143,6 +150,7 @@ static int qpvtabConnect(
|
||||
#define QPVTAB_D 9
|
||||
#define QPVTAB_E 10
|
||||
#define QPVTAB_FLAGS 11
|
||||
#define QPVTAB_NONE 12
|
||||
if( rc==SQLITE_OK ){
|
||||
pNew = sqlite3_malloc( sizeof(*pNew) );
|
||||
*ppVtab = (sqlite3_vtab*)pNew;
|
||||
@@ -226,7 +234,7 @@ static int qpvtabColumn(
|
||||
sqlite3_result_text64(ctx, z, zEnd-z, SQLITE_TRANSIENT, SQLITE_UTF8);
|
||||
}
|
||||
}else if( i>=QPVTAB_A && i<=QPVTAB_E ){
|
||||
if( pCur->flags & 1 ){
|
||||
if( pCur->flags & 0x001 ){
|
||||
sqlite3_result_int(ctx, i-QPVTAB_A+1);
|
||||
}else{
|
||||
char x = 'a'+i-QPVTAB_A;
|
||||
@@ -338,19 +346,25 @@ static int qpvtabBestIndex(
|
||||
for(i=0; i<pIdxInfo->nConstraint; i++){
|
||||
sqlite3_value *pVal;
|
||||
int iCol = pIdxInfo->aConstraint[i].iColumn;
|
||||
int op = pIdxInfo->aConstraint[i].op;
|
||||
if( iCol==QPVTAB_FLAGS && pIdxInfo->aConstraint[i].usable ){
|
||||
pVal = 0;
|
||||
rc = sqlite3_vtab_rhs_value(pIdxInfo, i, &pVal);
|
||||
assert( rc==SQLITE_OK || pVal==0 );
|
||||
if( pVal ){
|
||||
pIdxInfo->idxNum = sqlite3_value_int(pVal);
|
||||
if( pIdxInfo->idxNum & 2 ) pIdxInfo->orderByConsumed = 1;
|
||||
if( pIdxInfo->idxNum & 0x002 ) pIdxInfo->orderByConsumed = 1;
|
||||
}
|
||||
}
|
||||
if( op==SQLITE_INDEX_CONSTRAINT_LIMIT
|
||||
|| op==SQLITE_INDEX_CONSTRAINT_OFFSET
|
||||
){
|
||||
iCol = QPVTAB_NONE;
|
||||
}
|
||||
sqlite3_str_appendf(pStr,"aConstraint,%d,%s,%d,%d,",
|
||||
i,
|
||||
azColname[iCol],
|
||||
pIdxInfo->aConstraint[i].op,
|
||||
op,
|
||||
pIdxInfo->aConstraint[i].usable);
|
||||
pVal = 0;
|
||||
rc = sqlite3_vtab_rhs_value(pIdxInfo, i, &pVal);
|
||||
@@ -359,11 +373,22 @@ static int qpvtabBestIndex(
|
||||
qpvtabStrAppendValue(pStr, pVal);
|
||||
}
|
||||
sqlite3_str_append(pStr, "\n", 1);
|
||||
}
|
||||
for(i=0; i<pIdxInfo->nConstraint; i++){
|
||||
int iCol = pIdxInfo->aConstraint[i].iColumn;
|
||||
int op = pIdxInfo->aConstraint[i].op;
|
||||
if( op==SQLITE_INDEX_CONSTRAINT_LIMIT
|
||||
|| op==SQLITE_INDEX_CONSTRAINT_OFFSET
|
||||
){
|
||||
iCol = QPVTAB_NONE;
|
||||
}
|
||||
if( iCol>=QPVTAB_A && pIdxInfo->aConstraint[i].usable ){
|
||||
pIdxInfo->aConstraintUsage[i].argvIndex = ++k;
|
||||
if( iCol<=QPVTAB_FLAGS || (pIdxInfo->idxNum & 0x004)!=0 ){
|
||||
pIdxInfo->aConstraintUsage[i].omit = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
sqlite3_str_appendf(pStr, "nOrderBy,%d,,,,\n", pIdxInfo->nOrderBy);
|
||||
for(i=0; i<pIdxInfo->nOrderBy; i++){
|
||||
int iCol = pIdxInfo->aOrderBy[i].iColumn;
|
||||
|
100
manifest
100
manifest
@@ -1,5 +1,5 @@
|
||||
C Merge\sthe\slatest\strunk\senhancements\sinto\sthe\sbegin-concurrent\sbranch.
|
||||
D 2022-01-25T15:20:16.510
|
||||
C Merge\strunk\senhancements\sinto\sthe\sbegin-concurrent\sbranch.
|
||||
D 2022-02-04T17:40:59.906
|
||||
F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
|
||||
F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
|
||||
F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724
|
||||
@@ -317,7 +317,7 @@ F ext/misc/noop.c 81efe4cad9ec740e64388b14281cb983e6e2c223fed43eb77ab3e34946e0c1
|
||||
F ext/misc/normalize.c bd84355c118e297522aba74de34a4fd286fc775524e0499b14473918d09ea61f
|
||||
F ext/misc/percentile.c b9086e223d583bdaf8cb73c98a6539d501a2fc4282654adbfea576453d82e691
|
||||
F ext/misc/prefixes.c 0f4f8cff5aebc00a7e3ac4021fd59cfe1a8e17c800ceaf592859ecb9cbc38196
|
||||
F ext/misc/qpvtab.c c662fa0a452ad286e49b6c83ac917600656b2eb47d2225ff6185c56bf80cf8d2
|
||||
F ext/misc/qpvtab.c 09738419e25f603a35c0ac8bd0a04daab794f48d08a9bc07a6085b9057b99009
|
||||
F ext/misc/regexp.c b267fd05ff8d38b22f4c2809d7b7a2c61d522e9faf2feb928dbb9662e4a3a386
|
||||
F ext/misc/remember.c add730f0f7e7436cd15ea3fd6a90fd83c3f706ab44169f7f048438b7d6baa69c
|
||||
F ext/misc/rot13.c 51ac5f51e9d5fd811db58a9c23c628ad5f333c173f1fc53c8491a3603d38556c
|
||||
@@ -494,7 +494,7 @@ F src/alter.c e8ac1df663bf4ec74920edd1299435f2a616d2404de0ac4013c151ea4e7a11f2
|
||||
F src/analyze.c 7518b99e07c5494111fe3bd867f28f804b6c5c1ad0703ec3d116de9bab3fa516
|
||||
F src/attach.c f26d400f3ffe2cdca01406bca70e5f58c5488bf165b4fc37c228136dfcf1b583
|
||||
F src/auth.c f4fa91b6a90bbc8e0d0f738aa284551739c9543a367071f55574681e0f24f8cf
|
||||
F src/backup.c 58880b9a9adf88f1a57cb3b0db6b891626ae76113ebd0f2417a87c2634edfc65
|
||||
F src/backup.c a2891172438e385fdbe97c11c9745676bec54f518d4447090af97189fd8e52d7
|
||||
F src/bitvec.c 3907fcbe8a0c8c2db58d97087d15cdabbf2842adb9125df9ab9ff87d3db16775
|
||||
F src/btmutex.c 8acc2f464ee76324bf13310df5692a262b801808984c1b79defb2503bbafadb6
|
||||
F src/btree.c bdcf1b01f61807ea66e40c8259235ba6d865f23daea05b9e6eeb22343befe941
|
||||
@@ -504,23 +504,23 @@ F src/build.c d62a68c0bcefc4a1154e3f44424b3c3c74699c6a30fb9535e2cbedb9c376a9ff
|
||||
F src/callback.c 4c19af69835787bfe790ac560f3071a824eb629f34e41f97b52ce5235c77de1c
|
||||
F src/complete.c a3634ab1e687055cd002e11b8f43eb75c17da23e
|
||||
F src/ctime.c 2cce39df1a13e05b7633e6d21b651f21492471f991dd7b323a4ee4e7b7f0b7f1
|
||||
F src/date.c e25773f06a8f9043bfa1e5fa0bee93483c41933adfff0891752f00eadd12ab1c
|
||||
F src/date.c 41627dec396f3d33e2c317a065f9d59bb535982b2ea3a561c96e4d4cf1137b65
|
||||
F src/dbpage.c 8a01e865bf8bc6d7b1844b4314443a6436c07c3efe1d488ed89e81719047833a
|
||||
F src/dbstat.c 861e08690fcb0f2ee1165eff0060ea8d4f3e2ea10f80dab7d32ad70443a6ff2d
|
||||
F src/delete.c 52897a8516dc40753503c25eed0e305f09cc50ae474f22b0b4fd31d3b879cc08
|
||||
F src/expr.c 9658bccd1598211ace848c8ca9480dbf8be08dfee1db5cf03897b34b7b6e8fef
|
||||
F src/delete.c b5f1716b4d723db48254ee0f896e362cd029e865e05414139ea7f539f3884e1d
|
||||
F src/expr.c 31d23e6b57827b4cb8f0054f44e2ed86feb1dfcaa9b0c4d6c960b42536a17ca0
|
||||
F src/fault.c 460f3e55994363812d9d60844b2a6de88826e007
|
||||
F src/fkey.c 5b73f7a7c00f06017531a5bd258cbc2c7a294e55a7f84a729fe27aa525242560
|
||||
F src/func.c 88b07637d07b3bf7f89a6fe2da8edad7d34f962a69708afe48d4c28aa682515e
|
||||
F src/fkey.c 06e4ac33031b02dde7130c12e79cddf4dc5cfa72b23d8e63a3c26878fc9c1d3c
|
||||
F src/func.c 1c25ea71ccfa323e713306755d8d757404c93b8291867ec3a93550fcae23aa51
|
||||
F src/global.c 1f56aead86e8a18c4415638f5e6c4d0a0550427f4b3f5d065ba5164cc09c22e8
|
||||
F src/hash.c 8d7dda241d0ebdafb6ffdeda3149a412d7df75102cecfc1021c98d6219823b19
|
||||
F src/hash.h 3340ab6e1d13e725571d7cee6d3e3135f0779a7d8e76a9ce0a85971fa3953c51
|
||||
F src/hwtime.h cb1d7e3e1ed94b7aa6fde95ae2c2daccc3df826be26fc9ed7fd90d1750ae6144
|
||||
F src/in-operator.md 10cd8f4bcd225a32518407c2fb2484089112fd71
|
||||
F src/insert.c 1eea44389de3768ac98588c1410171cd53e7c6ad1af74049983dcbac82093de0
|
||||
F src/json.c 78fdec9af3a8bfb5ae685707b2701276fec1942b8f5f26689b2701debe32bcd2
|
||||
F src/json.c 225b00422112ecd7094a555f3ace16b25d7d5894062b823269ed03899907c2a2
|
||||
F src/legacy.c d7874bc885906868cd51e6c2156698f2754f02d9eee1bae2d687323c3ca8e5aa
|
||||
F src/loadext.c 657534339585ac234839e5187aa51d8802f292e0771c4f874b3af1f1223f81e2
|
||||
F src/loadext.c aa919a6a7884f8b34d7b791841b24d14b1b0ab43f45b3940f4851043b2855c0c
|
||||
F src/main.c 4806307e415189bbf91df6467d6ff923643fdeb84e782ef0d5451a14465db935
|
||||
F src/malloc.c fec841aa0a0400a6f7d20706178a5d8e8219a6bf562b6fe712c17f6c26813266
|
||||
F src/mem0.c 6a55ebe57c46ca1a7d98da93aaa07f99f1059645
|
||||
@@ -552,17 +552,17 @@ F src/pcache.h 4f87acd914cef5016fae3030343540d75f5b85a1877eed1a2a19b9f284248586
|
||||
F src/pcache1.c 54881292a9a5db202b2c0ac541c5e3ef9a5e8c4f1c1383adb2601d5499a60e65
|
||||
F src/pragma.c 7c024d690a3dc93f61830f11f900e4af2357f31d081b0c79099ca5e28919cba7
|
||||
F src/pragma.h f98354c48571c490927029510566839bf9e7242569bfbb48032dafeb008481d2
|
||||
F src/prepare.c 1e23522c934d90ff42de1b9b4f782fdf0fb690b06b92d7480b471ccb2b5899ea
|
||||
F src/prepare.c a187dade741c1f09ae118fcbbf0302511807bfc0355880927d7152eb75b8260d
|
||||
F src/printf.c 975f1f5417f2526365b6e6d7f22332e3e11806dad844701d92846292b654ba9a
|
||||
F src/random.c d4127b3d8ba155e293cc1abb19f70992c147abd5cc911df25582cb481b705974
|
||||
F src/resolve.c 24032ae57aec10df2f3fa2e20be0aae7d256bc704124b76c52d763440c7c0fe9
|
||||
F src/resolve.c 0dd8e23fda88411d63b2e6650f2380fdb08e88112e9a095fc23d5a08de4b2641
|
||||
F src/rowset.c ba9515a922af32abe1f7d39406b9d35730ed65efab9443dc5702693b60854c92
|
||||
F src/select.c 760d9f28dbeaafef9ea54c509af94ff5695df73ad5c854efd53de32a0564ed56
|
||||
F src/shell.c.in 4ccfb3b7f38756d11d9a31f78ba1339edda8e89f616e8c67046939c87bd14ace
|
||||
F src/sqlite.h.in d54bb1cc5e5cfb577c7e188813a06b785299121c6f5822a3770f22da3245ce33
|
||||
F src/select.c 23af49702cfb73e604f5befcebddb089d285a9a7ddf786e0a04920ff6a0fce21
|
||||
F src/shell.c.in 5d1b61ed8641cf470534d360b91ebcf7487559121be04ed330a7bd3ece42dc40
|
||||
F src/sqlite.h.in 3b3ce1eac1cab6298530e38b03d7b9cbc28918e865ff64c3e1c70781e43f197b
|
||||
F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8
|
||||
F src/sqlite3ext.h 5d54cf13d3406d8eb65d921a0d3c349de6126b732e695e79ecd4830ce86b4f8a
|
||||
F src/sqliteInt.h c142e938cebd7dab169f2a760c2d79803ae1015e680d8f66a73d182387836b7c
|
||||
F src/sqlite3ext.h a95cb9ed106e3d39e2118e4dcc15a14faec3fa50d0093425083d340d9dfd96e6
|
||||
F src/sqliteInt.h 21f4ad9b41ce7c5decd5398ada43468d4c07de8dd3cdcee02a06e937231fcc4f
|
||||
F src/sqliteLimit.h d7323ffea5208c6af2734574bae933ca8ed2ab728083caa117c9738581a31657
|
||||
F src/status.c 4b8bc2a6905163a38b739854a35b826c737333fab5b1f8e03fa7eb9a4799c4c1
|
||||
F src/table.c 0f141b58a16de7e2fbe81c308379e7279f4c6b50eb08efeec5892794a0ba30d1
|
||||
@@ -579,7 +579,7 @@ F src/test9.c 12e5ba554d2d1cbe0158f6ab3f7ffcd7a86ee4e5
|
||||
F src/test_async.c 195ab49da082053fdb0f949c114b806a49ca770a
|
||||
F src/test_autoext.c 915d245e736652a219a907909bb6710f0d587871
|
||||
F src/test_backup.c bf5da90c9926df0a4b941f2d92825a01bbe090a0
|
||||
F src/test_bestindex.c 78809f11026f18a93fcfd798d9479cba37e1201c830260bf1edc674b2fa9b857
|
||||
F src/test_bestindex.c 8294d8223b7f18a3ddb7f9a0e30815dcca4e61681f78b538c870f7d934f88b81
|
||||
F src/test_blob.c ae4a0620b478548afb67963095a7417cd06a4ec0a56adb453542203bfdcb31ce
|
||||
F src/test_btree.c 8b2dc8b8848cf3a4db93f11578f075e82252a274
|
||||
F src/test_config.c 8544c71df9131c25c4d0255188ec37a6e9af452f5520ee9df4bf30f35c4c7d29
|
||||
@@ -624,18 +624,18 @@ F src/threads.c 4ae07fa022a3dc7c5beb373cf744a85d3c5c6c3c
|
||||
F src/tokenize.c b74d878aa7c82ec8460779468061a96185e22257f68ab785b69abce354b70446
|
||||
F src/treeview.c 9dfdb7ff7f6645d0a6458dbdf4ffac041c071c4533a6db8bb6e502b979ac67bc
|
||||
F src/trigger.c 692972e4393dfc8017a1a527c1ea1b96ce3d101e84584cd832fcfb83d22b50b2
|
||||
F src/update.c 1f3a7bda1c7489b64e9b9392d829b6a2d4ffb6d2b97152cb2b3d05f0c016b3d4
|
||||
F src/update.c ee9b4f2a29d93af457b098a80ef34d2228dc38e951910430ea04eb3e81de2b68
|
||||
F src/upsert.c 8789047a8f0a601ea42fa0256d1ba3190c13746b6ba940fe2d25643a7e991937
|
||||
F src/utf.c ee39565f0843775cc2c81135751ddd93eceb91a673ea2c57f61c76f288b041a0
|
||||
F src/util.c 602fe229f32a96ceccae4f40824129669582096f7c355f53dbac156c9fecef23
|
||||
F src/vacuum.c 72867c740476d13f1c397015e4d3168b4e96a237a80b9afa67e1bb8500bfeeab
|
||||
F src/vdbe.c d2f53c56dabf175bf229dfd65b7fd53815e4a8e6e4c4766252afdfc7c84fb8d1
|
||||
F src/vdbe.c c3f0c0e71d08b37ec11757f3f3a055b064c7c9ada24774eb1f70ffc1214562a0
|
||||
F src/vdbe.h 25dabb25c7e157b84e59260cfb5b466c3ac103ede9f36f4db371332c47601abe
|
||||
F src/vdbeInt.h d89d5d2150500cfb08615329fd20aea9d746bba5f2c3ecb8a17e2d2d9be029e5
|
||||
F src/vdbeapi.c 22c79072ae7d8a01e9bcae8ba16e918d60d202eaa9553b5fda38f99f7464d99a
|
||||
F src/vdbeInt.h b45599a2b59f1ce042512ab6786b0b82a8cf3002f6b0fa60b4834e2cd3ac61d8
|
||||
F src/vdbeapi.c 06bff35393ca5daa3e02e38fb516df320bd52720a2781eb70c2db23ea1c746dd
|
||||
F src/vdbeaux.c 312d57e71e6e152243867390308aec667e05a4e7e676a674114a33f6687d3702
|
||||
F src/vdbeblob.c 5e61ce31aca17db8fb60395407457a8c1c7fb471dde405e0cd675974611dcfcd
|
||||
F src/vdbemem.c da4d594084d581be6436582bb44bb128feeb138a3e6c313eda6749ebdc3a65ec
|
||||
F src/vdbemem.c eb6042667c02c3ef1f968235b4a170e31b23a4b6a57f65a8454eab4d36f14b7f
|
||||
F src/vdbesort.c 43756031ca7430f7aec3ef904824a7883c4ede783e51f280d99b9b65c0796e35
|
||||
F src/vdbetrace.c fe0bc29ebd4e02c8bc5c1945f1d2e6be5927ec12c06d89b03ef2a4def34bf823
|
||||
F src/vdbevtab.c f99b275366c5fc5e2d99f734729880994ab9500bdafde7fae3b02d562b9d323c
|
||||
@@ -644,10 +644,10 @@ F src/vxworks.h d2988f4e5a61a4dfe82c6524dd3d6e4f2ce3cdb9
|
||||
F src/wal.c 419c0a98dd65b9419fd07bdd4f9facc61143c33d57b0dbeffd83cf38a2e0e33e
|
||||
F src/wal.h 7ffe787437f20a098af347011967a6d3bb8e5c3dc645e6be59eff44d2b2c5297
|
||||
F src/walker.c f890a3298418d7cba3b69b8803594fdc484ea241206a8dfa99db6dd36f8cbb3b
|
||||
F src/where.c a14990c7b35e95f8f9cb0dc0d6d2e32fa99135a716a04027cefa48138d280ecb
|
||||
F src/whereInt.h 8a215acde0f833a4dea3d30a7bbed9f48b4b547b5d5e34cd02acee366476ab80
|
||||
F src/wherecode.c 8da0f873278ed6aad42bf2028404d7178dd9cfcdc7179ecc61a87591a15a07d2
|
||||
F src/whereexpr.c 9f64c39e53070584e99e4d20c1dd3397e125fabbae8fd414ffec574c410ac7d3
|
||||
F src/where.c 31bc1f43b0bf679e93c3e7a7d67cbcaddc7ae746694b149b282427d337b06caa
|
||||
F src/whereInt.h 15d2975c3b4c193c78c26674400a840da8647fe1777ae3b026e2d15937b38a03
|
||||
F src/wherecode.c 4a0dd0403e1c9b628a420eefbe1d60da0003356de6ee18e6707480c9b995bae7
|
||||
F src/whereexpr.c 2da56404a024dc8dc41a31d8b498eed2c7c6b0bb412150d88cb3327516aa3b9f
|
||||
F src/window.c dfaec4abc6012cbc18e4a202ca3a5d5a0efcc4011d86a06d882ddaab8aedee4d
|
||||
F test/8_3_names.test ebbb5cd36741350040fd28b432ceadf495be25b2
|
||||
F test/affinity2.test ce1aafc86e110685b324e9a763eab4f2a73f737842ec3b687bd965867de90627
|
||||
@@ -720,7 +720,7 @@ F test/autovacuum_ioerr2.test 8a367b224183ad801e0e24dcb7d1501f45f244b4
|
||||
F test/avfs.test 0c3a38e03cccb0fc3127838462dc05dc3f4c1480d770c084b388304c25de3652
|
||||
F test/avtrans.test b7dc25459ecbd86c6fa9c606ee3068f59d81e225118617dcf2bbb6ded2ade89e
|
||||
F test/backcompat.test 3e64cedda754c778ef6bbe417b6e7a295e662a4d
|
||||
F test/backup.test dd4a5ff756e3df3931dacb1791db0584d4bad989
|
||||
F test/backup.test fc1ecefce723fad5199b55cec7a5a992ec8c3ad6873419e5e8919066dec457f3
|
||||
F test/backup2.test 8facb54df1388419d34b362ab1f7e233310ff3a3af64e8ad5ec47ba3c2bbe5cf
|
||||
F test/backup4.test 8f6fd48e0dfde77b9a3bb26dc471ede3e101df32
|
||||
F test/backup5.test ee5da6d7fe5082f5b9b0bbfa31d016f52412a2e4
|
||||
@@ -730,13 +730,14 @@ F test/badutf.test d5360fc31f643d37a973ab0d8b4fb85799c3169f
|
||||
F test/badutf2.test f310fd3b24a491b6b77bccdf14923b85d6ebcce751068c180d93a6b8ff854399
|
||||
F test/bc_common.tcl b5e42d80305be95697e6370e015af571e5333a1c
|
||||
F test/bc_test1.c e0a092579552e066ed4ce7bcdaecfa69c4aacc8d
|
||||
F test/bestindex1.test 7cc626f1f4a7483bb6b38487d467db4477083be5cd93958aeda5d5127640dc81
|
||||
F test/bestindex2.test 60266e2854055788459cbfd86cef575601eabe74a2c61faba72601739fea4398
|
||||
F test/bestindex3.test e061a6ed0f519beee037ba7e7a4c37f80c8a7e4a303e2559ed1f760e4b0235eb
|
||||
F test/bestindex4.test 82250e7dcc6d5b15244edc9d6554b1760583af1b8548c2a255a1c4f28e744c0e
|
||||
F test/bestindex5.test 67c1166131bb59f9e47c00118f7d432ca5491e6cae6ca3f87ca9db20103a78f9
|
||||
F test/bestindex6.test d856a9bb63d927493575823eed44053bc36251e241aa364e54d0f2a2d302e1d4
|
||||
F test/bestindex7.test a11348824aed0de2bb9030f092636929000cd72882bdf919adacc3792f67ccbd
|
||||
F test/bestindex1.test 856a453dff8c68b4568601eed5a8b5e20b4763af9229f3947c215729ed878db0
|
||||
F test/bestindex2.test 394ff8fbf34703391247116d6a44e1c50ee7282236ee77909044573cefc37bc0
|
||||
F test/bestindex3.test 34bea272b0e0f835651b16a3931dbe7ac927039be6b2e1cb617bbe1d584b492b
|
||||
F test/bestindex4.test 3039894f2dad50f3a68443dffad1b44c9b067ac03870102df1ce3d9a46ea602e
|
||||
F test/bestindex5.test a0c90b2dad7836e80a01379e200e5f8ec9476d49b349af02c0dbff2fb75dc98d
|
||||
F test/bestindex6.test 16942535b551273f3ad9df8d7cc4b7f22b1fcd8882714358859eb049a6f99dd4
|
||||
F test/bestindex7.test f094c669a6400777f4d2ddc3ed28e39169f1adb5be3d59b55f22ccf8c414b71e
|
||||
F test/bestindex8.test abd0016fc04f19dc382976750b06df5463d2757e11e78a8ba7d7dc50671f3337
|
||||
F test/between.test b9a65fb065391980119e8a781a7409d3fcf059d89968279c750e190a9a1d5263
|
||||
F test/bigfile.test aa74f4e5db51c8e54a1d9de9fa65d01d1eb20b59
|
||||
F test/bigfile2.test 1b489a3a39ae90c7f027b79110d6b4e1dbc71bfc
|
||||
@@ -852,6 +853,7 @@ F test/cursorhint2.test 6f3aa9cb19e7418967a10ec6905209bcbb5968054da855fc36c8beee
|
||||
F test/dataversion1.test 6e5e86ac681f0782e766ebcb56c019ae001522d114e0e111e5ebf68ccf2a7bb8
|
||||
F test/date.test 9b73bbeb1b82d9c1f44dec5cf563bf7da58d2373
|
||||
F test/date2.test 7e12ec14aaf4d5e6294b4ba140445b0eca06ea50062a9c3a69c4ee13d0b6f8b1
|
||||
F test/date3.test a1b77abf05c6772fe5ca2337cac1398892f2a41e62bce7e6be0f4a08a0e64ae5
|
||||
F test/dbdata.test 042f49acff3438f940eeba5868d3af080ae64ddf26ae78f80c92bec3ca7d8603
|
||||
F test/dbfuzz.c 73047c920d6210e5912c87cdffd9a1c281d4252e
|
||||
F test/dbfuzz001.test 55e1a3504f8dea84155e09912fe3b1c3ad77e0b1a938ec42ca03b8e51b321e30
|
||||
@@ -1155,7 +1157,7 @@ F test/join.test 25cf0ac11c3b81fedfd166f9062166bdb39dea92f5a7c16cacbf6dc1f7f6702
|
||||
F test/join2.test 9bdc615841b91c97a16d68bad9508aea11fa0c6b34e5689847bcc4dac70e4990
|
||||
F test/join3.test 6f0c774ff1ba0489e6c88a3e77b9d3528fb4fda0
|
||||
F test/join4.test 1a352e4e267114444c29266ce79e941af5885916
|
||||
F test/join5.test d22395f7d4020a58cabbc8316f300a5cfef84aee9e8ba7ce79b33cc43a3e1e2e
|
||||
F test/join5.test 37864d567928652cab79a7872ebde74b3c67a1feb0366d98bb3bc7832885f388
|
||||
F test/join6.test f809c025fa253f9e150c0e9afd4cef8813257bceeb6f46e04041228c9403cc2c
|
||||
F test/journal1.test c7b768041b7f494471531e17abc2f4f5ebf9e5096984f43ed17c4eb80ba34497
|
||||
F test/journal2.test 9dac6b4ba0ca79c3b21446bbae993a462c2397c4
|
||||
@@ -1163,11 +1165,11 @@ F test/journal3.test 7c3cf23ffc77db06601c1fcfc9743de8441cb77db9d1aa931863d94f5ff
|
||||
F test/jrnlmode.test 9b5bc01dac22223cb60ec2d5f97acf568d73820794386de5634dcadbea9e1946
|
||||
F test/jrnlmode2.test 8759a1d4657c064637f8b079592651530db738419e1d649c6df7048cd724363d
|
||||
F test/jrnlmode3.test 556b447a05be0e0963f4311e95ab1632b11c9eaa
|
||||
F test/json101.test bb71538005f2d9e18620bdd3b76839a93ca0be61903eb8d751a64e78cf99b8fb
|
||||
F test/json102.test 86edc7d283085addff8593b178997e75875530d1385f5926717543d3475e6b01
|
||||
F test/json103.test aff6b7a4c17d5a20b487a7bc1a274bfdc63b829413bdfb83bedac42ec7f67e3b
|
||||
F test/json104.test 2cb7ff2cca2c8214d3e5260eeb9ce45faec0926f68b3e40c1aaa6ca247284144
|
||||
F test/json105.test 45f7d6a9a54c85f8a9589b68d3e7a1f42d02f2359911a8cdbad1f9988f571173
|
||||
F test/json101.test d7c84854acafaf80f883e183ac4248ea2742615086c94a61a46ad7d7382ce123
|
||||
F test/json102.test 327e77275f338c028faefa2da5164daf6b142a165e3015ff2a6e4251ddc6a0ac
|
||||
F test/json103.test 53df87f83a4e5fa0c0a56eb29ff6c94055c6eb919f33316d62161a8880112dbe
|
||||
F test/json104.test a502dc01853aada95d721b3b275afbe2dc18fffdac1fea6e96fb20c13586bbb5
|
||||
F test/json105.test 11670a4387f4308ae0318cadcbd6a918ea7edcd19fbafde020720a073952675d
|
||||
F test/keyword1.test 37ef6bba5d2ed5b07ecdd6810571de2956599dff
|
||||
F test/kvtest.c feb4358fb022da8ebd098c45811f2f6507688bb6c43aa72b3e840df19026317b
|
||||
F test/lastinsert.test 42e948fd6442f07d60acbd15d33fb86473e0ef63
|
||||
@@ -1337,11 +1339,11 @@ F test/round1.test 768018b04522ca420b1aba8a24bd76091d269f3bce3902af3ec6ebcee41ab
|
||||
F test/rowallock.test 3f88ec6819489d0b2341c7a7528ae17c053ab7cc
|
||||
F test/rowhash.test 0bc1d31415e4575d10cacf31e1a66b5cc0f8be81
|
||||
F test/rowid.test e29025be95baf6b32f0d5edef59a7633028325896a98f1caa8019559ca910350
|
||||
F test/rowvalue.test 02214016f747854ef636e64ff204778649937aa801ca78e2495a960f8e0d509d
|
||||
F test/rowvalue.test 228b312f8526ed000ecda559a6a9adf30aa2d79dcb12afa5c04eebaafcf55eae
|
||||
F test/rowvalue2.test 060d238b7e5639a7c5630cb5e63e311b44efef2b
|
||||
F test/rowvalue3.test 3068f508753af69884b12125995f023da0dbb256
|
||||
F test/rowvalue4.test 441e7e366ac6d939a3a95a574031c56ec2a854077a91d66eee5ff1d86cb5be58
|
||||
F test/rowvalue5.test c81c7d8cf36711ab37675ad7376084ae2a359cb6
|
||||
F test/rowvalue5.test 00740304ea6a53a8704640c7405690f0045d5d2a6b4b04dde7bccc14c3068ea7
|
||||
F test/rowvalue6.test d19b54feb604d5601f8614b15e214e0774c01087
|
||||
F test/rowvalue7.test c1cbdbf407029db01f87764097c6ac02a1c5a37efd2776eff32a9cdfdf6f2dba
|
||||
F test/rowvalue8.test 5900eddad9e2c3c2e26f1a95f74aafc1232ee5e0
|
||||
@@ -1400,7 +1402,7 @@ F test/sharedA.test 49d87ec54ab640fbbc3786ee3c01de94aaa482a3a9f834ad3fe92770eb69
|
||||
F test/sharedB.test 16cc7178e20965d75278f410943109b77b2e645e
|
||||
F test/shared_err.test 32634e404a3317eeb94abc7a099c556a346fdb8fb3858dbe222a4cbb8926a939
|
||||
F test/sharedlock.test 5ede3c37439067c43b0198f580fd374ebf15d304
|
||||
F test/shell1.test 70f46b5d07776a107335c3c2c9cbd0431d44637bfeae1f6b9ded5e33b4c7c0bf
|
||||
F test/shell1.test ce2f370886645f38fabdde44976c14a004400f166edea8fdd9741079b645fef6
|
||||
F test/shell2.test f00a0501c00583cbc46f7510e1d713366326b2b3e63d06d15937284171a8787c
|
||||
F test/shell3.test cb4b835a901742c9719437a89171172ecc4a8823ad97349af8e4e841e6f82566
|
||||
F test/shell4.test 8f6c0fce4abed19a8a7f7262517149812a04caa905d01bdc8f5e92573504b759
|
||||
@@ -1931,7 +1933,7 @@ F tool/stack_usage.tcl f8e71b92cdb099a147dad572375595eae55eca43
|
||||
F tool/symbols-mingw.sh 4dbcea7e74768305384c9fd2ed2b41bbf9f0414d
|
||||
F tool/symbols.sh 1612bd947750e21e7b47befad5f6b3825b06cce0705441f903bf35ced65ae9b9
|
||||
F tool/varint.c 5d94cb5003db9dbbcbcc5df08d66f16071aee003
|
||||
F tool/vdbe-compress.tcl 5926c71f9c12d2ab73ef35c29376e756eb68361c
|
||||
F tool/vdbe-compress.tcl 1dcb7632e57cf57105248029e6e162fddaf6c0fccb3bb9e6215603752c5a2d4a
|
||||
F tool/vdbe_profile.tcl 246d0da094856d72d2c12efec03250d71639d19f
|
||||
F tool/warnings-clang.sh bbf6a1e685e534c92ec2bfba5b1745f34fb6f0bc2a362850723a9ee87c1b31a7
|
||||
F tool/warnings.sh 09311479bdc290e20ec8e35a3d1b14b096bbd96222277cfd6274c3a99b3d012f
|
||||
@@ -1957,8 +1959,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93
|
||||
F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc
|
||||
F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e
|
||||
F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0
|
||||
P 02daae7a67927e5e6188e16219a453e7fa1b27cface6616171dd7cde717aca51 a8db69411b0d1275909adeb21027784ada17af24efe3a59ae0ae2a897659ff17
|
||||
R d3a825a70991aba4988e43f815069f24
|
||||
P dae81f45d2bee8e5bced2564a6a85ffb23dc93fce6547760cd811acddbc09e25 70049342d5ad57ea3e863bba19253934b868bacdd1c26c9371bac024a829badf
|
||||
R b23502faac945ebd993a7d5170df0824
|
||||
U drh
|
||||
Z 5101960e875508d300aed0c1856cfe51
|
||||
Z 512010d85591fc13397cb5074c1b9277
|
||||
# Remove this line to create a well-formed Fossil manifest.
|
||||
|
@@ -1 +1 @@
|
||||
dae81f45d2bee8e5bced2564a6a85ffb23dc93fce6547760cd811acddbc09e25
|
||||
85054a8691cec593b0490b78715a506f25a8cbe04cbf673129c0e17f7f5fa912
|
@@ -85,7 +85,7 @@ static Btree *findBtree(sqlite3 *pErrorDb, sqlite3 *pDb, const char *zDb){
|
||||
if( i==1 ){
|
||||
Parse sParse;
|
||||
int rc = 0;
|
||||
sqlite3ParseObjectInit(&sParse,pErrorDb);
|
||||
sqlite3ParseObjectInit(&sParse,pDb);
|
||||
if( sqlite3OpenTempDatabase(&sParse) ){
|
||||
sqlite3ErrorWithMsg(pErrorDb, sParse.rc, "%s", sParse.zErrMsg);
|
||||
rc = SQLITE_ERROR;
|
||||
|
@@ -697,7 +697,7 @@ static int parseModifier(
|
||||
** SQLite (0..5373484.5) then the result will be NULL.
|
||||
*/
|
||||
if( sqlite3_stricmp(z, "julianday")==0 ){
|
||||
if( idx>1 ) return 1;
|
||||
if( idx>1 ) return 1; /* IMP: R-31176-64601 */
|
||||
if( p->validJD && p->rawS ){
|
||||
rc = 0;
|
||||
p->rawS = 0;
|
||||
@@ -728,6 +728,7 @@ static int parseModifier(
|
||||
** seconds since 1970. Convert to a real julian day number.
|
||||
*/
|
||||
if( sqlite3_stricmp(z, "unixepoch")==0 && p->rawS ){
|
||||
if( idx>1 ) return 1; /* IMP: R-49255-55373 */
|
||||
r = p->s*1000.0 + 210866760000000.0;
|
||||
if( r>=0.0 && r<464269060800000.0 ){
|
||||
clearYMD_HMS_TZ(p);
|
||||
|
@@ -478,7 +478,7 @@ void sqlite3DeleteFrom(
|
||||
** ONEPASS_SINGLE: One-pass approach - at most one row deleted.
|
||||
** ONEPASS_MULTI: One-pass approach - any number of rows may be deleted.
|
||||
*/
|
||||
pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere, 0, 0, wcf, iTabCur+1);
|
||||
pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere, 0, 0,0,wcf,iTabCur+1);
|
||||
if( pWInfo==0 ) goto delete_from_cleanup;
|
||||
eOnePass = sqlite3WhereOkOnePass(pWInfo, aiCurOnePass);
|
||||
assert( IsVirtual(pTab)==0 || eOnePass!=ONEPASS_MULTI );
|
||||
|
@@ -3497,7 +3497,6 @@ static void sqlite3ExprCodeIN(
|
||||
}else{
|
||||
destStep2 = destStep6 = sqlite3VdbeMakeLabel(pParse);
|
||||
}
|
||||
// if( pParse->nErr ) goto sqlite3ExprCodeIN_finished;
|
||||
for(i=0; i<nVector; i++){
|
||||
Expr *p = sqlite3VectorFieldSubexpr(pExpr->pLeft, i);
|
||||
if( pParse->nErr ) goto sqlite3ExprCodeIN_oom_error;
|
||||
|
@@ -651,7 +651,7 @@ static void fkScanChildren(
|
||||
** clause. For each row found, increment either the deferred or immediate
|
||||
** foreign key constraint counter. */
|
||||
if( pParse->nErr==0 ){
|
||||
pWInfo = sqlite3WhereBegin(pParse, pSrc, pWhere, 0, 0, 0, 0);
|
||||
pWInfo = sqlite3WhereBegin(pParse, pSrc, pWhere, 0, 0, 0, 0, 0);
|
||||
sqlite3VdbeAddOp2(v, OP_FkCounter, pFKey->isDeferred, nIncr);
|
||||
if( pWInfo ){
|
||||
sqlite3WhereEnd(pWInfo);
|
||||
|
@@ -106,6 +106,7 @@ static void subtypeFunc(
|
||||
int argc,
|
||||
sqlite3_value **argv
|
||||
){
|
||||
UNUSED_PARAMETER(argc);
|
||||
sqlite3_result_int(context, sqlite3_value_subtype(argv[0]));
|
||||
}
|
||||
|
||||
|
@@ -2666,7 +2666,7 @@ int sqlite3JsonTableFunctions(sqlite3 *db){
|
||||
{ "json_each", &jsonEachModule },
|
||||
{ "json_tree", &jsonTreeModule },
|
||||
};
|
||||
int i;
|
||||
unsigned int i;
|
||||
for(i=0; i<sizeof(aMod)/sizeof(aMod[0]) && rc==SQLITE_OK; i++){
|
||||
rc = sqlite3_create_module(db, aMod[i].zName, aMod[i].pModule, 0);
|
||||
}
|
||||
|
@@ -489,6 +489,9 @@ static const sqlite3_api_routines sqlite3Apis = {
|
||||
sqlite3_error_offset,
|
||||
sqlite3_vtab_rhs_value,
|
||||
sqlite3_vtab_distinct,
|
||||
sqlite3_vtab_in,
|
||||
sqlite3_vtab_in_first,
|
||||
sqlite3_vtab_in_next
|
||||
};
|
||||
|
||||
/* True if x is the directory separator character
|
||||
|
@@ -660,6 +660,7 @@ void sqlite3ParseObjectInit(Parse *pParse, sqlite3 *db){
|
||||
pParse->pOuterParse = db->pParse;
|
||||
db->pParse = pParse;
|
||||
pParse->db = db;
|
||||
if( db->mallocFailed ) sqlite3ErrorMsg(pParse, "out of memory");
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -686,7 +687,7 @@ static int sqlite3Prepare(
|
||||
sParse.db = db;
|
||||
sParse.pReprepare = pReprepare;
|
||||
assert( ppStmt && *ppStmt==0 );
|
||||
/* assert( !db->mallocFailed ); // not true with SQLITE_USE_ALLOCA */
|
||||
if( db->mallocFailed ) sqlite3ErrorMsg(&sParse, "out of memory");
|
||||
assert( sqlite3_mutex_held(db->mutex) );
|
||||
|
||||
/* For a long-term use prepared statement avoid the use of
|
||||
|
@@ -995,7 +995,7 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){
|
||||
/* Internal-use-only functions are disallowed unless the
|
||||
** SQL is being compiled using sqlite3NestedParse() or
|
||||
** the SQLITE_TESTCTRL_INTERNAL_FUNCTIONS test-control has be
|
||||
** used to activate internal functionsn for testing purposes */
|
||||
** used to activate internal functions for testing purposes */
|
||||
no_such_func = 1;
|
||||
pDef = 0;
|
||||
}else
|
||||
|
@@ -6898,7 +6898,7 @@ int sqlite3Select(
|
||||
/* Begin the database scan. */
|
||||
SELECTTRACE(1,pParse,p,("WhereBegin\n"));
|
||||
pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere, sSort.pOrderBy,
|
||||
p->pEList, wctrlFlags, p->nSelectRow);
|
||||
p->pEList, p, wctrlFlags, p->nSelectRow);
|
||||
if( pWInfo==0 ) goto select_end;
|
||||
if( sqlite3WhereOutputRowCount(pWInfo) < p->nSelectRow ){
|
||||
p->nSelectRow = sqlite3WhereOutputRowCount(pWInfo);
|
||||
@@ -7162,7 +7162,7 @@ int sqlite3Select(
|
||||
sqlite3VdbeAddOp2(v, OP_Gosub, regReset, addrReset);
|
||||
SELECTTRACE(1,pParse,p,("WhereBegin\n"));
|
||||
pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere, pGroupBy, pDistinct,
|
||||
WHERE_GROUPBY | (orderByGrp ? WHERE_SORTBYGROUP : 0) | distFlag, 0
|
||||
0, (WHERE_GROUPBY|(orderByGrp ? WHERE_SORTBYGROUP : 0)|distFlag), 0
|
||||
);
|
||||
if( pWInfo==0 ){
|
||||
sqlite3ExprListDelete(db, pDistinct);
|
||||
@@ -7460,7 +7460,7 @@ int sqlite3Select(
|
||||
|
||||
SELECTTRACE(1,pParse,p,("WhereBegin\n"));
|
||||
pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere, pMinMaxOrderBy,
|
||||
pDistinct, minMaxFlag|distFlag, 0);
|
||||
pDistinct, 0, minMaxFlag|distFlag, 0);
|
||||
if( pWInfo==0 ){
|
||||
goto select_end;
|
||||
}
|
||||
|
355
src/shell.c.in
355
src/shell.c.in
@@ -1066,6 +1066,15 @@ struct EQPGraph {
|
||||
char zPrefix[100]; /* Graph prefix */
|
||||
};
|
||||
|
||||
/* Parameters affecting columnar mode result display (defaulting together) */
|
||||
typedef struct ColModeOpts {
|
||||
int iWrap; /* In columnar modes, wrap lines reaching this limit */
|
||||
u8 bQuote; /* Quote results for .mode box and table */
|
||||
u8 bWordWrap; /* In columnar modes, wrap at word boundaries */
|
||||
} ColModeOpts;
|
||||
#define ColModeOpts_default { 60, 0, 0 }
|
||||
#define ColModeOpts_default_qbox { 60, 1, 0 }
|
||||
|
||||
/*
|
||||
** State information about the database connection is contained in an
|
||||
** instance of the following structure.
|
||||
@@ -1084,6 +1093,7 @@ struct ShellState {
|
||||
u8 eTraceType; /* SHELL_TRACE_* value for type of trace */
|
||||
u8 bSafeMode; /* True to prohibit unsafe operations */
|
||||
u8 bSafeModePersist; /* The long-term value of bSafeMode */
|
||||
ColModeOpts cmOpts; /* Option values affecting columnar mode output */
|
||||
unsigned statsOn; /* True to display memory stats before each finalize */
|
||||
unsigned mEqpLines; /* Mask of veritical lines in the EQP output graph */
|
||||
int inputNesting; /* Track nesting level of .read and other redirects */
|
||||
@@ -3164,7 +3174,134 @@ static void print_box_row_separator(
|
||||
fputs("\n", p->out);
|
||||
}
|
||||
|
||||
/*
|
||||
** z[] is a line of text that is to be displayed the .mode box or table or
|
||||
** similar tabular formats. z[] might contain control characters such
|
||||
** as \n, \t, \f, or \r.
|
||||
**
|
||||
** Compute characters to display on the first line of z[]. Stop at the
|
||||
** first \r, \n, or \f. Expand \t into spaces. Return a copy (obtained
|
||||
** from malloc()) of that first line, which caller should free sometime.
|
||||
** Write anything to display on the next line into *pzTail. If this is
|
||||
** the last line, write a NULL into *pzTail. (*pzTail is not allocated.)
|
||||
*/
|
||||
static char *translateForDisplayAndDup(
|
||||
const unsigned char *z, /* Input text to be transformed */
|
||||
const unsigned char **pzTail, /* OUT: Tail of the input for next line */
|
||||
int mxWidth, /* Max width. 0 means no limit */
|
||||
u8 bWordWrap /* If true, avoid breaking mid-word */
|
||||
){
|
||||
int i; /* Input bytes consumed */
|
||||
int j; /* Output bytes generated */
|
||||
int k; /* Input bytes to be displayed */
|
||||
int n; /* Output column number */
|
||||
unsigned char *zOut; /* Output text */
|
||||
|
||||
if( z==0 ){
|
||||
*pzTail = 0;
|
||||
return 0;
|
||||
}
|
||||
if( mxWidth<0 ) mxWidth = -mxWidth;
|
||||
if( mxWidth==0 ) mxWidth = 1000000;
|
||||
i = j = n = 0;
|
||||
while( n<mxWidth ){
|
||||
if( z[i]>=' ' ){
|
||||
n++;
|
||||
do{ i++; j++; }while( (z[i]&0xc0)==0x80 );
|
||||
continue;
|
||||
}
|
||||
if( z[i]=='\t' ){
|
||||
do{
|
||||
n++;
|
||||
j++;
|
||||
}while( (n&7)!=0 && n<mxWidth );
|
||||
i++;
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
if( n>=mxWidth && bWordWrap ){
|
||||
/* Perhaps try to back up to a better place to break the line */
|
||||
for(k=i; k>i/2; k--){
|
||||
if( isspace(z[k-1]) ) break;
|
||||
}
|
||||
if( k<=i/2 ){
|
||||
for(k=i; k>i/2; k--){
|
||||
if( isalnum(z[k-1])!=isalnum(z[k]) && (z[k]&0xc0)!=0x80 ) break;
|
||||
}
|
||||
}
|
||||
if( k<=i/2 ){
|
||||
k = i;
|
||||
}else{
|
||||
i = k;
|
||||
while( z[i]==' ' ) i++;
|
||||
}
|
||||
}else{
|
||||
k = i;
|
||||
}
|
||||
if( n>=mxWidth && z[i]>=' ' ){
|
||||
*pzTail = &z[i];
|
||||
}else if( z[i]=='\r' && z[i+1]=='\n' ){
|
||||
*pzTail = z[i+2] ? &z[i+2] : 0;
|
||||
}else if( z[i]==0 || z[i+1]==0 ){
|
||||
*pzTail = 0;
|
||||
}else{
|
||||
*pzTail = &z[i+1];
|
||||
}
|
||||
zOut = malloc( j+1 );
|
||||
shell_check_oom(zOut);
|
||||
i = j = n = 0;
|
||||
while( i<k ){
|
||||
if( z[i]>=' ' ){
|
||||
n++;
|
||||
do{ zOut[j++] = z[i++]; }while( (z[i]&0xc0)==0x80 );
|
||||
continue;
|
||||
}
|
||||
if( z[i]=='\t' ){
|
||||
do{
|
||||
n++;
|
||||
zOut[j++] = ' ';
|
||||
}while( (n&7)!=0 && n<mxWidth );
|
||||
i++;
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
zOut[j] = 0;
|
||||
return (char*)zOut;
|
||||
}
|
||||
|
||||
/* Extract the value of the i-th current column for pStmt as an SQL literal
|
||||
** value. Memory is obtained from sqlite3_malloc64() and must be freed by
|
||||
** the caller.
|
||||
*/
|
||||
static char *quoted_column(sqlite3_stmt *pStmt, int i){
|
||||
switch( sqlite3_column_type(pStmt, i) ){
|
||||
case SQLITE_NULL: {
|
||||
return sqlite3_mprintf("NULL");
|
||||
}
|
||||
case SQLITE_INTEGER:
|
||||
case SQLITE_FLOAT: {
|
||||
return sqlite3_mprintf("%s",sqlite3_column_text(pStmt,i));
|
||||
}
|
||||
case SQLITE_TEXT: {
|
||||
return sqlite3_mprintf("%Q",sqlite3_column_text(pStmt,i));
|
||||
}
|
||||
case SQLITE_BLOB: {
|
||||
int j;
|
||||
sqlite3_str *pStr = sqlite3_str_new(0);
|
||||
const unsigned char *a = sqlite3_column_blob(pStmt,i);
|
||||
int n = sqlite3_column_bytes(pStmt,i);
|
||||
sqlite3_str_append(pStr, "x'", 2);
|
||||
for(j=0; j<n; j++){
|
||||
sqlite3_str_appendf(pStr, "%02x", a[j]);
|
||||
}
|
||||
sqlite3_str_append(pStr, "'", 1);
|
||||
return sqlite3_str_finish(pStr);
|
||||
}
|
||||
}
|
||||
return 0; /* Not reached */
|
||||
}
|
||||
|
||||
/*
|
||||
** Run a prepared statement and output the result in one of the
|
||||
@@ -3184,12 +3321,19 @@ static void exec_prepared_stmt_columnar(
|
||||
int nColumn = 0;
|
||||
char **azData = 0;
|
||||
sqlite3_int64 nAlloc = 0;
|
||||
char *abRowDiv = 0;
|
||||
const unsigned char *uz;
|
||||
const char *z;
|
||||
char **azQuoted = 0;
|
||||
int rc;
|
||||
sqlite3_int64 i, nData;
|
||||
int j, nTotal, w, n;
|
||||
const char *colSep = 0;
|
||||
const char *rowSep = 0;
|
||||
const unsigned char **azNextLine = 0;
|
||||
int bNextLine = 0;
|
||||
int bMultiLineRowExists = 0;
|
||||
int bw = p->cmOpts.bWordWrap;
|
||||
|
||||
rc = sqlite3_step(pStmt);
|
||||
if( rc!=SQLITE_ROW ) return;
|
||||
@@ -3198,21 +3342,16 @@ static void exec_prepared_stmt_columnar(
|
||||
if( nAlloc<=0 ) nAlloc = 1;
|
||||
azData = sqlite3_malloc64( nAlloc*sizeof(char*) );
|
||||
shell_check_oom(azData);
|
||||
for(i=0; i<nColumn; i++){
|
||||
azData[i] = strdup(sqlite3_column_name(pStmt,i));
|
||||
azNextLine = sqlite3_malloc64( nColumn*sizeof(char*) );
|
||||
shell_check_oom((void*)azNextLine);
|
||||
memset((void*)azNextLine, 0, nColumn*sizeof(char*) );
|
||||
if( p->cmOpts.bQuote ){
|
||||
azQuoted = sqlite3_malloc64( nColumn*sizeof(char*) );
|
||||
shell_check_oom(azQuoted);
|
||||
memset(azQuoted, 0, nColumn*sizeof(char*) );
|
||||
}
|
||||
do{
|
||||
if( (nRow+2)*nColumn >= nAlloc ){
|
||||
nAlloc *= 2;
|
||||
azData = sqlite3_realloc64(azData, nAlloc*sizeof(char*));
|
||||
shell_check_oom(azData);
|
||||
}
|
||||
nRow++;
|
||||
for(i=0; i<nColumn; i++){
|
||||
z = (const char*)sqlite3_column_text(pStmt,i);
|
||||
azData[nRow*nColumn + i] = z ? strdup(z) : 0;
|
||||
}
|
||||
}while( sqlite3_step(pStmt)==SQLITE_ROW );
|
||||
abRowDiv = sqlite3_malloc64( nAlloc/nColumn );
|
||||
shell_check_oom(abRowDiv);
|
||||
if( nColumn>p->nWidth ){
|
||||
p->colWidth = realloc(p->colWidth, (nColumn+1)*2*sizeof(int));
|
||||
shell_check_oom(p->colWidth);
|
||||
@@ -3226,6 +3365,52 @@ static void exec_prepared_stmt_columnar(
|
||||
if( w<0 ) w = -w;
|
||||
p->actualWidth[i] = w;
|
||||
}
|
||||
for(i=0; i<nColumn; i++){
|
||||
const unsigned char *zNotUsed;
|
||||
int wx = p->colWidth[i];
|
||||
if( wx==0 ){
|
||||
wx = p->cmOpts.iWrap;
|
||||
}
|
||||
if( wx<0 ) wx = -wx;
|
||||
uz = (const unsigned char*)sqlite3_column_name(pStmt,i);
|
||||
azData[i] = translateForDisplayAndDup(uz, &zNotUsed, wx, bw);
|
||||
}
|
||||
do{
|
||||
int useNextLine = bNextLine;
|
||||
bNextLine = 0;
|
||||
if( (nRow+2)*nColumn >= nAlloc ){
|
||||
nAlloc *= 2;
|
||||
azData = sqlite3_realloc64(azData, nAlloc*sizeof(char*));
|
||||
shell_check_oom(azData);
|
||||
abRowDiv = sqlite3_realloc64(abRowDiv, nAlloc/nColumn);
|
||||
shell_check_oom(abRowDiv);
|
||||
}
|
||||
abRowDiv[nRow] = 1;
|
||||
nRow++;
|
||||
for(i=0; i<nColumn; i++){
|
||||
int wx = p->colWidth[i];
|
||||
if( wx==0 ){
|
||||
wx = p->cmOpts.iWrap;
|
||||
}
|
||||
if( wx<0 ) wx = -wx;
|
||||
if( useNextLine ){
|
||||
uz = azNextLine[i];
|
||||
}else if( p->cmOpts.bQuote ){
|
||||
sqlite3_free(azQuoted[i]);
|
||||
azQuoted[i] = quoted_column(pStmt,i);
|
||||
uz = (const unsigned char*)azQuoted[i];
|
||||
}else{
|
||||
uz = (const unsigned char*)sqlite3_column_text(pStmt,i);
|
||||
}
|
||||
azData[nRow*nColumn + i]
|
||||
= translateForDisplayAndDup(uz, &azNextLine[i], wx, bw);
|
||||
if( azNextLine[i] ){
|
||||
bNextLine = 1;
|
||||
abRowDiv[nRow-1] = 0;
|
||||
bMultiLineRowExists = 1;
|
||||
}
|
||||
}
|
||||
}while( bNextLine || sqlite3_step(pStmt)==SQLITE_ROW );
|
||||
nTotal = nColumn*(nRow+1);
|
||||
for(i=0; i<nTotal; i++){
|
||||
z = azData[i];
|
||||
@@ -3308,6 +3493,15 @@ static void exec_prepared_stmt_columnar(
|
||||
utf8_width_print(p->out, w, z);
|
||||
if( j==nColumn-1 ){
|
||||
utf8_printf(p->out, "%s", rowSep);
|
||||
if( bMultiLineRowExists && abRowDiv[i/nColumn-1] && i+1<nTotal ){
|
||||
if( p->cMode==MODE_Table ){
|
||||
print_row_separator(p, nColumn, "+");
|
||||
}else if( p->cMode==MODE_Box ){
|
||||
print_box_row_separator(p, nColumn, BOX_123, BOX_1234, BOX_134);
|
||||
}else if( p->cMode==MODE_Column ){
|
||||
raw_printf(p->out, "\n");
|
||||
}
|
||||
}
|
||||
j = -1;
|
||||
if( seenInterrupt ) goto columnar_end;
|
||||
}else{
|
||||
@@ -3326,6 +3520,12 @@ columnar_end:
|
||||
nData = (nRow+1)*nColumn;
|
||||
for(i=0; i<nData; i++) free(azData[i]);
|
||||
sqlite3_free(azData);
|
||||
sqlite3_free((void*)azNextLine);
|
||||
sqlite3_free(abRowDiv);
|
||||
if( azQuoted ){
|
||||
for(i=0; i<nColumn; i++) sqlite3_free(azQuoted[i]);
|
||||
sqlite3_free(azQuoted);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -4103,7 +4303,7 @@ static const char *(azHelp[]) = {
|
||||
".load FILE ?ENTRY? Load an extension library",
|
||||
#endif
|
||||
".log FILE|off Turn logging on or off. FILE can be stderr/stdout",
|
||||
".mode MODE ?TABLE? Set output mode",
|
||||
".mode MODE ?OPTIONS? Set output mode",
|
||||
" MODE is one of:",
|
||||
" ascii Columns/rows delimited by 0x1F and 0x1E",
|
||||
" box Tables using unicode box-drawing characters",
|
||||
@@ -4115,11 +4315,19 @@ static const char *(azHelp[]) = {
|
||||
" line One value per line",
|
||||
" list Values delimited by \"|\"",
|
||||
" markdown Markdown table format",
|
||||
" qbox Shorthand for \"box --width 60 --quote\"",
|
||||
" quote Escape answers as for SQL",
|
||||
" table ASCII-art table",
|
||||
" tabs Tab-separated values",
|
||||
" tcl TCL list elements",
|
||||
".nonce STRING Disable safe mode for one command if the nonce matches",
|
||||
" OPTIONS: (for columnar modes or insert mode):",
|
||||
" --wrap N Wrap output lines to no longer than N characters",
|
||||
" --wordwrap B Wrap or not at word boundaries per B (on/off)",
|
||||
" --ww Shorthand for \"--wordwrap 1\"",
|
||||
" --quote Quote output text as SQL literals",
|
||||
" --noquote Do not quote output text",
|
||||
" TABLE The name of SQL table used for \"insert\" mode",
|
||||
".nonce STRING Suspend safe mode for one command if nonce matches",
|
||||
".nullvalue STRING Use STRING in place of NULL values",
|
||||
".once ?OPTIONS? ?FILE? Output for the next SQL command only to FILE",
|
||||
" If FILE begins with '|' then open as a pipe",
|
||||
@@ -8899,64 +9107,123 @@ static int do_meta_command(char *zLine, ShellState *p){
|
||||
}else
|
||||
|
||||
if( c=='m' && strncmp(azArg[0], "mode", n)==0 ){
|
||||
const char *zMode = nArg>=2 ? azArg[1] : "";
|
||||
int n2 = strlen30(zMode);
|
||||
int c2 = zMode[0];
|
||||
if( c2=='l' && n2>2 && strncmp(azArg[1],"lines",n2)==0 ){
|
||||
const char *zMode = 0;
|
||||
const char *zTabname = 0;
|
||||
int i, n2;
|
||||
ColModeOpts cmOpts = ColModeOpts_default;
|
||||
for(i=1; i<nArg; i++){
|
||||
const char *z = azArg[i];
|
||||
if( optionMatch(z,"wrap") && i+1<nArg ){
|
||||
cmOpts.iWrap = integerValue(azArg[++i]);
|
||||
}else if( optionMatch(z,"ww") ){
|
||||
cmOpts.bWordWrap = 1;
|
||||
}else if( optionMatch(z,"wordwrap") && i+1<nArg ){
|
||||
cmOpts.bWordWrap = (u8)booleanValue(azArg[++i]);
|
||||
}else if( optionMatch(z,"quote") ){
|
||||
cmOpts.bQuote = 1;
|
||||
}else if( optionMatch(z,"noquote") ){
|
||||
cmOpts.bQuote = 0;
|
||||
}else if( zMode==0 ){
|
||||
zMode = z;
|
||||
/* Apply defaults for qbox pseudo-mods. If that
|
||||
* overwrites already-set values, user was informed of this.
|
||||
*/
|
||||
if( strcmp(z, "qbox")==0 ){
|
||||
ColModeOpts cmo = ColModeOpts_default_qbox;
|
||||
zMode = "box";
|
||||
cmOpts = cmo;
|
||||
}
|
||||
}else if( zTabname==0 ){
|
||||
zTabname = z;
|
||||
}else if( z[0]=='-' ){
|
||||
utf8_printf(stderr, "unknown option: %s\n", z);
|
||||
utf8_printf(stderr, "options:\n"
|
||||
" --noquote\n"
|
||||
" --quote\n"
|
||||
" --wordwrap on/off\n"
|
||||
" --wrap N\n"
|
||||
" --ww\n");
|
||||
rc = 1;
|
||||
goto meta_command_exit;
|
||||
}else{
|
||||
utf8_printf(stderr, "extra argument: \"%s\"\n", z);
|
||||
rc = 1;
|
||||
goto meta_command_exit;
|
||||
}
|
||||
}
|
||||
if( zMode==0 ){
|
||||
if( p->mode==MODE_Column
|
||||
|| (p->mode>=MODE_Markdown && p->mode<=MODE_Box)
|
||||
){
|
||||
raw_printf
|
||||
(p->out,
|
||||
"current output mode: %s --wrap %d --wordwrap %s --%squote\n",
|
||||
modeDescr[p->mode], p->cmOpts.iWrap,
|
||||
p->cmOpts.bWordWrap ? "on" : "off",
|
||||
p->cmOpts.bQuote ? "" : "no");
|
||||
}else{
|
||||
raw_printf(p->out, "current output mode: %s\n", modeDescr[p->mode]);
|
||||
}
|
||||
zMode = modeDescr[p->mode];
|
||||
}
|
||||
n2 = strlen30(zMode);
|
||||
if( strncmp(zMode,"lines",n2)==0 ){
|
||||
p->mode = MODE_Line;
|
||||
sqlite3_snprintf(sizeof(p->rowSeparator), p->rowSeparator, SEP_Row);
|
||||
}else if( c2=='c' && strncmp(azArg[1],"columns",n2)==0 ){
|
||||
}else if( strncmp(zMode,"columns",n2)==0 ){
|
||||
p->mode = MODE_Column;
|
||||
if( (p->shellFlgs & SHFLG_HeaderSet)==0 ){
|
||||
p->showHeader = 1;
|
||||
}
|
||||
sqlite3_snprintf(sizeof(p->rowSeparator), p->rowSeparator, SEP_Row);
|
||||
}else if( c2=='l' && n2>2 && strncmp(azArg[1],"list",n2)==0 ){
|
||||
p->cmOpts = cmOpts;
|
||||
}else if( strncmp(zMode,"list",n2)==0 ){
|
||||
p->mode = MODE_List;
|
||||
sqlite3_snprintf(sizeof(p->colSeparator), p->colSeparator, SEP_Column);
|
||||
sqlite3_snprintf(sizeof(p->rowSeparator), p->rowSeparator, SEP_Row);
|
||||
}else if( c2=='h' && strncmp(azArg[1],"html",n2)==0 ){
|
||||
}else if( strncmp(zMode,"html",n2)==0 ){
|
||||
p->mode = MODE_Html;
|
||||
}else if( c2=='t' && strncmp(azArg[1],"tcl",n2)==0 ){
|
||||
}else if( strncmp(zMode,"tcl",n2)==0 ){
|
||||
p->mode = MODE_Tcl;
|
||||
sqlite3_snprintf(sizeof(p->colSeparator), p->colSeparator, SEP_Space);
|
||||
sqlite3_snprintf(sizeof(p->rowSeparator), p->rowSeparator, SEP_Row);
|
||||
}else if( c2=='c' && strncmp(azArg[1],"csv",n2)==0 ){
|
||||
}else if( strncmp(zMode,"csv",n2)==0 ){
|
||||
p->mode = MODE_Csv;
|
||||
sqlite3_snprintf(sizeof(p->colSeparator), p->colSeparator, SEP_Comma);
|
||||
sqlite3_snprintf(sizeof(p->rowSeparator), p->rowSeparator, SEP_CrLf);
|
||||
}else if( c2=='t' && strncmp(azArg[1],"tabs",n2)==0 ){
|
||||
}else if( strncmp(zMode,"tabs",n2)==0 ){
|
||||
p->mode = MODE_List;
|
||||
sqlite3_snprintf(sizeof(p->colSeparator), p->colSeparator, SEP_Tab);
|
||||
}else if( c2=='i' && strncmp(azArg[1],"insert",n2)==0 ){
|
||||
}else if( strncmp(zMode,"insert",n2)==0 ){
|
||||
p->mode = MODE_Insert;
|
||||
set_table_name(p, nArg>=3 ? azArg[2] : "table");
|
||||
}else if( c2=='q' && strncmp(azArg[1],"quote",n2)==0 ){
|
||||
set_table_name(p, zTabname ? zTabname : "table");
|
||||
}else if( strncmp(zMode,"quote",n2)==0 ){
|
||||
p->mode = MODE_Quote;
|
||||
sqlite3_snprintf(sizeof(p->colSeparator), p->colSeparator, SEP_Comma);
|
||||
sqlite3_snprintf(sizeof(p->rowSeparator), p->rowSeparator, SEP_Row);
|
||||
}else if( c2=='a' && strncmp(azArg[1],"ascii",n2)==0 ){
|
||||
}else if( strncmp(zMode,"ascii",n2)==0 ){
|
||||
p->mode = MODE_Ascii;
|
||||
sqlite3_snprintf(sizeof(p->colSeparator), p->colSeparator, SEP_Unit);
|
||||
sqlite3_snprintf(sizeof(p->rowSeparator), p->rowSeparator, SEP_Record);
|
||||
}else if( c2=='m' && strncmp(azArg[1],"markdown",n2)==0 ){
|
||||
}else if( strncmp(zMode,"markdown",n2)==0 ){
|
||||
p->mode = MODE_Markdown;
|
||||
}else if( c2=='t' && strncmp(azArg[1],"table",n2)==0 ){
|
||||
p->cmOpts = cmOpts;
|
||||
}else if( strncmp(zMode,"table",n2)==0 ){
|
||||
p->mode = MODE_Table;
|
||||
}else if( c2=='b' && strncmp(azArg[1],"box",n2)==0 ){
|
||||
p->cmOpts = cmOpts;
|
||||
}else if( strncmp(zMode,"box",n2)==0 ){
|
||||
p->mode = MODE_Box;
|
||||
}else if( c2=='c' && strncmp(azArg[1],"count",n2)==0 ){
|
||||
p->cmOpts = cmOpts;
|
||||
}else if( strncmp(zMode,"count",n2)==0 ){
|
||||
p->mode = MODE_Count;
|
||||
}else if( c2=='o' && strncmp(azArg[1],"off",n2)==0 ){
|
||||
}else if( strncmp(zMode,"off",n2)==0 ){
|
||||
p->mode = MODE_Off;
|
||||
}else if( c2=='j' && strncmp(azArg[1],"json",n2)==0 ){
|
||||
}else if( strncmp(zMode,"json",n2)==0 ){
|
||||
p->mode = MODE_Json;
|
||||
}else if( nArg==1 ){
|
||||
raw_printf(p->out, "current output mode: %s\n", modeDescr[p->mode]);
|
||||
}else{
|
||||
raw_printf(stderr, "Error: mode should be one of: "
|
||||
"ascii box column csv html insert json line list markdown "
|
||||
"quote table tabs tcl\n");
|
||||
"qbox quote table tabs tcl\n");
|
||||
rc = 1;
|
||||
}
|
||||
p->cMode = p->mode;
|
||||
@@ -10104,7 +10371,17 @@ static int do_meta_command(char *zLine, ShellState *p){
|
||||
utf8_printf(p->out, "%12.12s: %s\n","explain",
|
||||
p->mode==MODE_Explain ? "on" : p->autoExplain ? "auto" : "off");
|
||||
utf8_printf(p->out,"%12.12s: %s\n","headers", azBool[p->showHeader!=0]);
|
||||
if( p->mode==MODE_Column
|
||||
|| (p->mode>=MODE_Markdown && p->mode<=MODE_Box)
|
||||
){
|
||||
utf8_printf
|
||||
(p->out, "%12.12s: %s --wrap %d --wordwrap %s --%squote\n", "mode",
|
||||
modeDescr[p->mode], p->cmOpts.iWrap,
|
||||
p->cmOpts.bWordWrap ? "on" : "off",
|
||||
p->cmOpts.bQuote ? "" : "no");
|
||||
}else{
|
||||
utf8_printf(p->out, "%12.12s: %s\n","mode", modeDescr[p->mode]);
|
||||
}
|
||||
utf8_printf(p->out, "%12.12s: ", "nullvalue");
|
||||
output_c_string(p->out, p->nullValue);
|
||||
raw_printf(p->out, "\n");
|
||||
|
213
src/sqlite.h.in
213
src/sqlite.h.in
@@ -4352,6 +4352,8 @@ int sqlite3_stmt_busy(sqlite3_stmt*);
|
||||
**
|
||||
** ^The sqlite3_value objects that are passed as parameters into the
|
||||
** implementation of [application-defined SQL functions] are protected.
|
||||
** ^The sqlite3_value objects returned by [sqlite3_vtab_rhs_value()]
|
||||
** are protected.
|
||||
** ^The sqlite3_value object returned by
|
||||
** [sqlite3_column_value()] is unprotected.
|
||||
** Unprotected sqlite3_value objects may only be used as arguments
|
||||
@@ -7131,8 +7133,38 @@ struct sqlite3_index_info {
|
||||
**
|
||||
** These macros define the allowed values for the
|
||||
** [sqlite3_index_info].aConstraint[].op field. Each value represents
|
||||
** an operator that is part of a constraint term in the wHERE clause of
|
||||
** an operator that is part of a constraint term in the WHERE clause of
|
||||
** a query that uses a [virtual table].
|
||||
**
|
||||
** ^The left-hand operand of the operator is given by the corresponding
|
||||
** aConstraint[].iColumn field. ^An iColumn of -1 indicates the left-hand
|
||||
** operand is the rowid.
|
||||
** The SQLITE_INDEX_CONSTRAINT_LIMIT and SQLITE_INDEX_CONSTRAINT_OFFSET
|
||||
** operators have no left-hand operand, and so for those operators the
|
||||
** corresponding aConstraint[].iColumn is meaningless and should not be
|
||||
** used.
|
||||
**
|
||||
** All operator values from SQLITE_INDEX_CONSTRAINT_FUNCTION through
|
||||
** value 255 are reserved to represent functions that are overloaded
|
||||
** by the [xFindFunction|xFindFunction method] of the virtual table
|
||||
** implementation.
|
||||
**
|
||||
** The right-hand operands for each constraint might be accessible using
|
||||
** the [sqlite3_vtab_rhs_value()] interface. Usually the right-hand
|
||||
** operand is only available if it appears as a single constant literal
|
||||
** in the input SQL. If the right-hand operand is another column or an
|
||||
** expression (even a constant expression) or a parameter, then the
|
||||
** sqlite3_vtab_rhs_value() probably will not be able to extract it.
|
||||
** ^The SQLITE_INDEX_CONSTRAINT_ISNULL and
|
||||
** SQLITE_INDEX_CONSTRAINT_ISNOTNULL operators have no right-hand operand
|
||||
** and hence calls to sqlite3_vtab_rhs_value() for those operators will
|
||||
** always return SQLITE_NOTFOUND.
|
||||
**
|
||||
** The collating sequence to be used for comparison can be found using
|
||||
** the [sqlite3_vtab_collation()] interface. For most real-world virtual
|
||||
** tables, the collating sequence of constraints does not matter (for example
|
||||
** because the constraints are numeric) and so the sqlite3_vtab_collation()
|
||||
** interface is no commonly needed.
|
||||
*/
|
||||
#define SQLITE_INDEX_CONSTRAINT_EQ 2
|
||||
#define SQLITE_INDEX_CONSTRAINT_GT 4
|
||||
@@ -7148,6 +7180,8 @@ struct sqlite3_index_info {
|
||||
#define SQLITE_INDEX_CONSTRAINT_ISNOTNULL 70
|
||||
#define SQLITE_INDEX_CONSTRAINT_ISNULL 71
|
||||
#define SQLITE_INDEX_CONSTRAINT_IS 72
|
||||
#define SQLITE_INDEX_CONSTRAINT_LIMIT 73
|
||||
#define SQLITE_INDEX_CONSTRAINT_OFFSET 74
|
||||
#define SQLITE_INDEX_CONSTRAINT_FUNCTION 150
|
||||
|
||||
/*
|
||||
@@ -9508,15 +9542,16 @@ SQLITE_EXPERIMENTAL const char *sqlite3_vtab_collation(sqlite3_index_info*,int);
|
||||
** CAPI3REF: Determine if a virtual table query is DISTINCT
|
||||
** METHOD: sqlite3_index_info
|
||||
**
|
||||
** This API may only be used from within an xBestIndex() callback. The
|
||||
** results of calling it from outside of an xBestIndex() callback are
|
||||
** undefined and probably harmful.
|
||||
** This API may only be used from within an [xBestIndex|xBestIndex method]
|
||||
** of a [virtual table] implementation. The result of calling this
|
||||
** interface from outside of xBestIndex() is undefined and probably harmful.
|
||||
**
|
||||
** ^The sqlite3_vtab_distinct() returns an integer that is either 0, 1, or
|
||||
** 2. The integer returned by sqlite3_vtab_distinct() gives the virtual table
|
||||
** additional information about how the query planner wants the output to be
|
||||
** ordered. As long as the virtual table can meet the ordering requirements
|
||||
** of the query planner, it may set the "orderByConsumed" flag.
|
||||
** ^The sqlite3_vtab_distinct() interface returns an integer that is
|
||||
** either 0, 1, or 2. The integer returned by sqlite3_vtab_distinct()
|
||||
** gives the virtual table additional information about how the query
|
||||
** planner wants the output to be ordered. As long as the virtual table
|
||||
** can meet the ordering requirements of the query planner, it may set
|
||||
** the "orderByConsumed" flag.
|
||||
**
|
||||
** <ol><li value="0"><p>
|
||||
** ^If the sqlite3_vtab_distinct() interface returns 0, that means
|
||||
@@ -9525,7 +9560,7 @@ SQLITE_EXPERIMENTAL const char *sqlite3_vtab_collation(sqlite3_index_info*,int);
|
||||
** [sqlite3_index_info] object. This is the default expectation. If the
|
||||
** virtual table outputs all rows in sorted order, then it is always safe for
|
||||
** the xBestIndex method to set the "orderByConsumed" flag, regardless of
|
||||
** what the return value from sqlite3_vtab_distinct().
|
||||
** the return value from sqlite3_vtab_distinct().
|
||||
** <li value="1"><p>
|
||||
** ^(If the sqlite3_vtab_distinct() interface returns 1, that means
|
||||
** that the query planner does not need the rows to be returned in sorted order
|
||||
@@ -9538,7 +9573,7 @@ SQLITE_EXPERIMENTAL const char *sqlite3_vtab_collation(sqlite3_index_info*,int);
|
||||
** order, as long as rows with the same values in all "aOrderBy" columns
|
||||
** are adjacent.)^ ^(Furthermore, only a single row for each particular
|
||||
** combination of values in the columns identified by the "aOrderBy" field
|
||||
** needs to be returned.)^ ^It is ok always for two or more rows with the same
|
||||
** needs to be returned.)^ ^It is always ok for two or more rows with the same
|
||||
** values in all "aOrderBy" columns to be returned, as long as all such rows
|
||||
** are adjacent. ^The virtual table may, if it chooses, omit extra rows
|
||||
** that have the same value for all columns identified by "aOrderBy".
|
||||
@@ -9569,31 +9604,167 @@ SQLITE_EXPERIMENTAL const char *sqlite3_vtab_collation(sqlite3_index_info*,int);
|
||||
*/
|
||||
int sqlite3_vtab_distinct(sqlite3_index_info*);
|
||||
|
||||
/*
|
||||
** CAPI3REF: Identify and handle IN constraints in xBestIndex
|
||||
**
|
||||
** This interface may only be used from within an
|
||||
** [xBestIndex|xBestIndex() method] of a [virtual table] implementation.
|
||||
** The result of invoking this interface from any other context is
|
||||
** undefined and probably harmful.
|
||||
**
|
||||
** ^(A constraint on a virtual table of the form
|
||||
** "[IN operator|column IN (...)]" is
|
||||
** communicated to the xBestIndex method as a
|
||||
** [SQLITE_INDEX_CONSTRAINT_EQ] constraint.)^ If xBestIndex wants to use
|
||||
** this constraint, it must set the corresponding
|
||||
** aConstraintUsage[].argvIndex to a postive integer. ^(Then, under
|
||||
** the usual mode of handling IN operators, SQLite generates [bytecode]
|
||||
** that invokes the [xFilter|xFilter() method] once for each value
|
||||
** on the right-hand side of the IN operator.)^ Thus the virtual table
|
||||
** only sees a single value from the right-hand side of the IN operator
|
||||
** at a time.
|
||||
**
|
||||
** In some cases, however, it would be advantageous for the virtual
|
||||
** table to see all values on the right-hand of the IN operator all at
|
||||
** once. The sqlite3_vtab_in() interfaces facilitates this in two ways:
|
||||
**
|
||||
** <ol>
|
||||
** <li><p>
|
||||
** ^A call to sqlite3_vtab_in(P,N,-1) will return true (non-zero)
|
||||
** if and only if the [sqlite3_index_info|P->aConstraint][N] constraint
|
||||
** is an [IN operator] that can be processed all at once. ^In other words,
|
||||
** sqlite3_vtab_in() with -1 in the third argument is a mechanism
|
||||
** by which the virtual table can ask SQLite if all-at-once processing
|
||||
** of the IN operator is even possible.
|
||||
**
|
||||
** <li><p>
|
||||
** ^A call to sqlite3_vtab_in(P,N,F) with F==1 or F==0 indicates
|
||||
** to SQLite that the virtual table does or does not want to process
|
||||
** the IN operator all-at-once, respectively. ^Thus when the third
|
||||
** parameter (F) is non-negative, this interface is the mechanism by
|
||||
** which the virtual table tells SQLite how it wants to process in
|
||||
** IN operator.
|
||||
** </ol>
|
||||
**
|
||||
** ^The sqlite3_vtab_in(P,N,F) interface can be invoked multiple times
|
||||
** within the same xBestIndex method call. ^For any given P,N pair,
|
||||
** the return value from sqlite3_vtab_in(P,N,F) will always be the same
|
||||
** within the same xBestIndex call. ^If the interface returns true
|
||||
** (non-zero), that means that the constraint is an IN operator
|
||||
** that can be processed all-at-once. ^If the constraint is not an IN
|
||||
** operator or cannot be processed all-at-once, then the interface returns
|
||||
** false.
|
||||
**
|
||||
** ^(All-at-once processing of the IN operator is selected if both of the
|
||||
** following conditions are met:
|
||||
**
|
||||
** <ol>
|
||||
** <li><p> The P->aConstraintUsage[N].argvIndex value is set to a positive
|
||||
** integer. This is how the virtual table tells SQLite that it wants to
|
||||
** use the N-th constraint.
|
||||
**
|
||||
** <li><p> The last call to sqlite3_vtab_in(P,N,F) for which F was
|
||||
** non-negative had F>=1.
|
||||
** </ol>)^
|
||||
**
|
||||
** ^If either or both of the conditions above are false, then SQLite uses
|
||||
** the traditional one-at-a-time processing strategy for IN constraint.
|
||||
** ^If both conditions are true, then the argvIndex-th parameter to the
|
||||
** xFilter method will be an [sqlite3_value] that appears to be NULL,
|
||||
** but which can be passed to [sqlite3_vtab_in_first()] and
|
||||
** [sqlite3_vtab_in_next()] to find all values on the right-hand side
|
||||
** of the IN constraint.
|
||||
*/
|
||||
int sqlite3_vtab_in(sqlite3_index_info*, int iCons, int bHandle);
|
||||
|
||||
/*
|
||||
** CAPI3REF: Find all elements on the right-hand side of an IN constraint.
|
||||
**
|
||||
** These interfaces are only useful from within the
|
||||
** [xFilter|xFilter() method] of a [virtual table] implementation.
|
||||
** The result of invoking these interfaces from any other context
|
||||
** is undefined and probably harmful.
|
||||
**
|
||||
** The X parameter in a call to sqlite3_vtab_in_first(X,P) or
|
||||
** sqlite3_vtab_in_next(X,P) must be one of the parameters to the
|
||||
** xFilter method which invokes these routines, and specifically
|
||||
** a parameter that was previously selected for all-at-once IN constraint
|
||||
** processing use the [sqlite3_vtab_in()] interface in the
|
||||
** [xBestIndex|xBestIndex method]. ^(If the X parameter is not
|
||||
** an xFilter argument that was selected for all-at-once IN constraint
|
||||
** processing, then these routines return [SQLITE_MISUSE])^ or perhaps
|
||||
** exhibit some other undefined or harmful behavior.
|
||||
**
|
||||
** ^(Use these routines to access all values on the right-hand side
|
||||
** of the IN constraint using code like the following:
|
||||
**
|
||||
** <blockquote><pre>
|
||||
** for(rc=sqlite3_vtab_in_first(pList, &pVal);
|
||||
** rc==SQLITE_OK && pVal
|
||||
** rc=sqlite3_vtab_in_next(pList, &pVal)
|
||||
** ){
|
||||
** // do something with pVal
|
||||
** }
|
||||
** if( rc!=SQLITE_OK ){
|
||||
** // an error has occurred
|
||||
** }
|
||||
** </pre></blockquote>)^
|
||||
**
|
||||
** ^On success, the sqlite3_vtab_in_first(X,P) and sqlite3_vtab_in_next(X,P)
|
||||
** routines return SQLITE_OK and set *P to point to the first or next value
|
||||
** on the RHS of the IN constraint. ^If there are no more values on the
|
||||
** right hand side of the IN constraint, then *P is set to NULL and these
|
||||
** routines return [SQLITE_DONE]. ^The return value might be
|
||||
** some other value, such as SQLITE_NOMEM, in the event of a malfunction.
|
||||
**
|
||||
** The *ppOut values returned by these routines are only valid until the
|
||||
** next call to either of these routines or until the end of the xFilter
|
||||
** method from which these routines were called. If the virtual table
|
||||
** implementation needs to retain the *ppOut values for longer, it must make
|
||||
** copies. The *ppOut values are [protected sqlite3_value|protected].
|
||||
*/
|
||||
int sqlite3_vtab_in_first(sqlite3_value *pVal, sqlite3_value **ppOut);
|
||||
int sqlite3_vtab_in_next(sqlite3_value *pVal, sqlite3_value **ppOut);
|
||||
|
||||
/*
|
||||
** CAPI3REF: Constraint values in xBestIndex()
|
||||
** METHOD: sqlite3_index_info
|
||||
**
|
||||
** This API may only be used from within an xBestIndex() callback. The
|
||||
** results of calling it from outside of an xBestIndex() callback are
|
||||
** undefined and probably harmful.
|
||||
** This API may only be used from within the [xBestIndex|xBestIndex method]
|
||||
** of a [virtual table] implementation. The result of calling this interface
|
||||
** from outside of an xBestIndex method are undefined and probably harmful.
|
||||
**
|
||||
** ^When the sqlite3_vtab_rhs_value(P,J,V) interface is invoked from within
|
||||
** the [xBestIndex] method of a [virtual table] implementation, with P being
|
||||
** a copy of the [sqlite3_index_info] object pointer passed into xBestIndex and
|
||||
** J being a 0-based index into P->aConstraint[], then this routine
|
||||
** attempts to set *V to be the value on the right-hand side of
|
||||
** that constraint if the right-hand side is a known constant. ^If the
|
||||
** right-hand side of the constraint is not known, then *V is set to a NULL
|
||||
** pointer. ^The sqlite3_vtab_rhs_value(P,J,V) interface returns SQLITE_OK if
|
||||
** attempts to set *V to the value of the right-hand operand of
|
||||
** that constraint if the right-hand operand is known. ^If the
|
||||
** right-hand operand is not known, then *V is set to a NULL pointer.
|
||||
** ^The sqlite3_vtab_rhs_value(P,J,V) interface returns SQLITE_OK if
|
||||
** and only if *V is set to a value. ^The sqlite3_vtab_rhs_value(P,J,V)
|
||||
** inteface returns SQLITE_NOTFOUND if the right-hand side of the J-th
|
||||
** constraint is not available. ^The sqlite3_vtab_rhs_value() interface
|
||||
** can return an result code other than SQLITE_OK or SQLITE_NOTFOUND if
|
||||
** something goes wrong.
|
||||
**
|
||||
** ^The sqlite3_value object returned in *V remains valid for the duration of
|
||||
** the xBestIndex method code. ^When xBestIndex returns, the sqlite3_value
|
||||
** object returned by sqlite3_vtab_rhs_value() is automatically deallocated.
|
||||
** The sqlite3_vtab_rhs_value() interface is usually only successful if
|
||||
** the right-hand operand of a constraint is a literal value in the original
|
||||
** SQL statement. If the right-hand operand is an expression or a reference
|
||||
** to some other column or a [host parameter], then sqlite3_vtab_rhs_value()
|
||||
** will probably return [SQLITE_NOTFOUND].
|
||||
**
|
||||
** ^(Some constraints, such as [SQLITE_INDEX_CONSTRAINT_ISNULL] and
|
||||
** [SQLITE_INDEX_CONSTRAINT_ISNOTNULL], have no right-hand operand. For such
|
||||
** constraints, sqlite3_vtab_rhs_value() always returns SQLITE_NOTFOUND.)^
|
||||
**
|
||||
** ^The [sqlite3_value] object returned in *V is a protected sqlite3_value
|
||||
** and remains valid for the duration of the xBestIndex method call.
|
||||
** ^When xBestIndex returns, the sqlite3_value object returned by
|
||||
** sqlite3_vtab_rhs_value() is automatically deallocated.
|
||||
**
|
||||
** The "_rhs_" in the name of this routine is an appreviation for
|
||||
** "Right-Hand Side".
|
||||
*/
|
||||
int sqlite3_vtab_rhs_value(sqlite3_index_info*, int, sqlite3_value **ppVal);
|
||||
|
||||
|
@@ -348,6 +348,9 @@ struct sqlite3_api_routines {
|
||||
int (*error_offset)(sqlite3*);
|
||||
int (*vtab_rhs_value)(sqlite3_index_info*,int,sqlite3_value**);
|
||||
int (*vtab_distinct)(sqlite3_index_info*);
|
||||
int (*vtab_in)(sqlite3_index_info*,int,int);
|
||||
int (*vtab_in_first)(sqlite3_value*,sqlite3_value**);
|
||||
int (*vtab_in_next)(sqlite3_value*,sqlite3_value**);
|
||||
};
|
||||
|
||||
/*
|
||||
@@ -663,6 +666,9 @@ typedef int (*sqlite3_loadext_entry)(
|
||||
#define sqlite3_error_offset sqlite3_api->error_offset
|
||||
#define sqlite3_vtab_rhs_value sqlite3_api->vtab_rhs_value
|
||||
#define sqlite3_vtab_distinct sqlite3_api->vtab_distinct
|
||||
#define sqlite3_vtab_in sqlite3_api->vtab_in
|
||||
#define sqlite3_vtab_in_first sqlite3_api->vtab_in_first
|
||||
#define sqlite3_vtab_in_next sqlite3_api->vtab_in_next
|
||||
#endif /* !defined(SQLITE_CORE) && !defined(SQLITE_OMIT_LOAD_EXTENSION) */
|
||||
|
||||
#if !defined(SQLITE_CORE) && !defined(SQLITE_OMIT_LOAD_EXTENSION)
|
||||
|
@@ -1235,6 +1235,7 @@ typedef struct With With;
|
||||
#define MASKBIT(n) (((Bitmask)1)<<(n))
|
||||
#define MASKBIT64(n) (((u64)1)<<(n))
|
||||
#define MASKBIT32(n) (((unsigned int)1)<<(n))
|
||||
#define SMASKBIT32(n) ((n)<=31?((unsigned int)1)<<(n):0)
|
||||
#define ALLBITS ((Bitmask)-1)
|
||||
|
||||
/* A VList object records a mapping between parameters/variables/wildcards
|
||||
@@ -4598,7 +4599,8 @@ void sqlite3CodeChangeCount(Vdbe*,int,const char*);
|
||||
void sqlite3DeleteFrom(Parse*, SrcList*, Expr*, ExprList*, Expr*);
|
||||
void sqlite3Update(Parse*, SrcList*, ExprList*,Expr*,int,ExprList*,Expr*,
|
||||
Upsert*);
|
||||
WhereInfo *sqlite3WhereBegin(Parse*,SrcList*,Expr*,ExprList*,ExprList*,u16,int);
|
||||
WhereInfo *sqlite3WhereBegin(Parse*,SrcList*,Expr*,ExprList*,
|
||||
ExprList*,Select*,u16,int);
|
||||
void sqlite3WhereEnd(WhereInfo*);
|
||||
LogEst sqlite3WhereOutputRowCount(WhereInfo*);
|
||||
int sqlite3WhereIsDistinct(WhereInfo*);
|
||||
|
@@ -299,7 +299,21 @@ static int tclFilter(
|
||||
const char *zVal = (const char*)sqlite3_value_text(argv[ii]);
|
||||
Tcl_Obj *pVal;
|
||||
if( zVal==0 ){
|
||||
sqlite3_value *pMem;
|
||||
pVal = Tcl_NewObj();
|
||||
for(rc=sqlite3_vtab_in_first(argv[ii], &pMem);
|
||||
rc==SQLITE_OK && pMem;
|
||||
rc=sqlite3_vtab_in_next(argv[ii], &pMem)
|
||||
){
|
||||
Tcl_Obj *pVal2 = 0;
|
||||
zVal = (const char*)sqlite3_value_text(pMem);
|
||||
if( zVal ){
|
||||
pVal2 = Tcl_NewStringObj(zVal, -1);
|
||||
}else{
|
||||
pVal2 = Tcl_NewObj();
|
||||
}
|
||||
Tcl_ListObjAppendElement(interp, pVal, pVal2);
|
||||
}
|
||||
}else{
|
||||
pVal = Tcl_NewStringObj(zVal, -1);
|
||||
}
|
||||
@@ -374,20 +388,13 @@ static int tclEof(sqlite3_vtab_cursor *pVtabCursor){
|
||||
return (pCsr->pStmt==0);
|
||||
}
|
||||
|
||||
static int tclBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){
|
||||
tcl_vtab *pTab = (tcl_vtab*)tab;
|
||||
Tcl_Interp *interp = pTab->interp;
|
||||
Tcl_Obj *pArg;
|
||||
Tcl_Obj *pScript;
|
||||
static void testBestIndexObjConstraints(
|
||||
Tcl_Interp *interp,
|
||||
sqlite3_index_info *pIdxInfo
|
||||
){
|
||||
int ii;
|
||||
int rc = SQLITE_OK;
|
||||
|
||||
pScript = Tcl_DuplicateObj(pTab->pCmd);
|
||||
Tcl_IncrRefCount(pScript);
|
||||
Tcl_ListObjAppendElement(interp, pScript, Tcl_NewStringObj("xBestIndex", -1));
|
||||
|
||||
pArg = Tcl_NewObj();
|
||||
Tcl_IncrRefCount(pArg);
|
||||
Tcl_Obj *pRes = Tcl_NewObj();
|
||||
Tcl_IncrRefCount(pRes);
|
||||
for(ii=0; ii<pIdxInfo->nConstraint; ii++){
|
||||
struct sqlite3_index_constraint const *pCons = &pIdxInfo->aConstraint[ii];
|
||||
Tcl_Obj *pElem = Tcl_NewObj();
|
||||
@@ -424,6 +431,10 @@ static int tclBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){
|
||||
zOp = "isnull"; break;
|
||||
case SQLITE_INDEX_CONSTRAINT_IS:
|
||||
zOp = "is"; break;
|
||||
case SQLITE_INDEX_CONSTRAINT_LIMIT:
|
||||
zOp = "limit"; break;
|
||||
case SQLITE_INDEX_CONSTRAINT_OFFSET:
|
||||
zOp = "offset"; break;
|
||||
}
|
||||
|
||||
Tcl_ListObjAppendElement(0, pElem, Tcl_NewStringObj("op", -1));
|
||||
@@ -433,15 +444,21 @@ static int tclBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){
|
||||
Tcl_ListObjAppendElement(0, pElem, Tcl_NewStringObj("usable", -1));
|
||||
Tcl_ListObjAppendElement(0, pElem, Tcl_NewIntObj(pCons->usable));
|
||||
|
||||
Tcl_ListObjAppendElement(0, pArg, pElem);
|
||||
Tcl_ListObjAppendElement(0, pRes, pElem);
|
||||
Tcl_DecrRefCount(pElem);
|
||||
}
|
||||
|
||||
Tcl_ListObjAppendElement(0, pScript, pArg);
|
||||
Tcl_DecrRefCount(pArg);
|
||||
Tcl_SetObjResult(interp, pRes);
|
||||
Tcl_DecrRefCount(pRes);
|
||||
}
|
||||
|
||||
pArg = Tcl_NewObj();
|
||||
Tcl_IncrRefCount(pArg);
|
||||
static void testBestIndexObjOrderby(
|
||||
Tcl_Interp *interp,
|
||||
sqlite3_index_info *pIdxInfo
|
||||
){
|
||||
int ii;
|
||||
Tcl_Obj *pRes = Tcl_NewObj();
|
||||
Tcl_IncrRefCount(pRes);
|
||||
for(ii=0; ii<pIdxInfo->nOrderBy; ii++){
|
||||
struct sqlite3_index_orderby const *pOrder = &pIdxInfo->aOrderBy[ii];
|
||||
Tcl_Obj *pElem = Tcl_NewObj();
|
||||
@@ -452,17 +469,150 @@ static int tclBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){
|
||||
Tcl_ListObjAppendElement(0, pElem, Tcl_NewStringObj("desc", -1));
|
||||
Tcl_ListObjAppendElement(0, pElem, Tcl_NewIntObj(pOrder->desc));
|
||||
|
||||
Tcl_ListObjAppendElement(0, pArg, pElem);
|
||||
Tcl_ListObjAppendElement(0, pRes, pElem);
|
||||
Tcl_DecrRefCount(pElem);
|
||||
}
|
||||
|
||||
Tcl_ListObjAppendElement(0, pScript, pArg);
|
||||
Tcl_DecrRefCount(pArg);
|
||||
Tcl_SetObjResult(interp, pRes);
|
||||
Tcl_DecrRefCount(pRes);
|
||||
}
|
||||
|
||||
Tcl_ListObjAppendElement(0, pScript, Tcl_NewWideIntObj(pIdxInfo->colUsed));
|
||||
/*
|
||||
** Implementation of the handle passed to each xBestIndex callback. This
|
||||
** object features the following sub-commands:
|
||||
**
|
||||
** $hdl constraints
|
||||
** $hdl orderby
|
||||
** $hdl mask
|
||||
**
|
||||
** $hdl distinct
|
||||
** Return the result (an integer) of calling sqlite3_vtab_distinct()
|
||||
** on the index-info structure.
|
||||
**
|
||||
** $hdl in IDX BOOLEAN
|
||||
** Wrapper around sqlite3_vtab_in(). Returns an integer.
|
||||
**
|
||||
** $hdl rhs_value IDX ?DEFAULT?
|
||||
** Wrapper around sqlite3_vtab_rhs_value().
|
||||
*/
|
||||
static int SQLITE_TCLAPI testBestIndexObj(
|
||||
ClientData clientData, /* Pointer to sqlite3_enable_XXX function */
|
||||
Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
|
||||
int objc, /* Number of arguments */
|
||||
Tcl_Obj *CONST objv[] /* Command arguments */
|
||||
){
|
||||
const char *azSub[] = {
|
||||
"constraints", /* 0 */
|
||||
"orderby", /* 1 */
|
||||
"mask", /* 2 */
|
||||
"distinct", /* 3 */
|
||||
"in", /* 4 */
|
||||
"rhs_value", /* 5 */
|
||||
0
|
||||
};
|
||||
int ii;
|
||||
sqlite3_index_info *pIdxInfo = (sqlite3_index_info*)clientData;
|
||||
|
||||
if( objc<2 ){
|
||||
Tcl_WrongNumArgs(interp, 1, objv, "SUB-COMMAND");
|
||||
return TCL_ERROR;
|
||||
}
|
||||
if( Tcl_GetIndexFromObj(interp, objv[1], azSub, "sub-command", 0, &ii) ){
|
||||
return TCL_ERROR;
|
||||
}
|
||||
|
||||
if( ii<4 && objc!=2 ){
|
||||
Tcl_WrongNumArgs(interp, 2, objv, "");
|
||||
return TCL_ERROR;
|
||||
}
|
||||
if( ii==4 && objc!=4 ){
|
||||
Tcl_WrongNumArgs(interp, 2, objv, "INDEX BOOLEAN");
|
||||
return TCL_ERROR;
|
||||
}
|
||||
if( ii==5 && objc!=3 && objc!=4 ){
|
||||
Tcl_WrongNumArgs(interp, 2, objv, "INDEX ?DEFAULT?");
|
||||
return TCL_ERROR;
|
||||
}
|
||||
|
||||
switch( ii ){
|
||||
case 0: assert( sqlite3_stricmp(azSub[ii], "constraints")==0 );
|
||||
testBestIndexObjConstraints(interp, pIdxInfo);
|
||||
break;
|
||||
|
||||
case 1: assert( sqlite3_stricmp(azSub[ii], "orderby")==0 );
|
||||
testBestIndexObjOrderby(interp, pIdxInfo);
|
||||
break;
|
||||
|
||||
case 2: assert( sqlite3_stricmp(azSub[ii], "mask")==0 );
|
||||
Tcl_SetObjResult(interp, Tcl_NewWideIntObj(pIdxInfo->colUsed));
|
||||
break;
|
||||
|
||||
case 3: assert( sqlite3_stricmp(azSub[ii], "distinct")==0 ); {
|
||||
int bDistinct = sqlite3_vtab_distinct(pIdxInfo);
|
||||
Tcl_SetObjResult(interp, Tcl_NewIntObj(bDistinct));
|
||||
break;
|
||||
}
|
||||
|
||||
case 4: assert( sqlite3_stricmp(azSub[ii], "in")==0 ); {
|
||||
int iCons;
|
||||
int bHandle;
|
||||
if( Tcl_GetIntFromObj(interp, objv[2], &iCons)
|
||||
|| Tcl_GetBooleanFromObj(interp, objv[3], &bHandle)
|
||||
){
|
||||
return TCL_ERROR;
|
||||
}
|
||||
Tcl_SetObjResult(interp,
|
||||
Tcl_NewIntObj(sqlite3_vtab_in(pIdxInfo, iCons, bHandle))
|
||||
);
|
||||
break;
|
||||
}
|
||||
|
||||
case 5: assert( sqlite3_stricmp(azSub[ii], "rhs_value")==0 ); {
|
||||
int iCons = 0;
|
||||
int rc;
|
||||
sqlite3_value *pVal = 0;
|
||||
const char *zVal = "";
|
||||
if( Tcl_GetIntFromObj(interp, objv[2], &iCons) ){
|
||||
return TCL_ERROR;
|
||||
}
|
||||
rc = sqlite3_vtab_rhs_value(pIdxInfo, iCons, &pVal);
|
||||
if( rc!=SQLITE_OK && rc!=SQLITE_NOTFOUND ){
|
||||
Tcl_SetResult(interp, (char *)sqlite3ErrName(rc), TCL_VOLATILE);
|
||||
return TCL_ERROR;
|
||||
}
|
||||
if( pVal ){
|
||||
zVal = (const char*)sqlite3_value_text(pVal);
|
||||
}else if( objc==4 ){
|
||||
zVal = Tcl_GetString(objv[3]);
|
||||
}
|
||||
Tcl_SetObjResult(interp, Tcl_NewStringObj(zVal, -1));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
static int tclBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){
|
||||
tcl_vtab *pTab = (tcl_vtab*)tab;
|
||||
Tcl_Interp *interp = pTab->interp;
|
||||
int rc = SQLITE_OK;
|
||||
|
||||
static int iNext = 43;
|
||||
char zHdl[24];
|
||||
Tcl_Obj *pScript;
|
||||
|
||||
pScript = Tcl_DuplicateObj(pTab->pCmd);
|
||||
Tcl_IncrRefCount(pScript);
|
||||
Tcl_ListObjAppendElement(interp, pScript, Tcl_NewStringObj("xBestIndex", -1));
|
||||
|
||||
sqlite3_snprintf(sizeof(zHdl), zHdl, "bestindex%d", iNext++);
|
||||
Tcl_CreateObjCommand(interp, zHdl, testBestIndexObj, pIdxInfo, 0);
|
||||
Tcl_ListObjAppendElement(interp, pScript, Tcl_NewStringObj(zHdl, -1));
|
||||
rc = Tcl_EvalObjEx(interp, pScript, TCL_EVAL_GLOBAL);
|
||||
Tcl_DeleteCommand(interp, zHdl);
|
||||
Tcl_DecrRefCount(pScript);
|
||||
|
||||
if( rc!=TCL_OK ){
|
||||
const char *zErr = Tcl_GetStringResult(interp);
|
||||
rc = SQLITE_ERROR;
|
||||
@@ -489,6 +639,7 @@ static int tclBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){
|
||||
rc = SQLITE_ERROR;
|
||||
pTab->base.zErrMsg = sqlite3_mprintf("%s", zErr);
|
||||
}else{
|
||||
int ii;
|
||||
int iArgv = 1;
|
||||
for(ii=0; rc==SQLITE_OK && ii<nElem; ii+=2){
|
||||
const char *zCmd = Tcl_GetString(apElem[ii]);
|
||||
|
@@ -734,7 +734,7 @@ void sqlite3Update(
|
||||
if( !pParse->nested && !pTrigger && !hasFK && !chngKey && !bReplace ){
|
||||
flags |= WHERE_ONEPASS_MULTIROW;
|
||||
}
|
||||
pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere, 0, 0, flags,iIdxCur);
|
||||
pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere,0,0,0,flags,iIdxCur);
|
||||
if( pWInfo==0 ) goto update_cleanup;
|
||||
|
||||
/* A one-pass strategy that might update more than one row may not
|
||||
@@ -1256,7 +1256,9 @@ static void updateVirtualTable(
|
||||
regRowid = ++pParse->nMem;
|
||||
|
||||
/* Start scanning the virtual table */
|
||||
pWInfo = sqlite3WhereBegin(pParse, pSrc,pWhere,0,0,WHERE_ONEPASS_DESIRED,0);
|
||||
pWInfo = sqlite3WhereBegin(
|
||||
pParse, pSrc, pWhere, 0, 0, 0, WHERE_ONEPASS_DESIRED, 0
|
||||
);
|
||||
if( pWInfo==0 ) return;
|
||||
|
||||
/* Populate the argument registers. */
|
||||
|
28
src/vdbe.c
28
src/vdbe.c
@@ -7775,6 +7775,34 @@ case OP_VOpen: {
|
||||
}
|
||||
#endif /* SQLITE_OMIT_VIRTUALTABLE */
|
||||
|
||||
#ifndef SQLITE_OMIT_VIRTUALTABLE
|
||||
/* Opcode: VInitIn P1 P2 P3 * *
|
||||
** Synopsis: r[P2]=ValueList(P1,P3)
|
||||
**
|
||||
** Set register P2 to be a pointer to a ValueList object for cursor P1
|
||||
** with cache register P3 and output register P3+1. This ValueList object
|
||||
** can be used as the first argument to sqlite3_vtab_in_first() and
|
||||
** sqlite3_vtab_in_next() to extract all of the values stored in the P1
|
||||
** cursor. Register P3 is used to hold the values returned by
|
||||
** sqlite3_vtab_in_first() and sqlite3_vtab_in_next().
|
||||
*/
|
||||
case OP_VInitIn: { /* out2 */
|
||||
VdbeCursor *pC; /* The cursor containing the RHS values */
|
||||
ValueList *pRhs; /* New ValueList object to put in reg[P2] */
|
||||
|
||||
pC = p->apCsr[pOp->p1];
|
||||
pRhs = sqlite3_malloc64( sizeof(*pRhs) );
|
||||
if( pRhs==0 ) goto no_mem;
|
||||
pRhs->pCsr = pC->uc.pCursor;
|
||||
pRhs->pOut = &aMem[pOp->p3];
|
||||
pOut = out2Prerelease(p, pOp);
|
||||
pOut->flags = MEM_Null;
|
||||
sqlite3VdbeMemSetPointer(pOut, pRhs, "ValueList", sqlite3_free);
|
||||
break;
|
||||
}
|
||||
#endif /* SQLITE_OMIT_VIRTUALTABLE */
|
||||
|
||||
|
||||
#ifndef SQLITE_OMIT_VIRTUALTABLE
|
||||
/* Opcode: VFilter P1 P2 P3 P4 *
|
||||
** Synopsis: iplan=r[P3] zplan='P4'
|
||||
|
@@ -482,6 +482,24 @@ struct PreUpdate {
|
||||
Index *pPk; /* PK index if pTab is WITHOUT ROWID */
|
||||
};
|
||||
|
||||
/*
|
||||
** An instance of this object is used to pass an vector of values into
|
||||
** OP_VFilter, the xFilter method of a virtual table. The vector is the
|
||||
** set of values on the right-hand side of an IN constraint.
|
||||
**
|
||||
** The value as passed into xFilter is an sqlite3_value with a "pointer"
|
||||
** type, such as is generated by sqlite3_result_pointer() and read by
|
||||
** sqlite3_value_pointer. Such values have MEM_Term|MEM_Subtype|MEM_Null
|
||||
** and a subtype of 'p'. The sqlite3_vtab_in_first() and _next() interfaces
|
||||
** know how to use this object to step through all the values in the
|
||||
** right operand of the IN constraint.
|
||||
*/
|
||||
typedef struct ValueList ValueList;
|
||||
struct ValueList {
|
||||
BtCursor *pCsr; /* An ephemeral table holding all values */
|
||||
sqlite3_value *pOut; /* Register to hold each decoded output value */
|
||||
};
|
||||
|
||||
/*
|
||||
** Function prototypes
|
||||
*/
|
||||
|
@@ -846,6 +846,70 @@ int sqlite3_vtab_nochange(sqlite3_context *p){
|
||||
return sqlite3_value_nochange(p->pOut);
|
||||
}
|
||||
|
||||
/*
|
||||
** Implementation of sqlite3_vtab_in_first() (if bNext==0) and
|
||||
** sqlite3_vtab_in_next() (if bNext!=0).
|
||||
*/
|
||||
static int valueFromValueList(
|
||||
sqlite3_value *pVal, /* Pointer to the ValueList object */
|
||||
sqlite3_value **ppOut, /* Store the next value from the list here */
|
||||
int bNext /* 1 for _next(). 0 for _first() */
|
||||
){
|
||||
int rc;
|
||||
ValueList *pRhs;
|
||||
|
||||
*ppOut = 0;
|
||||
if( pVal==0 ) return SQLITE_MISUSE;
|
||||
pRhs = (ValueList*)sqlite3_value_pointer(pVal, "ValueList");
|
||||
if( pRhs==0 ) return SQLITE_MISUSE;
|
||||
if( bNext ){
|
||||
rc = sqlite3BtreeNext(pRhs->pCsr, 0);
|
||||
}else{
|
||||
int dummy = 0;
|
||||
rc = sqlite3BtreeFirst(pRhs->pCsr, &dummy);
|
||||
assert( rc==SQLITE_OK || sqlite3BtreeEof(pRhs->pCsr) );
|
||||
if( sqlite3BtreeEof(pRhs->pCsr) ) rc = SQLITE_DONE;
|
||||
}
|
||||
if( rc==SQLITE_OK ){
|
||||
u32 sz; /* Size of current row in bytes */
|
||||
Mem sMem; /* Raw content of current row */
|
||||
memset(&sMem, 0, sizeof(sMem));
|
||||
sz = sqlite3BtreePayloadSize(pRhs->pCsr);
|
||||
rc = sqlite3VdbeMemFromBtreeZeroOffset(pRhs->pCsr,(int)sz,&sMem);
|
||||
if( rc==SQLITE_OK ){
|
||||
u8 *zBuf = (u8*)sMem.z;
|
||||
u32 iSerial;
|
||||
sqlite3_value *pOut = pRhs->pOut;
|
||||
int iOff = 1 + getVarint32(&zBuf[1], iSerial);
|
||||
sqlite3VdbeSerialGet(&zBuf[iOff], iSerial, pOut);
|
||||
pOut->enc = ENC(pOut->db);
|
||||
if( (pOut->flags & MEM_Ephem)!=0 && sqlite3VdbeMemMakeWriteable(pOut) ){
|
||||
rc = SQLITE_NOMEM;
|
||||
}else{
|
||||
*ppOut = pOut;
|
||||
}
|
||||
}
|
||||
sqlite3VdbeMemRelease(&sMem);
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** Set the iterator value pVal to point to the first value in the set.
|
||||
** Set (*ppOut) to point to this value before returning.
|
||||
*/
|
||||
int sqlite3_vtab_in_first(sqlite3_value *pVal, sqlite3_value **ppOut){
|
||||
return valueFromValueList(pVal, ppOut, 0);
|
||||
}
|
||||
|
||||
/*
|
||||
** Set the iterator value pVal to point to the next value in the set.
|
||||
** Set (*ppOut) to point to this value before returning.
|
||||
*/
|
||||
int sqlite3_vtab_in_next(sqlite3_value *pVal, sqlite3_value **ppOut){
|
||||
return valueFromValueList(pVal, ppOut, 1);
|
||||
}
|
||||
|
||||
/*
|
||||
** Return the current time for a statement. If the current time
|
||||
** is requested more than once within the same run of a single prepared
|
||||
|
@@ -1530,11 +1530,7 @@ static int valueFromExpr(
|
||||
|
||||
assert( pExpr!=0 );
|
||||
while( (op = pExpr->op)==TK_UPLUS || op==TK_SPAN ) pExpr = pExpr->pLeft;
|
||||
#if defined(SQLITE_ENABLE_STAT4)
|
||||
if( op==TK_REGISTER ) op = pExpr->op2;
|
||||
#else
|
||||
if( NEVER(op==TK_REGISTER) ) op = pExpr->op2;
|
||||
#endif
|
||||
|
||||
/* Compressed expressions only appear when parsing the DEFAULT clause
|
||||
** on a table column definition, and hence only when pCtx==0. This
|
||||
|
98
src/where.c
98
src/where.c
@@ -33,6 +33,8 @@ struct HiddenIndexInfo {
|
||||
WhereClause *pWC; /* The Where clause being analyzed */
|
||||
Parse *pParse; /* The parsing context */
|
||||
int eDistinct; /* Value to return from sqlite3_vtab_distinct() */
|
||||
u32 mIn; /* Mask of terms that are <col> IN (...) */
|
||||
u32 mHandleIn; /* Terms that vtab will handle as <col> IN (...) */
|
||||
sqlite3_value *aRhs[1]; /* RHS values for constraints. MUST BE LAST
|
||||
** because extra space is allocated to hold up
|
||||
** to nTerm such values */
|
||||
@@ -1143,6 +1145,7 @@ static sqlite3_index_info *allocateIndexInfo(
|
||||
testcase( pTerm->eOperator & WO_ALL );
|
||||
if( (pTerm->eOperator & ~(WO_EQUIV))==0 ) continue;
|
||||
if( pTerm->wtFlags & TERM_VNULL ) continue;
|
||||
|
||||
assert( (pTerm->eOperator & (WO_OR|WO_AND))==0 );
|
||||
assert( pTerm->u.x.leftColumn>=XN_ROWID );
|
||||
assert( pTerm->u.x.leftColumn<pTab->nCol );
|
||||
@@ -1204,7 +1207,7 @@ static sqlite3_index_info *allocateIndexInfo(
|
||||
/* No matches cause a break out of the loop */
|
||||
break;
|
||||
}
|
||||
if( i==n){
|
||||
if( i==n ){
|
||||
nOrderBy = n;
|
||||
if( (pWInfo->wctrlFlags & (WHERE_GROUPBY|WHERE_DISTINCTBY)) ){
|
||||
eDistinct = 1 + ((pWInfo->wctrlFlags & WHERE_DISTINCTBY)!=0);
|
||||
@@ -1232,13 +1235,17 @@ static sqlite3_index_info *allocateIndexInfo(
|
||||
pHidden->pWC = pWC;
|
||||
pHidden->pParse = pParse;
|
||||
pHidden->eDistinct = eDistinct;
|
||||
pHidden->mIn = 0;
|
||||
for(i=j=0, pTerm=pWC->a; i<pWC->nTerm; i++, pTerm++){
|
||||
u16 op;
|
||||
if( (pTerm->wtFlags & TERM_OK)==0 ) continue;
|
||||
pIdxCons[j].iColumn = pTerm->u.x.leftColumn;
|
||||
pIdxCons[j].iTermOffset = i;
|
||||
op = pTerm->eOperator & WO_ALL;
|
||||
if( op==WO_IN ) op = WO_EQ;
|
||||
if( op==WO_IN ){
|
||||
pHidden->mIn |= SMASKBIT32(j);
|
||||
op = WO_EQ;
|
||||
}
|
||||
if( op==WO_AUX ){
|
||||
pIdxCons[j].op = pTerm->eMatchOp;
|
||||
}else if( op & (WO_ISNULL|WO_IS) ){
|
||||
@@ -1328,7 +1335,9 @@ static int vtabBestIndex(Parse *pParse, Table *pTab, sqlite3_index_info *p){
|
||||
int rc;
|
||||
|
||||
whereTraceIndexInfoInputs(p);
|
||||
pParse->db->nSchemaLock++;
|
||||
rc = pVtab->pModule->xBestIndex(pVtab, p);
|
||||
pParse->db->nSchemaLock--;
|
||||
whereTraceIndexInfoOutputs(p);
|
||||
|
||||
if( rc!=SQLITE_OK && rc!=SQLITE_CONSTRAINT ){
|
||||
@@ -3459,6 +3468,15 @@ static int whereLoopAddBtree(
|
||||
|
||||
#ifndef SQLITE_OMIT_VIRTUALTABLE
|
||||
|
||||
/*
|
||||
** Return true if pTerm is a virtual table LIMIT or OFFSET term.
|
||||
*/
|
||||
static int isLimitTerm(WhereTerm *pTerm){
|
||||
assert( pTerm->eOperator==WO_AUX || pTerm->eMatchOp==0 );
|
||||
return pTerm->eMatchOp>=SQLITE_INDEX_CONSTRAINT_LIMIT
|
||||
&& pTerm->eMatchOp<=SQLITE_INDEX_CONSTRAINT_OFFSET;
|
||||
}
|
||||
|
||||
/*
|
||||
** Argument pIdxInfo is already populated with all constraints that may
|
||||
** be used by the virtual table identified by pBuilder->pNew->iTab. This
|
||||
@@ -3486,9 +3504,11 @@ static int whereLoopAddVirtualOne(
|
||||
u16 mExclude, /* Exclude terms using these operators */
|
||||
sqlite3_index_info *pIdxInfo, /* Populated object for xBestIndex */
|
||||
u16 mNoOmit, /* Do not omit these constraints */
|
||||
int *pbIn /* OUT: True if plan uses an IN(...) op */
|
||||
int *pbIn, /* OUT: True if plan uses an IN(...) op */
|
||||
int *pbRetryLimit /* OUT: Retry without LIMIT/OFFSET */
|
||||
){
|
||||
WhereClause *pWC = pBuilder->pWC;
|
||||
HiddenIndexInfo *pHidden = (HiddenIndexInfo*)&pIdxInfo[1];
|
||||
struct sqlite3_index_constraint *pIdxCons;
|
||||
struct sqlite3_index_constraint_usage *pUsage = pIdxInfo->aConstraintUsage;
|
||||
int i;
|
||||
@@ -3511,6 +3531,7 @@ static int whereLoopAddVirtualOne(
|
||||
pIdxCons->usable = 0;
|
||||
if( (pTerm->prereqRight & mUsable)==pTerm->prereqRight
|
||||
&& (pTerm->eOperator & mExclude)==0
|
||||
&& (pbRetryLimit || !isLimitTerm(pTerm))
|
||||
){
|
||||
pIdxCons->usable = 1;
|
||||
}
|
||||
@@ -3526,6 +3547,7 @@ static int whereLoopAddVirtualOne(
|
||||
pIdxInfo->estimatedRows = 25;
|
||||
pIdxInfo->idxFlags = 0;
|
||||
pIdxInfo->colUsed = (sqlite3_int64)pSrc->colUsed;
|
||||
pHidden->mHandleIn = 0;
|
||||
|
||||
/* Invoke the virtual table xBestIndex() method */
|
||||
rc = vtabBestIndex(pParse, pSrc->pTab, pIdxInfo);
|
||||
@@ -3543,8 +3565,8 @@ static int whereLoopAddVirtualOne(
|
||||
|
||||
mxTerm = -1;
|
||||
assert( pNew->nLSlot>=nConstraint );
|
||||
for(i=0; i<nConstraint; i++) pNew->aLTerm[i] = 0;
|
||||
pNew->u.vtab.omitMask = 0;
|
||||
memset(pNew->aLTerm, 0, sizeof(pNew->aLTerm[0])*nConstraint );
|
||||
memset(&pNew->u.vtab, 0, sizeof(pNew->u.vtab));
|
||||
pIdxCons = *(struct sqlite3_index_constraint**)&pIdxInfo->aConstraint;
|
||||
for(i=0; i<nConstraint; i++, pIdxCons++){
|
||||
int iTerm;
|
||||
@@ -3578,8 +3600,13 @@ static int whereLoopAddVirtualOne(
|
||||
}else{
|
||||
testcase( i!=iTerm );
|
||||
}
|
||||
if( pTerm->eMatchOp==SQLITE_INDEX_CONSTRAINT_OFFSET ){
|
||||
pNew->u.vtab.bOmitOffset = 1;
|
||||
}
|
||||
if( (pTerm->eOperator & WO_IN)!=0 ){
|
||||
}
|
||||
if( SMASKBIT32(i) & pHidden->mHandleIn ){
|
||||
pNew->u.vtab.mHandleIn |= MASKBIT32(iTerm);
|
||||
}else if( (pTerm->eOperator & WO_IN)!=0 ){
|
||||
/* A virtual table that is constrained by an IN clause may not
|
||||
** consume the ORDER BY clause because (1) the order of IN terms
|
||||
** is not necessarily related to the order of output terms and
|
||||
@@ -3589,6 +3616,21 @@ static int whereLoopAddVirtualOne(
|
||||
pIdxInfo->idxFlags &= ~SQLITE_INDEX_SCAN_UNIQUE;
|
||||
*pbIn = 1; assert( (mExclude & WO_IN)==0 );
|
||||
}
|
||||
|
||||
if( isLimitTerm(pTerm) && *pbIn ){
|
||||
/* If there is an IN(...) term handled as an == (separate call to
|
||||
** xFilter for each value on the RHS of the IN) and a LIMIT or
|
||||
** OFFSET term handled as well, the plan is unusable. Set output
|
||||
** variable *pbRetryLimit to true to tell the caller to retry with
|
||||
** LIMIT and OFFSET disabled. */
|
||||
if( pIdxInfo->needToFreeIdxStr ){
|
||||
sqlite3_free(pIdxInfo->idxStr);
|
||||
pIdxInfo->idxStr = 0;
|
||||
pIdxInfo->needToFreeIdxStr = 0;
|
||||
}
|
||||
*pbRetryLimit = 1;
|
||||
return SQLITE_OK;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3662,6 +3704,25 @@ const char *sqlite3_vtab_collation(sqlite3_index_info *pIdxInfo, int iCons){
|
||||
return zRet;
|
||||
}
|
||||
|
||||
/*
|
||||
** Return true if constraint iCons is really an IN(...) constraint, or
|
||||
** false otherwise. If iCons is an IN(...) constraint, set (if bHandle!=0)
|
||||
** or clear (if bHandle==0) the flag to handle it using an iterator.
|
||||
*/
|
||||
int sqlite3_vtab_in(sqlite3_index_info *pIdxInfo, int iCons, int bHandle){
|
||||
HiddenIndexInfo *pHidden = (HiddenIndexInfo*)&pIdxInfo[1];
|
||||
u32 m = SMASKBIT32(iCons);
|
||||
if( m & pHidden->mIn ){
|
||||
if( bHandle==0 ){
|
||||
pHidden->mHandleIn &= ~m;
|
||||
}else if( bHandle>0 ){
|
||||
pHidden->mHandleIn |= m;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
** This interface is callable from within the xBestIndex callback only.
|
||||
**
|
||||
@@ -3751,6 +3812,7 @@ static int whereLoopAddVirtual(
|
||||
WhereLoop *pNew;
|
||||
Bitmask mBest; /* Tables used by best possible plan */
|
||||
u16 mNoOmit;
|
||||
int bRetry = 0; /* True to retry with LIMIT/OFFSET disabled */
|
||||
|
||||
assert( (mPrereq & mUnusable)==0 );
|
||||
pWInfo = pBuilder->pWInfo;
|
||||
@@ -3774,7 +3836,15 @@ static int whereLoopAddVirtual(
|
||||
/* First call xBestIndex() with all constraints usable. */
|
||||
WHERETRACE(0x800, ("BEGIN %s.addVirtual()\n", pSrc->pTab->zName));
|
||||
WHERETRACE(0x40, (" VirtualOne: all usable\n"));
|
||||
rc = whereLoopAddVirtualOne(pBuilder, mPrereq, ALLBITS, 0, p, mNoOmit, &bIn);
|
||||
rc = whereLoopAddVirtualOne(
|
||||
pBuilder, mPrereq, ALLBITS, 0, p, mNoOmit, &bIn, &bRetry
|
||||
);
|
||||
if( bRetry ){
|
||||
assert( rc==SQLITE_OK );
|
||||
rc = whereLoopAddVirtualOne(
|
||||
pBuilder, mPrereq, ALLBITS, 0, p, mNoOmit, &bIn, 0
|
||||
);
|
||||
}
|
||||
|
||||
/* If the call to xBestIndex() with all terms enabled produced a plan
|
||||
** that does not require any source tables (IOW: a plan with mBest==0)
|
||||
@@ -3792,7 +3862,7 @@ static int whereLoopAddVirtual(
|
||||
if( bIn ){
|
||||
WHERETRACE(0x40, (" VirtualOne: all usable w/o IN\n"));
|
||||
rc = whereLoopAddVirtualOne(
|
||||
pBuilder, mPrereq, ALLBITS, WO_IN, p, mNoOmit, &bIn);
|
||||
pBuilder, mPrereq, ALLBITS, WO_IN, p, mNoOmit, &bIn, 0);
|
||||
assert( bIn==0 );
|
||||
mBestNoIn = pNew->prereq & ~mPrereq;
|
||||
if( mBestNoIn==0 ){
|
||||
@@ -3819,7 +3889,7 @@ static int whereLoopAddVirtual(
|
||||
WHERETRACE(0x40, (" VirtualOne: mPrev=%04llx mNext=%04llx\n",
|
||||
(sqlite3_uint64)mPrev, (sqlite3_uint64)mNext));
|
||||
rc = whereLoopAddVirtualOne(
|
||||
pBuilder, mPrereq, mNext|mPrereq, 0, p, mNoOmit, &bIn);
|
||||
pBuilder, mPrereq, mNext|mPrereq, 0, p, mNoOmit, &bIn, 0);
|
||||
if( pNew->prereq==mPrereq ){
|
||||
seenZero = 1;
|
||||
if( bIn==0 ) seenZeroNoIN = 1;
|
||||
@@ -3832,7 +3902,7 @@ static int whereLoopAddVirtual(
|
||||
if( rc==SQLITE_OK && seenZero==0 ){
|
||||
WHERETRACE(0x40, (" VirtualOne: all disabled\n"));
|
||||
rc = whereLoopAddVirtualOne(
|
||||
pBuilder, mPrereq, mPrereq, 0, p, mNoOmit, &bIn);
|
||||
pBuilder, mPrereq, mPrereq, 0, p, mNoOmit, &bIn, 0);
|
||||
if( bIn==0 ) seenZeroNoIN = 1;
|
||||
}
|
||||
|
||||
@@ -3842,7 +3912,7 @@ static int whereLoopAddVirtual(
|
||||
if( rc==SQLITE_OK && seenZeroNoIN==0 ){
|
||||
WHERETRACE(0x40, (" VirtualOne: all disabled and w/o IN\n"));
|
||||
rc = whereLoopAddVirtualOne(
|
||||
pBuilder, mPrereq, mPrereq, WO_IN, p, mNoOmit, &bIn);
|
||||
pBuilder, mPrereq, mPrereq, WO_IN, p, mNoOmit, &bIn, 0);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5248,6 +5318,7 @@ WhereInfo *sqlite3WhereBegin(
|
||||
Expr *pWhere, /* The WHERE clause */
|
||||
ExprList *pOrderBy, /* An ORDER BY (or GROUP BY) clause, or NULL */
|
||||
ExprList *pResultSet, /* Query result set. Req'd for DISTINCT */
|
||||
Select *pLimit, /* Use this LIMIT/OFFSET clause, if any */
|
||||
u16 wctrlFlags, /* The WHERE_* flags defined in sqliteInt.h */
|
||||
int iAuxArg /* If WHERE_OR_SUBCLAUSE is set, index cursor number
|
||||
** If WHERE_USE_LIMIT, then the limit amount */
|
||||
@@ -5324,6 +5395,9 @@ WhereInfo *sqlite3WhereBegin(
|
||||
pWInfo->wctrlFlags = wctrlFlags;
|
||||
pWInfo->iLimit = iAuxArg;
|
||||
pWInfo->savedNQueryLoop = pParse->nQueryLoop;
|
||||
#ifndef SQLITE_OMIT_VIRTUALTABLE
|
||||
pWInfo->pLimit = pLimit;
|
||||
#endif
|
||||
memset(&pWInfo->nOBSat, 0,
|
||||
offsetof(WhereInfo,sWC) - offsetof(WhereInfo,nOBSat));
|
||||
memset(&pWInfo->a[0], 0, sizeof(WhereLoop)+nTabList*sizeof(WhereLevel));
|
||||
@@ -5392,6 +5466,7 @@ WhereInfo *sqlite3WhereBegin(
|
||||
|
||||
/* Analyze all of the subexpressions. */
|
||||
sqlite3WhereExprAnalyze(pTabList, &pWInfo->sWC);
|
||||
sqlite3WhereAddLimit(&pWInfo->sWC, pLimit);
|
||||
if( db->mallocFailed ) goto whereBeginError;
|
||||
|
||||
/* Special case: WHERE terms that do not refer to any tables in the join
|
||||
@@ -5638,6 +5713,7 @@ WhereInfo *sqlite3WhereBegin(
|
||||
if( pWInfo->eOnePass==ONEPASS_OFF
|
||||
&& pTab->nCol<BMS
|
||||
&& (pTab->tabFlags & (TF_HasGenerated|TF_WithoutRowid))==0
|
||||
&& (pLoop->wsFlags & (WHERE_AUTO_INDEX|WHERE_BLOOMFILTER))==0
|
||||
){
|
||||
/* If we know that only a prefix of the record will be used,
|
||||
** it is advantageous to reduce the "column count" field in
|
||||
|
@@ -123,10 +123,12 @@ struct WhereLoop {
|
||||
} btree;
|
||||
struct { /* Information for virtual tables */
|
||||
int idxNum; /* Index number */
|
||||
u8 needFree; /* True if sqlite3_free(idxStr) is needed */
|
||||
u32 needFree : 1; /* True if sqlite3_free(idxStr) is needed */
|
||||
u32 bOmitOffset : 1; /* True to let virtual table handle offset */
|
||||
i8 isOrdered; /* True if satisfies ORDER BY */
|
||||
u16 omitMask; /* Terms that may be omitted */
|
||||
char *idxStr; /* Index identifier string */
|
||||
u32 mHandleIn; /* Terms to handle as IN(...) instead of == */
|
||||
} vtab;
|
||||
} u;
|
||||
u32 wsFlags; /* WHERE_* flags describing the plan */
|
||||
@@ -283,6 +285,7 @@ struct WhereTerm {
|
||||
#else
|
||||
# define TERM_HIGHTRUTH 0 /* Only used with STAT4 */
|
||||
#endif
|
||||
#define TERM_SLICE 0x8000 /* One slice of a row-value/vector comparison */
|
||||
|
||||
/*
|
||||
** An instance of the WhereScan object is used as an iterator for locating
|
||||
@@ -453,6 +456,9 @@ struct WhereInfo {
|
||||
ExprList *pOrderBy; /* The ORDER BY clause or NULL */
|
||||
ExprList *pResultSet; /* Result set of the query */
|
||||
Expr *pWhere; /* The complete WHERE clause */
|
||||
#ifndef SQLITE_OMIT_VIRTUALTABLE
|
||||
Select *pLimit; /* Used to access LIMIT expr/registers for vtabs */
|
||||
#endif
|
||||
int aiCurOnePass[2]; /* OP_OpenWrite cursors for the ONEPASS opt */
|
||||
int iContinue; /* Jump here to continue with next record */
|
||||
int iBreak; /* Jump here to break out of the loop */
|
||||
@@ -538,6 +544,7 @@ Bitmask sqlite3WhereCodeOneLoopStart(
|
||||
void sqlite3WhereClauseInit(WhereClause*,WhereInfo*);
|
||||
void sqlite3WhereClauseClear(WhereClause*);
|
||||
void sqlite3WhereSplit(WhereClause*,Expr*,u8);
|
||||
void sqlite3WhereAddLimit(WhereClause*, Select*);
|
||||
Bitmask sqlite3WhereExprUsage(WhereMaskSet*, Expr*);
|
||||
Bitmask sqlite3WhereExprUsageNN(WhereMaskSet*, Expr*);
|
||||
Bitmask sqlite3WhereExprListUsage(WhereMaskSet*, ExprList*);
|
||||
@@ -608,5 +615,6 @@ void sqlite3WhereTabFuncArgs(Parse*, SrcItem*, WhereClause*);
|
||||
#define WHERE_TRANSCONS 0x00200000 /* Uses a transitive constraint */
|
||||
#define WHERE_BLOOMFILTER 0x00400000 /* Consider using a Bloom-filter */
|
||||
#define WHERE_SELFCULL 0x00800000 /* nOut reduced by extra WHERE terms */
|
||||
#define WHERE_OMIT_OFFSET 0x01000000 /* Set offset counter to zero */
|
||||
|
||||
#endif /* !defined(SQLITE_WHEREINT_H) */
|
||||
|
@@ -1532,11 +1532,27 @@ Bitmask sqlite3WhereCodeOneLoopStart(
|
||||
pTerm = pLoop->aLTerm[j];
|
||||
if( NEVER(pTerm==0) ) continue;
|
||||
if( pTerm->eOperator & WO_IN ){
|
||||
if( SMASKBIT32(j) & pLoop->u.vtab.mHandleIn ){
|
||||
int iTab = pParse->nTab++;
|
||||
int iCache = ++pParse->nMem;
|
||||
sqlite3CodeRhsOfIN(pParse, pTerm->pExpr, iTab);
|
||||
sqlite3VdbeAddOp3(v, OP_VInitIn, iTab, iTarget, iCache);
|
||||
}else{
|
||||
codeEqualityTerm(pParse, pTerm, pLevel, j, bRev, iTarget);
|
||||
addrNotFound = pLevel->addrNxt;
|
||||
}
|
||||
}else{
|
||||
Expr *pRight = pTerm->pExpr->pRight;
|
||||
codeExprOrVector(pParse, pRight, iTarget, 1);
|
||||
if( pTerm->eMatchOp==SQLITE_INDEX_CONSTRAINT_OFFSET
|
||||
&& pLoop->u.vtab.bOmitOffset
|
||||
){
|
||||
assert( pTerm->eOperator==WO_AUX );
|
||||
assert( pWInfo->pLimit!=0 );
|
||||
assert( pWInfo->pLimit->iOffset>0 );
|
||||
sqlite3VdbeAddOp2(v, OP_Integer, 0, pWInfo->pLimit->iOffset);
|
||||
VdbeComment((v,"Zero OFFSET counter"));
|
||||
}
|
||||
}
|
||||
}
|
||||
sqlite3VdbeAddOp2(v, OP_Integer, pLoop->u.vtab.idxNum, iReg);
|
||||
@@ -1559,13 +1575,19 @@ Bitmask sqlite3WhereCodeOneLoopStart(
|
||||
iIn = 0;
|
||||
}
|
||||
for(j=nConstraint-1; j>=0; j--){
|
||||
int bIn; /* True to generate byte code to loop over RHS IN values */
|
||||
pTerm = pLoop->aLTerm[j];
|
||||
if( (pTerm->eOperator & WO_IN)!=0 ) iIn--;
|
||||
if( (pTerm->eOperator & WO_IN)!=0
|
||||
&& (SMASKBIT32(j) & pLoop->u.vtab.mHandleIn)==0
|
||||
){
|
||||
bIn = 1;
|
||||
}else{
|
||||
bIn = 0;
|
||||
}
|
||||
if( bIn ) iIn--;
|
||||
if( j<16 && (pLoop->u.vtab.omitMask>>j)&1 ){
|
||||
disableTerm(pLevel, pTerm);
|
||||
}else if( (pTerm->eOperator & WO_IN)!=0
|
||||
&& sqlite3ExprVectorSize(pTerm->pExpr->pLeft)==1
|
||||
){
|
||||
}else if( bIn && sqlite3ExprVectorSize(pTerm->pExpr->pLeft)==1 ){
|
||||
Expr *pCompare; /* The comparison operator */
|
||||
Expr *pRight; /* RHS of the comparison */
|
||||
VdbeOp *pOp; /* Opcode to access the value of the IN constraint */
|
||||
@@ -2295,7 +2317,7 @@ Bitmask sqlite3WhereCodeOneLoopStart(
|
||||
iRetInit = sqlite3VdbeAddOp2(v, OP_Integer, 0, regReturn);
|
||||
|
||||
/* If the original WHERE clause is z of the form: (x1 OR x2 OR ...) AND y
|
||||
** Then for every term xN, evaluate as the subexpression: xN AND z
|
||||
** Then for every term xN, evaluate as the subexpression: xN AND y
|
||||
** That way, terms in y that are factored into the disjunction will
|
||||
** be picked up by the recursive calls to sqlite3WhereBegin() below.
|
||||
**
|
||||
@@ -2307,6 +2329,12 @@ Bitmask sqlite3WhereCodeOneLoopStart(
|
||||
** This optimization also only applies if the (x1 OR x2 OR ...) term
|
||||
** is not contained in the ON clause of a LEFT JOIN.
|
||||
** See ticket http://www.sqlite.org/src/info/f2369304e4
|
||||
**
|
||||
** 2022-02-04: Do not push down slices of a row-value comparison.
|
||||
** In other words, "w" or "y" may not be a slice of a vector. Otherwise,
|
||||
** the initialization of the right-hand operand of the vector comparison
|
||||
** might not occur, or might occur only in an OR branch that is not
|
||||
** taken. dbsqlfuzz 80a9fade844b4fb43564efc972bcb2c68270f5d1.
|
||||
*/
|
||||
if( pWC->nTerm>1 ){
|
||||
int iTerm;
|
||||
@@ -2315,7 +2343,10 @@ Bitmask sqlite3WhereCodeOneLoopStart(
|
||||
if( &pWC->a[iTerm] == pTerm ) continue;
|
||||
testcase( pWC->a[iTerm].wtFlags & TERM_VIRTUAL );
|
||||
testcase( pWC->a[iTerm].wtFlags & TERM_CODED );
|
||||
if( (pWC->a[iTerm].wtFlags & (TERM_VIRTUAL|TERM_CODED))!=0 ) continue;
|
||||
testcase( pWC->a[iTerm].wtFlags & TERM_SLICE );
|
||||
if( (pWC->a[iTerm].wtFlags & (TERM_VIRTUAL|TERM_CODED|TERM_SLICE))!=0 ){
|
||||
continue;
|
||||
}
|
||||
if( (pWC->a[iTerm].eOperator & WO_ALL)==0 ) continue;
|
||||
testcase( pWC->a[iTerm].wtFlags & TERM_ORINFO );
|
||||
pExpr = sqlite3ExprDup(db, pExpr, 0);
|
||||
@@ -2358,7 +2389,7 @@ Bitmask sqlite3WhereCodeOneLoopStart(
|
||||
/* Loop through table entries that match term pOrTerm. */
|
||||
ExplainQueryPlan((pParse, 1, "INDEX %d", ii+1));
|
||||
WHERETRACE(0xffff, ("Subplan for OR-clause:\n"));
|
||||
pSubWInfo = sqlite3WhereBegin(pParse, pOrTab, pOrExpr, 0, 0,
|
||||
pSubWInfo = sqlite3WhereBegin(pParse, pOrTab, pOrExpr, 0, 0, 0,
|
||||
WHERE_OR_SUBCLAUSE, iCovCur);
|
||||
assert( pSubWInfo || pParse->nErr );
|
||||
if( pSubWInfo ){
|
||||
|
116
src/whereexpr.c
116
src/whereexpr.c
@@ -1379,7 +1379,10 @@ static void exprAnalyze(
|
||||
** no longer used.
|
||||
**
|
||||
** This is only required if at least one side of the comparison operation
|
||||
** is not a sub-select. */
|
||||
** is not a sub-select.
|
||||
**
|
||||
** tag-20220128a
|
||||
*/
|
||||
if( (pExpr->op==TK_EQ || pExpr->op==TK_IS)
|
||||
&& (nLeft = sqlite3ExprVectorSize(pExpr->pLeft))>1
|
||||
&& sqlite3ExprVectorSize(pExpr->pRight)==nLeft
|
||||
@@ -1396,7 +1399,7 @@ static void exprAnalyze(
|
||||
|
||||
pNew = sqlite3PExpr(pParse, pExpr->op, pLeft, pRight);
|
||||
transferJoinMarkings(pNew, pExpr);
|
||||
idxNew = whereClauseInsert(pWC, pNew, TERM_DYNAMIC);
|
||||
idxNew = whereClauseInsert(pWC, pNew, TERM_DYNAMIC|TERM_SLICE);
|
||||
exprAnalyze(pSrc, pWC, idxNew);
|
||||
}
|
||||
pTerm = &pWC->a[idxTerm];
|
||||
@@ -1522,6 +1525,113 @@ void sqlite3WhereSplit(WhereClause *pWC, Expr *pExpr, u8 op){
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** Add either a LIMIT (if eMatchOp==SQLITE_INDEX_CONSTRAINT_LIMIT) or
|
||||
** OFFSET (if eMatchOp==SQLITE_INDEX_CONSTRAINT_OFFSET) term to the
|
||||
** where-clause passed as the first argument. The value for the term
|
||||
** is found in register iReg.
|
||||
**
|
||||
** In the common case where the value is a simple integer
|
||||
** (example: "LIMIT 5 OFFSET 10") then the expression codes as a
|
||||
** TK_INTEGER so that it will be available to sqlite3_vtab_rhs_value().
|
||||
** If not, then it codes as a TK_REGISTER expression.
|
||||
*/
|
||||
void whereAddLimitExpr(
|
||||
WhereClause *pWC, /* Add the constraint to this WHERE clause */
|
||||
int iReg, /* Register that will hold value of the limit/offset */
|
||||
Expr *pExpr, /* Expression that defines the limit/offset */
|
||||
int iCsr, /* Cursor to which the constraint applies */
|
||||
int eMatchOp /* SQLITE_INDEX_CONSTRAINT_LIMIT or _OFFSET */
|
||||
){
|
||||
Parse *pParse = pWC->pWInfo->pParse;
|
||||
sqlite3 *db = pParse->db;
|
||||
Expr *pNew;
|
||||
int iVal = 0;
|
||||
|
||||
if( sqlite3ExprIsInteger(pExpr, &iVal) && iVal>=0 ){
|
||||
Expr *pVal = sqlite3Expr(db, TK_INTEGER, 0);
|
||||
if( pVal==0 ) return;
|
||||
ExprSetProperty(pVal, EP_IntValue);
|
||||
pVal->u.iValue = iVal;
|
||||
pNew = sqlite3PExpr(pParse, TK_MATCH, 0, pVal);
|
||||
}else{
|
||||
Expr *pVal = sqlite3Expr(db, TK_REGISTER, 0);
|
||||
if( pVal==0 ) return;
|
||||
pVal->iTable = iReg;
|
||||
pNew = sqlite3PExpr(pParse, TK_MATCH, 0, pVal);
|
||||
}
|
||||
if( pNew ){
|
||||
WhereTerm *pTerm;
|
||||
int idx;
|
||||
idx = whereClauseInsert(pWC, pNew, TERM_DYNAMIC|TERM_VIRTUAL);
|
||||
pTerm = &pWC->a[idx];
|
||||
pTerm->leftCursor = iCsr;
|
||||
pTerm->eOperator = WO_AUX;
|
||||
pTerm->eMatchOp = eMatchOp;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** Possibly add terms corresponding to the LIMIT and OFFSET clauses of the
|
||||
** SELECT statement passed as the second argument. These terms are only
|
||||
** added if:
|
||||
**
|
||||
** 1. The SELECT statement has a LIMIT clause, and
|
||||
** 2. The SELECT statement is not an aggregate or DISTINCT query, and
|
||||
** 3. The SELECT statement has exactly one object in its from clause, and
|
||||
** that object is a virtual table, and
|
||||
** 4. There are no terms in the WHERE clause that will not be passed
|
||||
** to the virtual table xBestIndex method.
|
||||
** 5. The ORDER BY clause, if any, will be made available to the xBestIndex
|
||||
** method.
|
||||
**
|
||||
** LIMIT and OFFSET terms are ignored by most of the planner code. They
|
||||
** exist only so that they may be passed to the xBestIndex method of the
|
||||
** single virtual table in the FROM clause of the SELECT.
|
||||
*/
|
||||
void sqlite3WhereAddLimit(WhereClause *pWC, Select *p){
|
||||
assert( p==0 || (p->pGroupBy==0 && (p->selFlags & SF_Aggregate)==0) );
|
||||
if( (p && p->pLimit) /* 1 */
|
||||
&& (p->selFlags & (SF_Distinct|SF_Aggregate))==0 /* 2 */
|
||||
&& (p->pSrc->nSrc==1 && IsVirtual(p->pSrc->a[0].pTab)) /* 3 */
|
||||
){
|
||||
ExprList *pOrderBy = p->pOrderBy;
|
||||
int iCsr = p->pSrc->a[0].iCursor;
|
||||
int ii;
|
||||
|
||||
/* Check condition (4). Return early if it is not met. */
|
||||
for(ii=0; ii<pWC->nTerm; ii++){
|
||||
if( pWC->a[ii].wtFlags & TERM_CODED ){
|
||||
/* This term is a vector operation that has been decomposed into
|
||||
** other, subsequent terms. It can be ignored. See tag-20220128a */
|
||||
assert( pWC->a[ii].wtFlags & TERM_VIRTUAL );
|
||||
assert( pWC->a[ii].eOperator==0 );
|
||||
continue;
|
||||
}
|
||||
if( pWC->a[ii].leftCursor!=iCsr ) return;
|
||||
}
|
||||
|
||||
/* Check condition (5). Return early if it is not met. */
|
||||
if( pOrderBy ){
|
||||
for(ii=0; ii<pOrderBy->nExpr; ii++){
|
||||
Expr *pExpr = pOrderBy->a[ii].pExpr;
|
||||
if( pExpr->op!=TK_COLUMN ) return;
|
||||
if( pExpr->iTable!=iCsr ) return;
|
||||
if( pOrderBy->a[ii].sortFlags & KEYINFO_ORDER_BIGNULL ) return;
|
||||
}
|
||||
}
|
||||
|
||||
/* All conditions are met. Add the terms to the where-clause object. */
|
||||
assert( p->pLimit->op==TK_LIMIT );
|
||||
whereAddLimitExpr(pWC, p->iLimit, p->pLimit->pLeft,
|
||||
iCsr, SQLITE_INDEX_CONSTRAINT_LIMIT);
|
||||
if( p->iOffset>0 ){
|
||||
whereAddLimitExpr(pWC, p->iOffset, p->pLimit->pRight,
|
||||
iCsr, SQLITE_INDEX_CONSTRAINT_OFFSET);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** Initialize a preallocated WhereClause structure.
|
||||
*/
|
||||
@@ -1557,6 +1667,7 @@ void sqlite3WhereClauseClear(WhereClause *pWC){
|
||||
}
|
||||
#endif
|
||||
while(1){
|
||||
assert( a->eMatchOp==0 || a->eOperator==WO_AUX );
|
||||
if( a->wtFlags & TERM_DYNAMIC ){
|
||||
sqlite3ExprDelete(db, a->pExpr);
|
||||
}
|
||||
@@ -1714,6 +1825,7 @@ void sqlite3WhereTabFuncArgs(
|
||||
pColRef->iColumn = k++;
|
||||
assert( ExprUseYTab(pColRef) );
|
||||
pColRef->y.pTab = pTab;
|
||||
pItem->colUsed |= sqlite3ExprColUsed(pColRef);
|
||||
pRhs = sqlite3PExpr(pParse, TK_UPLUS,
|
||||
sqlite3ExprDup(pParse->db, pArgs->a[j].pExpr, 0), 0);
|
||||
pTerm = sqlite3PExpr(pParse, TK_EQ, pColRef, pRhs);
|
||||
|
@@ -969,4 +969,13 @@ foreach {tn file rc} {
|
||||
db2 close
|
||||
}
|
||||
|
||||
# 2021-01-31 https://sqlite.org/forum/forumpost/8b39fbf3e7
|
||||
#
|
||||
do_test backup-11.1 {
|
||||
sqlite3 db1 :memory:
|
||||
sqlite3 db2 :memory:
|
||||
sqlite3_backup B db1 main db2 temp
|
||||
B finish
|
||||
} {SQLITE_OK}
|
||||
|
||||
finish_test
|
||||
|
@@ -29,7 +29,10 @@ proc vtab_command {method args} {
|
||||
}
|
||||
|
||||
xBestIndex {
|
||||
set clist [lindex $args 0]
|
||||
set hdl [lindex $args 0]
|
||||
set clist [$hdl constraints]
|
||||
set orderby [$hdl orderby]
|
||||
|
||||
if {[llength $clist]!=1} { error "unexpected constraint list" }
|
||||
catch { array unset C }
|
||||
array set C [lindex $clist 0]
|
||||
@@ -76,10 +79,13 @@ proc t1_vtab {mode method args} {
|
||||
}
|
||||
|
||||
xBestIndex {
|
||||
set hdl [lindex $args 0]
|
||||
set clist [$hdl constraints]
|
||||
set orderby [$hdl orderby]
|
||||
|
||||
set SQL_FILTER {SELECT * FROM t1x WHERE a='%1%'}
|
||||
set SQL_SCAN {SELECT * FROM t1x}
|
||||
|
||||
set clist [lindex $args 0]
|
||||
set idx 0
|
||||
for {set idx 0} {$idx < [llength $clist]} {incr idx} {
|
||||
array unset C
|
||||
@@ -177,7 +183,10 @@ proc vtab_command {method args} {
|
||||
}
|
||||
|
||||
xBestIndex {
|
||||
set clist [lindex $args 0]
|
||||
set hdl [lindex $args 0]
|
||||
set clist [$hdl constraints]
|
||||
set orderby [$hdl orderby]
|
||||
|
||||
#puts $clist
|
||||
set W [list]
|
||||
set U [list]
|
||||
@@ -287,12 +296,17 @@ proc vtab_command {method args} {
|
||||
}
|
||||
|
||||
xBestIndex {
|
||||
set clist [lindex $args 0]
|
||||
set hdl [lindex $args 0]
|
||||
set clist [$hdl constraints]
|
||||
set orderby [$hdl orderby]
|
||||
|
||||
lappend ::bestindex_calls $clist
|
||||
set ret "cost 1000000 idxnum 555"
|
||||
for {set i 0} {$i < [llength $clist]} {incr i} {
|
||||
array set C [lindex $clist $i]
|
||||
if {$C(usable)} { lappend ret use $i }
|
||||
if {$C(usable)} {
|
||||
lappend ret use $i
|
||||
}
|
||||
}
|
||||
return $ret
|
||||
}
|
||||
|
@@ -47,7 +47,10 @@ proc vtab_cmd {tbl cols method args} {
|
||||
return "CREATE TABLE $tbl ([join $cols ,])"
|
||||
}
|
||||
xBestIndex {
|
||||
foreach {clist orderby mask} $args {}
|
||||
set hdl [lindex $args 0]
|
||||
set clist [$hdl constraints]
|
||||
set orderby [$hdl orderby]
|
||||
set mask [$hdl mask]
|
||||
|
||||
set cons [list]
|
||||
set used [list]
|
||||
|
@@ -34,7 +34,10 @@ proc vtab_cmd {bOmit method args} {
|
||||
}
|
||||
|
||||
xBestIndex {
|
||||
foreach {clist orderby mask} $args {}
|
||||
set hdl [lindex $args 0]
|
||||
set clist [$hdl constraints]
|
||||
set orderby [$hdl orderby]
|
||||
set mask [$hdl mask]
|
||||
|
||||
set ret [list]
|
||||
set use use
|
||||
|
@@ -48,7 +48,10 @@ proc vtab_cmd {param method args} {
|
||||
}
|
||||
|
||||
xBestIndex {
|
||||
foreach {clist orderby mask} $args {}
|
||||
set hdl [lindex $args 0]
|
||||
set clist [$hdl constraints]
|
||||
set orderby [$hdl orderby]
|
||||
set mask [$hdl mask]
|
||||
|
||||
set ret [list]
|
||||
|
||||
@@ -135,7 +138,11 @@ proc vtab_command {method args} {
|
||||
}
|
||||
|
||||
xBestIndex {
|
||||
set clist [lindex $args 0]
|
||||
set hdl [lindex $args 0]
|
||||
set clist [$hdl constraints]
|
||||
set orderby [$hdl orderby]
|
||||
set mask [$hdl mask]
|
||||
|
||||
if {[llength $clist]!=1} { error "unexpected constraint list" }
|
||||
catch { array unset C }
|
||||
array set C [lindex $clist 0]
|
||||
|
@@ -14,7 +14,7 @@
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
set testprefix bestindex4
|
||||
set testprefix bestindex5
|
||||
|
||||
ifcapable !vtab {
|
||||
finish_test
|
||||
@@ -44,7 +44,10 @@ proc vtab_cmd {method args} {
|
||||
}
|
||||
|
||||
xBestIndex {
|
||||
foreach {clist orderby mask} $args {}
|
||||
set hdl [lindex $args 0]
|
||||
set clist [$hdl constraints]
|
||||
set orderby [$hdl orderby]
|
||||
set mask [$hdl mask]
|
||||
|
||||
set cost 1000000.0
|
||||
set ret [list]
|
||||
|
@@ -28,7 +28,11 @@ proc vtab_command {src method args} {
|
||||
}
|
||||
|
||||
xBestIndex {
|
||||
set clist [lindex $args 0]
|
||||
set hdl [lindex $args 0]
|
||||
set clist [$hdl constraints]
|
||||
set orderby [$hdl orderby]
|
||||
set mask [$hdl mask]
|
||||
|
||||
set wlist 1
|
||||
|
||||
set iCons 0
|
||||
|
@@ -28,7 +28,11 @@ proc vtab_command {src method args} {
|
||||
}
|
||||
|
||||
xBestIndex {
|
||||
set clist [lindex $args 0]
|
||||
set hdl [lindex $args 0]
|
||||
set clist [$hdl constraints]
|
||||
set orderby [$hdl orderby]
|
||||
set mask [$hdl mask]
|
||||
|
||||
set iCons 0
|
||||
set ret [list]
|
||||
foreach cons $clist {
|
||||
|
463
test/bestindex8.test
Normal file
463
test/bestindex8.test
Normal file
@@ -0,0 +1,463 @@
|
||||
# 2020-01-29
|
||||
#
|
||||
# The author disclaims copyright to this source code. In place of
|
||||
# a legal notice, here is a blessing:
|
||||
#
|
||||
# May you do good and not evil.
|
||||
# May you find forgiveness for yourself and forgive others.
|
||||
# May you share freely, never taking more than you give.
|
||||
#
|
||||
#***********************************************************************
|
||||
#
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
set testprefix bestindex8
|
||||
|
||||
ifcapable !vtab {
|
||||
finish_test
|
||||
return
|
||||
}
|
||||
|
||||
register_tcl_module db
|
||||
|
||||
proc vtab_command {src method args} {
|
||||
switch -- $method {
|
||||
xConnect {
|
||||
return "CREATE TABLE xxx(a, b)"
|
||||
}
|
||||
|
||||
xBestIndex {
|
||||
set hdl [lindex $args 0]
|
||||
set clist [$hdl constraints]
|
||||
set orderby [$hdl orderby]
|
||||
lappend ::lBestIndexDistinct [$hdl distinct]
|
||||
|
||||
#puts "ORDERBY: $orderby"
|
||||
set iCons 0
|
||||
set ret [list]
|
||||
foreach cons $clist {
|
||||
catch { array unset C }
|
||||
array set C $cons
|
||||
if {$C(usable)} {
|
||||
lappend ret use $iCons
|
||||
}
|
||||
incr iCons
|
||||
}
|
||||
if {$orderby=="{column 0 desc 0} {column 1 desc 0}"
|
||||
|| $orderby=="{column 0 desc 0}"
|
||||
} {
|
||||
lappend ret orderby 1
|
||||
lappend ret idxnum 1
|
||||
set ::lOrderByConsumed 1
|
||||
}
|
||||
return $ret
|
||||
}
|
||||
|
||||
xFilter {
|
||||
set idxnum [lindex $args 0]
|
||||
if {$idxnum} {
|
||||
return [list sql "SELECT rowid, a, b FROM $src order by 2, 3"]
|
||||
}
|
||||
return [list sql "SELECT rowid, a, b FROM $src"]
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return {}
|
||||
}
|
||||
|
||||
do_execsql_test 1.0 {
|
||||
CREATE TABLE t1(a, b);
|
||||
CREATE INDEX i1 ON t1(a, b);
|
||||
INSERT INTO t1 VALUES('a', 'b'), ('c', 'd');
|
||||
INSERT INTO t1 VALUES('a', 'b'), ('c', 'd');
|
||||
CREATE VIRTUAL TABLE vt1 USING tcl(vtab_command t1);
|
||||
|
||||
CREATE TABLE t0(c0);
|
||||
INSERT INTO t0(c0) VALUES (1), (0);
|
||||
}
|
||||
|
||||
foreach {tn sql bDistinct idxinsert bConsumed res} {
|
||||
1 "SELECT a, b FROM vt1" 0 0 0 {a b c d a b c d}
|
||||
2 "SELECT DISTINCT a, b FROM vt1" 2 1 1 {a b c d}
|
||||
3 "SELECT DISTINCT a FROM vt1" 2 1 1 {a c}
|
||||
4 "SELECT DISTINCT b FROM vt1" 2 1 0 {b d}
|
||||
5 "SELECT DISTINCT b FROM vt1 ORDER BY a" 0 1 1 {b d}
|
||||
6 "SELECT DISTINCT t0.c0 FROM vt1, t0 ORDER BY vt1.a" 0 1 1 {1 0}
|
||||
7 "SELECT DISTINCT a, b FROM vt1 ORDER BY a, b" 1 0 1 {a b c d}
|
||||
8 "SELECT DISTINCT a, b FROM vt1 ORDER BY a" 0 1 1 {a b c d}
|
||||
9 "SELECT DISTINCT a FROM vt1 ORDER BY a, b" 0 1 1 {a c}
|
||||
|
||||
10 "SELECT DISTINCT a, b FROM vt1 WHERE b='b'" 2 1 1 {a b}
|
||||
11 "SELECT DISTINCT a, b FROM vt1 WHERE +b='b'" 2 1 1 {a b}
|
||||
} {
|
||||
set ::lBestIndexDistinct ""
|
||||
set ::lOrderByConsumed 0
|
||||
do_execsql_test 1.$tn.1 $sql $res
|
||||
do_test 1.$tn.2 {
|
||||
set ::lBestIndexDistinct
|
||||
} $bDistinct
|
||||
do_test 1.$tn.3 {
|
||||
expr {[lsearch [execsql "explain $sql"] IdxInsert]>=0}
|
||||
} $idxinsert
|
||||
do_test 1.$tn.4 {
|
||||
set ::lOrderByConsumed
|
||||
} $bConsumed
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
reset_db
|
||||
register_tcl_module db
|
||||
|
||||
proc vtab_command {src method args} {
|
||||
switch -- $method {
|
||||
xConnect {
|
||||
return "CREATE TABLE xxx(a, b)"
|
||||
}
|
||||
|
||||
xBestIndex {
|
||||
set hdl [lindex $args 0]
|
||||
set ret [list]
|
||||
|
||||
set iCons 0
|
||||
foreach cons [$hdl constraints] {
|
||||
array set C $cons
|
||||
if {($C(op)=="limit" || $C(op)=="offset") && $C(usable)} {
|
||||
lappend ret use $iCons
|
||||
}
|
||||
incr iCons
|
||||
}
|
||||
|
||||
return $ret
|
||||
}
|
||||
|
||||
xFilter {
|
||||
lappend ::lFilterArgs [lindex $args 2]
|
||||
return [list sql "SELECT rowid, a, b FROM $src"]
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return {}
|
||||
}
|
||||
|
||||
do_execsql_test 2.0 {
|
||||
CREATE TABLE t1(a, b);
|
||||
CREATE INDEX i1 ON t1(a, b);
|
||||
CREATE VIRTUAL TABLE vt1 USING tcl(vtab_command t1);
|
||||
}
|
||||
|
||||
do_test 2.1 {
|
||||
set ::lFilterArgs [list]
|
||||
execsql { SELECT * FROM vt1 LIMIT 10 }
|
||||
set ::lFilterArgs
|
||||
} {10}
|
||||
|
||||
do_test 2.2 {
|
||||
set ::lFilterArgs [list]
|
||||
execsql { SELECT * FROM vt1 LIMIT 5 OFFSET 50 }
|
||||
set ::lFilterArgs
|
||||
} {{5 50}}
|
||||
|
||||
do_test 2.3 {
|
||||
set ::lFilterArgs [list]
|
||||
execsql { SELECT * FROM vt1 ORDER BY a, b LIMIT 1 OFFSET 1 }
|
||||
set ::lFilterArgs
|
||||
} {{1 1}}
|
||||
|
||||
do_test 2.4 {
|
||||
set ::lFilterArgs [list]
|
||||
execsql { SELECT * FROM vt1 ORDER BY a, +b LIMIT 1 OFFSET 1 }
|
||||
set ::lFilterArgs
|
||||
} {{}}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
reset_db
|
||||
register_tcl_module db
|
||||
|
||||
proc vtab_command {src method args} {
|
||||
switch -- $method {
|
||||
xConnect {
|
||||
return "CREATE TABLE xxx(a, b)"
|
||||
}
|
||||
|
||||
xBestIndex {
|
||||
set hdl [lindex $args 0]
|
||||
set lCons [$hdl constraints]
|
||||
|
||||
set ret [list]
|
||||
for {set i 0} {$i < [llength $lCons]} {incr i} {
|
||||
array set C [lindex $lCons $i]
|
||||
if {$C(usable)} {
|
||||
lappend ret use $i
|
||||
$hdl in $i 1
|
||||
}
|
||||
}
|
||||
return $ret
|
||||
}
|
||||
|
||||
xFilter {
|
||||
set lArg [lindex $args 2]
|
||||
lappend ::lFilterArg {*}$lArg
|
||||
return [list sql "SELECT rowid, a, b FROM $src"]
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return {}
|
||||
}
|
||||
|
||||
do_execsql_test 3.0 {
|
||||
CREATE TABLE t1(a, b);
|
||||
CREATE INDEX i1 ON t1(a, b);
|
||||
CREATE VIRTUAL TABLE vt1 USING tcl(vtab_command t1);
|
||||
}
|
||||
|
||||
foreach {tn sql lfa} {
|
||||
1 "SELECT * FROM vt1 WHERE b IN (10, 20, 30)" {{10 20 30}}
|
||||
2 "SELECT * FROM vt1 WHERE b IN ('abc', 'def')" {{abc def}}
|
||||
3 "SELECT * FROM vt1 WHERE a IS NULL AND b IN ('abc', 'def')" {{} {abc def}}
|
||||
4 "SELECT * FROM vt1 WHERE a IN (1,2,3) AND b IN ('abc', 'def')"
|
||||
{{1 2 3} {abc def}}
|
||||
|
||||
5 "SELECT * FROM vt1
|
||||
WHERE a IN (SELECT 1 UNION SELECT 2) AND b IN ('abc', 'def')"
|
||||
{{1 2} {abc def}}
|
||||
|
||||
6 "SELECT * FROM vt1
|
||||
WHERE b IN ('abc', 'def') AND a IN (SELECT 1 UNION SELECT 2)"
|
||||
{{abc def} {1 2}}
|
||||
} {
|
||||
do_test 3.$tn {
|
||||
set ::lFilterArg [list]
|
||||
execsql $sql
|
||||
set ::lFilterArg
|
||||
} $lfa
|
||||
}
|
||||
|
||||
#explain_i { SELECT * FROM vt1 WHERE b IN (10, 20, 30) }
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
reset_db
|
||||
register_tcl_module db
|
||||
|
||||
proc vtab_command {src method args} {
|
||||
switch -- $method {
|
||||
xConnect {
|
||||
return "CREATE TABLE xxx(a, b, c)"
|
||||
}
|
||||
|
||||
xBestIndex {
|
||||
set hdl [lindex $args 0]
|
||||
set lCons [$hdl constraints]
|
||||
|
||||
set ret [list]
|
||||
for {set i 0} {$i < [llength $lCons]} {incr i} {
|
||||
lappend ::lBestIndexRhs [$hdl rhs_value $i -]
|
||||
}
|
||||
return $ret
|
||||
}
|
||||
|
||||
xFilter {
|
||||
return [list sql "SELECT rowid, a, b, c FROM $src"]
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return {}
|
||||
}
|
||||
|
||||
do_execsql_test 4.0 {
|
||||
CREATE TABLE t1(a, b, c);
|
||||
CREATE VIRTUAL TABLE vt1 USING tcl(vtab_command t1);
|
||||
}
|
||||
|
||||
foreach {tn sql lbir} {
|
||||
1 "SELECT * FROM vt1 WHERE b = 10" {10}
|
||||
2 "SELECT * FROM vt1 WHERE a = 'abc' AND b < 30" {abc 30}
|
||||
3 "SELECT * FROM vt1 WHERE a = 'abc' AND b < 30+2" {abc -}
|
||||
4 "SELECT * FROM vt1 WHERE a IN (1,2,3) AND b < 30+2" {- -}
|
||||
5 "SELECT * FROM vt1 WHERE a IS 111 AND b < 30+2" {111 -}
|
||||
} {
|
||||
do_test 4.$tn {
|
||||
set ::lBestIndexRhs [list]
|
||||
execsql $sql
|
||||
set ::lBestIndexRhs
|
||||
} $lbir
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
reset_db
|
||||
db cache size 0
|
||||
register_tcl_module db
|
||||
|
||||
set ::vtab_handle_in 1
|
||||
proc vtab_command {src method args} {
|
||||
switch -- $method {
|
||||
xConnect {
|
||||
return "CREATE TABLE xxx(a, b, c)"
|
||||
}
|
||||
|
||||
xBestIndex {
|
||||
set lCols [list a b c]
|
||||
|
||||
set hdl [lindex $args 0]
|
||||
set lCons [$hdl constraints]
|
||||
set lOrder [$hdl order]
|
||||
|
||||
set L ""
|
||||
set O ""
|
||||
set W [list]
|
||||
set a 0
|
||||
for {set i 0} {$i < [llength $lCons]} {incr i} {
|
||||
array set C [lindex $lCons $i]
|
||||
if {$C(usable)} {
|
||||
if { $C(op)=="eq" } {
|
||||
set bIn 0
|
||||
if {$::vtab_handle_in} { set bIn [$hdl in $i 1] }
|
||||
if {$bIn} {
|
||||
lappend W "[lindex $lCols $C(column)] IN (%I$a%)"
|
||||
} else {
|
||||
lappend W "[lindex $lCols $C(column)] = %$a%"
|
||||
}
|
||||
lappend ret omit $i
|
||||
}
|
||||
if { $C(op)=="limit" } { set L " LIMIT %$a%" ; lappend ret use $i }
|
||||
if { $C(op)=="offset" } { set O " OFFSET %$a%" ; lappend ret use $i }
|
||||
incr a
|
||||
}
|
||||
}
|
||||
|
||||
set order ""
|
||||
set selectlist "rowid, a, b, c"
|
||||
if {[llength $lOrder]} {
|
||||
array set sl [list]
|
||||
set lO [list]
|
||||
foreach s $lOrder {
|
||||
array set C $s
|
||||
set ad ""
|
||||
if {$C(desc)} { set ad " DESC" }
|
||||
lappend lO "[lindex $lCols $C(column)]$ad"
|
||||
set sl($C(column)) 1
|
||||
}
|
||||
if {[$hdl distinct]==2} {
|
||||
set selectlist "DISTINCT 0"
|
||||
foreach i {0 1 2} {
|
||||
if {[info exists sl($i)]} {
|
||||
append selectlist ", [lindex $lCols $i]"
|
||||
} else {
|
||||
append selectlist ", 0"
|
||||
}
|
||||
}
|
||||
} else {
|
||||
set order " ORDER BY [join $lO ,]"
|
||||
}
|
||||
}
|
||||
|
||||
set where ""
|
||||
if {[llength $W]} { set where " WHERE [join $W { AND }]" }
|
||||
set sql "SELECT $selectlist FROM $src$where$order$L$O"
|
||||
|
||||
lappend ret idxStr $sql
|
||||
return $ret
|
||||
}
|
||||
|
||||
xFilter {
|
||||
foreach {idxnum idxstr lArg} $args {}
|
||||
set ii 0
|
||||
set sql $idxstr
|
||||
foreach a $lArg {
|
||||
set sql [string map [list %$ii% $a] $sql]
|
||||
set sql [string map [list %I$ii% [join $a ,]] $sql]
|
||||
incr ii
|
||||
}
|
||||
lappend ::lFilterSql $sql
|
||||
|
||||
if {[regexp {OFFSET (.*)$} $sql -> off]} {
|
||||
set real_sql "
|
||||
WITH c(i) AS ( SELECT 1 UNION ALL SELECT i+1 FROM c WHERE i<$off )
|
||||
SELECT 0,0,0,0 FROM c
|
||||
UNION ALL SELECT * FROM (
|
||||
$sql
|
||||
)
|
||||
"
|
||||
} else {
|
||||
set real_sql $sql
|
||||
}
|
||||
|
||||
return [list sql $real_sql]
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return {}
|
||||
}
|
||||
|
||||
do_execsql_test 5.0 {
|
||||
CREATE TABLE t1(a, b, c);
|
||||
CREATE VIRTUAL TABLE vt1 USING tcl(vtab_command t1);
|
||||
INSERT INTO t1 VALUES(1, 2, 3);
|
||||
INSERT INTO t1 VALUES(2, 3, 4);
|
||||
INSERT INTO t1 VALUES(3, 4, 5);
|
||||
INSERT INTO t1 VALUES(1, 5, 6);
|
||||
INSERT INTO t1 VALUES(2, 6, 7);
|
||||
INSERT INTO t1 VALUES(3, 7, 8);
|
||||
INSERT INTO t1 VALUES(1, 8, 9);
|
||||
INSERT INTO t1 VALUES(2, 9, 0);
|
||||
}
|
||||
|
||||
proc do_vtab_test {tn sql vtsql {res {}}} {
|
||||
set ::lFilterSql [list]
|
||||
uplevel [list do_execsql_test $tn.1 $sql $res]
|
||||
uplevel [list do_test $tn.2 {set ::lFilterSql} [list {*}$vtsql]]
|
||||
}
|
||||
|
||||
do_vtab_test 5.1.1 {
|
||||
SELECT DISTINCT a FROM vt1
|
||||
} {
|
||||
{SELECT DISTINCT 0, a, 0, 0 FROM t1}
|
||||
} {1 2 3}
|
||||
|
||||
do_vtab_test 5.1.2 {
|
||||
SELECT DISTINCT a FROM vt1 ORDER BY a
|
||||
} {
|
||||
{SELECT rowid, a, b, c FROM t1 ORDER BY a}
|
||||
} {1 2 3}
|
||||
|
||||
do_vtab_test 5.1.3 {
|
||||
SELECT DISTINCT a FROM vt1 WHERE c IN (4,5,6,7,8)
|
||||
} {
|
||||
{SELECT DISTINCT 0, a, 0, 0 FROM t1 WHERE c IN (4,5,6,7,8)}
|
||||
} {2 3 1}
|
||||
|
||||
set ::vtab_handle_in 0
|
||||
do_vtab_test 5.1.4 {
|
||||
SELECT DISTINCT a FROM vt1 WHERE c IN (4,5,6,7,8)
|
||||
} {
|
||||
{SELECT DISTINCT 0, a, 0, 0 FROM t1 WHERE c = 4}
|
||||
{SELECT DISTINCT 0, a, 0, 0 FROM t1 WHERE c = 5}
|
||||
{SELECT DISTINCT 0, a, 0, 0 FROM t1 WHERE c = 6}
|
||||
{SELECT DISTINCT 0, a, 0, 0 FROM t1 WHERE c = 7}
|
||||
{SELECT DISTINCT 0, a, 0, 0 FROM t1 WHERE c = 8}
|
||||
} {2 3 1}
|
||||
|
||||
set ::vtab_handle_in 1
|
||||
do_vtab_test 5.1.5a {
|
||||
SELECT a, b, c FROM vt1 WHERE c IN (4,5,6,7,8) LIMIT 2 OFFSET 2
|
||||
} {
|
||||
{SELECT rowid, a, b, c FROM t1 WHERE c IN (4,5,6,7,8) LIMIT 2 OFFSET 2}
|
||||
} {1 5 6 2 6 7}
|
||||
|
||||
set ::vtab_handle_in 0
|
||||
do_vtab_test 5.1.5b {
|
||||
SELECT a, b, c FROM vt1 WHERE c IN (4,5,6,7,8) LIMIT 2 OFFSET 2
|
||||
} {
|
||||
{SELECT rowid, a, b, c FROM t1 WHERE c = 4}
|
||||
{SELECT rowid, a, b, c FROM t1 WHERE c = 5}
|
||||
{SELECT rowid, a, b, c FROM t1 WHERE c = 6}
|
||||
{SELECT rowid, a, b, c FROM t1 WHERE c = 7}
|
||||
} {1 5 6 2 6 7}
|
||||
set ::vtab_handle_in 1
|
||||
|
||||
finish_test
|
149
test/date3.test
Normal file
149
test/date3.test
Normal file
@@ -0,0 +1,149 @@
|
||||
# 2022-01-27
|
||||
#
|
||||
# The author disclaims copyright to this source code. In place of
|
||||
# a legal notice, here is a blessing:
|
||||
#
|
||||
# May you do good and not evil.
|
||||
# May you find forgiveness for yourself and forgive others.
|
||||
# May you share freely, never taking more than you give.
|
||||
#
|
||||
#***********************************************************************
|
||||
# This file implements regression tests for SQLite library. The
|
||||
# focus of this file is testing date and time functions.
|
||||
#
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
|
||||
# Skip this whole file if date and time functions are omitted
|
||||
# at compile-time
|
||||
#
|
||||
ifcapable {!datetime} {
|
||||
finish_test
|
||||
return
|
||||
}
|
||||
|
||||
proc datetest {tnum expr result} {
|
||||
do_test date3-$tnum [subst {
|
||||
execsql "SELECT coalesce($expr,'NULL')"
|
||||
}] [list $result]
|
||||
}
|
||||
set tcl_precision 15
|
||||
|
||||
# EVIDENCE-OF: R-45708-63005 unixepoch(time-value, modifier, modifier,
|
||||
# ...)
|
||||
#
|
||||
datetest 1.1 {unixepoch('1970-01-01')} {0}
|
||||
datetest 1.2 {unixepoch('1969-12-31 23:59:59')} {-1}
|
||||
datetest 1.3 {unixepoch('2106-02-07 06:28:15')} {4294967295}
|
||||
datetest 1.4 {unixepoch('2106-02-07 06:28:16')} {4294967296}
|
||||
datetest 1.5 {unixepoch('9999-12-31 23:59:59')} {253402300799}
|
||||
datetest 1.6 {unixepoch('0000-01-01 00:00:00')} {-62167219200}
|
||||
|
||||
# EVIDENCE-OF: R-30877-63179 The unixepoch() function returns a unix
|
||||
# timestamp - the number of seconds since 1970-01-01 00:00:00 UTC.
|
||||
#
|
||||
for {set i 1} {$i<=100} {incr i} {
|
||||
set x [expr {int(rand()*0xfffffffff)-0xffffffff}]
|
||||
datetest 1.7.$i "unixepoch($x,'unixepoch')==$x" {1}
|
||||
}
|
||||
|
||||
# EVIDENCE-OF: R-62992-54137 The unixepoch() always returns an integer,
|
||||
# even if the input time-value has millisecond precision.
|
||||
#
|
||||
datetest 1.8 {unixepoch('2022-01-27 12:59:28.052')} {1643288368}
|
||||
|
||||
# EVIDENCE-OF: R-05412-24332 If the time-value is numeric (the
|
||||
# DDDDDDDDDD format) then the 'auto' modifier causes the time-value to
|
||||
# interpreted as either a julian day number or a unix timestamp,
|
||||
# depending on its magnitude.
|
||||
#
|
||||
# EVIDENCE-OF: R-56763-40111 If the value is between 0.0 and
|
||||
# 5373484.499999, then it is interpreted as a julian day number
|
||||
# (corresponding to dates between -4713-11-24 12:00:00 and 9999-12-31
|
||||
# 23:59:59, inclusive).
|
||||
#
|
||||
# EVIDENCE-OF: R-07289-49223 For numeric values outside of the range of
|
||||
# valid julian day numbers, but within the range of -210866760000 to
|
||||
# 253402300799, the 'auto' modifier causes the value to be interpreted
|
||||
# as a unix timestamp.
|
||||
#
|
||||
# EVIDENCE-OF: R-20795-34947 Other numeric values are out of range and
|
||||
# cause a NULL return.
|
||||
#
|
||||
foreach {tn jd date} {
|
||||
2.1 0.0 {-4713-11-24 12:00:00}
|
||||
2.2 5373484.4999999 {9999-12-31 23:59:59}
|
||||
2.3 2440587.5 {1970-01-01 00:00:00}
|
||||
2.4 2440587.49998843 {1969-12-31 23:59:59}
|
||||
2.5 2440615.7475463 {1970-01-29 05:56:28}
|
||||
|
||||
2.10 -1 {1969-12-31 23:59:59}
|
||||
2.11 5373485 {1970-03-04 04:38:05}
|
||||
2.12 -210866760000 {-4713-11-24 12:00:00}
|
||||
2.13 253402300799 {9999-12-31 23:59:59}
|
||||
|
||||
2.20 -210866760001 {NULL}
|
||||
2.21 253402300800 {NULL}
|
||||
} {
|
||||
datetest $tn "datetime($jd,'auto')" $date
|
||||
}
|
||||
|
||||
# EVIDENCE-OF: R-38886-35357 The 'auto' modifier is a no-op for text
|
||||
# time-values.
|
||||
#
|
||||
datetest 2.30 {date('2022-01-29','auto')==date('2022-01-29')} {1}
|
||||
|
||||
# EVIDENCE-OF: R-53132-26856 The 'auto' modifier can be used to work
|
||||
# with date/time values even in cases where it is not known if the
|
||||
# julian day number or unix timestamp formats are in use.
|
||||
#
|
||||
do_execsql_test date3-2.40 {
|
||||
WITH tx(timeval,datetime) AS (
|
||||
VALUES('2022-01-27 13:15:44','2022-01-27 13:15:44'),
|
||||
(2459607.05260275,'2022-01-27 13:15:44'),
|
||||
(1643289344,'2022-01-27 13:15:44')
|
||||
)
|
||||
SELECT datetime(timeval,'auto') == datetime FROM tx;
|
||||
} {1 1 1}
|
||||
|
||||
# EVIDENCE-OF: R-49255-55373 The "unixepoch" modifier (11) only works if
|
||||
# it immediately follows a time value in the DDDDDDDDDD format.
|
||||
#
|
||||
# EVIDENCE-OF: R-23075-39245 This modifier causes the DDDDDDDDDD to be
|
||||
# interpreted not as a Julian day number as it normally would be, but as
|
||||
# Unix Time - the number of seconds since 1970.
|
||||
#
|
||||
datetest 3.1 {datetime(2459607.05,'+1 hour','unixepoch')} {NULL}
|
||||
datetest 3.2 {datetime(2459607.05,'unixepoch','+1 hour')} {1970-01-29 12:13:27}
|
||||
|
||||
# EVIDENCE-OF: R-21150-52363 The "julianday" modifier must immediately
|
||||
# follow the initial time-value which must be of the form DDDDDDDDD.
|
||||
#
|
||||
# EVIDENCE-OF: R-31176-64601 Any other use of the 'julianday' modifier
|
||||
# is an error and causes the function to return NULL.
|
||||
#
|
||||
# EVIDENCE-OF: R-32483-36353 The 'julianday' modifier forces the
|
||||
# time-value number to be interpreted as a julian-day number.
|
||||
#
|
||||
# EVIDENCE-OF: R-25859-20124 The only difference is that adding
|
||||
# 'julianday' forces the DDDDDDDDD time-value format, and causes a NULL
|
||||
# to be returned if any other time-value format is used.
|
||||
#
|
||||
datetest 4.1 {datetime(2459607,'julianday')} {2022-01-27 12:00:00}
|
||||
datetest 4.2 {datetime(2459607,'+1 hour','julianday')} {NULL}
|
||||
datetest 4.3 {datetime('2022-01-27','julianday')} {NULL}
|
||||
|
||||
|
||||
|
||||
# EVIDENCE-OF: R-33431-18865 Unix timestamps for the first 63 days of
|
||||
# 1970 will be interpreted as julian day numbers.
|
||||
#
|
||||
do_execsql_test date3-5.0 {
|
||||
WITH inc(x) AS (VALUES(-10) UNION ALL SELECT x+1 FROM inc WHERE x<100)
|
||||
SELECT count(*) FROM inc
|
||||
WHERE datetime('1970-01-01',format('%+d days',x))
|
||||
<> datetime(unixepoch('1970-01-01',format('%+d days',x)),'auto');
|
||||
} {63}
|
||||
|
||||
finish_test
|
@@ -328,4 +328,18 @@ do_execsql_test 8.1 {
|
||||
2 10 2
|
||||
}
|
||||
|
||||
|
||||
# 2022-01-31 dbsqlfuzz 787d9bd73164c6f0c85469e2e48b2aff19af6938
|
||||
#
|
||||
reset_db
|
||||
do_execsql_test 9.1 {
|
||||
CREATE TABLE t1(a ,b FLOAT);
|
||||
INSERT INTO t1 VALUES(1,1);
|
||||
CREATE INDEX t1x1 ON t1(a,b,a,a,a,a,a,a,a,a,a,b);
|
||||
ANALYZE sqlite_schema;
|
||||
INSERT INTO sqlite_stat1 VALUES('t1','t1x1','648 324 81 81 81 81 81 81 81081 81 81 81');
|
||||
ANALYZE sqlite_schema;
|
||||
SELECT a FROM (SELECT a FROM t1 NATURAL LEFT JOIN t1) NATURAL LEFT JOIN t1 WHERE (rowid,1)<=(5,0);
|
||||
} {1}
|
||||
|
||||
finish_test
|
||||
|
@@ -15,11 +15,6 @@
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
|
||||
ifcapable !json1 {
|
||||
finish_test
|
||||
return
|
||||
}
|
||||
|
||||
do_execsql_test json101-1.1.00 {
|
||||
SELECT json_array(1,2.5,null,'hello');
|
||||
} {[1,2.5,null,"hello"]}
|
||||
@@ -846,5 +841,13 @@ do_execsql_test json-16.30 {
|
||||
SELECT unicode(json_extract('"\uD834\uDD1E"','$'));
|
||||
} {119070}
|
||||
|
||||
# 2022-01-30 dbsqlfuzz 4678cf825d27f87c9b8343720121e12cf944b71a
|
||||
do_execsql_test json-17.1 {
|
||||
DROP TABLE IF EXISTS t1;
|
||||
DROP TABLE IF EXISTS t2;
|
||||
CREATE TABLE t1(a,b,c);
|
||||
CREATE TABLE t2(d);
|
||||
SELECT * FROM t1 LEFT JOIN t2 ON (SELECT b FROM json_each ORDER BY 1);
|
||||
} {}
|
||||
|
||||
finish_test
|
||||
|
@@ -18,11 +18,6 @@
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
|
||||
ifcapable !json1 {
|
||||
finish_test
|
||||
return
|
||||
}
|
||||
|
||||
do_execsql_test json102-100 {
|
||||
SELECT json_object('ex','[52,3.14159]');
|
||||
} {{{"ex":"[52,3.14159]"}}}
|
||||
|
@@ -14,11 +14,6 @@
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
|
||||
ifcapable !json1 {
|
||||
finish_test
|
||||
return
|
||||
}
|
||||
|
||||
do_execsql_test json103-100 {
|
||||
CREATE TABLE t1(a,b,c);
|
||||
WITH RECURSIVE c(x) AS (VALUES(1) UNION SELECT x+1 FROM c WHERE x<100)
|
||||
|
@@ -15,11 +15,6 @@ set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
set testprefix json104
|
||||
|
||||
ifcapable !json1 {
|
||||
finish_test
|
||||
return
|
||||
}
|
||||
|
||||
# This is the example from pages 2 and 3 of RFC-7396
|
||||
do_execsql_test json104-100 {
|
||||
SELECT json_patch('{
|
||||
|
@@ -15,11 +15,6 @@ set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
set testprefix json104
|
||||
|
||||
ifcapable !json1 {
|
||||
finish_test
|
||||
return
|
||||
}
|
||||
|
||||
# This is the example from pages 2 and 3 of RFC-7396
|
||||
db eval {
|
||||
CREATE TABLE t1(j);
|
||||
|
@@ -708,5 +708,23 @@ do_execsql_test 31.2 {
|
||||
SELECT * FROM t1 LEFT JOIN t2 ON b=NULL WHERE (c,d)==(SELECT 123, 456+a);
|
||||
} {}
|
||||
|
||||
# 2022-02-03 dbsqlfuzz 80a9fade844b4fb43564efc972bcb2c68270f5d1
|
||||
reset_db
|
||||
do_execsql_test 32.1 {
|
||||
CREATE TABLE t1(a INTEGER PRIMARY KEY, b INT, c INT);
|
||||
CREATE TABLE t2(d INTEGER PRIMARY KEY);
|
||||
INSERT INTO t1(a,b,c) VALUES(500,654,456);
|
||||
INSERT INTO t1(a,b,c) VALUES(501,655,456);
|
||||
INSERT INTO t1(a,b,c) VALUES(502,654,122);
|
||||
INSERT INTO t1(a,b,c) VALUES(503,654,221);
|
||||
INSERT INTO t1(a,b,c) VALUES(601,654,122);
|
||||
INSERT INTO t2(d) VALUES(456);
|
||||
INSERT INTO t2(d) VALUES(122);
|
||||
SELECT a FROM (
|
||||
SELECT t1.a FROM t2, t1
|
||||
WHERE (987, t1.b) = ( SELECT 987, 654 ) AND t2.d=t1.c
|
||||
) AS t3
|
||||
WHERE a=1234 OR a<=567;
|
||||
} {500 502}
|
||||
|
||||
finish_test
|
||||
|
@@ -46,7 +46,9 @@ proc vtab_command {method args} {
|
||||
set OP(glob) GLOB
|
||||
set OP(regexp) REGEXP
|
||||
|
||||
set clist [lindex $args 0]
|
||||
set hdl [lindex $args 0]
|
||||
set clist [$hdl constraints]
|
||||
|
||||
set ret [list]
|
||||
set elist [list]
|
||||
set i 0
|
||||
|
@@ -205,10 +205,10 @@ do_test shell1-2.2.4 {
|
||||
} {0 {}}
|
||||
do_test shell1-2.2.5 {
|
||||
catchcmd "test.db" ".mode \"insert FOO"
|
||||
} {1 {Error: mode should be one of: ascii box column csv html insert json line list markdown quote table tabs tcl}}
|
||||
} {1 {Error: mode should be one of: ascii box column csv html insert json line list markdown qbox quote table tabs tcl}}
|
||||
do_test shell1-2.2.6 {
|
||||
catchcmd "test.db" ".mode \'insert FOO"
|
||||
} {1 {Error: mode should be one of: ascii box column csv html insert json line list markdown quote table tabs tcl}}
|
||||
} {1 {Error: mode should be one of: ascii box column csv html insert json line list markdown qbox quote table tabs tcl}}
|
||||
|
||||
# check multiple tokens, and quoted tokens
|
||||
do_test shell1-2.3.1 {
|
||||
@@ -236,7 +236,7 @@ do_test shell1-2.3.7 {
|
||||
# check quoted args are unquoted
|
||||
do_test shell1-2.4.1 {
|
||||
catchcmd "test.db" ".mode FOO"
|
||||
} {1 {Error: mode should be one of: ascii box column csv html insert json line list markdown quote table tabs tcl}}
|
||||
} {1 {Error: mode should be one of: ascii box column csv html insert json line list markdown qbox quote table tabs tcl}}
|
||||
do_test shell1-2.4.2 {
|
||||
catchcmd "test.db" ".mode csv"
|
||||
} {0 {}}
|
||||
@@ -437,7 +437,7 @@ do_test shell1-3.13.1 {
|
||||
} {0 {current output mode: list}}
|
||||
do_test shell1-3.13.2 {
|
||||
catchcmd "test.db" ".mode FOO"
|
||||
} {1 {Error: mode should be one of: ascii box column csv html insert json line list markdown quote table tabs tcl}}
|
||||
} {1 {Error: mode should be one of: ascii box column csv html insert json line list markdown qbox quote table tabs tcl}}
|
||||
do_test shell1-3.13.3 {
|
||||
catchcmd "test.db" ".mode csv"
|
||||
} {0 {}}
|
||||
@@ -467,17 +467,6 @@ do_test shell1-3.13.11 {
|
||||
catchcmd "test.db" ".mode tcl BAD"
|
||||
} {0 {}}
|
||||
|
||||
# don't allow partial mode type matches
|
||||
do_test shell1-3.13.12 {
|
||||
catchcmd "test.db" ".mode l"
|
||||
} {1 {Error: mode should be one of: ascii box column csv html insert json line list markdown quote table tabs tcl}}
|
||||
do_test shell1-3.13.13 {
|
||||
catchcmd "test.db" ".mode li"
|
||||
} {1 {Error: mode should be one of: ascii box column csv html insert json line list markdown quote table tabs tcl}}
|
||||
do_test shell1-3.13.14 {
|
||||
catchcmd "test.db" ".mode lin"
|
||||
} {0 {}}
|
||||
|
||||
# .nullvalue STRING Print STRING in place of NULL values
|
||||
do_test shell1-3.14.1 {
|
||||
catchcmd "test.db" ".nullvalue"
|
||||
|
@@ -65,7 +65,7 @@ while {![eof stdin]} {
|
||||
#
|
||||
set vlist {}
|
||||
set seenDecl 0
|
||||
set namechars {abcdefghijklmnopqrstuvwxyz}
|
||||
set namechars {abcefghjklmnopqrstuvwxyz}
|
||||
set nnc [string length $namechars]
|
||||
while {![eof stdin]} {
|
||||
set line [gets stdin]
|
||||
|
Reference in New Issue
Block a user