171b65e88Sdrh /*
271b65e88Sdrh ** This file implements an eponymous, read-only table-valued function
371b65e88Sdrh ** (a virtual table) designed to be used for testing. We are not aware
471b65e88Sdrh ** of any practical real-world use case for the virtual table.
571b65e88Sdrh **
671b65e88Sdrh ** This virtual table originated in the TH3 test suite. It is still used
771b65e88Sdrh ** there, but has now been copied into the public SQLite source tree and
871b65e88Sdrh ** reused for a variety of testing purpose. The name "vt02" comes from the
971b65e88Sdrh ** fact that there are many different testing virtual tables in TH3, of which
1071b65e88Sdrh ** this one is the second.
1171b65e88Sdrh **
1271b65e88Sdrh ** ## SUBJECT TO CHANGE
1371b65e88Sdrh **
1471b65e88Sdrh ** Because this virtual table is intended for testing, its interface is not
1571b65e88Sdrh ** guaranteed to be stable across releases. Future releases may contain
1671b65e88Sdrh ** changes in the vt02 design and interface.
1771b65e88Sdrh **
1871b65e88Sdrh ** ## OVERVIEW
1971b65e88Sdrh **
2071b65e88Sdrh ** The vt02 table-valued function has 10000 rows with 5 data columns.
2171b65e88Sdrh ** Column X contains all integer values between 0 and 9999 inclusive.
2271b65e88Sdrh ** Columns A, B, C, and D contain the individual base-10 digits associated
2371b65e88Sdrh ** with each X value:
2471b65e88Sdrh **
2571b65e88Sdrh ** X A B C D
2671b65e88Sdrh ** ---- - - - -
2771b65e88Sdrh ** 0 0 0 0 0
2871b65e88Sdrh ** 1 0 0 0 1
2971b65e88Sdrh ** 2 0 0 0 2
3071b65e88Sdrh ** ...
3171b65e88Sdrh ** 4998 4 9 9 8
3271b65e88Sdrh ** 4999 4 9 9 9
3371b65e88Sdrh ** 5000 5 0 0 0
3471b65e88Sdrh ** ...
3571b65e88Sdrh ** 9995 9 9 9 5
3671b65e88Sdrh ** 9996 9 9 9 6
3771b65e88Sdrh ** 9997 9 9 9 7
3871b65e88Sdrh **
3971b65e88Sdrh ** The xBestIndex method recognizes a variety of equality constraints
4071b65e88Sdrh ** and attempts to optimize its output accordingly.
4171b65e88Sdrh **
4271b65e88Sdrh ** x=...
4371b65e88Sdrh ** a=...
4471b65e88Sdrh ** a=... AND b=...
4571b65e88Sdrh ** a=... AND b=... AND c=...
4671b65e88Sdrh ** a=... AND b=... AND c=... AND d=...
4771b65e88Sdrh **
4871b65e88Sdrh ** Various ORDER BY constraints are also recognized and consumed. The
4971b65e88Sdrh ** OFFSET constraint is recognized and consumed.
5071b65e88Sdrh **
5171b65e88Sdrh ** ## TABLE-VALUED FUNCTION
5271b65e88Sdrh **
5371b65e88Sdrh ** The vt02 virtual table is eponymous and has two hidden columns, meaning
5471b65e88Sdrh ** that it can functions a table-valued function. The two hidden columns
5571b65e88Sdrh ** are "flags" and "logtab", in that order. The "flags" column can be set
5671b65e88Sdrh ** to an integer where various bits enable or disable behaviors of the
5771b65e88Sdrh ** virtual table. The "logtab" can set to the name of an ordinary SQLite
5871b65e88Sdrh ** table into which is written information about each call to xBestIndex.
5971b65e88Sdrh **
6071b65e88Sdrh ** The bits of "flags" are as follows:
6171b65e88Sdrh **
6271b65e88Sdrh ** 0x01 Ignore the aConstraint[].usable flag. This might
6371b65e88Sdrh ** result in the xBestIndex method incorrectly using
6471b65e88Sdrh ** unusable entries in the aConstraint[] array, which
6571b65e88Sdrh ** should result in the SQLite core detecting and
6671b65e88Sdrh ** reporting that the virtual table is not behaving
6771b65e88Sdrh ** to spec.
6871b65e88Sdrh **
6971b65e88Sdrh ** 0x02 Do not set the orderByConsumed flag, even if it
7071b65e88Sdrh ** could be set.
7171b65e88Sdrh **
7271b65e88Sdrh ** 0x04 Do not consume the OFFSET constraint, if there is
7371b65e88Sdrh ** one. Instead, let the generated byte-code visit
7471b65e88Sdrh ** and ignore the first few columns of output.
7571b65e88Sdrh **
7671b65e88Sdrh ** 0x08 Use sqlite3_mprintf() to allocate an idxStr string.
7771b65e88Sdrh ** The string is never used, but allocating it does
7871b65e88Sdrh ** test the idxStr deallocation logic inside of the
7971b65e88Sdrh ** SQLite core.
8071b65e88Sdrh **
8171b65e88Sdrh ** 0x10 Cause the xBestIndex method to generate an idxNum
8271b65e88Sdrh ** that xFilter does not understand, thus causing
8371b65e88Sdrh ** the OP_VFilter opcode to raise an error.
8471b65e88Sdrh **
8571b65e88Sdrh ** 0x20 Set the omit flag for all equality constraints on
8671b65e88Sdrh ** columns X, A, B, C, and D that are used to limit
8771b65e88Sdrh ** the search.
8871b65e88Sdrh **
8971b65e88Sdrh ** 0x40 Add all constraints against X,A,B,C,D to the
9071b65e88Sdrh ** vector of results sent to xFilter. Only the first
9171b65e88Sdrh ** few are used, as required by idxNum.
9271b65e88Sdrh **
9371b65e88Sdrh ** Because these flags take effect during xBestIndex, the RHS of the
9471b65e88Sdrh ** flag= constraint must be accessible. In other words, the RHS of flag=
9571b65e88Sdrh ** needs to be an integer literal, not another column of a join or a
9671b65e88Sdrh ** bound parameter.
9771b65e88Sdrh **
9871b65e88Sdrh ** ## LOGGING OUTPUT
9971b65e88Sdrh **
10071b65e88Sdrh ** If the "logtab" columns is set, then each call to the xBestIndex method
10171b65e88Sdrh ** inserts multiple rows into the table identified by "logtab". These
10271b65e88Sdrh ** rows collectively show the content of the sqlite3_index_info object and
10371b65e88Sdrh ** other context associated with the xBestIndex call.
10471b65e88Sdrh **
10571b65e88Sdrh ** If the table named by "logtab" does not previously exist, it is created
10671b65e88Sdrh ** automatically. The schema for the logtab table is like this:
10771b65e88Sdrh **
10871b65e88Sdrh ** CREATE TEMP TABLE vt02_log(
10971b65e88Sdrh ** bi INT, -- BestIndex call counter
11071b65e88Sdrh ** vn TEXT, -- Variable Name
11171b65e88Sdrh ** ix INT, -- Index or value
11271b65e88Sdrh ** cn TEXT, -- Column Name
11371b65e88Sdrh ** op INT, -- Opcode or "DESC" value
11471b65e88Sdrh ** ux INT, -- "Usable" flag
11571b65e88Sdrh ** ra BOOLEAN, -- Right-hand side Available.
11671b65e88Sdrh ** rhs ANY, -- Right-Hand Side value
11771b65e88Sdrh ** cs TEXT -- Collating Sequence for this constraint
11871b65e88Sdrh ** );
11971b65e88Sdrh **
12071b65e88Sdrh ** Because logging happens during xBestIindex, the RHS value of "logtab" must
12171b65e88Sdrh ** be known to xBestIndex, which means it must be a string literal, not a
12271b65e88Sdrh ** column in a join, or a bound parameter.
12371b65e88Sdrh **
12471b65e88Sdrh ** ## VIRTUAL TABLE SCHEMA
12571b65e88Sdrh **
12671b65e88Sdrh ** CREATE TABLE vt02(
12771b65e88Sdrh ** x INT, -- integer between 0 and 9999 inclusive
12871b65e88Sdrh ** a INT, -- The 1000s digit
12971b65e88Sdrh ** b INT, -- The 100s digit
13071b65e88Sdrh ** c INT, -- The 10s digit
13171b65e88Sdrh ** d INT, -- The 1s digit
13271b65e88Sdrh ** flags INT HIDDEN, -- Option flags
13371b65e88Sdrh ** logtab TEXT HIDDEN, -- Name of table into which to log xBestIndex
13471b65e88Sdrh ** );
13571b65e88Sdrh **
13671b65e88Sdrh ** ## COMPILING AND RUNNING
13771b65e88Sdrh **
13871b65e88Sdrh ** This file can also be compiled separately as a loadable extension
13971b65e88Sdrh ** for SQLite (as long as the -DTH3_VERSION is not defined). To compile as a
14071b65e88Sdrh ** loadable extension do his:
14171b65e88Sdrh **
14271b65e88Sdrh ** gcc -Wall -g -shared -fPIC -I. -DSQLITE_DEBUG vt02.c -o vt02.so
14371b65e88Sdrh **
14471b65e88Sdrh ** Or on Windows:
14571b65e88Sdrh **
14671b65e88Sdrh ** cl vt02.c -link -dll -out:vt02.dll
14771b65e88Sdrh **
14871b65e88Sdrh ** Then load into the CLI using:
14971b65e88Sdrh **
15071b65e88Sdrh ** .load ./vt02 sqlite3_vt02_init
15171b65e88Sdrh **
15271b65e88Sdrh ** ## IDXNUM SUMMARY
15371b65e88Sdrh **
15471b65e88Sdrh ** The xBestIndex method communicates the query plan to xFilter using
15571b65e88Sdrh ** the idxNum value, as follows:
15671b65e88Sdrh **
15771b65e88Sdrh ** 0 unconstrained
15871b65e88Sdrh ** 1 X=argv[0]
15971b65e88Sdrh ** 2 A=argv[0]
16071b65e88Sdrh ** 3 A=argv[0], B=argv[1]
16171b65e88Sdrh ** 4 A=argv[0], B=argv[1], C=argv[2]
16271b65e88Sdrh ** 5 A=argv[0], B=argv[1], C=argv[2], D=argv[3]
16371b65e88Sdrh ** 6 A=argv[0], D IN argv[2]
16471b65e88Sdrh ** 7 A=argv[0], B=argv[2], D IN argv[3]
16571b65e88Sdrh ** 8 A=argv[0], B=argv[2], C=argv[3], D IN argv[4]
16671b65e88Sdrh ** 1x increment by 10
16771b65e88Sdrh ** 2x increment by 100
16871b65e88Sdrh ** 3x increment by 1000
16971b65e88Sdrh ** 1xx Use offset provided by argv[N]
17071b65e88Sdrh */
17171b65e88Sdrh #ifndef TH3_VERSION
17271b65e88Sdrh /* These bits for separate compilation as a loadable extension, only */
17371b65e88Sdrh #include "sqlite3ext.h"
17471b65e88Sdrh SQLITE_EXTENSION_INIT1
17571b65e88Sdrh #include <stdlib.h>
17671b65e88Sdrh #include <string.h>
17771b65e88Sdrh #include <assert.h>
17871b65e88Sdrh #endif
17971b65e88Sdrh
18071b65e88Sdrh /* Forward declarations */
18171b65e88Sdrh typedef struct vt02_vtab vt02_vtab;
18271b65e88Sdrh typedef struct vt02_cur vt02_cur;
18371b65e88Sdrh
18471b65e88Sdrh /*
18571b65e88Sdrh ** The complete virtual table
18671b65e88Sdrh */
18771b65e88Sdrh struct vt02_vtab {
18871b65e88Sdrh sqlite3_vtab parent; /* Base clase. Must be first. */
18971b65e88Sdrh sqlite3 *db; /* Database connection */
19071b65e88Sdrh int busy; /* Currently running xBestIndex */
19171b65e88Sdrh };
19271b65e88Sdrh
19371b65e88Sdrh #define VT02_IGNORE_USABLE 0x0001 /* Ignore usable flags */
19471b65e88Sdrh #define VT02_NO_SORT_OPT 0x0002 /* Do not do any sorting optimizations */
19571b65e88Sdrh #define VT02_NO_OFFSET 0x0004 /* Omit the offset optimization */
19671b65e88Sdrh #define VT02_ALLOC_IDXSTR 0x0008 /* Alloate an idxStr */
19771b65e88Sdrh #define VT02_BAD_IDXNUM 0x0010 /* Generate an invalid idxNum */
19871b65e88Sdrh
19971b65e88Sdrh /*
20071b65e88Sdrh ** A cursor
20171b65e88Sdrh */
20271b65e88Sdrh struct vt02_cur {
20371b65e88Sdrh sqlite3_vtab_cursor parent; /* Base class. Must be first */
20471b65e88Sdrh sqlite3_int64 i; /* Current entry */
20571b65e88Sdrh sqlite3_int64 iEof; /* Indicate EOF when reaching this value */
20671b65e88Sdrh int iIncr; /* Amount by which to increment */
20771b65e88Sdrh unsigned int mD; /* Mask of allowed D-column values */
20871b65e88Sdrh };
20971b65e88Sdrh
21071b65e88Sdrh /* The xConnect method */
vt02Connect(sqlite3 * db,void * pAux,int argc,const char * const * argv,sqlite3_vtab ** ppVTab,char ** pzErr)21171b65e88Sdrh int vt02Connect(
21271b65e88Sdrh sqlite3 *db, /* The database connection */
21371b65e88Sdrh void *pAux, /* Pointer to an alternative schema */
21471b65e88Sdrh int argc, /* Number of arguments */
21571b65e88Sdrh const char *const*argv, /* Text of the arguments */
21671b65e88Sdrh sqlite3_vtab **ppVTab, /* Write the new vtab here */
21771b65e88Sdrh char **pzErr /* Error message written here */
21871b65e88Sdrh ){
21971b65e88Sdrh vt02_vtab *pVtab;
22071b65e88Sdrh int rc;
22171b65e88Sdrh const char *zSchema = (const char*)pAux;
22271b65e88Sdrh static const char zDefaultSchema[] =
22371b65e88Sdrh "CREATE TABLE x(x INT, a INT, b INT, c INT, d INT,"
22471b65e88Sdrh " flags INT HIDDEN, logtab TEXT HIDDEN);";
22571b65e88Sdrh #define VT02_COL_X 0
22671b65e88Sdrh #define VT02_COL_A 1
22771b65e88Sdrh #define VT02_COL_B 2
22871b65e88Sdrh #define VT02_COL_C 3
22971b65e88Sdrh #define VT02_COL_D 4
23071b65e88Sdrh #define VT02_COL_FLAGS 5
23171b65e88Sdrh #define VT02_COL_LOGTAB 6
23271b65e88Sdrh #define VT02_COL_NONE 7
23371b65e88Sdrh
23471b65e88Sdrh pVtab = sqlite3_malloc( sizeof(*pVtab) );
23571b65e88Sdrh if( pVtab==0 ){
23671b65e88Sdrh *pzErr = sqlite3_mprintf("out of memory");
23771b65e88Sdrh return SQLITE_NOMEM;
23871b65e88Sdrh }
23971b65e88Sdrh memset(pVtab, 0, sizeof(*pVtab));
24071b65e88Sdrh pVtab->db = db;
24171b65e88Sdrh rc = sqlite3_declare_vtab(db, zSchema ? zSchema : zDefaultSchema);
24271b65e88Sdrh if( rc ){
24371b65e88Sdrh sqlite3_free(pVtab);
24471b65e88Sdrh }else{
24571b65e88Sdrh *ppVTab = &pVtab->parent;
24671b65e88Sdrh }
24771b65e88Sdrh return rc;
24871b65e88Sdrh }
24971b65e88Sdrh
25071b65e88Sdrh /* the xDisconnect method
25171b65e88Sdrh */
vt02Disconnect(sqlite3_vtab * pVTab)25271b65e88Sdrh int vt02Disconnect(sqlite3_vtab *pVTab){
25371b65e88Sdrh sqlite3_free(pVTab);
25471b65e88Sdrh return SQLITE_OK;
25571b65e88Sdrh }
25671b65e88Sdrh
25771b65e88Sdrh /* Put an error message into the zErrMsg string of the virtual table.
25871b65e88Sdrh */
vt02ErrMsg(sqlite3_vtab * pVtab,const char * zFormat,...)25971b65e88Sdrh static void vt02ErrMsg(sqlite3_vtab *pVtab, const char *zFormat, ...){
26071b65e88Sdrh va_list ap;
26171b65e88Sdrh sqlite3_free(pVtab->zErrMsg);
26271b65e88Sdrh va_start(ap, zFormat);
26371b65e88Sdrh pVtab->zErrMsg = sqlite3_vmprintf(zFormat, ap);
26471b65e88Sdrh va_end(ap);
26571b65e88Sdrh }
26671b65e88Sdrh
26771b65e88Sdrh
26871b65e88Sdrh /* Open a cursor for scanning
26971b65e88Sdrh */
vt02Open(sqlite3_vtab * pVTab,sqlite3_vtab_cursor ** ppCursor)27071b65e88Sdrh static int vt02Open(sqlite3_vtab *pVTab, sqlite3_vtab_cursor **ppCursor){
27171b65e88Sdrh vt02_cur *pCur;
27271b65e88Sdrh pCur = sqlite3_malloc( sizeof(*pCur) );
27371b65e88Sdrh if( pCur==0 ){
27471b65e88Sdrh vt02ErrMsg(pVTab, "out of memory");
27571b65e88Sdrh return SQLITE_NOMEM;
27671b65e88Sdrh }
27771b65e88Sdrh *ppCursor = &pCur->parent;
27871b65e88Sdrh pCur->i = -1;
27971b65e88Sdrh return SQLITE_OK;
28071b65e88Sdrh }
28171b65e88Sdrh
28271b65e88Sdrh /* Close a cursor
28371b65e88Sdrh */
vt02Close(sqlite3_vtab_cursor * pCursor)28471b65e88Sdrh static int vt02Close(sqlite3_vtab_cursor *pCursor){
28571b65e88Sdrh vt02_cur *pCur = (vt02_cur*)pCursor;
28671b65e88Sdrh sqlite3_free(pCur);
28771b65e88Sdrh return SQLITE_OK;
28871b65e88Sdrh }
28971b65e88Sdrh
29071b65e88Sdrh /* Return TRUE if we are at the end of the BVS and there are
29171b65e88Sdrh ** no more entries.
29271b65e88Sdrh */
vt02Eof(sqlite3_vtab_cursor * pCursor)29371b65e88Sdrh static int vt02Eof(sqlite3_vtab_cursor *pCursor){
29471b65e88Sdrh vt02_cur *pCur = (vt02_cur*)pCursor;
29571b65e88Sdrh return pCur->i<0 || pCur->i>=pCur->iEof;
29671b65e88Sdrh }
29771b65e88Sdrh
29871b65e88Sdrh /* Advance the cursor to the next row in the table
29971b65e88Sdrh */
vt02Next(sqlite3_vtab_cursor * pCursor)30071b65e88Sdrh static int vt02Next(sqlite3_vtab_cursor *pCursor){
30171b65e88Sdrh vt02_cur *pCur = (vt02_cur*)pCursor;
30271b65e88Sdrh do{
30371b65e88Sdrh pCur->i += pCur->iIncr;
30471b65e88Sdrh if( pCur->i<0 ) pCur->i = pCur->iEof;
30571b65e88Sdrh }while( (pCur->mD & (1<<(pCur->i%10)))==0 && pCur->i<pCur->iEof );
30671b65e88Sdrh return SQLITE_OK;
30771b65e88Sdrh }
30871b65e88Sdrh
30971b65e88Sdrh /* Rewind a cursor back to the beginning of its scan.
31071b65e88Sdrh **
31171b65e88Sdrh ** Scanning is always increasing.
31271b65e88Sdrh **
31371b65e88Sdrh ** idxNum
31471b65e88Sdrh ** 0 unconstrained
31571b65e88Sdrh ** 1 X=argv[0]
31671b65e88Sdrh ** 2 A=argv[0]
31771b65e88Sdrh ** 3 A=argv[0], B=argv[1]
31871b65e88Sdrh ** 4 A=argv[0], B=argv[1], C=argv[2]
31971b65e88Sdrh ** 5 A=argv[0], B=argv[1], C=argv[2], D=argv[3]
32071b65e88Sdrh ** 6 A=argv[0], D IN argv[2]
32171b65e88Sdrh ** 7 A=argv[0], B=argv[2], D IN argv[3]
32271b65e88Sdrh ** 8 A=argv[0], B=argv[2], C=argv[3], D IN argv[4]
32371b65e88Sdrh ** 1x increment by 10
32471b65e88Sdrh ** 2x increment by 100
32571b65e88Sdrh ** 3x increment by 1000
32671b65e88Sdrh ** 1xx Use offset provided by argv[N]
32771b65e88Sdrh */
vt02Filter(sqlite3_vtab_cursor * pCursor,int idxNum,const char * idxStr,int argc,sqlite3_value ** argv)32871b65e88Sdrh static int vt02Filter(
32971b65e88Sdrh sqlite3_vtab_cursor *pCursor, /* The cursor to rewind */
33071b65e88Sdrh int idxNum, /* Search strategy */
33171b65e88Sdrh const char *idxStr, /* Not used */
33271b65e88Sdrh int argc, /* Not used */
33371b65e88Sdrh sqlite3_value **argv /* Not used */
33471b65e88Sdrh ){
33571b65e88Sdrh vt02_cur *pCur = (vt02_cur*)pCursor; /* The vt02 cursor */
33671b65e88Sdrh int bUseOffset = 0; /* True to use OFFSET value */
33771b65e88Sdrh int iArg = 0; /* argv[] values used so far */
33871b65e88Sdrh int iOrigIdxNum = idxNum; /* Original value for idxNum */
33971b65e88Sdrh
34071b65e88Sdrh pCur->iIncr = 1;
34171b65e88Sdrh pCur->mD = 0x3ff;
34271b65e88Sdrh if( idxNum>=100 ){
34371b65e88Sdrh bUseOffset = 1;
34471b65e88Sdrh idxNum -= 100;
34571b65e88Sdrh }
34671b65e88Sdrh if( idxNum<0 || idxNum>38 ) goto vt02_bad_idxnum;
34771b65e88Sdrh while( idxNum>=10 ){
34871b65e88Sdrh pCur->iIncr *= 10;
34971b65e88Sdrh idxNum -= 10;
35071b65e88Sdrh }
35171b65e88Sdrh if( idxNum==0 ){
35271b65e88Sdrh pCur->i = 0;
35371b65e88Sdrh pCur->iEof = 10000;
35471b65e88Sdrh }else if( idxNum==1 ){
35571b65e88Sdrh pCur->i = sqlite3_value_int64(argv[0]);
35671b65e88Sdrh if( pCur->i<0 ) pCur->i = -1;
35771b65e88Sdrh if( pCur->i>9999 ) pCur->i = 10000;
35871b65e88Sdrh pCur->iEof = pCur->i+1;
35971b65e88Sdrh if( pCur->i<0 || pCur->i>9999 ) pCur->i = pCur->iEof;
36071b65e88Sdrh }else if( idxNum>=2 && idxNum<=5 ){
36171b65e88Sdrh int i, e, m;
36271b65e88Sdrh e = idxNum - 2;
36371b65e88Sdrh assert( e<=argc-1 );
36471b65e88Sdrh pCur->i = 0;
36571b65e88Sdrh for(m=1000, i=0; i<=e; i++, m /= 10){
36671b65e88Sdrh sqlite3_int64 v = sqlite3_value_int64(argv[iArg++]);
36771b65e88Sdrh if( v<0 ) v = 0;
36871b65e88Sdrh if( v>9 ) v = 9;
36971b65e88Sdrh pCur->i += m*v;
37071b65e88Sdrh pCur->iEof = pCur->i+m;
37171b65e88Sdrh }
37271b65e88Sdrh }else if( idxNum>=6 && idxNum<=8 ){
37371b65e88Sdrh int i, e, m, rc;
37471b65e88Sdrh sqlite3_value *pIn, *pVal;
37571b65e88Sdrh e = idxNum - 6;
37671b65e88Sdrh assert( e<=argc-2 );
37771b65e88Sdrh pCur->i = 0;
37871b65e88Sdrh for(m=1000, i=0; i<=e; i++, m /= 10){
37971b65e88Sdrh sqlite3_int64 v;
380*053bb22fSdrh pVal = 0;
38171b65e88Sdrh if( sqlite3_vtab_in_first(0, &pVal)!=SQLITE_MISUSE
38271b65e88Sdrh || sqlite3_vtab_in_first(argv[iArg], &pVal)!=SQLITE_MISUSE
38371b65e88Sdrh ){
38471b65e88Sdrh vt02ErrMsg(pCursor->pVtab,
38571b65e88Sdrh "unexpected success from sqlite3_vtab_in_first()");
38671b65e88Sdrh return SQLITE_ERROR;
38771b65e88Sdrh }
38871b65e88Sdrh v = sqlite3_value_int64(argv[iArg++]);
38971b65e88Sdrh if( v<0 ) v = 0;
39071b65e88Sdrh if( v>9 ) v = 9;
39171b65e88Sdrh pCur->i += m*v;
39271b65e88Sdrh pCur->iEof = pCur->i+m;
39371b65e88Sdrh }
39471b65e88Sdrh pCur->mD = 0;
39571b65e88Sdrh pIn = argv[iArg++];
39671b65e88Sdrh assert( sqlite3_value_type(pIn)==SQLITE_NULL );
39771b65e88Sdrh for( rc = sqlite3_vtab_in_first(pIn, &pVal);
39871b65e88Sdrh rc==SQLITE_OK && pVal!=0;
39971b65e88Sdrh rc = sqlite3_vtab_in_next(pIn, &pVal)
40071b65e88Sdrh ){
40171b65e88Sdrh int eType = sqlite3_value_numeric_type(pVal);
40271b65e88Sdrh if( eType==SQLITE_FLOAT ){
40371b65e88Sdrh double r = sqlite3_value_double(pVal);
40471b65e88Sdrh if( r<0.0 || r>9.0 || r!=(int)r ) continue;
40571b65e88Sdrh }else if( eType!=SQLITE_INTEGER ){
40671b65e88Sdrh continue;
40771b65e88Sdrh }
40871b65e88Sdrh i = sqlite3_value_int(pVal);
40971b65e88Sdrh if( i<0 || i>9 ) continue;
41071b65e88Sdrh pCur->mD |= 1<<i;
41171b65e88Sdrh }
41271b65e88Sdrh if( rc!=SQLITE_OK && rc!=SQLITE_DONE ){
41371b65e88Sdrh vt02ErrMsg(pCursor->pVtab, "Error from sqlite3_vtab_in_first/next()");
41471b65e88Sdrh return rc;
41571b65e88Sdrh }
41671b65e88Sdrh }else{
41771b65e88Sdrh goto vt02_bad_idxnum;
41871b65e88Sdrh }
41971b65e88Sdrh if( bUseOffset ){
42071b65e88Sdrh int nSkip = sqlite3_value_int(argv[iArg]);
42171b65e88Sdrh while( nSkip-- > 0 ) vt02Next(pCursor);
42271b65e88Sdrh }
42371b65e88Sdrh return SQLITE_OK;
42471b65e88Sdrh
42571b65e88Sdrh vt02_bad_idxnum:
42671b65e88Sdrh vt02ErrMsg(pCursor->pVtab, "invalid idxNum for vt02: %d", iOrigIdxNum);
42771b65e88Sdrh return SQLITE_ERROR;
42871b65e88Sdrh }
42971b65e88Sdrh
43071b65e88Sdrh /* Return the Nth column of the current row.
43171b65e88Sdrh */
vt02Column(sqlite3_vtab_cursor * pCursor,sqlite3_context * context,int N)43271b65e88Sdrh static int vt02Column(
43371b65e88Sdrh sqlite3_vtab_cursor *pCursor,
43471b65e88Sdrh sqlite3_context *context,
43571b65e88Sdrh int N
43671b65e88Sdrh ){
43771b65e88Sdrh vt02_cur *pCur = (vt02_cur*)pCursor;
43871b65e88Sdrh int v = pCur->i;
43971b65e88Sdrh if( N==VT02_COL_X ){
44071b65e88Sdrh sqlite3_result_int(context, v);
44171b65e88Sdrh }else if( N>=VT02_COL_A && N<=VT02_COL_D ){
44271b65e88Sdrh static const int iDivisor[] = { 1, 1000, 100, 10, 1 };
44371b65e88Sdrh v = (v/iDivisor[N])%10;
44471b65e88Sdrh sqlite3_result_int(context, v);
44571b65e88Sdrh }
44671b65e88Sdrh return SQLITE_OK;
44771b65e88Sdrh }
44871b65e88Sdrh
44971b65e88Sdrh /* Return the rowid of the current row
45071b65e88Sdrh */
vt02Rowid(sqlite3_vtab_cursor * pCursor,sqlite3_int64 * pRowid)45171b65e88Sdrh static int vt02Rowid(sqlite3_vtab_cursor *pCursor, sqlite3_int64 *pRowid){
45271b65e88Sdrh vt02_cur *pCur = (vt02_cur*)pCursor;
45371b65e88Sdrh *pRowid = pCur->i+1;
45471b65e88Sdrh return SQLITE_OK;
45571b65e88Sdrh }
45671b65e88Sdrh
45771b65e88Sdrh /*************************************************************************
45871b65e88Sdrh ** Logging Subsystem
45971b65e88Sdrh **
46071b65e88Sdrh ** The sqlite3BestIndexLog() routine implements a logging system for
46171b65e88Sdrh ** xBestIndex calls. This code is portable to any virtual table.
46271b65e88Sdrh **
46371b65e88Sdrh ** sqlite3BestIndexLog() is the main routine, sqlite3RunSql() is a
46471b65e88Sdrh ** helper routine used for running various SQL statements as part of
46571b65e88Sdrh ** creating the log.
46671b65e88Sdrh **
46771b65e88Sdrh ** These two routines should be portable to other virtual tables. Simply
46871b65e88Sdrh ** extract this code and call sqlite3BestIndexLog() near the end of the
46971b65e88Sdrh ** xBestIndex method in cases where logging is desired.
47071b65e88Sdrh */
47171b65e88Sdrh /*
47271b65e88Sdrh ** Run SQL on behalf of sqlite3BestIndexLog.
47371b65e88Sdrh **
47471b65e88Sdrh ** Construct the SQL using the zFormat string and subsequent arguments.
47571b65e88Sdrh ** Or if zFormat is NULL, take the SQL as the first argument after the
47671b65e88Sdrh ** zFormat. In either case, the dynamically allocated SQL string is
47771b65e88Sdrh ** freed after it has been run. If something goes wrong with the SQL,
47871b65e88Sdrh ** then an error is left in pVTab->zErrMsg.
47971b65e88Sdrh */
sqlite3RunSql(sqlite3 * db,sqlite3_vtab * pVTab,const char * zFormat,...)48071b65e88Sdrh static void sqlite3RunSql(
48171b65e88Sdrh sqlite3 *db, /* Run the SQL on this database connection */
48271b65e88Sdrh sqlite3_vtab *pVTab, /* Report errors to this virtual table */
48371b65e88Sdrh const char *zFormat, /* Format string for SQL, or NULL */
48471b65e88Sdrh ... /* Arguments, according to the format string */
48571b65e88Sdrh ){
48671b65e88Sdrh char *zSql;
48771b65e88Sdrh
48871b65e88Sdrh va_list ap;
48971b65e88Sdrh va_start(ap, zFormat);
49071b65e88Sdrh if( zFormat==0 ){
49171b65e88Sdrh zSql = va_arg(ap, char*);
49271b65e88Sdrh }else{
49371b65e88Sdrh zSql = sqlite3_vmprintf(zFormat, ap);
49471b65e88Sdrh }
49571b65e88Sdrh va_end(ap);
49671b65e88Sdrh if( zSql ){
49771b65e88Sdrh char *zErrMsg = 0;
49871b65e88Sdrh (void)sqlite3_exec(db, zSql, 0, 0, &zErrMsg);
49971b65e88Sdrh if( zErrMsg ){
50071b65e88Sdrh if( pVTab->zErrMsg==0 ){
50171b65e88Sdrh pVTab->zErrMsg = sqlite3_mprintf("%s in [%s]", zErrMsg, zSql);
50271b65e88Sdrh }
50371b65e88Sdrh sqlite3_free(zErrMsg);
50471b65e88Sdrh }
50571b65e88Sdrh sqlite3_free(zSql);
50671b65e88Sdrh }
50771b65e88Sdrh }
50871b65e88Sdrh
50971b65e88Sdrh /*
51071b65e88Sdrh ** Record information about each xBestIndex method call in a separate
51171b65e88Sdrh ** table:
51271b65e88Sdrh **
51371b65e88Sdrh ** CREATE TEMP TABLE [log-table-name] (
51471b65e88Sdrh ** bi INT, -- BestIndex call number
51571b65e88Sdrh ** vn TEXT, -- Variable Name
51671b65e88Sdrh ** ix INT, -- Index or value
51771b65e88Sdrh ** cn TEXT, -- Column Name
51871b65e88Sdrh ** op INT, -- Opcode or argvIndex
51971b65e88Sdrh ** ux INT, -- "usable" or "omit" flag
52071b65e88Sdrh ** rx BOOLEAN, -- True if has a RHS value
52171b65e88Sdrh ** rhs ANY, -- The RHS value
52271b65e88Sdrh ** cs TEXT, -- Collating Sequence
52371b65e88Sdrh ** inop BOOLEAN -- True if this is a batchable IN operator
52471b65e88Sdrh ** );
52571b65e88Sdrh **
52671b65e88Sdrh ** If an error occurs, leave an error message in pVTab->zErrMsg.
52771b65e88Sdrh */
sqlite3BestIndexLog(sqlite3_index_info * pInfo,const char * zLogTab,sqlite3 * db,const char ** azColname,sqlite3_vtab * pVTab)52871b65e88Sdrh static void sqlite3BestIndexLog(
52971b65e88Sdrh sqlite3_index_info *pInfo, /* The sqlite3_index_info object */
53071b65e88Sdrh const char *zLogTab, /* Log into this table */
53171b65e88Sdrh sqlite3 *db, /* Database connection containing zLogTab */
53271b65e88Sdrh const char **azColname, /* Names of columns in the virtual table */
53371b65e88Sdrh sqlite3_vtab *pVTab /* Record errors into this object */
53471b65e88Sdrh ){
53571b65e88Sdrh int i, rc;
53671b65e88Sdrh sqlite3_str *pStr;
53771b65e88Sdrh int iBI;
53871b65e88Sdrh
53971b65e88Sdrh if( sqlite3_table_column_metadata(db,0,zLogTab,0,0,0,0,0,0) ){
54071b65e88Sdrh /* The log table does not previously exist. Create it. */
54171b65e88Sdrh sqlite3RunSql(db,pVTab,
54271b65e88Sdrh "CREATE TABLE IF NOT EXISTS temp.\"%w\"(\n"
54371b65e88Sdrh " bi INT, -- BestIndex call number\n"
54471b65e88Sdrh " vn TEXT, -- Variable Name\n"
54571b65e88Sdrh " ix INT, -- Index or value\n"
54671b65e88Sdrh " cn TEXT, -- Column Name\n"
54771b65e88Sdrh " op INT, -- Opcode or argvIndex\n"
54871b65e88Sdrh " ux INT, -- usable for omit flag\n"
54971b65e88Sdrh " rx BOOLEAN, -- Right-hand side value is available\n"
55071b65e88Sdrh " rhs ANY, -- RHS value\n"
55171b65e88Sdrh " cs TEXT, -- Collating Sequence\n"
55271b65e88Sdrh " inop BOOLEAN -- IN operator capable of batch reads\n"
55371b65e88Sdrh ");", zLogTab
55471b65e88Sdrh );
55571b65e88Sdrh iBI = 1;
55671b65e88Sdrh }else{
55771b65e88Sdrh /* The log table does already exist. We assume that it has the
55871b65e88Sdrh ** correct schema and proceed to find the largest prior "bi" value.
55971b65e88Sdrh ** If the schema is wrong, errors might result. The code is able
56071b65e88Sdrh ** to deal with this. */
56171b65e88Sdrh sqlite3_stmt *pStmt;
56271b65e88Sdrh char *zSql;
56371b65e88Sdrh zSql = sqlite3_mprintf("SELECT max(bi) FROM temp.\"%w\"",zLogTab);
56471b65e88Sdrh if( zSql==0 ){
56571b65e88Sdrh sqlite3_free(pVTab->zErrMsg);
56671b65e88Sdrh pVTab->zErrMsg = sqlite3_mprintf("out of memory");
56771b65e88Sdrh return;
56871b65e88Sdrh }
56971b65e88Sdrh rc = sqlite3_prepare_v2(db, zSql, -1, &pStmt, 0);
57071b65e88Sdrh sqlite3_free(zSql);
57171b65e88Sdrh if( rc ){
57271b65e88Sdrh sqlite3_free(pVTab->zErrMsg);
57371b65e88Sdrh pVTab->zErrMsg = sqlite3_mprintf("%s", sqlite3_errmsg(db));
57471b65e88Sdrh iBI = 0;
57571b65e88Sdrh }else if( sqlite3_step(pStmt)==SQLITE_ROW ){
57671b65e88Sdrh iBI = sqlite3_column_int(pStmt, 0)+1;
57771b65e88Sdrh }else{
57871b65e88Sdrh iBI = 1;
57971b65e88Sdrh }
58071b65e88Sdrh sqlite3_finalize(pStmt);
58171b65e88Sdrh }
58271b65e88Sdrh sqlite3RunSql(db,pVTab,
58371b65e88Sdrh "INSERT INTO temp.\"%w\"(bi,vn,ix) VALUES(%d,'nConstraint',%d)",
58471b65e88Sdrh zLogTab, iBI, pInfo->nConstraint
58571b65e88Sdrh );
58671b65e88Sdrh for(i=0; i<pInfo->nConstraint; i++){
58771b65e88Sdrh sqlite3_value *pVal;
58871b65e88Sdrh char *zSql;
58971b65e88Sdrh int iCol = pInfo->aConstraint[i].iColumn;
59071b65e88Sdrh int op = pInfo->aConstraint[i].op;
59171b65e88Sdrh const char *zCol;
59271b65e88Sdrh if( op==SQLITE_INDEX_CONSTRAINT_LIMIT
59371b65e88Sdrh || op==SQLITE_INDEX_CONSTRAINT_OFFSET
59471b65e88Sdrh ){
59571b65e88Sdrh zCol = "";
59671b65e88Sdrh }else if( iCol<0 ){
59771b65e88Sdrh zCol = "rowid";
59871b65e88Sdrh }else{
59971b65e88Sdrh zCol = azColname[iCol];
60071b65e88Sdrh }
60171b65e88Sdrh pStr = sqlite3_str_new(0);
60271b65e88Sdrh sqlite3_str_appendf(pStr,
60371b65e88Sdrh "INSERT INTO temp.\"%w\"(bi,vn,ix,cn,op,ux,rx,rhs,cs,inop)"
60471b65e88Sdrh "VALUES(%d,'aConstraint',%d,%Q,%d,%d",
60571b65e88Sdrh zLogTab, iBI,
60671b65e88Sdrh i,
60771b65e88Sdrh zCol,
60871b65e88Sdrh op,
60971b65e88Sdrh pInfo->aConstraint[i].usable);
61071b65e88Sdrh pVal = 0;
61171b65e88Sdrh rc = sqlite3_vtab_rhs_value(pInfo, i, &pVal);
61271b65e88Sdrh assert( pVal!=0 || rc!=SQLITE_OK );
61371b65e88Sdrh if( rc==SQLITE_OK ){
61471b65e88Sdrh sqlite3_str_appendf(pStr,",1,?1");
61571b65e88Sdrh }else{
61671b65e88Sdrh sqlite3_str_appendf(pStr,",0,NULL");
61771b65e88Sdrh }
61871b65e88Sdrh sqlite3_str_appendf(pStr,",%Q,%d)",
61971b65e88Sdrh sqlite3_vtab_collation(pInfo,i),
62071b65e88Sdrh sqlite3_vtab_in(pInfo,i,-1));
62171b65e88Sdrh zSql = sqlite3_str_finish(pStr);
62271b65e88Sdrh if( zSql==0 ){
62371b65e88Sdrh if( pVTab->zErrMsg==0 ) pVTab->zErrMsg = sqlite3_mprintf("out of memory");
62471b65e88Sdrh }else{
62571b65e88Sdrh sqlite3_stmt *pStmt = 0;
62671b65e88Sdrh rc = sqlite3_prepare_v2(db, zSql, -1, &pStmt, 0);
62771b65e88Sdrh if( rc ){
62871b65e88Sdrh if( pVTab->zErrMsg==0 ){
62971b65e88Sdrh pVTab->zErrMsg = sqlite3_mprintf("%s", sqlite3_errmsg(db));
63071b65e88Sdrh }
63171b65e88Sdrh }else{
63271b65e88Sdrh if( pVal ) sqlite3_bind_value(pStmt, 1, pVal);
63371b65e88Sdrh sqlite3_step(pStmt);
63471b65e88Sdrh rc = sqlite3_reset(pStmt);
63571b65e88Sdrh if( rc && pVTab->zErrMsg==0 ){
63671b65e88Sdrh pVTab->zErrMsg = sqlite3_mprintf("%s", sqlite3_errmsg(db));
63771b65e88Sdrh }
63871b65e88Sdrh }
63971b65e88Sdrh sqlite3_finalize(pStmt);
64071b65e88Sdrh sqlite3_free(zSql);
64171b65e88Sdrh }
64271b65e88Sdrh }
64371b65e88Sdrh sqlite3RunSql(db,pVTab,
64471b65e88Sdrh "INSERT INTO temp.\"%w\"(bi,vn,ix) VALUES(%d,'nOrderBy',%d)",
64571b65e88Sdrh zLogTab, iBI, pInfo->nOrderBy
64671b65e88Sdrh );
64771b65e88Sdrh for(i=0; i<pInfo->nOrderBy; i++){
64871b65e88Sdrh int iCol = pInfo->aOrderBy[i].iColumn;
64971b65e88Sdrh sqlite3RunSql(db,pVTab,
65071b65e88Sdrh "INSERT INTO temp.\"%w\"(bi,vn,ix,cn,op)VALUES(%d,'aOrderBy',%d,%Q,%d)",
65171b65e88Sdrh zLogTab, iBI,
65271b65e88Sdrh i,
65371b65e88Sdrh iCol>=0 ? azColname[iCol] : "rowid",
65471b65e88Sdrh pInfo->aOrderBy[i].desc
65571b65e88Sdrh );
65671b65e88Sdrh }
65771b65e88Sdrh sqlite3RunSql(db,pVTab,
65871b65e88Sdrh "INSERT INTO temp.\"%w\"(bi,vn,ix) VALUES(%d,'sqlite3_vtab_distinct',%d)",
65971b65e88Sdrh zLogTab, iBI, sqlite3_vtab_distinct(pInfo)
66071b65e88Sdrh );
66171b65e88Sdrh sqlite3RunSql(db,pVTab,
66271b65e88Sdrh "INSERT INTO temp.\"%w\"(bi,vn,ix) VALUES(%d,'colUsed',%lld)",
66371b65e88Sdrh zLogTab, iBI, pInfo->colUsed
66471b65e88Sdrh );
66571b65e88Sdrh for(i=0; i<pInfo->nConstraint; i++){
66671b65e88Sdrh int iCol = pInfo->aConstraint[i].iColumn;
66771b65e88Sdrh int op = pInfo->aConstraint[i].op;
66871b65e88Sdrh const char *zCol;
66971b65e88Sdrh if( op==SQLITE_INDEX_CONSTRAINT_LIMIT
67071b65e88Sdrh || op==SQLITE_INDEX_CONSTRAINT_OFFSET
67171b65e88Sdrh ){
67271b65e88Sdrh zCol = "";
67371b65e88Sdrh }else if( iCol<0 ){
67471b65e88Sdrh zCol = "rowid";
67571b65e88Sdrh }else{
67671b65e88Sdrh zCol = azColname[iCol];
67771b65e88Sdrh }
67871b65e88Sdrh sqlite3RunSql(db,pVTab,
67971b65e88Sdrh "INSERT INTO temp.\"%w\"(bi,vn,ix,cn,op,ux)"
68071b65e88Sdrh "VALUES(%d,'aConstraintUsage',%d,%Q,%d,%d)",
68171b65e88Sdrh zLogTab, iBI,
68271b65e88Sdrh i,
68371b65e88Sdrh zCol,
68471b65e88Sdrh pInfo->aConstraintUsage[i].argvIndex,
68571b65e88Sdrh pInfo->aConstraintUsage[i].omit
68671b65e88Sdrh );
68771b65e88Sdrh }
68871b65e88Sdrh sqlite3RunSql(db,pVTab,
68971b65e88Sdrh "INSERT INTO temp.\"%w\"(bi,vn,ix)VALUES(%d,'idxNum',%d)",
69071b65e88Sdrh zLogTab, iBI, pInfo->idxNum
69171b65e88Sdrh );
69271b65e88Sdrh sqlite3RunSql(db,pVTab,
69371b65e88Sdrh "INSERT INTO temp.\"%w\"(bi,vn,ix)VALUES(%d,'estimatedCost',%f)",
69471b65e88Sdrh zLogTab, iBI, pInfo->estimatedCost
69571b65e88Sdrh );
69671b65e88Sdrh sqlite3RunSql(db,pVTab,
69771b65e88Sdrh "INSERT INTO temp.\"%w\"(bi,vn,ix)VALUES(%d,'estimatedRows',%lld)",
69871b65e88Sdrh zLogTab, iBI, pInfo->estimatedRows
69971b65e88Sdrh );
70071b65e88Sdrh if( pInfo->idxStr ){
70171b65e88Sdrh sqlite3RunSql(db,pVTab,
70271b65e88Sdrh "INSERT INTO temp.\"%w\"(bi,vn,ix)VALUES(%d,'idxStr',%Q)",
70371b65e88Sdrh zLogTab, iBI, pInfo->idxStr
70471b65e88Sdrh );
70571b65e88Sdrh sqlite3RunSql(db,pVTab,
70671b65e88Sdrh "INSERT INTO temp.\"%w\"(bi,vn,ix)VALUES(%d,'needToFreeIdxStr',%d)",
70771b65e88Sdrh zLogTab, iBI, pInfo->needToFreeIdxStr
70871b65e88Sdrh );
70971b65e88Sdrh }
71071b65e88Sdrh if( pInfo->nOrderBy ){
71171b65e88Sdrh sqlite3RunSql(db,pVTab,
71271b65e88Sdrh "INSERT INTO temp.\"%w\"(bi,vn,ix)VALUES(%d,'orderByConsumed',%d)",
71371b65e88Sdrh zLogTab, iBI, pInfo->orderByConsumed
71471b65e88Sdrh );
71571b65e88Sdrh }
71671b65e88Sdrh }
71771b65e88Sdrh /*
71871b65e88Sdrh ** End of Logging Subsystem
71971b65e88Sdrh *****************************************************************************/
72071b65e88Sdrh
72171b65e88Sdrh
72271b65e88Sdrh /* Find an estimated cost of running a query against vt02.
72371b65e88Sdrh */
vt02BestIndex(sqlite3_vtab * pVTab,sqlite3_index_info * pInfo)72471b65e88Sdrh static int vt02BestIndex(sqlite3_vtab *pVTab, sqlite3_index_info *pInfo){
72571b65e88Sdrh int i; /* Loop counter */
72671b65e88Sdrh int isEq[5]; /* Equality constraints on X, A, B, C, and D */
72771b65e88Sdrh int isUsed[5]; /* Other non-== cosntraints X, A, B, C, and D */
72871b65e88Sdrh int argvIndex = 0; /* Next available argv[] slot */
72971b65e88Sdrh int iOffset = -1; /* Constraint for OFFSET */
73071b65e88Sdrh void *pX = 0; /* idxStr value */
73171b65e88Sdrh int flags = 0; /* RHS value for flags= */
73271b65e88Sdrh const char *zLogTab = 0; /* RHS value for logtab= */
73371b65e88Sdrh int iFlagTerm = -1; /* Constraint term for flags= */
73471b65e88Sdrh int iLogTerm = -1; /* Constraint term for logtab= */
73571b65e88Sdrh int iIn = -1; /* Index of the IN constraint */
73671b65e88Sdrh vt02_vtab *pSelf; /* This virtual table */
73771b65e88Sdrh
73871b65e88Sdrh pSelf = (vt02_vtab*)pVTab;
73971b65e88Sdrh if( pSelf->busy ){
74071b65e88Sdrh vt02ErrMsg(pVTab, "recursive use of vt02 prohibited");
74171b65e88Sdrh return SQLITE_CONSTRAINT;
74271b65e88Sdrh }
74371b65e88Sdrh pSelf->busy++;
74471b65e88Sdrh
74571b65e88Sdrh
74671b65e88Sdrh /* Do an initial scan for flags=N and logtab=TAB constraints with
74771b65e88Sdrh ** usable RHS values */
74871b65e88Sdrh for(i=0; i<pInfo->nConstraint; i++){
74971b65e88Sdrh sqlite3_value *pVal;
75071b65e88Sdrh if( !pInfo->aConstraint[i].usable ) continue;
75171b65e88Sdrh if( pInfo->aConstraint[i].op!=SQLITE_INDEX_CONSTRAINT_EQ ) continue;
75271b65e88Sdrh switch( pInfo->aConstraint[i].iColumn ){
75371b65e88Sdrh case VT02_COL_FLAGS:
75471b65e88Sdrh if( sqlite3_vtab_rhs_value(pInfo, i, &pVal)==SQLITE_OK
75571b65e88Sdrh && sqlite3_value_type(pVal)==SQLITE_INTEGER
75671b65e88Sdrh ){
75771b65e88Sdrh flags = sqlite3_value_int(pVal);
75871b65e88Sdrh }
75971b65e88Sdrh iFlagTerm = i;
76071b65e88Sdrh break;
76171b65e88Sdrh case VT02_COL_LOGTAB:
76271b65e88Sdrh if( sqlite3_vtab_rhs_value(pInfo, i, &pVal)==SQLITE_OK
76371b65e88Sdrh && sqlite3_value_type(pVal)==SQLITE_TEXT
76471b65e88Sdrh ){
76571b65e88Sdrh zLogTab = (const char*)sqlite3_value_text(pVal);
76671b65e88Sdrh }
76771b65e88Sdrh iLogTerm = i;
76871b65e88Sdrh break;
76971b65e88Sdrh }
77071b65e88Sdrh }
77171b65e88Sdrh
77271b65e88Sdrh /* Do a second scan to actually analyze the index information */
77371b65e88Sdrh memset(isEq, 0xff, sizeof(isEq));
77471b65e88Sdrh memset(isUsed, 0xff, sizeof(isUsed));
77571b65e88Sdrh for(i=0; i<pInfo->nConstraint; i++){
77671b65e88Sdrh int j = pInfo->aConstraint[i].iColumn;
77771b65e88Sdrh if( j>=VT02_COL_FLAGS ) continue;
77871b65e88Sdrh if( pInfo->aConstraint[i].usable==0
77971b65e88Sdrh && (flags & VT02_IGNORE_USABLE)==0 ) continue;
78071b65e88Sdrh if( j<0 ) j = VT02_COL_X;
78171b65e88Sdrh switch( pInfo->aConstraint[i].op ){
78271b65e88Sdrh case SQLITE_INDEX_CONSTRAINT_FUNCTION:
78371b65e88Sdrh case SQLITE_INDEX_CONSTRAINT_EQ:
78471b65e88Sdrh isEq[j] = i;
78571b65e88Sdrh break;
78671b65e88Sdrh case SQLITE_INDEX_CONSTRAINT_LT:
78771b65e88Sdrh case SQLITE_INDEX_CONSTRAINT_LE:
78871b65e88Sdrh case SQLITE_INDEX_CONSTRAINT_GT:
78971b65e88Sdrh case SQLITE_INDEX_CONSTRAINT_GE:
79071b65e88Sdrh isUsed[j] = i;
79171b65e88Sdrh break;
79271b65e88Sdrh case SQLITE_INDEX_CONSTRAINT_OFFSET:
79371b65e88Sdrh iOffset = i;
79471b65e88Sdrh break;
79571b65e88Sdrh }
79671b65e88Sdrh }
79771b65e88Sdrh
79871b65e88Sdrh /* Use the analysis to find an appropriate query plan */
79971b65e88Sdrh if( isEq[0]>=0 ){
80071b65e88Sdrh /* A constraint of X= takes priority */
80171b65e88Sdrh pInfo->estimatedCost = 1;
80271b65e88Sdrh pInfo->aConstraintUsage[isEq[0]].argvIndex = ++argvIndex;
80371b65e88Sdrh if( flags & 0x20 ) pInfo->aConstraintUsage[isEq[0]].omit = 1;
80471b65e88Sdrh pInfo->idxNum = 1;
80571b65e88Sdrh }else if( isEq[1]<0 ){
80671b65e88Sdrh /* If there is no X= nor A= then we have to do a full scan */
80771b65e88Sdrh pInfo->idxNum = 0;
80871b65e88Sdrh pInfo->estimatedCost = 10000;
80971b65e88Sdrh }else{
81071b65e88Sdrh int v = 1000;
81171b65e88Sdrh pInfo->aConstraintUsage[isEq[1]].argvIndex = ++argvIndex;
81271b65e88Sdrh if( flags & 0x20 ) pInfo->aConstraintUsage[isEq[1]].omit = 1;
81371b65e88Sdrh for(i=2; i<=4 && isEq[i]>=0; i++){
81471b65e88Sdrh if( i==4 && sqlite3_vtab_in(pInfo, isEq[4], 0) ) break;
81571b65e88Sdrh pInfo->aConstraintUsage[isEq[i]].argvIndex = ++argvIndex;
81671b65e88Sdrh if( flags & 0x20 ) pInfo->aConstraintUsage[isEq[i]].omit = 1;
81771b65e88Sdrh v /= 10;
81871b65e88Sdrh }
81971b65e88Sdrh pInfo->idxNum = i;
82071b65e88Sdrh if( isEq[4]>=0 && sqlite3_vtab_in(pInfo,isEq[4],1) ){
82171b65e88Sdrh iIn = isEq[4];
82271b65e88Sdrh pInfo->aConstraintUsage[iIn].argvIndex = ++argvIndex;
82371b65e88Sdrh if( flags & 0x20 ) pInfo->aConstraintUsage[iIn].omit = 1;
82471b65e88Sdrh v /= 5;
82571b65e88Sdrh i++;
82671b65e88Sdrh pInfo->idxNum += 4;
82771b65e88Sdrh }
82871b65e88Sdrh pInfo->estimatedCost = v;
82971b65e88Sdrh }
83071b65e88Sdrh pInfo->estimatedRows = (sqlite3_int64)pInfo->estimatedCost;
83171b65e88Sdrh
83271b65e88Sdrh /* Attempt to consume the ORDER BY clause. Except, always leave
83371b65e88Sdrh ** orderByConsumed set to 0 for vt02_no_sort_opt. In this way,
83471b65e88Sdrh ** we can compare vt02 and vt02_no_sort_opt to ensure they get
83571b65e88Sdrh ** the same answer.
83671b65e88Sdrh */
83771b65e88Sdrh if( pInfo->nOrderBy>0 && (flags & VT02_NO_SORT_OPT)==0 ){
83871b65e88Sdrh if( pInfo->idxNum==1 ){
83971b65e88Sdrh /* There will only be one row of output. So it is always sorted. */
84071b65e88Sdrh pInfo->orderByConsumed = 1;
84171b65e88Sdrh }else
84271b65e88Sdrh if( pInfo->aOrderBy[0].iColumn<=0
84371b65e88Sdrh && pInfo->aOrderBy[0].desc==0
84471b65e88Sdrh ){
84571b65e88Sdrh /* First column of order by is X ascending */
84671b65e88Sdrh pInfo->orderByConsumed = 1;
84771b65e88Sdrh }else
84871b65e88Sdrh if( sqlite3_vtab_distinct(pInfo)>=1 ){
84971b65e88Sdrh unsigned int x = 0;
85071b65e88Sdrh for(i=0; i<pInfo->nOrderBy; i++){
85171b65e88Sdrh int iCol = pInfo->aOrderBy[i].iColumn;
85271b65e88Sdrh if( iCol<0 ) iCol = 0;
85371b65e88Sdrh x |= 1<<iCol;
85471b65e88Sdrh }
85571b65e88Sdrh if( sqlite3_vtab_distinct(pInfo)==2 ){
85671b65e88Sdrh if( x==0x02 ){
85771b65e88Sdrh /* DISTINCT A */
85871b65e88Sdrh pInfo->idxNum += 30;
85971b65e88Sdrh pInfo->orderByConsumed = 1;
86071b65e88Sdrh }else if( x==0x06 ){
86171b65e88Sdrh /* DISTINCT A,B */
86271b65e88Sdrh pInfo->idxNum += 20;
86371b65e88Sdrh pInfo->orderByConsumed = 1;
86471b65e88Sdrh }else if( x==0x0e ){
86571b65e88Sdrh /* DISTINCT A,B,C */
86671b65e88Sdrh pInfo->idxNum += 10;
86771b65e88Sdrh pInfo->orderByConsumed = 1;
86871b65e88Sdrh }else if( x & 0x01 ){
86971b65e88Sdrh /* DISTINCT X */
87071b65e88Sdrh pInfo->orderByConsumed = 1;
87171b65e88Sdrh }else if( x==0x1e ){
87271b65e88Sdrh /* DISTINCT A,B,C,D */
87371b65e88Sdrh pInfo->orderByConsumed = 1;
87471b65e88Sdrh }
87571b65e88Sdrh }else{
87671b65e88Sdrh if( x==0x02 ){
87771b65e88Sdrh /* GROUP BY A */
87871b65e88Sdrh pInfo->orderByConsumed = 1;
87971b65e88Sdrh }else if( x==0x06 ){
88071b65e88Sdrh /* GROUP BY A,B */
88171b65e88Sdrh pInfo->orderByConsumed = 1;
88271b65e88Sdrh }else if( x==0x0e ){
88371b65e88Sdrh /* GROUP BY A,B,C */
88471b65e88Sdrh pInfo->orderByConsumed = 1;
88571b65e88Sdrh }else if( x & 0x01 ){
88671b65e88Sdrh /* GROUP BY X */
88771b65e88Sdrh pInfo->orderByConsumed = 1;
88871b65e88Sdrh }else if( x==0x1e ){
88971b65e88Sdrh /* GROUP BY A,B,C,D */
89071b65e88Sdrh pInfo->orderByConsumed = 1;
89171b65e88Sdrh }
89271b65e88Sdrh }
89371b65e88Sdrh }
89471b65e88Sdrh }
89571b65e88Sdrh
89671b65e88Sdrh if( flags & VT02_ALLOC_IDXSTR ){
89771b65e88Sdrh pInfo->idxStr = sqlite3_mprintf("test");
89871b65e88Sdrh pInfo->needToFreeIdxStr = 1;
89971b65e88Sdrh }
90071b65e88Sdrh if( flags & VT02_BAD_IDXNUM ){
90171b65e88Sdrh pInfo->idxNum += 1000;
90271b65e88Sdrh }
90371b65e88Sdrh
90471b65e88Sdrh if( iOffset>=0 ){
90571b65e88Sdrh pInfo->aConstraintUsage[iOffset].argvIndex = ++argvIndex;
90671b65e88Sdrh if( (flags & VT02_NO_OFFSET)==0
90771b65e88Sdrh && (pInfo->nOrderBy==0 || pInfo->orderByConsumed)
90871b65e88Sdrh ){
90971b65e88Sdrh pInfo->aConstraintUsage[iOffset].omit = 1;
91071b65e88Sdrh pInfo->idxNum += 100;
91171b65e88Sdrh }
91271b65e88Sdrh }
91371b65e88Sdrh
91471b65e88Sdrh
91571b65e88Sdrh /* Always omit flags= and logtab= constraints to prevent them from
91671b65e88Sdrh ** interfering with the bytecode. Put them at the end of the argv[]
91771b65e88Sdrh ** array to keep them out of the way.
91871b65e88Sdrh */
91971b65e88Sdrh if( iFlagTerm>=0 ){
92071b65e88Sdrh pInfo->aConstraintUsage[iFlagTerm].omit = 1;
92171b65e88Sdrh pInfo->aConstraintUsage[iFlagTerm].argvIndex = ++argvIndex;
92271b65e88Sdrh }
92371b65e88Sdrh if( iLogTerm>=0 ){
92471b65e88Sdrh pInfo->aConstraintUsage[iLogTerm].omit = 1;
92571b65e88Sdrh pInfo->aConstraintUsage[iLogTerm].argvIndex = ++argvIndex;
92671b65e88Sdrh }
92771b65e88Sdrh
92871b65e88Sdrh /* The 0x40 flag means add all usable constraints to the output set */
92971b65e88Sdrh if( flags & 0x40 ){
93071b65e88Sdrh for(i=0; i<pInfo->nConstraint; i++){
93171b65e88Sdrh if( pInfo->aConstraint[i].usable
93271b65e88Sdrh && pInfo->aConstraintUsage[i].argvIndex==0
93371b65e88Sdrh ){
93471b65e88Sdrh pInfo->aConstraintUsage[i].argvIndex = ++argvIndex;
93571b65e88Sdrh if( flags & 0x20 ) pInfo->aConstraintUsage[i].omit = 1;
93671b65e88Sdrh }
93771b65e88Sdrh }
93871b65e88Sdrh }
93971b65e88Sdrh
94071b65e88Sdrh
94171b65e88Sdrh /* Generate the log if requested */
94271b65e88Sdrh if( zLogTab ){
94371b65e88Sdrh static const char *azColname[] = {
94471b65e88Sdrh "x", "a", "b", "c", "d", "flags", "logtab"
94571b65e88Sdrh };
94671b65e88Sdrh sqlite3 *db = ((vt02_vtab*)pVTab)->db;
94771b65e88Sdrh sqlite3BestIndexLog(pInfo, zLogTab, db, azColname, pVTab);
94871b65e88Sdrh }
94971b65e88Sdrh pSelf->busy--;
95071b65e88Sdrh
95171b65e88Sdrh /* Try to do a memory allocation solely for the purpose of causing
95271b65e88Sdrh ** an error under OOM testing loops */
95371b65e88Sdrh pX = sqlite3_malloc(800);
95471b65e88Sdrh if( pX==0 ) return SQLITE_NOMEM;
95571b65e88Sdrh sqlite3_free(pX);
95671b65e88Sdrh
95771b65e88Sdrh return pVTab->zErrMsg!=0 ? SQLITE_ERROR : SQLITE_OK;
95871b65e88Sdrh }
95971b65e88Sdrh
96071b65e88Sdrh /* This is the sqlite3_module definition for the the virtual table defined
96171b65e88Sdrh ** by this include file.
96271b65e88Sdrh */
96371b65e88Sdrh const sqlite3_module vt02Module = {
96471b65e88Sdrh /* iVersion */ 2,
96571b65e88Sdrh /* xCreate */ 0, /* This is an eponymous table */
96671b65e88Sdrh /* xConnect */ vt02Connect,
96771b65e88Sdrh /* xBestIndex */ vt02BestIndex,
96871b65e88Sdrh /* xDisconnect */ vt02Disconnect,
96971b65e88Sdrh /* xDestroy */ vt02Disconnect,
97071b65e88Sdrh /* xOpen */ vt02Open,
97171b65e88Sdrh /* xClose */ vt02Close,
97271b65e88Sdrh /* xFilter */ vt02Filter,
97371b65e88Sdrh /* xNext */ vt02Next,
97471b65e88Sdrh /* xEof */ vt02Eof,
97571b65e88Sdrh /* xColumn */ vt02Column,
97671b65e88Sdrh /* xRowid */ vt02Rowid,
97771b65e88Sdrh /* xUpdate */ 0,
97871b65e88Sdrh /* xBegin */ 0,
97971b65e88Sdrh /* xSync */ 0,
98071b65e88Sdrh /* xCommit */ 0,
98171b65e88Sdrh /* xRollback */ 0,
98271b65e88Sdrh /* xFindFunction */ 0,
98371b65e88Sdrh /* xRename */ 0,
98471b65e88Sdrh /* xSavepoint */ 0,
98571b65e88Sdrh /* xRelease */ 0,
98671b65e88Sdrh /* xRollbackTo */ 0
98771b65e88Sdrh };
98871b65e88Sdrh
vt02CoreInit(sqlite3 * db)98971b65e88Sdrh static void vt02CoreInit(sqlite3 *db){
99071b65e88Sdrh static const char zPkXSchema[] =
99171b65e88Sdrh "CREATE TABLE x(x INT NOT NULL PRIMARY KEY, a INT, b INT, c INT, d INT,"
99271b65e88Sdrh " flags INT HIDDEN, logtab TEXT HIDDEN);";
99371b65e88Sdrh static const char zPkABCDSchema[] =
99471b65e88Sdrh "CREATE TABLE x(x INT, a INT NOT NULL, b INT NOT NULL, c INT NOT NULL, "
99571b65e88Sdrh "d INT NOT NULL, flags INT HIDDEN, logtab TEXT HIDDEN, "
99671b65e88Sdrh "PRIMARY KEY(a,b,c,d));";
99771b65e88Sdrh sqlite3_create_module(db, "vt02", &vt02Module, 0);
99871b65e88Sdrh sqlite3_create_module(db, "vt02pkx", &vt02Module, (void*)zPkXSchema);
99971b65e88Sdrh sqlite3_create_module(db, "vt02pkabcd", &vt02Module, (void*)zPkABCDSchema);
100071b65e88Sdrh }
100171b65e88Sdrh
100271b65e88Sdrh #ifdef TH3_VERSION
vt02_init(th3state * p,int iDb,char * zArg)100371b65e88Sdrh static void vt02_init(th3state *p, int iDb, char *zArg){
100471b65e88Sdrh vt02CoreInit(th3dbPointer(p, iDb));
100571b65e88Sdrh }
100671b65e88Sdrh #else
100771b65e88Sdrh #ifdef _WIN32
100871b65e88Sdrh __declspec(dllexport)
100971b65e88Sdrh #endif
sqlite3_vt02_init(sqlite3 * db,char ** pzErrMsg,const sqlite3_api_routines * pApi)101071b65e88Sdrh int sqlite3_vt02_init(
101171b65e88Sdrh sqlite3 *db,
101271b65e88Sdrh char **pzErrMsg,
101371b65e88Sdrh const sqlite3_api_routines *pApi
101471b65e88Sdrh ){
101571b65e88Sdrh SQLITE_EXTENSION_INIT2(pApi);
101671b65e88Sdrh vt02CoreInit(db);
101771b65e88Sdrh return SQLITE_OK;
101871b65e88Sdrh }
101971b65e88Sdrh #endif /* TH3_VERSION */
1020