2021-05-14 09:07:09 +00:00
|
|
|
/*
|
|
|
|
** 2020-03-23
|
|
|
|
**
|
|
|
|
** 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 implements virtual-tables for examining the bytecode content
|
|
|
|
** of a prepared statement.
|
|
|
|
*/
|
2022-11-28 20:54:48 +00:00
|
|
|
#include "third_party/sqlite3/sqliteInt.h"
|
2021-05-14 09:07:09 +00:00
|
|
|
#if defined(SQLITE_ENABLE_BYTECODE_VTAB) && !defined(SQLITE_OMIT_VIRTUALTABLE)
|
2021-05-14 17:18:28 +00:00
|
|
|
#include "third_party/sqlite3/vdbeInt.inc"
|
2021-05-14 09:07:09 +00:00
|
|
|
|
|
|
|
/* An instance of the bytecode() table-valued function.
|
|
|
|
*/
|
|
|
|
typedef struct bytecodevtab bytecodevtab;
|
|
|
|
struct bytecodevtab {
|
|
|
|
sqlite3_vtab base; /* Base class - must be first */
|
|
|
|
sqlite3 *db; /* Database connection */
|
|
|
|
int bTablesUsed; /* 2 for tables_used(). 0 for bytecode(). */
|
|
|
|
};
|
|
|
|
|
|
|
|
/* A cursor for scanning through the bytecode
|
|
|
|
*/
|
|
|
|
typedef struct bytecodevtab_cursor bytecodevtab_cursor;
|
|
|
|
struct bytecodevtab_cursor {
|
|
|
|
sqlite3_vtab_cursor base; /* Base class - must be first */
|
|
|
|
sqlite3_stmt *pStmt; /* The statement whose bytecode is displayed */
|
|
|
|
int iRowid; /* The rowid of the output table */
|
|
|
|
int iAddr; /* Address */
|
|
|
|
int needFinalize; /* Cursors owns pStmt and must finalize it */
|
|
|
|
int showSubprograms; /* Provide a listing of subprograms */
|
|
|
|
Op *aOp; /* Operand array */
|
|
|
|
char *zP4; /* Rendered P4 value */
|
|
|
|
const char *zType; /* tables_used.type */
|
|
|
|
const char *zSchema; /* tables_used.schema */
|
|
|
|
const char *zName; /* tables_used.name */
|
|
|
|
Mem sub; /* Subprograms */
|
|
|
|
};
|
|
|
|
|
|
|
|
/*
|
|
|
|
** Create a new bytecode() table-valued function.
|
|
|
|
*/
|
|
|
|
static int bytecodevtabConnect(
|
|
|
|
sqlite3 *db,
|
|
|
|
void *pAux,
|
|
|
|
int argc, const char *const*argv,
|
|
|
|
sqlite3_vtab **ppVtab,
|
|
|
|
char **pzErr
|
|
|
|
){
|
|
|
|
bytecodevtab *pNew;
|
|
|
|
int rc;
|
|
|
|
int isTabUsed = pAux!=0;
|
|
|
|
const char *azSchema[2] = {
|
|
|
|
/* bytecode() schema */
|
|
|
|
"CREATE TABLE x("
|
|
|
|
"addr INT,"
|
|
|
|
"opcode TEXT,"
|
|
|
|
"p1 INT,"
|
|
|
|
"p2 INT,"
|
|
|
|
"p3 INT,"
|
|
|
|
"p4 TEXT,"
|
|
|
|
"p5 INT,"
|
|
|
|
"comment TEXT,"
|
2022-07-22 04:46:07 +00:00
|
|
|
"subprog TEXT,"
|
2021-05-14 09:07:09 +00:00
|
|
|
"stmt HIDDEN"
|
|
|
|
");",
|
|
|
|
|
|
|
|
/* Tables_used() schema */
|
|
|
|
"CREATE TABLE x("
|
|
|
|
"type TEXT,"
|
|
|
|
"schema TEXT,"
|
|
|
|
"name TEXT,"
|
|
|
|
"wr INT,"
|
2022-07-22 04:46:07 +00:00
|
|
|
"subprog TEXT,"
|
2021-05-14 09:07:09 +00:00
|
|
|
"stmt HIDDEN"
|
|
|
|
");"
|
|
|
|
};
|
|
|
|
|
|
|
|
rc = sqlite3_declare_vtab(db, azSchema[isTabUsed]);
|
|
|
|
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;
|
|
|
|
pNew->bTablesUsed = isTabUsed*2;
|
|
|
|
}
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
** This method is the destructor for bytecodevtab objects.
|
|
|
|
*/
|
|
|
|
static int bytecodevtabDisconnect(sqlite3_vtab *pVtab){
|
|
|
|
bytecodevtab *p = (bytecodevtab*)pVtab;
|
|
|
|
sqlite3_free(p);
|
|
|
|
return SQLITE_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
** Constructor for a new bytecodevtab_cursor object.
|
|
|
|
*/
|
|
|
|
static int bytecodevtabOpen(sqlite3_vtab *p, sqlite3_vtab_cursor **ppCursor){
|
|
|
|
bytecodevtab *pVTab = (bytecodevtab*)p;
|
|
|
|
bytecodevtab_cursor *pCur;
|
|
|
|
pCur = sqlite3_malloc( sizeof(*pCur) );
|
|
|
|
if( pCur==0 ) return SQLITE_NOMEM;
|
|
|
|
memset(pCur, 0, sizeof(*pCur));
|
|
|
|
sqlite3VdbeMemInit(&pCur->sub, pVTab->db, 1);
|
|
|
|
*ppCursor = &pCur->base;
|
|
|
|
return SQLITE_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
** Clear all internal content from a bytecodevtab cursor.
|
|
|
|
*/
|
|
|
|
static void bytecodevtabCursorClear(bytecodevtab_cursor *pCur){
|
|
|
|
sqlite3_free(pCur->zP4);
|
|
|
|
pCur->zP4 = 0;
|
|
|
|
sqlite3VdbeMemRelease(&pCur->sub);
|
|
|
|
sqlite3VdbeMemSetNull(&pCur->sub);
|
|
|
|
if( pCur->needFinalize ){
|
|
|
|
sqlite3_finalize(pCur->pStmt);
|
|
|
|
}
|
|
|
|
pCur->pStmt = 0;
|
|
|
|
pCur->needFinalize = 0;
|
|
|
|
pCur->zType = 0;
|
|
|
|
pCur->zSchema = 0;
|
|
|
|
pCur->zName = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
** Destructor for a bytecodevtab_cursor.
|
|
|
|
*/
|
|
|
|
static int bytecodevtabClose(sqlite3_vtab_cursor *cur){
|
|
|
|
bytecodevtab_cursor *pCur = (bytecodevtab_cursor*)cur;
|
|
|
|
bytecodevtabCursorClear(pCur);
|
|
|
|
sqlite3_free(pCur);
|
|
|
|
return SQLITE_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
** Advance a bytecodevtab_cursor to its next row of output.
|
|
|
|
*/
|
|
|
|
static int bytecodevtabNext(sqlite3_vtab_cursor *cur){
|
|
|
|
bytecodevtab_cursor *pCur = (bytecodevtab_cursor*)cur;
|
|
|
|
bytecodevtab *pTab = (bytecodevtab*)cur->pVtab;
|
|
|
|
int rc;
|
|
|
|
if( pCur->zP4 ){
|
|
|
|
sqlite3_free(pCur->zP4);
|
|
|
|
pCur->zP4 = 0;
|
|
|
|
}
|
|
|
|
if( pCur->zName ){
|
|
|
|
pCur->zName = 0;
|
|
|
|
pCur->zType = 0;
|
|
|
|
pCur->zSchema = 0;
|
|
|
|
}
|
|
|
|
rc = sqlite3VdbeNextOpcode(
|
2022-07-22 04:46:07 +00:00
|
|
|
(Vdbe*)pCur->pStmt,
|
2021-05-14 09:07:09 +00:00
|
|
|
pCur->showSubprograms ? &pCur->sub : 0,
|
|
|
|
pTab->bTablesUsed,
|
|
|
|
&pCur->iRowid,
|
|
|
|
&pCur->iAddr,
|
|
|
|
&pCur->aOp);
|
|
|
|
if( rc!=SQLITE_OK ){
|
|
|
|
sqlite3VdbeMemSetNull(&pCur->sub);
|
|
|
|
pCur->aOp = 0;
|
|
|
|
}
|
|
|
|
return SQLITE_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
** Return TRUE if the cursor has been moved off of the last
|
|
|
|
** row of output.
|
|
|
|
*/
|
|
|
|
static int bytecodevtabEof(sqlite3_vtab_cursor *cur){
|
|
|
|
bytecodevtab_cursor *pCur = (bytecodevtab_cursor*)cur;
|
|
|
|
return pCur->aOp==0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
** Return values of columns for the row at which the bytecodevtab_cursor
|
|
|
|
** is currently pointing.
|
|
|
|
*/
|
|
|
|
static int bytecodevtabColumn(
|
|
|
|
sqlite3_vtab_cursor *cur, /* The cursor */
|
|
|
|
sqlite3_context *ctx, /* First argument to sqlite3_result_...() */
|
|
|
|
int i /* Which column to return */
|
|
|
|
){
|
|
|
|
bytecodevtab_cursor *pCur = (bytecodevtab_cursor*)cur;
|
|
|
|
bytecodevtab *pVTab = (bytecodevtab*)cur->pVtab;
|
|
|
|
Op *pOp = pCur->aOp + pCur->iAddr;
|
|
|
|
if( pVTab->bTablesUsed ){
|
|
|
|
if( i==4 ){
|
|
|
|
i = 8;
|
|
|
|
}else{
|
|
|
|
if( i<=2 && pCur->zType==0 ){
|
|
|
|
Schema *pSchema;
|
|
|
|
HashElem *k;
|
|
|
|
int iDb = pOp->p3;
|
|
|
|
Pgno iRoot = (Pgno)pOp->p2;
|
|
|
|
sqlite3 *db = pVTab->db;
|
|
|
|
pSchema = db->aDb[iDb].pSchema;
|
|
|
|
pCur->zSchema = db->aDb[iDb].zDbSName;
|
|
|
|
for(k=sqliteHashFirst(&pSchema->tblHash); k; k=sqliteHashNext(k)){
|
|
|
|
Table *pTab = (Table*)sqliteHashData(k);
|
|
|
|
if( !IsVirtual(pTab) && pTab->tnum==iRoot ){
|
|
|
|
pCur->zName = pTab->zName;
|
|
|
|
pCur->zType = "table";
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if( pCur->zName==0 ){
|
|
|
|
for(k=sqliteHashFirst(&pSchema->idxHash); k; k=sqliteHashNext(k)){
|
|
|
|
Index *pIdx = (Index*)sqliteHashData(k);
|
|
|
|
if( pIdx->tnum==iRoot ){
|
|
|
|
pCur->zName = pIdx->zName;
|
|
|
|
pCur->zType = "index";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
i += 10;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
switch( i ){
|
|
|
|
case 0: /* addr */
|
|
|
|
sqlite3_result_int(ctx, pCur->iAddr);
|
|
|
|
break;
|
|
|
|
case 1: /* opcode */
|
|
|
|
sqlite3_result_text(ctx, (char*)sqlite3OpcodeName(pOp->opcode),
|
|
|
|
-1, SQLITE_STATIC);
|
|
|
|
break;
|
|
|
|
case 2: /* p1 */
|
|
|
|
sqlite3_result_int(ctx, pOp->p1);
|
|
|
|
break;
|
|
|
|
case 3: /* p2 */
|
|
|
|
sqlite3_result_int(ctx, pOp->p2);
|
|
|
|
break;
|
|
|
|
case 4: /* p3 */
|
|
|
|
sqlite3_result_int(ctx, pOp->p3);
|
|
|
|
break;
|
|
|
|
case 5: /* p4 */
|
|
|
|
case 7: /* comment */
|
|
|
|
if( pCur->zP4==0 ){
|
|
|
|
pCur->zP4 = sqlite3VdbeDisplayP4(pVTab->db, pOp);
|
|
|
|
}
|
|
|
|
if( i==5 ){
|
|
|
|
sqlite3_result_text(ctx, pCur->zP4, -1, SQLITE_STATIC);
|
|
|
|
}else{
|
|
|
|
#ifdef SQLITE_ENABLE_EXPLAIN_COMMENTS
|
|
|
|
char *zCom = sqlite3VdbeDisplayComment(pVTab->db, pOp, pCur->zP4);
|
|
|
|
sqlite3_result_text(ctx, zCom, -1, sqlite3_free);
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 6: /* p5 */
|
|
|
|
sqlite3_result_int(ctx, pOp->p5);
|
|
|
|
break;
|
|
|
|
case 8: { /* subprog */
|
|
|
|
Op *aOp = pCur->aOp;
|
|
|
|
assert( aOp[0].opcode==OP_Init );
|
|
|
|
assert( aOp[0].p4.z==0 || strncmp(aOp[0].p4.z,"-" "- ",3)==0 );
|
|
|
|
if( pCur->iRowid==pCur->iAddr+1 ){
|
|
|
|
break; /* Result is NULL for the main program */
|
|
|
|
}else if( aOp[0].p4.z!=0 ){
|
|
|
|
sqlite3_result_text(ctx, aOp[0].p4.z+3, -1, SQLITE_STATIC);
|
|
|
|
}else{
|
|
|
|
sqlite3_result_text(ctx, "(FK)", 4, SQLITE_STATIC);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case 10: /* tables_used.type */
|
|
|
|
sqlite3_result_text(ctx, pCur->zType, -1, SQLITE_STATIC);
|
|
|
|
break;
|
|
|
|
case 11: /* tables_used.schema */
|
|
|
|
sqlite3_result_text(ctx, pCur->zSchema, -1, SQLITE_STATIC);
|
|
|
|
break;
|
|
|
|
case 12: /* tables_used.name */
|
|
|
|
sqlite3_result_text(ctx, pCur->zName, -1, SQLITE_STATIC);
|
|
|
|
break;
|
|
|
|
case 13: /* tables_used.wr */
|
|
|
|
sqlite3_result_int(ctx, pOp->opcode==OP_OpenWrite);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
return SQLITE_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
** Return the rowid for the current row. In this implementation, the
|
|
|
|
** rowid is the same as the output value.
|
|
|
|
*/
|
|
|
|
static int bytecodevtabRowid(sqlite3_vtab_cursor *cur, sqlite_int64 *pRowid){
|
|
|
|
bytecodevtab_cursor *pCur = (bytecodevtab_cursor*)cur;
|
|
|
|
*pRowid = pCur->iRowid;
|
|
|
|
return SQLITE_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
** Initialize a cursor.
|
|
|
|
**
|
|
|
|
** idxNum==0 means show all subprograms
|
|
|
|
** idxNum==1 means show only the main bytecode and omit subprograms.
|
|
|
|
*/
|
|
|
|
static int bytecodevtabFilter(
|
2022-07-22 04:46:07 +00:00
|
|
|
sqlite3_vtab_cursor *pVtabCursor,
|
2021-05-14 09:07:09 +00:00
|
|
|
int idxNum, const char *idxStr,
|
|
|
|
int argc, sqlite3_value **argv
|
|
|
|
){
|
|
|
|
bytecodevtab_cursor *pCur = (bytecodevtab_cursor *)pVtabCursor;
|
|
|
|
bytecodevtab *pVTab = (bytecodevtab *)pVtabCursor->pVtab;
|
|
|
|
int rc = SQLITE_OK;
|
|
|
|
|
|
|
|
bytecodevtabCursorClear(pCur);
|
|
|
|
pCur->iRowid = 0;
|
|
|
|
pCur->iAddr = 0;
|
|
|
|
pCur->showSubprograms = idxNum==0;
|
|
|
|
assert( argc==1 );
|
|
|
|
if( sqlite3_value_type(argv[0])==SQLITE_TEXT ){
|
|
|
|
const char *zSql = (const char*)sqlite3_value_text(argv[0]);
|
|
|
|
if( zSql==0 ){
|
|
|
|
rc = SQLITE_NOMEM;
|
|
|
|
}else{
|
|
|
|
rc = sqlite3_prepare_v2(pVTab->db, zSql, -1, &pCur->pStmt, 0);
|
|
|
|
pCur->needFinalize = 1;
|
|
|
|
}
|
|
|
|
}else{
|
|
|
|
pCur->pStmt = (sqlite3_stmt*)sqlite3_value_pointer(argv[0],"stmt-pointer");
|
|
|
|
}
|
|
|
|
if( pCur->pStmt==0 ){
|
|
|
|
pVTab->base.zErrMsg = sqlite3_mprintf(
|
|
|
|
"argument to %s() is not a valid SQL statement",
|
|
|
|
pVTab->bTablesUsed ? "tables_used" : "bytecode"
|
|
|
|
);
|
|
|
|
rc = SQLITE_ERROR;
|
|
|
|
}else{
|
|
|
|
bytecodevtabNext(pVtabCursor);
|
|
|
|
}
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
** We must have a single stmt=? constraint that will be passed through
|
|
|
|
** into the xFilter method. If there is no valid stmt=? constraint,
|
|
|
|
** then return an SQLITE_CONSTRAINT error.
|
|
|
|
*/
|
|
|
|
static int bytecodevtabBestIndex(
|
|
|
|
sqlite3_vtab *tab,
|
|
|
|
sqlite3_index_info *pIdxInfo
|
|
|
|
){
|
|
|
|
int i;
|
|
|
|
int rc = SQLITE_CONSTRAINT;
|
|
|
|
struct sqlite3_index_constraint *p;
|
|
|
|
bytecodevtab *pVTab = (bytecodevtab*)tab;
|
|
|
|
int iBaseCol = pVTab->bTablesUsed ? 4 : 8;
|
|
|
|
pIdxInfo->estimatedCost = (double)100;
|
|
|
|
pIdxInfo->estimatedRows = 100;
|
|
|
|
pIdxInfo->idxNum = 0;
|
|
|
|
for(i=0, p=pIdxInfo->aConstraint; i<pIdxInfo->nConstraint; i++, p++){
|
|
|
|
if( p->usable==0 ) continue;
|
|
|
|
if( p->op==SQLITE_INDEX_CONSTRAINT_EQ && p->iColumn==iBaseCol+1 ){
|
|
|
|
rc = SQLITE_OK;
|
|
|
|
pIdxInfo->aConstraintUsage[i].omit = 1;
|
|
|
|
pIdxInfo->aConstraintUsage[i].argvIndex = 1;
|
|
|
|
}
|
|
|
|
if( p->op==SQLITE_INDEX_CONSTRAINT_ISNULL && p->iColumn==iBaseCol ){
|
|
|
|
pIdxInfo->aConstraintUsage[i].omit = 1;
|
|
|
|
pIdxInfo->idxNum = 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2022-07-22 04:46:07 +00:00
|
|
|
** This following structure defines all the methods for the
|
2021-05-14 09:07:09 +00:00
|
|
|
** virtual table.
|
|
|
|
*/
|
|
|
|
static sqlite3_module bytecodevtabModule = {
|
|
|
|
/* iVersion */ 0,
|
|
|
|
/* xCreate */ 0,
|
|
|
|
/* xConnect */ bytecodevtabConnect,
|
|
|
|
/* xBestIndex */ bytecodevtabBestIndex,
|
|
|
|
/* xDisconnect */ bytecodevtabDisconnect,
|
|
|
|
/* xDestroy */ 0,
|
|
|
|
/* xOpen */ bytecodevtabOpen,
|
|
|
|
/* xClose */ bytecodevtabClose,
|
|
|
|
/* xFilter */ bytecodevtabFilter,
|
|
|
|
/* xNext */ bytecodevtabNext,
|
|
|
|
/* xEof */ bytecodevtabEof,
|
|
|
|
/* xColumn */ bytecodevtabColumn,
|
|
|
|
/* xRowid */ bytecodevtabRowid,
|
|
|
|
/* xUpdate */ 0,
|
|
|
|
/* xBegin */ 0,
|
|
|
|
/* xSync */ 0,
|
|
|
|
/* xCommit */ 0,
|
|
|
|
/* xRollback */ 0,
|
|
|
|
/* xFindMethod */ 0,
|
|
|
|
/* xRename */ 0,
|
|
|
|
/* xSavepoint */ 0,
|
|
|
|
/* xRelease */ 0,
|
|
|
|
/* xRollbackTo */ 0,
|
|
|
|
/* xShadowName */ 0
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
int sqlite3VdbeBytecodeVtabInit(sqlite3 *db){
|
|
|
|
int rc;
|
|
|
|
rc = sqlite3_create_module(db, "bytecode", &bytecodevtabModule, 0);
|
|
|
|
if( rc==SQLITE_OK ){
|
|
|
|
rc = sqlite3_create_module(db, "tables_used", &bytecodevtabModule, &db);
|
|
|
|
}
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
#elif defined(SQLITE_ENABLE_BYTECODE_VTAB)
|
|
|
|
int sqlite3VdbeBytecodeVtabInit(sqlite3 *db){ return SQLITE_OK; }
|
|
|
|
#endif /* SQLITE_ENABLE_BYTECODE_VTAB */
|