mirror of
https://github.com/sqlite/sqlite.git
synced 2025-11-16 23:02:26 +03:00
Add the sqlite3_vtab_in() interface that allows virtual tables to process
IN constraints all at once, rather than one value at a time. FossilOrigin-Name: 52559af093809b572082b5ebaacf97b727ee1860ae118530761b62e937545163
This commit is contained in:
50
manifest
50
manifest
@@ -1,5 +1,5 @@
|
||||
C Fix\sa\sharmless\scode\sindentation\sissue.
|
||||
D 2022-02-02T11:37:49.562
|
||||
C Add\sthe\ssqlite3_vtab_in()\sinterface\sthat\sallows\svirtual\stables\sto\sprocess\nIN\sconstraints\sall\sat\sonce,\srather\sthan\sone\svalue\sat\sa\stime.
|
||||
D 2022-02-02T19:51:44.684
|
||||
F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
|
||||
F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
|
||||
F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724
|
||||
@@ -515,7 +515,7 @@ F src/in-operator.md 10cd8f4bcd225a32518407c2fb2484089112fd71
|
||||
F src/insert.c 1eea44389de3768ac98588c1410171cd53e7c6ad1af74049983dcbac82093de0
|
||||
F src/json.c 78fdec9af3a8bfb5ae685707b2701276fec1942b8f5f26689b2701debe32bcd2
|
||||
F src/legacy.c d7874bc885906868cd51e6c2156698f2754f02d9eee1bae2d687323c3ca8e5aa
|
||||
F src/loadext.c 657534339585ac234839e5187aa51d8802f292e0771c4f874b3af1f1223f81e2
|
||||
F src/loadext.c aa919a6a7884f8b34d7b791841b24d14b1b0ab43f45b3940f4851043b2855c0c
|
||||
F src/main.c 2b6b0dbfeb14d4bb57e368604b0736b2aa42b51b00339d399b01d6b1fc9b4960
|
||||
F src/malloc.c fec841aa0a0400a6f7d20706178a5d8e8219a6bf562b6fe712c17f6c26813266
|
||||
F src/mem0.c 6a55ebe57c46ca1a7d98da93aaa07f99f1059645
|
||||
@@ -554,10 +554,10 @@ F src/resolve.c 24032ae57aec10df2f3fa2e20be0aae7d256bc704124b76c52d763440c7c0fe9
|
||||
F src/rowset.c ba9515a922af32abe1f7d39406b9d35730ed65efab9443dc5702693b60854c92
|
||||
F src/select.c a6d2d4bed279d7fe4fcedaf297eaf6441e8e17c6e3947a32d24d23be52ac02f2
|
||||
F src/shell.c.in 2f58e6aa6b3d2012db32f1c5fa4591e9d12fd582904632b4fc8f57a382b98fd3
|
||||
F src/sqlite.h.in eaade58049152dac850d57415bcced885ca27ae9582f8aea2cfb7f1db78a521b
|
||||
F src/sqlite.h.in a6e6fd9defb576af6444a85f446aaa738dea3384c48db4fe9007fb8ff954b7c5
|
||||
F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8
|
||||
F src/sqlite3ext.h 5d54cf13d3406d8eb65d921a0d3c349de6126b732e695e79ecd4830ce86b4f8a
|
||||
F src/sqliteInt.h 8ef2996e02476f73e41ba977f819bda0cc68b7ce238cf404b9b8930df57bc1d0
|
||||
F src/sqlite3ext.h a95cb9ed106e3d39e2118e4dcc15a14faec3fa50d0093425083d340d9dfd96e6
|
||||
F src/sqliteInt.h 838df3e9ba9390058076d2f50c7efde9e0e8747303e788cf5bbe05402ab10924
|
||||
F src/sqliteLimit.h d7323ffea5208c6af2734574bae933ca8ed2ab728083caa117c9738581a31657
|
||||
F src/status.c 4b8bc2a6905163a38b739854a35b826c737333fab5b1f8e03fa7eb9a4799c4c1
|
||||
F src/table.c 0f141b58a16de7e2fbe81c308379e7279f4c6b50eb08efeec5892794a0ba30d1
|
||||
@@ -574,7 +574,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 ba0314d3bca8b00fa0daaf113a314986f73ea8ad85a7562f983638d09a29045c
|
||||
F src/test_bestindex.c 8294d8223b7f18a3ddb7f9a0e30815dcca4e61681f78b538c870f7d934f88b81
|
||||
F src/test_blob.c ae4a0620b478548afb67963095a7417cd06a4ec0a56adb453542203bfdcb31ce
|
||||
F src/test_btree.c 8b2dc8b8848cf3a4db93f11578f075e82252a274
|
||||
F src/test_config.c 284c29912736f68b0a583a920bf63fd8f9125dffb8a75cb0676e58502b2f7908
|
||||
@@ -624,10 +624,10 @@ F src/upsert.c 8789047a8f0a601ea42fa0256d1ba3190c13746b6ba940fe2d25643a7e991937
|
||||
F src/utf.c ee39565f0843775cc2c81135751ddd93eceb91a673ea2c57f61c76f288b041a0
|
||||
F src/util.c 602fe229f32a96ceccae4f40824129669582096f7c355f53dbac156c9fecef23
|
||||
F src/vacuum.c 6c38ddc52f0619865c91dae9c441d4d48bf3040d7dc1bc5b22da1e45547ed0b3
|
||||
F src/vdbe.c cfe1980fbeb87eb35297b4a41808034761f26277cf45c9cf3e4eac20edcba1d5
|
||||
F src/vdbe.c 13a4de20ee07bdfb3dc74ab49b7912208e309caf762a8d1678fb111e2223af35
|
||||
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 e761b8011baec7a4773f0a7594783f2cd71f699ab187c4aad917529ab8acd3fe
|
||||
F src/vdbeblob.c 5e61ce31aca17db8fb60395407457a8c1c7fb471dde405e0cd675974611dcfcd
|
||||
F src/vdbemem.c eb6042667c02c3ef1f968235b4a170e31b23a4b6a57f65a8454eab4d36f14b7f
|
||||
@@ -639,9 +639,9 @@ F src/vxworks.h d2988f4e5a61a4dfe82c6524dd3d6e4f2ce3cdb9
|
||||
F src/wal.c b9df133a705093da8977da5eb202eaadb844839f1c7297c08d33471f5491843d
|
||||
F src/wal.h c3aa7825bfa2fe0d85bef2db94655f99870a285778baa36307c0a16da32b226a
|
||||
F src/walker.c f890a3298418d7cba3b69b8803594fdc484ea241206a8dfa99db6dd36f8cbb3b
|
||||
F src/where.c c4a80044708b1000a2b875a6623acfb636acd1c740063b60ea50e359d305829e
|
||||
F src/whereInt.h 0748a6fce98b41862445906922a809146ff7ef4de16ed9022b0bc4e5c43aa60a
|
||||
F src/wherecode.c c313ccf5ed13dc7e88c64f93733f414dee369a212508a866878696d83c64fc36
|
||||
F src/where.c d7d996a5d4501fbf14997e965d7f641fe7b2a0dd89b2296e5310b506da3fb822
|
||||
F src/whereInt.h 1d821657238a0bd12b3c8f2926c7f8f9294bc5efe20af53c7c50d53a0a026cb9
|
||||
F src/wherecode.c 5879604677f0bdfb8d95ff616d834daecc12256346b7d9ad96a7e84a1cb08fdc
|
||||
F src/whereexpr.c fa8017f9fed4b50c59b4f105139679dd59ed7fea43179ebb0f6b8b5796a30515
|
||||
F src/window.c dfaec4abc6012cbc18e4a202ca3a5d5a0efcc4011d86a06d882ddaab8aedee4d
|
||||
F test/8_3_names.test ebbb5cd36741350040fd28b432ceadf495be25b2
|
||||
@@ -724,13 +724,14 @@ F test/backup_malloc.test 0c9abdf74c51e7bedb66d504cd684f28d4bd4027
|
||||
F test/badutf.test d5360fc31f643d37a973ab0d8b4fb85799c3169f
|
||||
F test/badutf2.test f310fd3b24a491b6b77bccdf14923b85d6ebcce751068c180d93a6b8ff854399
|
||||
F test/bc_common.tcl b5e42d80305be95697e6370e015af571e5333a1c
|
||||
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
|
||||
@@ -1327,7 +1328,7 @@ F test/rowvalue.test 02214016f747854ef636e64ff204778649937aa801ca78e2495a960f8e0
|
||||
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
|
||||
@@ -1942,8 +1943,9 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93
|
||||
F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc
|
||||
F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e
|
||||
F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0
|
||||
P 00b1b7020a564976da3237532434e47ccf17eb5d620e6ac45f3e70b5d5739200
|
||||
R e3feaf8ec370122d862f692465c41194
|
||||
P 41d8d26e48a440da44582763f6b0c097febe3b8ac16adb662f688c3662c301bf 733d81c3a6a513b0b893a7d14894f36aebbbca9da375c326db8a72df4f0c6238
|
||||
R bea83344da84fc60a55eb54290dfe95b
|
||||
T +closed 733d81c3a6a513b0b893a7d14894f36aebbbca9da375c326db8a72df4f0c6238
|
||||
U drh
|
||||
Z d6d32499a022b83aad108caf851b3d80
|
||||
Z 2ffca9e5143ef0b555ac148560cccc3c
|
||||
# Remove this line to create a well-formed Fossil manifest.
|
||||
|
||||
@@ -1 +1 @@
|
||||
41d8d26e48a440da44582763f6b0c097febe3b8ac16adb662f688c3662c301bf
|
||||
52559af093809b572082b5ebaacf97b727ee1860ae118530761b62e937545163
|
||||
@@ -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
|
||||
|
||||
122
src/sqlite.h.in
122
src/sqlite.h.in
@@ -9604,6 +9604,128 @@ 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.
|
||||
*/
|
||||
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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -1234,6 +1234,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
|
||||
|
||||
@@ -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();
|
||||
@@ -437,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();
|
||||
@@ -456,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;
|
||||
@@ -493,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]);
|
||||
|
||||
28
src/vdbe.c
28
src/vdbe.c
@@ -7734,6 +7734,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
|
||||
|
||||
36
src/where.c
36
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) ){
|
||||
@@ -3499,6 +3506,7 @@ static int whereLoopAddVirtualOne(
|
||||
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;
|
||||
@@ -3537,6 +3545,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);
|
||||
@@ -3593,7 +3602,9 @@ static int whereLoopAddVirtualOne(
|
||||
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
|
||||
@@ -3691,6 +3702,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.
|
||||
**
|
||||
|
||||
@@ -128,6 +128,7 @@ struct WhereLoop {
|
||||
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 */
|
||||
|
||||
@@ -1532,8 +1532,15 @@ 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);
|
||||
@@ -1568,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 */
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user