067b92ba00
FossilOrigin-Name: 7a876209a678a34c198b54ceef9e3c041f128a14dc73357f6a57cadadaa6cf7b
323 lines
9.6 KiB
C
323 lines
9.6 KiB
C
/*
|
|
** 2018-09-16
|
|
**
|
|
** The author disclaims copyright to this source code. In place of
|
|
** a legal notice, here is a blessing:
|
|
**
|
|
** May you do good and not evil.
|
|
** May you find forgiveness for yourself and forgive others.
|
|
** May you share freely, never taking more than you give.
|
|
**
|
|
*************************************************************************
|
|
**
|
|
** This file demonstrates an eponymous virtual table that returns the
|
|
** EXPLAIN output from an SQL statement.
|
|
**
|
|
** Usage example:
|
|
**
|
|
** .load ./explain
|
|
** SELECT p2 FROM explain('SELECT * FROM sqlite_schema')
|
|
** WHERE opcode='OpenRead';
|
|
**
|
|
** This module was originally written to help simplify SQLite testing,
|
|
** by providing an easier means of verifying certain patterns in the
|
|
** generated bytecode.
|
|
*/
|
|
#if !defined(SQLITEINT_H)
|
|
#include "sqlite3ext.h"
|
|
#endif
|
|
SQLITE_EXTENSION_INIT1
|
|
#include <assert.h>
|
|
#include <string.h>
|
|
|
|
#ifndef SQLITE_OMIT_VIRTUALTABLE
|
|
|
|
/* explain_vtab is a subclass of sqlite3_vtab which will
|
|
** serve as the underlying representation of a explain virtual table
|
|
*/
|
|
typedef struct explain_vtab explain_vtab;
|
|
struct explain_vtab {
|
|
sqlite3_vtab base; /* Base class - must be first */
|
|
sqlite3 *db; /* Database connection for this explain vtab */
|
|
};
|
|
|
|
/* explain_cursor is a subclass of sqlite3_vtab_cursor which will
|
|
** serve as the underlying representation of a cursor that scans
|
|
** over rows of the result from an EXPLAIN operation.
|
|
*/
|
|
typedef struct explain_cursor explain_cursor;
|
|
struct explain_cursor {
|
|
sqlite3_vtab_cursor base; /* Base class - must be first */
|
|
sqlite3 *db; /* Database connection for this cursor */
|
|
char *zSql; /* Value for the EXPLN_COLUMN_SQL column */
|
|
sqlite3_stmt *pExplain; /* Statement being explained */
|
|
int rc; /* Result of last sqlite3_step() on pExplain */
|
|
};
|
|
|
|
/*
|
|
** The explainConnect() method is invoked to create a new
|
|
** explain_vtab that describes the explain virtual table.
|
|
**
|
|
** Think of this routine as the constructor for explain_vtab objects.
|
|
**
|
|
** All this routine needs to do is:
|
|
**
|
|
** (1) Allocate the explain_vtab object and initialize all fields.
|
|
**
|
|
** (2) Tell SQLite (via the sqlite3_declare_vtab() interface) what the
|
|
** result set of queries against explain will look like.
|
|
*/
|
|
static int explainConnect(
|
|
sqlite3 *db,
|
|
void *pAux,
|
|
int argc, const char *const*argv,
|
|
sqlite3_vtab **ppVtab,
|
|
char **pzErr
|
|
){
|
|
explain_vtab *pNew;
|
|
int rc;
|
|
|
|
/* Column numbers */
|
|
#define EXPLN_COLUMN_ADDR 0 /* Instruction address */
|
|
#define EXPLN_COLUMN_OPCODE 1 /* Opcode */
|
|
#define EXPLN_COLUMN_P1 2 /* Operand 1 */
|
|
#define EXPLN_COLUMN_P2 3 /* Operand 2 */
|
|
#define EXPLN_COLUMN_P3 4 /* Operand 3 */
|
|
#define EXPLN_COLUMN_P4 5 /* Operand 4 */
|
|
#define EXPLN_COLUMN_P5 6 /* Operand 5 */
|
|
#define EXPLN_COLUMN_COMMENT 7 /* Comment */
|
|
#define EXPLN_COLUMN_SQL 8 /* SQL that is being explained */
|
|
|
|
|
|
rc = sqlite3_declare_vtab(db,
|
|
"CREATE TABLE x(addr,opcode,p1,p2,p3,p4,p5,comment,sql HIDDEN)");
|
|
if( rc==SQLITE_OK ){
|
|
pNew = sqlite3_malloc( sizeof(*pNew) );
|
|
*ppVtab = (sqlite3_vtab*)pNew;
|
|
if( pNew==0 ) return SQLITE_NOMEM;
|
|
memset(pNew, 0, sizeof(*pNew));
|
|
pNew->db = db;
|
|
}
|
|
return rc;
|
|
}
|
|
|
|
/*
|
|
** This method is the destructor for explain_cursor objects.
|
|
*/
|
|
static int explainDisconnect(sqlite3_vtab *pVtab){
|
|
sqlite3_free(pVtab);
|
|
return SQLITE_OK;
|
|
}
|
|
|
|
/*
|
|
** Constructor for a new explain_cursor object.
|
|
*/
|
|
static int explainOpen(sqlite3_vtab *p, sqlite3_vtab_cursor **ppCursor){
|
|
explain_cursor *pCur;
|
|
pCur = sqlite3_malloc( sizeof(*pCur) );
|
|
if( pCur==0 ) return SQLITE_NOMEM;
|
|
memset(pCur, 0, sizeof(*pCur));
|
|
pCur->db = ((explain_vtab*)p)->db;
|
|
*ppCursor = &pCur->base;
|
|
return SQLITE_OK;
|
|
}
|
|
|
|
/*
|
|
** Destructor for a explain_cursor.
|
|
*/
|
|
static int explainClose(sqlite3_vtab_cursor *cur){
|
|
explain_cursor *pCur = (explain_cursor*)cur;
|
|
sqlite3_finalize(pCur->pExplain);
|
|
sqlite3_free(pCur->zSql);
|
|
sqlite3_free(pCur);
|
|
return SQLITE_OK;
|
|
}
|
|
|
|
|
|
/*
|
|
** Advance a explain_cursor to its next row of output.
|
|
*/
|
|
static int explainNext(sqlite3_vtab_cursor *cur){
|
|
explain_cursor *pCur = (explain_cursor*)cur;
|
|
pCur->rc = sqlite3_step(pCur->pExplain);
|
|
if( pCur->rc!=SQLITE_DONE && pCur->rc!=SQLITE_ROW ) return pCur->rc;
|
|
return SQLITE_OK;
|
|
}
|
|
|
|
/*
|
|
** Return values of columns for the row at which the explain_cursor
|
|
** is currently pointing.
|
|
*/
|
|
static int explainColumn(
|
|
sqlite3_vtab_cursor *cur, /* The cursor */
|
|
sqlite3_context *ctx, /* First argument to sqlite3_result_...() */
|
|
int i /* Which column to return */
|
|
){
|
|
explain_cursor *pCur = (explain_cursor*)cur;
|
|
if( i==EXPLN_COLUMN_SQL ){
|
|
sqlite3_result_text(ctx, pCur->zSql, -1, SQLITE_TRANSIENT);
|
|
}else{
|
|
sqlite3_result_value(ctx, sqlite3_column_value(pCur->pExplain, i));
|
|
}
|
|
return SQLITE_OK;
|
|
}
|
|
|
|
/*
|
|
** Return the rowid for the current row. In this implementation, the
|
|
** rowid is the same as the output value.
|
|
*/
|
|
static int explainRowid(sqlite3_vtab_cursor *cur, sqlite_int64 *pRowid){
|
|
explain_cursor *pCur = (explain_cursor*)cur;
|
|
*pRowid = sqlite3_column_int64(pCur->pExplain, 0);
|
|
return SQLITE_OK;
|
|
}
|
|
|
|
/*
|
|
** Return TRUE if the cursor has been moved off of the last
|
|
** row of output.
|
|
*/
|
|
static int explainEof(sqlite3_vtab_cursor *cur){
|
|
explain_cursor *pCur = (explain_cursor*)cur;
|
|
return pCur->rc!=SQLITE_ROW;
|
|
}
|
|
|
|
/*
|
|
** This method is called to "rewind" the explain_cursor object back
|
|
** to the first row of output. This method is always called at least
|
|
** once prior to any call to explainColumn() or explainRowid() or
|
|
** explainEof().
|
|
**
|
|
** The argv[0] is the SQL statement that is to be explained.
|
|
*/
|
|
static int explainFilter(
|
|
sqlite3_vtab_cursor *pVtabCursor,
|
|
int idxNum, const char *idxStr,
|
|
int argc, sqlite3_value **argv
|
|
){
|
|
explain_cursor *pCur = (explain_cursor *)pVtabCursor;
|
|
char *zSql = 0;
|
|
int rc;
|
|
sqlite3_finalize(pCur->pExplain);
|
|
pCur->pExplain = 0;
|
|
if( sqlite3_value_type(argv[0])!=SQLITE_TEXT ){
|
|
pCur->rc = SQLITE_DONE;
|
|
return SQLITE_OK;
|
|
}
|
|
sqlite3_free(pCur->zSql);
|
|
pCur->zSql = sqlite3_mprintf("%s", sqlite3_value_text(argv[0]));
|
|
if( pCur->zSql ){
|
|
zSql = sqlite3_mprintf("EXPLAIN %s", pCur->zSql);
|
|
}
|
|
if( zSql==0 ){
|
|
rc = SQLITE_NOMEM;
|
|
}else{
|
|
rc = sqlite3_prepare_v2(pCur->db, zSql, -1, &pCur->pExplain, 0);
|
|
sqlite3_free(zSql);
|
|
}
|
|
if( rc ){
|
|
sqlite3_finalize(pCur->pExplain);
|
|
pCur->pExplain = 0;
|
|
sqlite3_free(pCur->zSql);
|
|
pCur->zSql = 0;
|
|
}else{
|
|
pCur->rc = sqlite3_step(pCur->pExplain);
|
|
rc = (pCur->rc==SQLITE_DONE || pCur->rc==SQLITE_ROW) ? SQLITE_OK : pCur->rc;
|
|
}
|
|
return rc;
|
|
}
|
|
|
|
/*
|
|
** SQLite will invoke this method one or more times while planning a query
|
|
** that uses the explain virtual table. This routine needs to create
|
|
** a query plan for each invocation and compute an estimated cost for that
|
|
** plan.
|
|
*/
|
|
static int explainBestIndex(
|
|
sqlite3_vtab *tab,
|
|
sqlite3_index_info *pIdxInfo
|
|
){
|
|
int i; /* Loop counter */
|
|
int idx = -1; /* Index of a usable == constraint against SQL */
|
|
int unusable = 0; /* True if there are unusable constraints on SQL */
|
|
|
|
pIdxInfo->estimatedRows = 500;
|
|
for(i=0; i<pIdxInfo->nConstraint; i++){
|
|
struct sqlite3_index_constraint *p = &pIdxInfo->aConstraint[i];
|
|
if( p->iColumn!=EXPLN_COLUMN_SQL ) continue;
|
|
if( !p->usable ){
|
|
unusable = 1;
|
|
}else if( p->op==SQLITE_INDEX_CONSTRAINT_EQ ){
|
|
idx = i;
|
|
}
|
|
}
|
|
if( idx>=0 ){
|
|
/* There exists a usable == constraint against the SQL column */
|
|
pIdxInfo->estimatedCost = 10.0;
|
|
pIdxInfo->idxNum = 1;
|
|
pIdxInfo->aConstraintUsage[idx].argvIndex = 1;
|
|
pIdxInfo->aConstraintUsage[idx].omit = 1;
|
|
}else if( unusable ){
|
|
/* There are unusable constraints against the SQL column. Do not allow
|
|
** this plan to continue forward. */
|
|
return SQLITE_CONSTRAINT;
|
|
}
|
|
return SQLITE_OK;
|
|
}
|
|
|
|
/*
|
|
** This following structure defines all the methods for the
|
|
** explain virtual table.
|
|
*/
|
|
static sqlite3_module explainModule = {
|
|
0, /* iVersion */
|
|
0, /* xCreate */
|
|
explainConnect, /* xConnect */
|
|
explainBestIndex, /* xBestIndex */
|
|
explainDisconnect, /* xDisconnect */
|
|
0, /* xDestroy */
|
|
explainOpen, /* xOpen - open a cursor */
|
|
explainClose, /* xClose - close a cursor */
|
|
explainFilter, /* xFilter - configure scan constraints */
|
|
explainNext, /* xNext - advance a cursor */
|
|
explainEof, /* xEof - check for end of scan */
|
|
explainColumn, /* xColumn - read data */
|
|
explainRowid, /* xRowid - read data */
|
|
0, /* xUpdate */
|
|
0, /* xBegin */
|
|
0, /* xSync */
|
|
0, /* xCommit */
|
|
0, /* xRollback */
|
|
0, /* xFindMethod */
|
|
0, /* xRename */
|
|
0, /* xSavepoint */
|
|
0, /* xRelease */
|
|
0, /* xRollbackTo */
|
|
0, /* xShadowName */
|
|
};
|
|
|
|
#endif /* SQLITE_OMIT_VIRTUALTABLE */
|
|
|
|
int sqlite3ExplainVtabInit(sqlite3 *db){
|
|
int rc = SQLITE_OK;
|
|
#ifndef SQLITE_OMIT_VIRTUALTABLE
|
|
rc = sqlite3_create_module(db, "explain", &explainModule, 0);
|
|
#endif
|
|
return rc;
|
|
}
|
|
|
|
#ifdef _WIN32
|
|
__declspec(dllexport)
|
|
#endif
|
|
int sqlite3_explain_init(
|
|
sqlite3 *db,
|
|
char **pzErrMsg,
|
|
const sqlite3_api_routines *pApi
|
|
){
|
|
int rc = SQLITE_OK;
|
|
SQLITE_EXTENSION_INIT2(pApi);
|
|
#ifndef SQLITE_OMIT_VIRTUALTABLE
|
|
rc = sqlite3ExplainVtabInit(db);
|
|
#endif
|
|
return rc;
|
|
}
|