diff options
author | dan <dan@noemail.net> | 2017-12-16 19:36:52 +0000 |
---|---|---|
committer | dan <dan@noemail.net> | 2017-12-16 19:36:52 +0000 |
commit | ebeffef36c5ad0e855cd208cc39989fc33511a6c (patch) | |
tree | ca101164f8a598d656b52ac5fc580658659e62a4 /src | |
parent | 468c649331dc631a5821adb24083bf5d96dfb9dc (diff) | |
parent | 11546779b711ca6ebadc272c735310c583555d98 (diff) | |
download | sqlite-ebeffef36c5ad0e855cd208cc39989fc33511a6c.tar.gz sqlite-ebeffef36c5ad0e855cd208cc39989fc33511a6c.zip |
Add the sqlite3_vtab_collation() function, which allows an xBestIndex callback
to determine the collation sequence that SQLite will use for a comparison. And
the SQLITE_DBCONFIG_FULL_EQP configuration option, which enhances the output
of "EXPLAIN QUERY PLAN" so that it includes statements run by triggers. And
the code for the sqlite3_expert extension and command line application.
FossilOrigin-Name: 4c782c950204c09c1d8f857c39c4cf476539ec4e7eee6fd86419d47cf0f8b9e0
Diffstat (limited to 'src')
-rw-r--r-- | src/main.c | 15 | ||||
-rw-r--r-- | src/pragma.c | 1 | ||||
-rw-r--r-- | src/sqlite.h.in | 30 | ||||
-rw-r--r-- | src/sqliteInt.h | 2 | ||||
-rw-r--r-- | src/test_tclsh.c | 3 | ||||
-rw-r--r-- | src/vdbeaux.c | 178 | ||||
-rw-r--r-- | src/where.c | 38 |
7 files changed, 181 insertions, 86 deletions
diff --git a/src/main.c b/src/main.c index c25d3a448..52d3be953 100644 --- a/src/main.c +++ b/src/main.c @@ -806,6 +806,21 @@ int sqlite3_db_config(sqlite3 *db, int op, ...){ rc = setupLookaside(db, pBuf, sz, cnt); break; } + case SQLITE_DBCONFIG_FULL_EQP: { + int onoff = va_arg(ap, int); + int *pRes = va_arg(ap, int*); + if( onoff>0 ){ + db->bFullEQP = 1; + }else if( onoff==0 ){ + db->bFullEQP = 0; + } + sqlite3ExpirePreparedStatements(db); + if( pRes ){ + *pRes = db->bFullEQP; + } + rc = SQLITE_OK; + break; + } default: { static const struct { int op; /* The opcode */ diff --git a/src/pragma.c b/src/pragma.c index 918b1d813..cdebcabae 100644 --- a/src/pragma.c +++ b/src/pragma.c @@ -1080,6 +1080,7 @@ void sqlite3Pragma( ** type: Column declaration type. ** notnull: True if 'NOT NULL' is part of column declaration ** dflt_value: The default value for the column, if any. + ** pk: Non-zero for PK fields. */ case PragTyp_TABLE_INFO: if( zRight ){ Table *pTab; diff --git a/src/sqlite.h.in b/src/sqlite.h.in index 7e26034bd..f4d44b52f 100644 --- a/src/sqlite.h.in +++ b/src/sqlite.h.in @@ -2060,7 +2060,6 @@ struct sqlite3_mem_methods { ** into which is written 0 or 1 to indicate whether checkpoints-on-close ** have been disabled - 0 if they are not disabled, 1 if they are. ** </dd> -** ** <dt>SQLITE_DBCONFIG_ENABLE_QPSG</dt> ** <dd>^(The SQLITE_DBCONFIG_ENABLE_QPSG option activates or deactivates ** the [query planner stability guarantee] (QPSG). When the QPSG is active, @@ -2071,7 +2070,16 @@ struct sqlite3_mem_methods { ** the QPSG active, SQLite will always use the same query plan in the field as ** was used during testing in the lab. ** </dd> -** +** <dt>SQLITE_DBCONFIG_FULL_EQP</dt> +** <dd> By default, the output of EXPLAIN QUERY PLAN commands does not +** include output for any operations performed by trigger programs. This +** option is used to set or clear (the default) a flag that governs this +** behavior. The first parameter passed to this operation is an integer - +** non-zero to enable output for trigger programs, or zero to disable it. +** The second parameter is a pointer to an integer into which is written +** 0 or 1 to indicate whether output-for-triggers has been disabled - 0 if +** it is not disabled, 1 if it is. +** </dd> ** </dl> */ #define SQLITE_DBCONFIG_MAINDBNAME 1000 /* const char* */ @@ -2082,7 +2090,7 @@ struct sqlite3_mem_methods { #define SQLITE_DBCONFIG_ENABLE_LOAD_EXTENSION 1005 /* int int* */ #define SQLITE_DBCONFIG_NO_CKPT_ON_CLOSE 1006 /* int int* */ #define SQLITE_DBCONFIG_ENABLE_QPSG 1007 /* int int* */ - +#define SQLITE_DBCONFIG_FULL_EQP 1008 /* int int* */ /* ** CAPI3REF: Enable Or Disable Extended Result Codes @@ -8288,6 +8296,22 @@ int sqlite3_vtab_config(sqlite3*, int op, ...); int sqlite3_vtab_on_conflict(sqlite3 *); /* +** CAPI3REF: Determine The Collation For a Virtual Table Constraint +** +** This function may only be called from within a call to the [xBestIndex] +** method of a [virtual table implementation]. +** +** The first argument must be the database handle with which the virtual +** table is associated (the one passed to the [xConnect] or [xCreate] method +** to create the sqlite3_vtab object. The second argument must be an index +** into the aConstraint[] array belonging to the sqlite3_index_info structure +** passed to xBestIndex. This function returns a pointer to a buffer +** containing the name of the collation sequence for the corresponding +** constraint. +*/ +SQLITE_EXPERIMENTAL const char *sqlite3_vtab_collation(sqlite3*, int); + +/* ** CAPI3REF: Conflict resolution modes ** KEYWORDS: {conflict resolution mode} ** diff --git a/src/sqliteInt.h b/src/sqliteInt.h index 47d9fc695..efe823a65 100644 --- a/src/sqliteInt.h +++ b/src/sqliteInt.h @@ -1356,6 +1356,7 @@ struct sqlite3 { u8 mTrace; /* zero or more SQLITE_TRACE flags */ u8 skipBtreeMutex; /* True if no shared-cache backends */ u8 nSqlExec; /* Number of pending OP_SqlExec opcodes */ + u8 bFullEQP; /* Include triggers in EQP output */ int nextPagesize; /* Pagesize after VACUUM if >0 */ u32 magic; /* Magic number for detect library misuse */ int nChange; /* Value returned by sqlite3_changes() */ @@ -1421,6 +1422,7 @@ struct sqlite3 { VtabCtx *pVtabCtx; /* Context for active vtab connect/create */ VTable **aVTrans; /* Virtual tables with open transactions */ VTable *pDisconnect; /* Disconnect these in next sqlite3_prepare() */ + void *pBestIndexCtx; /* For sqlite3_vtab_collation() */ #endif Hash aFunc; /* Hash table of connection functions */ Hash aCollSeq; /* All collating sequences */ diff --git a/src/test_tclsh.c b/src/test_tclsh.c index 976f7cb24..97f7f5d7a 100644 --- a/src/test_tclsh.c +++ b/src/test_tclsh.c @@ -104,6 +104,8 @@ const char *sqlite3TestInit(Tcl_Interp *interp){ #ifdef SQLITE_ENABLE_ZIPVFS extern int Zipvfs_Init(Tcl_Interp*); #endif + extern int TestExpert_Init(Tcl_Interp*); + Tcl_CmdInfo cmdInfo; /* Since the primary use case for this binary is testing of SQLite, @@ -166,6 +168,7 @@ const char *sqlite3TestInit(Tcl_Interp *interp){ #if defined(SQLITE_ENABLE_FTS3) || defined(SQLITE_ENABLE_FTS4) Sqlitetestfts3_Init(interp); #endif + TestExpert_Init(interp); Tcl_CreateObjCommand( interp, "load_testfixture_extensions", load_testfixture_extensions,0,0 diff --git a/src/vdbeaux.c b/src/vdbeaux.c index e73a339f8..1c354e0be 100644 --- a/src/vdbeaux.c +++ b/src/vdbeaux.c @@ -1639,6 +1639,8 @@ int sqlite3VdbeList( int i; /* Loop counter */ int rc = SQLITE_OK; /* Return code */ Mem *pMem = &p->aMem[1]; /* First Mem of result set */ + int bFull = (p->explain==1 || db->bFullEQP); + Op *pOp = 0; assert( p->explain ); assert( p->magic==VDBE_MAGIC_RUN ); @@ -1651,7 +1653,7 @@ int sqlite3VdbeList( releaseMemArray(pMem, 8); p->pResultSet = 0; - if( p->rc==SQLITE_NOMEM_BKPT ){ + if( p->rc==SQLITE_NOMEM ){ /* This happens if a malloc() inside a call to sqlite3_column_text() or ** sqlite3_column_text16() failed. */ sqlite3OomFault(db); @@ -1666,7 +1668,7 @@ int sqlite3VdbeList( ** encountered, but p->pc will eventually catch up to nRow. */ nRow = p->nOp; - if( p->explain==1 ){ + if( bFull ){ /* The first 8 memory cells are used for the result set. So we will ** commandeer the 9th cell to use as storage for an array of pointers ** to trigger subprograms. The VDBE is guaranteed to have at least 9 @@ -1686,17 +1688,11 @@ int sqlite3VdbeList( do{ i = p->pc++; - }while( i<nRow && p->explain==2 && p->aOp[i].opcode!=OP_Explain ); - if( i>=nRow ){ - p->rc = SQLITE_OK; - rc = SQLITE_DONE; - }else if( db->u1.isInterrupted ){ - p->rc = SQLITE_INTERRUPT; - rc = SQLITE_ERROR; - sqlite3VdbeError(p, sqlite3ErrStr(p->rc)); - }else{ - char *zP4; - Op *pOp; + if( i>=nRow ){ + p->rc = SQLITE_OK; + rc = SQLITE_DONE; + break; + } if( i<p->nOp ){ /* The output line number is small enough that we are still in the ** main program. */ @@ -1711,94 +1707,110 @@ int sqlite3VdbeList( } pOp = &apSub[j]->aOp[i]; } - if( p->explain==1 ){ - pMem->flags = MEM_Int; - pMem->u.i = i; /* Program counter */ - pMem++; - - pMem->flags = MEM_Static|MEM_Str|MEM_Term; - pMem->z = (char*)sqlite3OpcodeName(pOp->opcode); /* Opcode */ - assert( pMem->z!=0 ); - pMem->n = sqlite3Strlen30(pMem->z); - pMem->enc = SQLITE_UTF8; - pMem++; - /* When an OP_Program opcode is encounter (the only opcode that has - ** a P4_SUBPROGRAM argument), expand the size of the array of subprograms - ** kept in p->aMem[9].z to hold the new program - assuming this subprogram - ** has not already been seen. - */ - if( pOp->p4type==P4_SUBPROGRAM ){ - int nByte = (nSub+1)*sizeof(SubProgram*); - int j; - for(j=0; j<nSub; j++){ - if( apSub[j]==pOp->p4.pProgram ) break; - } - if( j==nSub && SQLITE_OK==sqlite3VdbeMemGrow(pSub, nByte, nSub!=0) ){ - apSub = (SubProgram **)pSub->z; - apSub[nSub++] = pOp->p4.pProgram; - pSub->flags |= MEM_Blob; - pSub->n = nSub*sizeof(SubProgram*); + /* When an OP_Program opcode is encounter (the only opcode that has + ** a P4_SUBPROGRAM argument), expand the size of the array of subprograms + ** kept in p->aMem[9].z to hold the new program - assuming this subprogram + ** has not already been seen. + */ + if( bFull && pOp->p4type==P4_SUBPROGRAM ){ + int nByte = (nSub+1)*sizeof(SubProgram*); + int j; + for(j=0; j<nSub; j++){ + if( apSub[j]==pOp->p4.pProgram ) break; + } + if( j==nSub ){ + p->rc = sqlite3VdbeMemGrow(pSub, nByte, nSub!=0); + if( p->rc!=SQLITE_OK ){ + rc = SQLITE_ERROR; + break; } + apSub = (SubProgram **)pSub->z; + apSub[nSub++] = pOp->p4.pProgram; + pSub->flags |= MEM_Blob; + pSub->n = nSub*sizeof(SubProgram*); + nRow += pOp->p4.pProgram->nOp; } } + }while( p->explain==2 && pOp->opcode!=OP_Explain ); - pMem->flags = MEM_Int; - pMem->u.i = pOp->p1; /* P1 */ - pMem++; + if( rc==SQLITE_OK ){ + if( db->u1.isInterrupted ){ + p->rc = SQLITE_INTERRUPT; + rc = SQLITE_ERROR; + sqlite3VdbeError(p, sqlite3ErrStr(p->rc)); + }else{ + char *zP4; + if( p->explain==1 ){ + pMem->flags = MEM_Int; + pMem->u.i = i; /* Program counter */ + pMem++; + + pMem->flags = MEM_Static|MEM_Str|MEM_Term; + pMem->z = (char*)sqlite3OpcodeName(pOp->opcode); /* Opcode */ + assert( pMem->z!=0 ); + pMem->n = sqlite3Strlen30(pMem->z); + pMem->enc = SQLITE_UTF8; + pMem++; + } - pMem->flags = MEM_Int; - pMem->u.i = pOp->p2; /* P2 */ - pMem++; + pMem->flags = MEM_Int; + pMem->u.i = pOp->p1; /* P1 */ + pMem++; - pMem->flags = MEM_Int; - pMem->u.i = pOp->p3; /* P3 */ - pMem++; + pMem->flags = MEM_Int; + pMem->u.i = pOp->p2; /* P2 */ + pMem++; - if( sqlite3VdbeMemClearAndResize(pMem, 100) ){ /* P4 */ - assert( p->db->mallocFailed ); - return SQLITE_ERROR; - } - pMem->flags = MEM_Str|MEM_Term; - zP4 = displayP4(pOp, pMem->z, pMem->szMalloc); - if( zP4!=pMem->z ){ - pMem->n = 0; - sqlite3VdbeMemSetStr(pMem, zP4, -1, SQLITE_UTF8, 0); - }else{ - assert( pMem->z!=0 ); - pMem->n = sqlite3Strlen30(pMem->z); - pMem->enc = SQLITE_UTF8; - } - pMem++; + pMem->flags = MEM_Int; + pMem->u.i = pOp->p3; /* P3 */ + pMem++; - if( p->explain==1 ){ - if( sqlite3VdbeMemClearAndResize(pMem, 4) ){ + if( sqlite3VdbeMemClearAndResize(pMem, 100) ){ /* P4 */ assert( p->db->mallocFailed ); return SQLITE_ERROR; } pMem->flags = MEM_Str|MEM_Term; - pMem->n = 2; - sqlite3_snprintf(3, pMem->z, "%.2x", pOp->p5); /* P5 */ - pMem->enc = SQLITE_UTF8; + zP4 = displayP4(pOp, pMem->z, pMem->szMalloc); + if( zP4!=pMem->z ){ + pMem->n = 0; + sqlite3VdbeMemSetStr(pMem, zP4, -1, SQLITE_UTF8, 0); + }else{ + assert( pMem->z!=0 ); + pMem->n = sqlite3Strlen30(pMem->z); + pMem->enc = SQLITE_UTF8; + } pMem++; - + + if( p->explain==1 ){ + if( sqlite3VdbeMemClearAndResize(pMem, 4) ){ + assert( p->db->mallocFailed ); + return SQLITE_ERROR; + } + pMem->flags = MEM_Str|MEM_Term; + pMem->n = 2; + sqlite3_snprintf(3, pMem->z, "%.2x", pOp->p5); /* P5 */ + pMem->enc = SQLITE_UTF8; + pMem++; + #ifdef SQLITE_ENABLE_EXPLAIN_COMMENTS - if( sqlite3VdbeMemClearAndResize(pMem, 500) ){ - assert( p->db->mallocFailed ); - return SQLITE_ERROR; - } - pMem->flags = MEM_Str|MEM_Term; - pMem->n = displayComment(pOp, zP4, pMem->z, 500); - pMem->enc = SQLITE_UTF8; + if( sqlite3VdbeMemClearAndResize(pMem, 500) ){ + assert( p->db->mallocFailed ); + return SQLITE_ERROR; + } + pMem->flags = MEM_Str|MEM_Term; + pMem->n = displayComment(pOp, zP4, pMem->z, 500); + pMem->enc = SQLITE_UTF8; #else - pMem->flags = MEM_Null; /* Comment */ + pMem->flags = MEM_Null; /* Comment */ #endif - } + } - p->nResColumn = 8 - 4*(p->explain-1); - p->pResultSet = &p->aMem[1]; - p->rc = SQLITE_OK; - rc = SQLITE_ROW; + p->nResColumn = 8 - 4*(p->explain-1); + p->pResultSet = &p->aMem[1]; + p->rc = SQLITE_OK; + rc = SQLITE_ROW; + } } return rc; } diff --git a/src/where.c b/src/where.c index 5b77b7662..0694031a5 100644 --- a/src/where.c +++ b/src/where.c @@ -3140,6 +3140,35 @@ static int whereLoopAddVirtualOne( /* +** Context object used to pass information from whereLoopAddVirtual() +** to sqlite3_vtab_collation(). +*/ +struct BestIndexCtx { + WhereClause *pWC; + sqlite3_index_info *pIdxInfo; + Parse *pParse; +}; + +/* +** If this function is invoked from within an xBestIndex() callback, it +** returns a pointer to a buffer containing the name of the collation +** sequence associated with element iCons of the sqlite3_index_info.aConstraint +** array. Or, if iCons is out of range or there is no active xBestIndex +** call, return NULL. +*/ +const char *sqlite3_vtab_collation(sqlite3 *db, int iCons){ + struct BestIndexCtx *p = (struct BestIndexCtx*)db->pBestIndexCtx; + const char *zRet = 0; + if( p && iCons>=0 && iCons<p->pIdxInfo->nConstraint ){ + int iTerm = p->pIdxInfo->aConstraint[iCons].iTermOffset; + Expr *pX = p->pWC->a[iTerm].pExpr; + CollSeq *pC = sqlite3BinaryCompareCollSeq(p->pParse,pX->pLeft,pX->pRight); + zRet = (pC ? pC->zName : "BINARY"); + } + return zRet; +} + +/* ** Add all WhereLoop objects for a table of the join identified by ** pBuilder->pNew->iTab. That table is guaranteed to be a virtual table. ** @@ -3180,6 +3209,8 @@ static int whereLoopAddVirtual( WhereLoop *pNew; Bitmask mBest; /* Tables used by best possible plan */ u16 mNoOmit; + struct BestIndexCtx bic; + void *pSaved; assert( (mPrereq & mUnusable)==0 ); pWInfo = pBuilder->pWInfo; @@ -3201,6 +3232,12 @@ static int whereLoopAddVirtual( return SQLITE_NOMEM_BKPT; } + bic.pWC = pWC; + bic.pIdxInfo = p; + bic.pParse = pParse; + pSaved = pParse->db->pBestIndexCtx; + pParse->db->pBestIndexCtx = (void*)&bic; + /* First call xBestIndex() with all constraints usable. */ WHERETRACE(0x40, (" VirtualOne: all usable\n")); rc = whereLoopAddVirtualOne(pBuilder, mPrereq, ALLBITS, 0, p, mNoOmit, &bIn); @@ -3277,6 +3314,7 @@ static int whereLoopAddVirtual( if( p->needToFreeIdxStr ) sqlite3_free(p->idxStr); sqlite3DbFreeNN(pParse->db, p); + pParse->db->pBestIndexCtx = pSaved; return rc; } #endif /* SQLITE_OMIT_VIRTUALTABLE */ |