From b30298d3ea05b73fb9c7d1556f6b2b054dd33d7a Mon Sep 17 00:00:00 2001 From: drh <> Date: Tue, 1 Feb 2022 21:59:43 +0000 Subject: [PATCH] Tweaks to the sqlite3_vtab_in() interface. FossilOrigin-Name: 75040183b8e14f20bfedfdcc1a9fb968f2f0193bc698605d1b4791a3699b93d9 --- manifest | 16 +++--- manifest.uuid | 2 +- src/sqlite.h.in | 144 +++++++++++++++++++++++++++++------------------- src/vdbeapi.c | 28 +++++----- src/where.c | 4 +- 5 files changed, 112 insertions(+), 82 deletions(-) diff --git a/manifest b/manifest index 7b186939fd..6f0bf53a93 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Index\sin\s2nd\sargument\sto\ssqlite3_vtab_in()\sshould\sbe\son\sthe\saConstraint[]\narray,\snot\sthe\sinternal\sarray\sof\sall\sconstraints. -D 2022-02-01T16:30:57.444 +C Tweaks\sto\sthe\ssqlite3_vtab_in()\sinterface. +D 2022-02-01T21:59:43.937 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -554,7 +554,7 @@ 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 0aed2b88e91d03314121cd1e546441e37513929793c3cf7584b5b7ce445a9128 +F src/sqlite.h.in 72f3e57c4c0b4284ab9238312f7fd797967cc43f44558a80469a3d9b86a7be2b F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8 F src/sqlite3ext.h a95cb9ed106e3d39e2118e4dcc15a14faec3fa50d0093425083d340d9dfd96e6 F src/sqliteInt.h 838df3e9ba9390058076d2f50c7efde9e0e8747303e788cf5bbe05402ab10924 @@ -627,7 +627,7 @@ F src/vacuum.c 6c38ddc52f0619865c91dae9c441d4d48bf3040d7dc1bc5b22da1e45547ed0b3 F src/vdbe.c d6694187a2819df7c2df3bd568fd059617c3edef4aa87e28a8121b02818f4ebf F src/vdbe.h 25dabb25c7e157b84e59260cfb5b466c3ac103ede9f36f4db371332c47601abe F src/vdbeInt.h 24d58f12f642dcac102fa75d08e99ad06b6cbc66bf4948bb61e2e223ef9518b6 -F src/vdbeapi.c 4d26cc9eb1a0f937e8d360578dc56d00145bac08afd503ba6ac843007f7d8c1f +F src/vdbeapi.c 84e7e8d161c8fb7259eaa5fe7234f2334ef9fb013674ce34705b56166052b5fa F src/vdbeaux.c e761b8011baec7a4773f0a7594783f2cd71f699ab187c4aad917529ab8acd3fe F src/vdbeblob.c 5e61ce31aca17db8fb60395407457a8c1c7fb471dde405e0cd675974611dcfcd F src/vdbemem.c eb6042667c02c3ef1f968235b4a170e31b23a4b6a57f65a8454eab4d36f14b7f @@ -639,7 +639,7 @@ F src/vxworks.h d2988f4e5a61a4dfe82c6524dd3d6e4f2ce3cdb9 F src/wal.c b9df133a705093da8977da5eb202eaadb844839f1c7297c08d33471f5491843d F src/wal.h c3aa7825bfa2fe0d85bef2db94655f99870a285778baa36307c0a16da32b226a F src/walker.c f890a3298418d7cba3b69b8803594fdc484ea241206a8dfa99db6dd36f8cbb3b -F src/where.c cbf02091ed71784f7972ca39aaecb22822781f3b96905ece1a30e80161629769 +F src/where.c 392d552fa6636e94d242954247e277eb9b0d9c45446afb0c0a57f8c6fcb3f792 F src/whereInt.h 1d821657238a0bd12b3c8f2926c7f8f9294bc5efe20af53c7c50d53a0a026cb9 F src/wherecode.c 5879604677f0bdfb8d95ff616d834daecc12256346b7d9ad96a7e84a1cb08fdc F src/whereexpr.c ddb6ab49f745154c37dbdb291433c933e00175929647290a11f487af701d0392 @@ -1942,8 +1942,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 eb84b80e1f6d8c32bf0c9e1731f0233de0160a13f714f766779ae01fdf504e7b -R 320e3be3dedfcb44854348cea4a95b96 +P 5acf90a931b27b7d627c0a8fee68170430e09b028d6643b959b0ec14fd59f7ac +R e3dc8c48dc1f78fd77820f7d72429762 U drh -Z 4a8e49183eb36a5777c7e6fa2fd8d0f2 +Z 89712fde261ff7ce2e71753d08fff668 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 93bc0e7791..bfff22a8a9 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -5acf90a931b27b7d627c0a8fee68170430e09b028d6643b959b0ec14fd59f7ac \ No newline at end of file +75040183b8e14f20bfedfdcc1a9fb968f2f0193bc698605d1b4791a3699b93d9 \ No newline at end of file diff --git a/src/sqlite.h.in b/src/sqlite.h.in index 40aa917757..e8122912ee 100644 --- a/src/sqlite.h.in +++ b/src/sqlite.h.in @@ -9607,72 +9607,94 @@ int sqlite3_vtab_distinct(sqlite3_index_info*); /* ** CAPI3REF: Identify and handle IN(...) constraints in xBestIndex ** -** This API may only be used from within an xBestIndex() callback. The -** results of calling it from outside of an xBestIndex() callback are -** undefined. +** 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. ** -** When a column of a virtual table is subject to a "col IN (...)" -** constraint, this is passed through to the xBestIndex method of the -** virtual table as an SQLITE_INDEX_CONSTRAINT_EQ constraint. Then, if -** the virtual table indicates that it can handle the constraint, SQLite -** uses the xFilter and xNext methods of the virtual table to query -** separately for each distinct element in RHS of the IN(...) expression. -** This API allows a virtual table implementation to determine which -** SQLITE_INDEX_CONSTRAINT_EQ constraints are actually IN(...) constraints, -** and to handle them using a single xFilter scan. +** A constraint on a virtual table of the form "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 generate 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: +** +**
    +**
  1. +** A call to sqlite3_vtab_in(P,I,-1) will return true (non-zero) +** if and only if the I-th constraint in P->aConstraint[] 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. +** +**

  2. +** A call to sqlite3_vtab_in(P,I,F) with F set to 1 or 0 indicates +** to SQLite that the virtual table does or does not want to process +** the IN operator all-at-once. 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. +**

+** +** The sqlite3_vtab_in(P,I,F) interface can be invoked multiple times +** within the same xBestIndex method call. For any given P and I parameters, +** the return value from sqlite3_vtab_in(P,I,F) will always be the same +** for every invocation 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: +** +**
    +**
  1. The P->aConstraintUsage[I].argvIndex value is set to a positive +** integer. This is how the virtual table tells SQLite that it wants to +** use the I-th constraint. +** +**

  2. The last call to sqlite3_vtab_in(P,I,F) for which F was +** non-negative had F>=1. +**

** -** If the second argument passed to this function is not the index of an -** SQLITE_INDEX_CONSTRAINT_EQ constraint in the aConstraint[] array of the -** sqlite3_index_info object, or if the SQLITE_INDEX_CONSTRAINT_EQ is not -** really an IN(...) expression, then this function is a no-op. Zero is -** returned in this case. -** -** Otherwise, if parameter iCons is the index of an SQLITE_INDEX_CONSTRAINT_EQ -** constraint that is really an IN(...) expression, then this function -** returns non-zero. In this case, the call also specifies whether SQLite -** should invoke xFilter() once for each element on the RHS of the IN(...) -** expression (the default, if bHandle is zero), or just once for the entire -** list (if bHandle is non-zero), should the associated -** aConstraintUsage[].argvIndex variable be set by xBestIndex. -** -** In cases where the list on the RHS of an IN(...) constraint is passed to a -** single xFilter() call, the (sqlite3_value*) passed appears in most -** respects to be a NULL value. Except, it may be used with the -** sqlite3_vtab_in_first() and sqlite3_vtab_in_next() APIs to interate through -** the list of values. +** If either or both of the conditions above are false, then 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: Visit the first element of an IN(...) list passed to xFilter +** CAPI3REF: Find all elements on the right-hand side of an IN constraint. ** -** This API may only be used from within an xFilter() callback. The -** results of calling it from outside of an xFilter() callback are -** undefined. +** 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. ** -** If the (sqlite3_value*) passed as the first argument to this function -** is not a value representing the RHS of an IN(...) operator (see -** API function sqlite3_vtab_in()), of if the RHS of the IN(...) operator -** is an empty set, this function sets (*ppOut) to NULL and returns -** SQLITE_OK. +** 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 those 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. ** -** Otherwise, if no error occurs, it sets (*ppOut) to point to an object -** containing the first value in the list and returns SQLITE_OK. The -** pointer is valid until either the xFilter() call returns or until the -** next call to sqlite3_vtab_in_first() or sqlite3_vtab_in_next() with -** the same first argument. -** -** If an error occurs, (*ppOut) is set to NULL and an SQLite error code -** returned. -*/ -int sqlite3_vtab_in_first(sqlite3_value *pVal, sqlite3_value **ppOut); - -/* -** CAPI3REF: Visit the next element of an IN(...) list passed to xFilter -** -** This function is called after a successful call to sqlite3_vtab_in_first() -** to visit the next and subsequent elements of an IN(...) list passed -** to an xFilter callback. Example usage: +** Use these routines to access all values on the right-hand side +** of the IN constraint using code like the following: ** **
 **   for(rc=sqlite3_vtab_in_first(pList, &pVal);
@@ -9685,7 +9707,15 @@ int sqlite3_vtab_in_first(sqlite3_value *pVal, sqlite3_value **ppOut);
 **     // an error has occurred
 **   }
 ** 
+** +** 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. */ +int sqlite3_vtab_in_first(sqlite3_value *pVal, sqlite3_value **ppOut); int sqlite3_vtab_in_next(sqlite3_value *pVal, sqlite3_value **ppOut); /* diff --git a/src/vdbeapi.c b/src/vdbeapi.c index 1521eee79b..46b466b6bb 100644 --- a/src/vdbeapi.c +++ b/src/vdbeapi.c @@ -888,21 +888,21 @@ static int vtabInLoadValue(sqlite3_value *pVal, sqlite3_value **ppOut){ ** sqlite3_vtab_in_next() (if bNext!=0). */ static int vtabInOp(sqlite3_value *pVal, sqlite3_value **ppOut, int bNext){ - int rc = SQLITE_OK; + int rc; + BtCursor *pCsr; *ppOut = 0; - if( pVal && pVal->uTemp==SQLITE_VTAB_IN_MAGIC ){ - BtCursor *pCsr = (BtCursor*)pVal->z; - - if( bNext ){ - rc = sqlite3BtreeNext(pCsr, 0); - }else{ - int dummy = 0; - rc = sqlite3BtreeFirst(pCsr, &dummy); - } - - if( rc==SQLITE_OK && sqlite3BtreeEof(pCsr)==0 ){ - rc = vtabInLoadValue(pVal, ppOut); - } + if( pVal==0 ) return SQLITE_MISUSE; + if( pVal->uTemp!=SQLITE_VTAB_IN_MAGIC ) return SQLITE_MISUSE; + pCsr = (BtCursor*)pVal->z; + if( bNext ){ + rc = sqlite3BtreeNext(pCsr, 0); + }else{ + int dummy = 0; + rc = sqlite3BtreeFirst(pCsr, &dummy); + if( rc==SQLITE_OK && sqlite3BtreeEof(pCsr) ) rc = SQLITE_DONE; + } + if( rc==SQLITE_OK ){ + rc = vtabInLoadValue(pVal, ppOut); } return rc; } diff --git a/src/where.c b/src/where.c index 4f65e934fc..169c4ccf14 100644 --- a/src/where.c +++ b/src/where.c @@ -3605,7 +3605,7 @@ static int whereLoopAddVirtualOne( } } if( SMASKBIT32(i) & pHidden->mHandleIn ){ - pNew->u.vtab.mHandleIn |= SMASKBIT32(iTerm); + 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 @@ -3715,7 +3715,7 @@ int sqlite3_vtab_in(sqlite3_index_info *pIdxInfo, int iCons, int bHandle){ if( m & pHidden->mIn ){ if( bHandle==0 ){ pHidden->mHandleIn &= ~m; - }else{ + }else if( bHandle>0 ){ pHidden->mHandleIn |= m; } return 1;