mirror of
https://github.com/sqlite/sqlite.git
synced 2025-11-06 15:49:35 +03:00
Import the experimental parse-tree explainer, with fixes, from the
tree-explain branch. FossilOrigin-Name: bcbc7152d49107afa926c8950360c61a6cf3d244
This commit is contained in:
32
manifest
32
manifest
@@ -1,5 +1,5 @@
|
||||
C Always\suse\s_msize()\sto\sget\smemory\sallocation\ssizes\son\swindows,\swithout\shaving\nto\sdo\sanything\sspecial\sin\sthe\smakefile.
|
||||
D 2011-12-10T13:49:44.752
|
||||
C Import\sthe\sexperimental\sparse-tree\sexplainer,\swith\sfixes,\sfrom\sthe\ntree-explain\sbranch.
|
||||
D 2011-12-10T15:55:01.825
|
||||
F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f
|
||||
F Makefile.in 5b4a3e12a850b021547e43daf886b25133b44c07
|
||||
F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23
|
||||
@@ -134,7 +134,7 @@ F src/complete.c dc1d136c0feee03c2f7550bafc0d29075e36deac
|
||||
F src/ctime.c a9c26822515f81ec21588cbb482ca6724be02e33
|
||||
F src/date.c 067a81c9942c497aafd2c260e13add8a7d0c7dd4
|
||||
F src/delete.c 51d32f0a9c880663e54ce309f52e40c325d5e112
|
||||
F src/expr.c b5920f108d6955b262f8db8e38f40f59b2bfcd7d
|
||||
F src/expr.c a68c194c7709388302266e9a5bd9de2243486835
|
||||
F src/fault.c 160a0c015b6c2629d3899ed2daf63d75754a32bb
|
||||
F src/fkey.c 657212460bf5cfd3ae607d12ea62092844c227b5
|
||||
F src/func.c 6261ce00aad9c63cd5b4219249b05683979060e9
|
||||
@@ -147,7 +147,7 @@ F src/journal.c 552839e54d1bf76fb8f7abe51868b66acacf6a0e
|
||||
F src/legacy.c a199d7683d60cef73089e892409113e69c23a99f
|
||||
F src/lempar.c 0ee69fca0be54cd93939df98d2aca4ca46f44416
|
||||
F src/loadext.c d0d2022a5a07274d408820b978b9e549189d314f
|
||||
F src/main.c 8be1ee70dd90ef7562c801dbe946a4f9f93bb128
|
||||
F src/main.c e10d5ad24ae1964d1dc53fbc283557c1c5cd29f3
|
||||
F src/malloc.c 591aedb20ae40813f1045f2ef253438a334775d9
|
||||
F src/mem0.c 6a55ebe57c46ca1a7d98da93aaa07f99f1059645
|
||||
F src/mem1.c 7998e7003a3047e323c849a26dda004debc04d03
|
||||
@@ -170,21 +170,21 @@ F src/os_unix.c 4fbb91726165e105c1679a2660f49a3f4c376e4f
|
||||
F src/os_win.c 8af100f78f157eb6185fd9153d7f35b829c4da04
|
||||
F src/pager.c d981f3bfcc0e4460537d983899620700ccf8f539
|
||||
F src/pager.h 5cd760857707529b403837d813d86b68938d6183
|
||||
F src/parse.y d02cc7bdb9ba11fb7aa212f55f99e59e4ee55439
|
||||
F src/parse.y fabb2e7047417d840e6fdb3ef0988a86849a08ba
|
||||
F src/pcache.c 1fdd77978c1525d1ca4b9ef48eb80abca710cb4c
|
||||
F src/pcache.h b1d8775a9bddf44e65edb0d20bfc57a4982f840f
|
||||
F src/pcache1.c 9d735349ac87ef08076c6b1230f04cd83b15c6da
|
||||
F src/pragma.c dd66f21fafe7be40e1a48ad4195764cc191cf583
|
||||
F src/prepare.c ec4989f7f480544bdc4192fe663470d2a2d7d61e
|
||||
F src/printf.c 03104cbff6959ff45df69dc9060ba6212f60a869
|
||||
F src/printf.c 7ffb4ebb8b341f67e049695ba031da717b3d2699
|
||||
F src/random.c cd4a67b3953b88019f8cd4ccd81394a8ddfaba50
|
||||
F src/resolve.c 365ab1c870e38596d6869e76fb544fe6e4ffc809
|
||||
F src/rowset.c 69afa95a97c524ba6faf3805e717b5b7ae85a697
|
||||
F src/select.c fd3046fb39a1de8dce269e9b5ecbf58fe7e7e480
|
||||
F src/shell.c 29812a900a780eb0f835c4bc65e216272689def8
|
||||
F src/sqlite.h.in 57081d8e6b53ce29541d7437c93bce6087ac53b5
|
||||
F src/select.c 4d64e3ccccfd2b46e60e7a4cfd21175c3341e859
|
||||
F src/shell.c a1eadb2fdbfa45e54307263f0c8da8ee8cd61b8b
|
||||
F src/sqlite.h.in 1dc07194eb1a2c69c8ef75f88022b170be08024a
|
||||
F src/sqlite3ext.h 6904f4aadf976f95241311fbffb00823075d9477
|
||||
F src/sqliteInt.h de3db02a1e4762a6ec9e1ab604ebc02d77948030
|
||||
F src/sqliteInt.h 7a64e24659e067f3882eeb75f31d59e7b3723b8b
|
||||
F src/sqliteLimit.h 164b0e6749d31e0daa1a4589a169d31c0dec7b3d
|
||||
F src/status.c 4568e72dfd36b6a5911f93457364deb072e0b03a
|
||||
F src/table.c 2cd62736f845d82200acfa1287e33feb3c15d62e
|
||||
@@ -241,13 +241,13 @@ F src/util.c 01238e2b0f24a14779181dbf991fe02620a80e31
|
||||
F src/vacuum.c 0c0ba2242355c6048d65e2b333abe0f7c06348fa
|
||||
F src/vdbe.c 029add0c5197a61db588824a58570547330b9d8f
|
||||
F src/vdbe.h 18f581cac1f4339ec3299f3e0cc6e11aec654cdb
|
||||
F src/vdbeInt.h 23a9506c9ab31e7823d7257d1828d2d7843443a0
|
||||
F src/vdbeInt.h 48c158b2fceca9682d1577e61c62da3c58cf0748
|
||||
F src/vdbeapi.c 86189ebba2c49791d75eaa12929f3ce6527596bd
|
||||
F src/vdbeaux.c 71a0cd6ae14ddb2e2a6d6b97d5b54ae753272682
|
||||
F src/vdbeaux.c 61a3cc2b3a713aa244598a13035471cc30b307e1
|
||||
F src/vdbeblob.c 32f2a4899d67f69634ea4dd93e3f651936d732cb
|
||||
F src/vdbemem.c 2fc78b3e0fabcc1eaa23cd79dd2e30e6dcfe1e56
|
||||
F src/vdbesort.c 468d43c057063e54da4f1988b38b4f46d60e7790
|
||||
F src/vdbetrace.c 5d0dc3d5fd54878cc8d6d28eb41deb8d5885b114
|
||||
F src/vdbetrace.c d6e50e04e1ec498150e519058f617d91b8f5c843
|
||||
F src/vtab.c e9318d88feac85be8e27ee783ac8f5397933fc8a
|
||||
F src/wal.c 7e6e7fe68ee649505dca38c8ab83eda0d0d96ae5
|
||||
F src/wal.h 66b40bd91bc29a5be1c88ddd1f5ade8f3f48728a
|
||||
@@ -978,7 +978,7 @@ F tool/tostr.awk e75472c2f98dd76e06b8c9c1367f4ab07e122d06
|
||||
F tool/vdbe-compress.tcl d70ea6d8a19e3571d7ab8c9b75cba86d1173ff0f
|
||||
F tool/warnings-clang.sh 9f406d66e750e8ac031c63a9ef3248aaa347ef2a
|
||||
F tool/warnings.sh fbc018d67fd7395f440c28f33ef0f94420226381
|
||||
P 0064bab77149768640d7758a3e271d5a1d63b256
|
||||
R 24b1670e7f54ceef1719656e8887fd23
|
||||
P 256e27bd118ed3ab6ecb19ad6a6494b71ac9bdd5 1a360da0f8314f232c224c71829646bc7558892b
|
||||
R 3dfa94d97a11ee2a078f9f1b12bd877f
|
||||
U drh
|
||||
Z 1195788bf6e0e867e75c58852af50db9
|
||||
Z 7f30df8f1f79a1cfbb2b882f754d8454
|
||||
|
||||
@@ -1 +1 @@
|
||||
256e27bd118ed3ab6ecb19ad6a6494b71ac9bdd5
|
||||
bcbc7152d49107afa926c8950360c61a6cf3d244
|
||||
258
src/expr.c
258
src/expr.c
@@ -2949,6 +2949,264 @@ int sqlite3ExprCodeAndCache(Parse *pParse, Expr *pExpr, int target){
|
||||
return inReg;
|
||||
}
|
||||
|
||||
#if defined(SQLITE_ENABLE_TREE_EXPLAIN)
|
||||
/*
|
||||
** Generate a human-readable explanation of an expression tree.
|
||||
*/
|
||||
void sqlite3ExplainExpr(Vdbe *pOut, Expr *pExpr){
|
||||
int op; /* The opcode being coded */
|
||||
const char *zBinOp = 0; /* Binary operator */
|
||||
const char *zUniOp = 0; /* Unary operator */
|
||||
if( pExpr==0 ){
|
||||
op = TK_NULL;
|
||||
}else{
|
||||
op = pExpr->op;
|
||||
}
|
||||
switch( op ){
|
||||
case TK_AGG_COLUMN: {
|
||||
sqlite3ExplainPrintf(pOut, "AGG{%d:%d}",
|
||||
pExpr->iTable, pExpr->iColumn);
|
||||
break;
|
||||
}
|
||||
case TK_COLUMN: {
|
||||
if( pExpr->iTable<0 ){
|
||||
/* This only happens when coding check constraints */
|
||||
sqlite3ExplainPrintf(pOut, "COLUMN(%d)", pExpr->iColumn);
|
||||
}else{
|
||||
sqlite3ExplainPrintf(pOut, "{%d:%d}",
|
||||
pExpr->iTable, pExpr->iColumn);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case TK_INTEGER: {
|
||||
if( pExpr->flags & EP_IntValue ){
|
||||
sqlite3ExplainPrintf(pOut, "%d", pExpr->u.iValue);
|
||||
}else{
|
||||
sqlite3ExplainPrintf(pOut, "%s", pExpr->u.zToken);
|
||||
}
|
||||
break;
|
||||
}
|
||||
#ifndef SQLITE_OMIT_FLOATING_POINT
|
||||
case TK_FLOAT: {
|
||||
sqlite3ExplainPrintf(pOut,"%s", pExpr->u.zToken);
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
case TK_STRING: {
|
||||
sqlite3ExplainPrintf(pOut,"%Q", pExpr->u.zToken);
|
||||
break;
|
||||
}
|
||||
case TK_NULL: {
|
||||
sqlite3ExplainPrintf(pOut,"NULL");
|
||||
break;
|
||||
}
|
||||
#ifndef SQLITE_OMIT_BLOB_LITERAL
|
||||
case TK_BLOB: {
|
||||
sqlite3ExplainPrintf(pOut,"%s", pExpr->u.zToken);
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
case TK_VARIABLE: {
|
||||
sqlite3ExplainPrintf(pOut,"VARIABLE(%s,%d)",
|
||||
pExpr->u.zToken, pExpr->iColumn);
|
||||
break;
|
||||
}
|
||||
case TK_REGISTER: {
|
||||
sqlite3ExplainPrintf(pOut,"REGISTER(%d)", pExpr->iTable);
|
||||
break;
|
||||
}
|
||||
case TK_AS: {
|
||||
sqlite3ExplainExpr(pOut, pExpr->pLeft);
|
||||
break;
|
||||
}
|
||||
#ifndef SQLITE_OMIT_CAST
|
||||
case TK_CAST: {
|
||||
/* Expressions of the form: CAST(pLeft AS token) */
|
||||
const char *zAff = "unk";
|
||||
switch( sqlite3AffinityType(pExpr->u.zToken) ){
|
||||
case SQLITE_AFF_TEXT: zAff = "TEXT"; break;
|
||||
case SQLITE_AFF_NONE: zAff = "NONE"; break;
|
||||
case SQLITE_AFF_NUMERIC: zAff = "NUMERIC"; break;
|
||||
case SQLITE_AFF_INTEGER: zAff = "INTEGER"; break;
|
||||
case SQLITE_AFF_REAL: zAff = "REAL"; break;
|
||||
}
|
||||
sqlite3ExplainPrintf(pOut, "CAST-%s(", zAff);
|
||||
sqlite3ExplainExpr(pOut, pExpr->pLeft);
|
||||
sqlite3ExplainPrintf(pOut, ")");
|
||||
break;
|
||||
}
|
||||
#endif /* SQLITE_OMIT_CAST */
|
||||
case TK_LT: zBinOp = "LT"; break;
|
||||
case TK_LE: zBinOp = "LE"; break;
|
||||
case TK_GT: zBinOp = "GT"; break;
|
||||
case TK_GE: zBinOp = "GE"; break;
|
||||
case TK_NE: zBinOp = "NE"; break;
|
||||
case TK_EQ: zBinOp = "EQ"; break;
|
||||
case TK_IS: zBinOp = "IS"; break;
|
||||
case TK_ISNOT: zBinOp = "ISNOT"; break;
|
||||
case TK_AND: zBinOp = "AND"; break;
|
||||
case TK_OR: zBinOp = "OR"; break;
|
||||
case TK_PLUS: zBinOp = "ADD"; break;
|
||||
case TK_STAR: zBinOp = "MUL"; break;
|
||||
case TK_MINUS: zBinOp = "SUB"; break;
|
||||
case TK_REM: zBinOp = "REM"; break;
|
||||
case TK_BITAND: zBinOp = "BITAND"; break;
|
||||
case TK_BITOR: zBinOp = "BITOR"; break;
|
||||
case TK_SLASH: zBinOp = "DIV"; break;
|
||||
case TK_LSHIFT: zBinOp = "LSHIFT"; break;
|
||||
case TK_RSHIFT: zBinOp = "RSHIFT"; break;
|
||||
case TK_CONCAT: zBinOp = "CONCAT"; break;
|
||||
|
||||
case TK_UMINUS: zUniOp = "UMINUS"; break;
|
||||
case TK_UPLUS: zUniOp = "UPLUS"; break;
|
||||
case TK_BITNOT: zUniOp = "BITNOT"; break;
|
||||
case TK_NOT: zUniOp = "NOT"; break;
|
||||
case TK_ISNULL: zUniOp = "ISNULL"; break;
|
||||
case TK_NOTNULL: zUniOp = "NOTNULL"; break;
|
||||
|
||||
case TK_AGG_FUNCTION:
|
||||
case TK_CONST_FUNC:
|
||||
case TK_FUNCTION: {
|
||||
ExprList *pFarg; /* List of function arguments */
|
||||
if( ExprHasAnyProperty(pExpr, EP_TokenOnly) ){
|
||||
pFarg = 0;
|
||||
}else{
|
||||
pFarg = pExpr->x.pList;
|
||||
}
|
||||
sqlite3ExplainPrintf(pOut, "%sFUNCTION:%s(",
|
||||
op==TK_AGG_FUNCTION ? "AGG_" : "",
|
||||
pExpr->u.zToken);
|
||||
if( pFarg ){
|
||||
sqlite3ExplainExprList(pOut, pFarg);
|
||||
}
|
||||
sqlite3ExplainPrintf(pOut, ")");
|
||||
break;
|
||||
}
|
||||
#ifndef SQLITE_OMIT_SUBQUERY
|
||||
case TK_EXISTS: {
|
||||
sqlite3ExplainPrintf(pOut, "EXISTS(");
|
||||
sqlite3ExplainSelect(pOut, pExpr->x.pSelect);
|
||||
sqlite3ExplainPrintf(pOut,")");
|
||||
break;
|
||||
}
|
||||
case TK_SELECT: {
|
||||
sqlite3ExplainPrintf(pOut, "(");
|
||||
sqlite3ExplainSelect(pOut, pExpr->x.pSelect);
|
||||
sqlite3ExplainPrintf(pOut, ")");
|
||||
break;
|
||||
}
|
||||
case TK_IN: {
|
||||
sqlite3ExplainPrintf(pOut, "IN(");
|
||||
sqlite3ExplainExpr(pOut, pExpr->pLeft);
|
||||
sqlite3ExplainPrintf(pOut, ",");
|
||||
if( ExprHasProperty(pExpr, EP_xIsSelect) ){
|
||||
sqlite3ExplainSelect(pOut, pExpr->x.pSelect);
|
||||
}else{
|
||||
sqlite3ExplainExprList(pOut, pExpr->x.pList);
|
||||
}
|
||||
sqlite3ExplainPrintf(pOut, ")");
|
||||
break;
|
||||
}
|
||||
#endif /* SQLITE_OMIT_SUBQUERY */
|
||||
|
||||
/*
|
||||
** x BETWEEN y AND z
|
||||
**
|
||||
** This is equivalent to
|
||||
**
|
||||
** x>=y AND x<=z
|
||||
**
|
||||
** X is stored in pExpr->pLeft.
|
||||
** Y is stored in pExpr->pList->a[0].pExpr.
|
||||
** Z is stored in pExpr->pList->a[1].pExpr.
|
||||
*/
|
||||
case TK_BETWEEN: {
|
||||
Expr *pX = pExpr->pLeft;
|
||||
Expr *pY = pExpr->x.pList->a[0].pExpr;
|
||||
Expr *pZ = pExpr->x.pList->a[1].pExpr;
|
||||
sqlite3ExplainPrintf(pOut, "BETWEEN(");
|
||||
sqlite3ExplainExpr(pOut, pX);
|
||||
sqlite3ExplainPrintf(pOut, ",");
|
||||
sqlite3ExplainExpr(pOut, pY);
|
||||
sqlite3ExplainPrintf(pOut, ",");
|
||||
sqlite3ExplainExpr(pOut, pZ);
|
||||
sqlite3ExplainPrintf(pOut, ")");
|
||||
break;
|
||||
}
|
||||
case TK_TRIGGER: {
|
||||
/* If the opcode is TK_TRIGGER, then the expression is a reference
|
||||
** to a column in the new.* or old.* pseudo-tables available to
|
||||
** trigger programs. In this case Expr.iTable is set to 1 for the
|
||||
** new.* pseudo-table, or 0 for the old.* pseudo-table. Expr.iColumn
|
||||
** is set to the column of the pseudo-table to read, or to -1 to
|
||||
** read the rowid field.
|
||||
*/
|
||||
sqlite3ExplainPrintf(pOut, "%s(%d)",
|
||||
pExpr->iTable ? "NEW" : "OLD", pExpr->iColumn);
|
||||
break;
|
||||
}
|
||||
case TK_CASE: {
|
||||
sqlite3ExplainPrintf(pOut, "CASE(");
|
||||
sqlite3ExplainExpr(pOut, pExpr->pLeft);
|
||||
sqlite3ExplainPrintf(pOut, ",");
|
||||
sqlite3ExplainExprList(pOut, pExpr->x.pList);
|
||||
break;
|
||||
}
|
||||
#ifndef SQLITE_OMIT_TRIGGER
|
||||
case TK_RAISE: {
|
||||
const char *zType = "unk";
|
||||
switch( pExpr->affinity ){
|
||||
case OE_Rollback: zType = "rollback"; break;
|
||||
case OE_Abort: zType = "abort"; break;
|
||||
case OE_Fail: zType = "fail"; break;
|
||||
case OE_Ignore: zType = "ignore"; break;
|
||||
}
|
||||
sqlite3ExplainPrintf(pOut, "RAISE-%s(%s)", zType, pExpr->u.zToken);
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
if( zBinOp ){
|
||||
sqlite3ExplainPrintf(pOut,"%s(", zBinOp);
|
||||
sqlite3ExplainExpr(pOut, pExpr->pLeft);
|
||||
sqlite3ExplainPrintf(pOut,",");
|
||||
sqlite3ExplainExpr(pOut, pExpr->pRight);
|
||||
sqlite3ExplainPrintf(pOut,")");
|
||||
}else if( zUniOp ){
|
||||
sqlite3ExplainPrintf(pOut,"%s(", zUniOp);
|
||||
sqlite3ExplainExpr(pOut, pExpr->pLeft);
|
||||
sqlite3ExplainPrintf(pOut,")");
|
||||
}
|
||||
}
|
||||
#endif /* defined(SQLITE_ENABLE_TREE_EXPLAIN) */
|
||||
|
||||
#if defined(SQLITE_ENABLE_TREE_EXPLAIN)
|
||||
/*
|
||||
** Generate a human-readable explanation of an expression list.
|
||||
*/
|
||||
void sqlite3ExplainExprList(Vdbe *pOut, ExprList *pList){
|
||||
int i;
|
||||
if( pList==0 || pList->nExpr==0 ){
|
||||
sqlite3ExplainPrintf(pOut, "(empty-list)");
|
||||
return;
|
||||
}else if( pList->nExpr==1 ){
|
||||
sqlite3ExplainExpr(pOut, pList->a[0].pExpr);
|
||||
}else{
|
||||
sqlite3ExplainPush(pOut);
|
||||
for(i=0; i<pList->nExpr; i++){
|
||||
sqlite3ExplainPrintf(pOut, "item[%d] = ", i);
|
||||
sqlite3ExplainPush(pOut);
|
||||
sqlite3ExplainExpr(pOut, pList->a[i].pExpr);
|
||||
sqlite3ExplainPop(pOut);
|
||||
if( i<pList->nExpr-1 ){
|
||||
sqlite3ExplainNL(pOut);
|
||||
}
|
||||
}
|
||||
sqlite3ExplainPop(pOut);
|
||||
}
|
||||
}
|
||||
#endif /* SQLITE_DEBUG */
|
||||
|
||||
/*
|
||||
** Return TRUE if pExpr is an constant expression that is appropriate
|
||||
** for factoring out of a loop. Appropriate expressions are:
|
||||
|
||||
16
src/main.c
16
src/main.c
@@ -2936,6 +2936,22 @@ int sqlite3_test_control(int op, ...){
|
||||
break;
|
||||
}
|
||||
|
||||
#if defined(SQLITE_ENABLE_TREE_EXPLAIN)
|
||||
/* sqlite3_test_control(SQLITE_TESTCTRL_EXPLAIN_STMT,
|
||||
** sqlite3_stmt*,const char**);
|
||||
**
|
||||
** If compiled with SQLITE_ENABLE_TREE_EXPLAIN, each sqlite3_stmt holds
|
||||
** a string that describes the optimized parse tree. This test-control
|
||||
** returns a pointer to that string.
|
||||
*/
|
||||
case SQLITE_TESTCTRL_EXPLAIN_STMT: {
|
||||
sqlite3_stmt *pStmt = va_arg(ap, sqlite3_stmt*);
|
||||
const char **pzRet = va_arg(ap, const char**);
|
||||
*pzRet = sqlite3VdbeExplanation((Vdbe*)pStmt);
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
|
||||
}
|
||||
va_end(ap);
|
||||
#endif /* SQLITE_OMIT_BUILTIN_TEST */
|
||||
|
||||
@@ -394,6 +394,9 @@ cmd ::= DROP VIEW ifexists(E) fullname(X). {
|
||||
cmd ::= select(X). {
|
||||
SelectDest dest = {SRT_Output, 0, 0, 0, 0};
|
||||
sqlite3Select(pParse, X, &dest);
|
||||
sqlite3ExplainBegin(pParse->pVdbe);
|
||||
sqlite3ExplainSelect(pParse->pVdbe, X);
|
||||
sqlite3ExplainFinish(pParse->pVdbe);
|
||||
sqlite3SelectDelete(pParse->db, X);
|
||||
}
|
||||
|
||||
|
||||
@@ -136,7 +136,7 @@ static char et_getdigit(LONGDOUBLE_TYPE *val, int *cnt){
|
||||
/*
|
||||
** Append N space characters to the given string buffer.
|
||||
*/
|
||||
static void appendSpace(StrAccum *pAccum, int N){
|
||||
void sqlite3AppendSpace(StrAccum *pAccum, int N){
|
||||
static const char zSpaces[] = " ";
|
||||
while( N>=(int)sizeof(zSpaces)-1 ){
|
||||
sqlite3StrAccumAppend(pAccum, zSpaces, sizeof(zSpaces)-1);
|
||||
@@ -664,7 +664,7 @@ void sqlite3VXPrintf(
|
||||
register int nspace;
|
||||
nspace = width-length;
|
||||
if( nspace>0 ){
|
||||
appendSpace(pAccum, nspace);
|
||||
sqlite3AppendSpace(pAccum, nspace);
|
||||
}
|
||||
}
|
||||
if( length>0 ){
|
||||
@@ -674,7 +674,7 @@ void sqlite3VXPrintf(
|
||||
register int nspace;
|
||||
nspace = width-length;
|
||||
if( nspace>0 ){
|
||||
appendSpace(pAccum, nspace);
|
||||
sqlite3AppendSpace(pAccum, nspace);
|
||||
}
|
||||
}
|
||||
sqlite3_free(zExtra);
|
||||
|
||||
134
src/select.c
134
src/select.c
@@ -4494,98 +4494,98 @@ select_end:
|
||||
return rc;
|
||||
}
|
||||
|
||||
#if defined(SQLITE_DEBUG)
|
||||
#if defined(SQLITE_ENABLE_TREE_EXPLAIN)
|
||||
/*
|
||||
*******************************************************************************
|
||||
** The following code is used for testing and debugging only. The code
|
||||
** that follows does not appear in normal builds.
|
||||
**
|
||||
** These routines are used to print out the content of all or part of a
|
||||
** parse structures such as Select or Expr. Such printouts are useful
|
||||
** for helping to understand what is happening inside the code generator
|
||||
** during the execution of complex SELECT statements.
|
||||
**
|
||||
** These routine are not called anywhere from within the normal
|
||||
** code base. Then are intended to be called from within the debugger
|
||||
** or from temporary "printf" statements inserted for debugging.
|
||||
** Generate a human-readable description of a the Select object.
|
||||
*/
|
||||
void sqlite3PrintExpr(Expr *p){
|
||||
if( !ExprHasProperty(p, EP_IntValue) && p->u.zToken ){
|
||||
sqlite3DebugPrintf("(%s", p->u.zToken);
|
||||
}else{
|
||||
sqlite3DebugPrintf("(%d", p->op);
|
||||
static void explainOneSelect(Vdbe *pVdbe, Select *p){
|
||||
sqlite3ExplainPrintf(pVdbe, "SELECT ");
|
||||
if( p->selFlags & (SF_Distinct|SF_Aggregate) ){
|
||||
if( p->selFlags & SF_Distinct ){
|
||||
sqlite3ExplainPrintf(pVdbe, "DISTINCT ");
|
||||
}
|
||||
if( p->pLeft ){
|
||||
sqlite3DebugPrintf(" ");
|
||||
sqlite3PrintExpr(p->pLeft);
|
||||
if( p->selFlags & SF_Aggregate ){
|
||||
sqlite3ExplainPrintf(pVdbe, "agg_flag ");
|
||||
}
|
||||
if( p->pRight ){
|
||||
sqlite3DebugPrintf(" ");
|
||||
sqlite3PrintExpr(p->pRight);
|
||||
sqlite3ExplainNL(pVdbe);
|
||||
sqlite3ExplainPrintf(pVdbe, " ");
|
||||
}
|
||||
sqlite3DebugPrintf(")");
|
||||
}
|
||||
void sqlite3PrintExprList(ExprList *pList){
|
||||
sqlite3ExplainExprList(pVdbe, p->pEList);
|
||||
sqlite3ExplainNL(pVdbe);
|
||||
if( p->pSrc && p->pSrc->nSrc ){
|
||||
int i;
|
||||
for(i=0; i<pList->nExpr; i++){
|
||||
sqlite3PrintExpr(pList->a[i].pExpr);
|
||||
if( i<pList->nExpr-1 ){
|
||||
sqlite3DebugPrintf(", ");
|
||||
}
|
||||
}
|
||||
}
|
||||
void sqlite3PrintSelect(Select *p, int indent){
|
||||
sqlite3DebugPrintf("%*sSELECT(%p) ", indent, "", p);
|
||||
sqlite3PrintExprList(p->pEList);
|
||||
sqlite3DebugPrintf("\n");
|
||||
if( p->pSrc ){
|
||||
char *zPrefix;
|
||||
int i;
|
||||
zPrefix = "FROM";
|
||||
sqlite3ExplainPrintf(pVdbe, "FROM ");
|
||||
sqlite3ExplainPush(pVdbe);
|
||||
for(i=0; i<p->pSrc->nSrc; i++){
|
||||
struct SrcList_item *pItem = &p->pSrc->a[i];
|
||||
sqlite3DebugPrintf("%*s ", indent+6, zPrefix);
|
||||
zPrefix = "";
|
||||
sqlite3ExplainPrintf(pVdbe, "{%d,*} = ", pItem->iCursor);
|
||||
if( pItem->pSelect ){
|
||||
sqlite3DebugPrintf("(\n");
|
||||
sqlite3PrintSelect(pItem->pSelect, indent+10);
|
||||
sqlite3DebugPrintf("%*s)", indent+8, "");
|
||||
}else if( pItem->zName ){
|
||||
sqlite3DebugPrintf("%s", pItem->zName);
|
||||
}
|
||||
sqlite3ExplainSelect(pVdbe, pItem->pSelect);
|
||||
if( pItem->pTab ){
|
||||
sqlite3DebugPrintf("(table: %s)", pItem->pTab->zName);
|
||||
sqlite3ExplainPrintf(pVdbe, " (tabname=%s)", pItem->pTab->zName);
|
||||
}
|
||||
}else if( pItem->zName ){
|
||||
sqlite3ExplainPrintf(pVdbe, "%s", pItem->zName);
|
||||
}
|
||||
if( pItem->zAlias ){
|
||||
sqlite3DebugPrintf(" AS %s", pItem->zAlias);
|
||||
sqlite3ExplainPrintf(pVdbe, " (AS %s)", pItem->zAlias);
|
||||
}
|
||||
if( i<p->pSrc->nSrc-1 ){
|
||||
sqlite3DebugPrintf(",");
|
||||
if( pItem->jointype & JT_LEFT ){
|
||||
sqlite3ExplainPrintf(pVdbe, " LEFT-JOIN");
|
||||
}
|
||||
sqlite3DebugPrintf("\n");
|
||||
sqlite3ExplainNL(pVdbe);
|
||||
}
|
||||
sqlite3ExplainPop(pVdbe);
|
||||
}
|
||||
if( p->pWhere ){
|
||||
sqlite3DebugPrintf("%*s WHERE ", indent, "");
|
||||
sqlite3PrintExpr(p->pWhere);
|
||||
sqlite3DebugPrintf("\n");
|
||||
sqlite3ExplainPrintf(pVdbe, "WHERE ");
|
||||
sqlite3ExplainExpr(pVdbe, p->pWhere);
|
||||
sqlite3ExplainNL(pVdbe);
|
||||
}
|
||||
if( p->pGroupBy ){
|
||||
sqlite3DebugPrintf("%*s GROUP BY ", indent, "");
|
||||
sqlite3PrintExprList(p->pGroupBy);
|
||||
sqlite3DebugPrintf("\n");
|
||||
sqlite3ExplainPrintf(pVdbe, "GROUPBY ");
|
||||
sqlite3ExplainExprList(pVdbe, p->pGroupBy);
|
||||
sqlite3ExplainNL(pVdbe);
|
||||
}
|
||||
if( p->pHaving ){
|
||||
sqlite3DebugPrintf("%*s HAVING ", indent, "");
|
||||
sqlite3PrintExpr(p->pHaving);
|
||||
sqlite3DebugPrintf("\n");
|
||||
sqlite3ExplainPrintf(pVdbe, "HAVING ");
|
||||
sqlite3ExplainExpr(pVdbe, p->pHaving);
|
||||
sqlite3ExplainNL(pVdbe);
|
||||
}
|
||||
if( p->pOrderBy ){
|
||||
sqlite3DebugPrintf("%*s ORDER BY ", indent, "");
|
||||
sqlite3PrintExprList(p->pOrderBy);
|
||||
sqlite3DebugPrintf("\n");
|
||||
sqlite3ExplainPrintf(pVdbe, "ORDERBY ");
|
||||
sqlite3ExplainExprList(pVdbe, p->pOrderBy);
|
||||
sqlite3ExplainNL(pVdbe);
|
||||
}
|
||||
if( p->pLimit ){
|
||||
sqlite3ExplainPrintf(pVdbe, "LIMIT ");
|
||||
sqlite3ExplainExpr(pVdbe, p->pLimit);
|
||||
sqlite3ExplainNL(pVdbe);
|
||||
}
|
||||
if( p->pOffset ){
|
||||
sqlite3ExplainPrintf(pVdbe, "OFFSET ");
|
||||
sqlite3ExplainExpr(pVdbe, p->pOffset);
|
||||
sqlite3ExplainNL(pVdbe);
|
||||
}
|
||||
}
|
||||
void sqlite3ExplainSelect(Vdbe *pVdbe, Select *p){
|
||||
if( p==0 ){
|
||||
sqlite3ExplainPrintf(pVdbe, "(null-select)");
|
||||
return;
|
||||
}
|
||||
while( p->pPrior ) p = p->pPrior;
|
||||
sqlite3ExplainPush(pVdbe);
|
||||
while( p ){
|
||||
explainOneSelect(pVdbe, p);
|
||||
p = p->pNext;
|
||||
if( p==0 ) break;
|
||||
sqlite3ExplainNL(pVdbe);
|
||||
sqlite3ExplainPrintf(pVdbe, "%s\n", selectOpName(p->op));
|
||||
}
|
||||
sqlite3ExplainPrintf(pVdbe, "END");
|
||||
sqlite3ExplainPop(pVdbe);
|
||||
}
|
||||
|
||||
/* End of the structure debug printing code
|
||||
*****************************************************************************/
|
||||
#endif /* defined(SQLITE_TEST) || defined(SQLITE_DEBUG) */
|
||||
|
||||
@@ -1127,6 +1127,15 @@ static int shell_exec(
|
||||
fprintf(pArg->out, "%s\n", zStmtSql ? zStmtSql : zSql);
|
||||
}
|
||||
|
||||
/* Output TESTCTRL_EXPLAIN text of requested */
|
||||
if( pArg && pArg->mode==MODE_Explain ){
|
||||
const char *zExplain = 0;
|
||||
sqlite3_test_control(SQLITE_TESTCTRL_EXPLAIN_STMT, pStmt, &zExplain);
|
||||
if( zExplain && zExplain[0] ){
|
||||
fprintf(pArg->out, "%s", zExplain);
|
||||
}
|
||||
}
|
||||
|
||||
/* perform the first step. this will tell us if we
|
||||
** have a result set or not and how wide it is.
|
||||
*/
|
||||
|
||||
@@ -5682,7 +5682,8 @@ int sqlite3_test_control(int op, ...);
|
||||
#define SQLITE_TESTCTRL_ISKEYWORD 16
|
||||
#define SQLITE_TESTCTRL_SCRATCHMALLOC 17
|
||||
#define SQLITE_TESTCTRL_LOCALTIME_FAULT 18
|
||||
#define SQLITE_TESTCTRL_LAST 18
|
||||
#define SQLITE_TESTCTRL_EXPLAIN_STMT 19
|
||||
#define SQLITE_TESTCTRL_LAST 19
|
||||
|
||||
/*
|
||||
** CAPI3REF: SQLite Runtime Status
|
||||
|
||||
@@ -1707,10 +1707,10 @@ struct Expr {
|
||||
#define EP_FixedDest 0x0200 /* Result needed in a specific register */
|
||||
#define EP_IntValue 0x0400 /* Integer value contained in u.iValue */
|
||||
#define EP_xIsSelect 0x0800 /* x.pSelect is valid (otherwise x.pList is) */
|
||||
|
||||
#define EP_Reduced 0x1000 /* Expr struct is EXPR_REDUCEDSIZE bytes only */
|
||||
#define EP_TokenOnly 0x2000 /* Expr struct is EXPR_TOKENONLYSIZE bytes only */
|
||||
#define EP_Static 0x4000 /* Held in memory not obtained from malloc() */
|
||||
#define EP_Hint 0x1000 /* Optimizer hint. Not required for correctness */
|
||||
#define EP_Reduced 0x2000 /* Expr struct is EXPR_REDUCEDSIZE bytes only */
|
||||
#define EP_TokenOnly 0x4000 /* Expr struct is EXPR_TOKENONLYSIZE bytes only */
|
||||
#define EP_Static 0x8000 /* Held in memory not obtained from malloc() */
|
||||
|
||||
/*
|
||||
** The following are the meanings of bits in the Expr.flags2 field.
|
||||
@@ -2651,6 +2651,29 @@ char *sqlite3MAppendf(sqlite3*,char*,const char*,...);
|
||||
#if defined(SQLITE_TEST)
|
||||
void *sqlite3TestTextToPtr(const char*);
|
||||
#endif
|
||||
|
||||
/* Output formatting for SQLITE_TESTCTRL_EXPLAIN */
|
||||
#if defined(SQLITE_ENABLE_TREE_EXPLAIN)
|
||||
void sqlite3ExplainBegin(Vdbe*);
|
||||
void sqlite3ExplainPrintf(Vdbe*, const char*, ...);
|
||||
void sqlite3ExplainNL(Vdbe*);
|
||||
void sqlite3ExplainPush(Vdbe*);
|
||||
void sqlite3ExplainPop(Vdbe*);
|
||||
void sqlite3ExplainFinish(Vdbe*);
|
||||
void sqlite3ExplainSelect(Vdbe*, Select*);
|
||||
void sqlite3ExplainExpr(Vdbe*, Expr*);
|
||||
void sqlite3ExplainExprList(Vdbe*, ExprList*);
|
||||
const char *sqlite3VdbeExplanation(Vdbe*);
|
||||
#else
|
||||
# define sqlite3ExplainBegin(X)
|
||||
# define sqlite3ExplainSelect(A,B)
|
||||
# define sqlite3ExplainExpr(A,B)
|
||||
# define sqlite3ExplainExprList(A,B)
|
||||
# define sqlite3ExplainFinish(X)
|
||||
# define sqlite3VdbeExplanation(X) 0
|
||||
#endif
|
||||
|
||||
|
||||
void sqlite3SetString(char **, sqlite3*, const char*, ...);
|
||||
void sqlite3ErrorMsg(Parse*, const char*, ...);
|
||||
int sqlite3Dequote(char*);
|
||||
@@ -3032,6 +3055,7 @@ int sqlite3OpenTempDatabase(Parse *);
|
||||
|
||||
void sqlite3StrAccumInit(StrAccum*, char*, int, int);
|
||||
void sqlite3StrAccumAppend(StrAccum*,const char*,int);
|
||||
void sqlite3AppendSpace(StrAccum*,int);
|
||||
char *sqlite3StrAccumFinish(StrAccum*);
|
||||
void sqlite3StrAccumReset(StrAccum*);
|
||||
void sqlite3SelectDestInit(SelectDest*,int,int);
|
||||
|
||||
@@ -33,6 +33,9 @@ typedef unsigned char Bool;
|
||||
/* Opaque type used by code in vdbesort.c */
|
||||
typedef struct VdbeSorter VdbeSorter;
|
||||
|
||||
/* Opaque type used by the explainer */
|
||||
typedef struct Explain Explain;
|
||||
|
||||
/*
|
||||
** A cursor is a pointer into a single BTree within a database file.
|
||||
** The cursor can seek to a BTree entry with a particular key, or
|
||||
@@ -257,6 +260,18 @@ struct sqlite3_context {
|
||||
CollSeq *pColl; /* Collating sequence */
|
||||
};
|
||||
|
||||
/*
|
||||
** An Explain object accumulates indented output which is helpful
|
||||
** in describing recursive data structures.
|
||||
*/
|
||||
struct Explain {
|
||||
Vdbe *pVdbe; /* Attach the explanation to this Vdbe */
|
||||
StrAccum str; /* The string being accumulated */
|
||||
int nIndent; /* Number of elements in aIndent */
|
||||
u16 aIndent[100]; /* Levels of indentation */
|
||||
char zBase[100]; /* Initial space */
|
||||
};
|
||||
|
||||
/*
|
||||
** An instance of the virtual machine. This structure contains the complete
|
||||
** state of the virtual machine.
|
||||
@@ -322,6 +337,10 @@ struct Vdbe {
|
||||
void *pFree; /* Free this when deleting the vdbe */
|
||||
#ifdef SQLITE_DEBUG
|
||||
FILE *trace; /* Write an execution trace here, if not NULL */
|
||||
#endif
|
||||
#ifdef SQLITE_ENABLE_TREE_EXPLAIN
|
||||
Explain *pExplain; /* The explainer */
|
||||
char *zExplain; /* Explanation of data structures */
|
||||
#endif
|
||||
VdbeFrame *pFrame; /* Parent frame */
|
||||
VdbeFrame *pDelFrame; /* List of frame objects to free on VM reset */
|
||||
|
||||
@@ -2474,6 +2474,10 @@ void sqlite3VdbeDeleteObject(sqlite3 *db, Vdbe *p){
|
||||
sqlite3DbFree(db, p->aColName);
|
||||
sqlite3DbFree(db, p->zSql);
|
||||
sqlite3DbFree(db, p->pFree);
|
||||
#if defined(SQLITE_ENABLE_TREE_EXPLAIN)
|
||||
sqlite3DbFree(db, p->zExplain);
|
||||
sqlite3DbFree(db, p->pExplain);
|
||||
#endif
|
||||
sqlite3DbFree(db, p);
|
||||
}
|
||||
|
||||
|
||||
117
src/vdbetrace.c
117
src/vdbetrace.c
@@ -12,6 +12,8 @@
|
||||
**
|
||||
** This file contains code used to insert the values of host parameters
|
||||
** (aka "wildcards") into the SQL text output by sqlite3_trace().
|
||||
**
|
||||
** The Vdbe parse-tree explainer is also found here.
|
||||
*/
|
||||
#include "sqliteInt.h"
|
||||
#include "vdbeInt.h"
|
||||
@@ -152,3 +154,118 @@ char *sqlite3VdbeExpandSql(
|
||||
}
|
||||
|
||||
#endif /* #ifndef SQLITE_OMIT_TRACE */
|
||||
|
||||
/*****************************************************************************
|
||||
** The following code implements the data-structure explaining logic
|
||||
** for the Vdbe.
|
||||
*/
|
||||
|
||||
#if defined(SQLITE_ENABLE_TREE_EXPLAIN)
|
||||
|
||||
/*
|
||||
** Allocate a new Explain object
|
||||
*/
|
||||
void sqlite3ExplainBegin(Vdbe *pVdbe){
|
||||
if( pVdbe ){
|
||||
sqlite3BeginBenignMalloc();
|
||||
Explain *p = sqlite3_malloc( sizeof(Explain) );
|
||||
if( p ){
|
||||
memset(p, 0, sizeof(*p));
|
||||
p->pVdbe = pVdbe;
|
||||
sqlite3_free(pVdbe->pExplain);
|
||||
pVdbe->pExplain = p;
|
||||
sqlite3StrAccumInit(&p->str, p->zBase, sizeof(p->zBase),
|
||||
SQLITE_MAX_LENGTH);
|
||||
p->str.useMalloc = 2;
|
||||
}else{
|
||||
sqlite3EndBenignMalloc();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** Return true if the Explain ends with a new-line.
|
||||
*/
|
||||
static int endsWithNL(Explain *p){
|
||||
return p && p->str.zText && p->str.nChar
|
||||
&& p->str.zText[p->str.nChar-1]=='\n';
|
||||
}
|
||||
|
||||
/*
|
||||
** Append text to the indentation
|
||||
*/
|
||||
void sqlite3ExplainPrintf(Vdbe *pVdbe, const char *zFormat, ...){
|
||||
Explain *p;
|
||||
if( pVdbe && (p = pVdbe->pExplain)!=0 ){
|
||||
va_list ap;
|
||||
if( p->nIndent && endsWithNL(p) ){
|
||||
int n = p->nIndent;
|
||||
if( n>ArraySize(p->aIndent) ) n = ArraySize(p->aIndent);
|
||||
sqlite3AppendSpace(&p->str, p->aIndent[n-1]);
|
||||
}
|
||||
va_start(ap, zFormat);
|
||||
sqlite3VXPrintf(&p->str, 1, zFormat, ap);
|
||||
va_end(ap);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** Append a '\n' if there is not already one.
|
||||
*/
|
||||
void sqlite3ExplainNL(Vdbe *pVdbe){
|
||||
Explain *p;
|
||||
if( pVdbe && (p = pVdbe->pExplain)!=0 && !endsWithNL(p) ){
|
||||
sqlite3StrAccumAppend(&p->str, "\n", 1);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** Push a new indentation level. Subsequent lines will be indented
|
||||
** so that they begin at the current cursor position.
|
||||
*/
|
||||
void sqlite3ExplainPush(Vdbe *pVdbe){
|
||||
Explain *p;
|
||||
if( pVdbe && (p = pVdbe->pExplain)!=0 ){
|
||||
if( p->str.zText && p->nIndent<ArraySize(p->aIndent) ){
|
||||
const char *z = p->str.zText;
|
||||
int i = p->str.nChar-1;
|
||||
int x;
|
||||
while( i>=0 && z[i]!='\n' ){ i--; }
|
||||
x = (p->str.nChar - 1) - i;
|
||||
if( p->nIndent && x<p->aIndent[p->nIndent-1] ){
|
||||
x = p->aIndent[p->nIndent-1];
|
||||
}
|
||||
p->aIndent[p->nIndent] = x;
|
||||
}
|
||||
p->nIndent++;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** Pop the indentation stack by one level.
|
||||
*/
|
||||
void sqlite3ExplainPop(Vdbe *p){
|
||||
if( p && p->pExplain ) p->pExplain->nIndent--;
|
||||
}
|
||||
|
||||
/*
|
||||
** Free the indentation structure
|
||||
*/
|
||||
void sqlite3ExplainFinish(Vdbe *pVdbe){
|
||||
if( pVdbe && pVdbe->pExplain ){
|
||||
sqlite3_free(pVdbe->zExplain);
|
||||
sqlite3ExplainNL(pVdbe);
|
||||
pVdbe->zExplain = sqlite3StrAccumFinish(&pVdbe->pExplain->str);
|
||||
sqlite3_free(pVdbe->pExplain);
|
||||
pVdbe->pExplain = 0;
|
||||
sqlite3EndBenignMalloc();
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** Return the explanation of a virtual machine.
|
||||
*/
|
||||
const char *sqlite3VdbeExplanation(Vdbe *pVdbe){
|
||||
return (pVdbe && pVdbe->zExplain) ? pVdbe->zExplain : 0;
|
||||
}
|
||||
#endif /* defined(SQLITE_DEBUG) */
|
||||
|
||||
Reference in New Issue
Block a user