summaryrefslogtreecommitdiffstats
path: root/ext/misc/explain.c
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-05 17:28:19 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-05 17:28:19 +0000
commit18657a960e125336f704ea058e25c27bd3900dcb (patch)
tree17b438b680ed45a996d7b59951e6aa34023783f2 /ext/misc/explain.c
parentInitial commit. (diff)
downloadsqlite3-upstream.tar.xz
sqlite3-upstream.zip
Adding upstream version 3.40.1.upstream/3.40.1upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'ext/misc/explain.c')
-rw-r--r--ext/misc/explain.c322
1 files changed, 322 insertions, 0 deletions
diff --git a/ext/misc/explain.c b/ext/misc/explain.c
new file mode 100644
index 0000000..0095194
--- /dev/null
+++ b/ext/misc/explain.c
@@ -0,0 +1,322 @@
+/*
+** 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;
+}