If anyone is interested, the attached code patch can dump an internal parse tree of an SQLite SELECT statement. Perhaps it may be of use to someone doing SQL statement analysis, transformations or SQL source-level optimizations.
An example: sqlite> select * from (select Type, Name from (select name, type from sqlite_master union all select 123 TYPE, 456 NAME)); sqlite3SelectDump Parse=0x22e6d0, Select=0x56eb30 Select 0x56eb30: { op: TK_SELECT pEList: { a[0]: { zName: Type pExpr: { op: TK_COLUMN token: {Type} span: {Type} affinity: SQLITE_AFF_TEXT iTable: 0 iColumn: 0 } } a[1]: { zName: Name pExpr: { op: TK_COLUMN token: {Name} span: {Name} affinity: SQLITE_AFF_TEXT iTable: 0 iColumn: 1 } } } pSrc: { a[0]: { zAlias: sqlite_subquery_56EAB8_ iCursor: 0 colUsed: 0x00000003 pTab: sqlite_subquery_56EAB8_ pSelect: { op: TK_SELECT pEList: { a[0]: { pExpr: { op: TK_COLUMN token: {Type} span: {Type} affinity: SQLITE_AFF_TEXT iTable: 1 iColumn: 1 } } a[1]: { pExpr: { op: TK_COLUMN token: {Name} span: {Name} affinity: SQLITE_AFF_TEXT iTable: 1 iColumn: 0 } } } pSrc: { a[0]: { zAlias: sqlite_subquery_56EA40_ iCursor: 1 colUsed: 0x00000003 pTab: sqlite_subquery_56EA40_ pSelect: { op: TK_ALL pEList: { a[0]: { zName: TYPE pExpr: { op: TK_INTEGER token: {123} span: {123} } } a[1]: { zName: NAME pExpr: { op: TK_INTEGER token: {456} span: {456} } } } pPrior: { op: TK_SELECT pEList: { a[0]: { pExpr: { op: TK_COLUMN token: {name} span: {name} affinity: SQLITE_AFF_TEXT iTable: 2 iColumn: 1 pTab: sqlite_master } } a[1]: { pExpr: { op: TK_COLUMN token: {type} span: {type} affinity: SQLITE_AFF_TEXT iTable: 2 iColumn: 0 pTab: sqlite_master } } } pSrc: { a[0]: { zName: sqlite_master iCursor: 2 colUsed: 0x00000003 pTab: sqlite_master } } } } } } } } } } Select tree after successful flattenSubquery(): sqlite3SelectDump Parse=0x0, Select=0x56eb30 Select 0x56eb30: { op: TK_SELECT pEList: { a[0]: { zName: Type pExpr: { op: TK_COLUMN token: {Type} span: {Type} affinity: SQLITE_AFF_TEXT iTable: 1 iColumn: 1 } } a[1]: { zName: Name pExpr: { op: TK_COLUMN token: {Name} span: {Name} affinity: SQLITE_AFF_TEXT iTable: 1 iColumn: 0 } } } pSrc: { a[0]: { zAlias: sqlite_subquery_56EA40_ iCursor: 1 colUsed: 0x00000003 pTab: sqlite_subquery_56EA40_ pSelect: { op: TK_ALL pEList: { a[0]: { zName: TYPE pExpr: { op: TK_INTEGER token: {123} span: {123} } } a[1]: { zName: NAME pExpr: { op: TK_INTEGER token: {456} span: {456} } } } pPrior: { op: TK_SELECT pEList: { a[0]: { pExpr: { op: TK_COLUMN token: {name} span: {name} affinity: SQLITE_AFF_TEXT iTable: 2 iColumn: 1 pTab: sqlite_master } } a[1]: { pExpr: { op: TK_COLUMN token: {type} span: {type} affinity: SQLITE_AFF_TEXT iTable: 2 iColumn: 0 pTab: sqlite_master } } } pSrc: { a[0]: { zName: sqlite_master iCursor: 2 colUsed: 0x00000003 pTab: sqlite_master } } pRightmost: 0x56ea40 } pRightmost: 0x56ea40 } } } } 456|123 Be aware that SQLite merges its code generation phase with its various transformations (such as sub-select flattening). As result, you may not be looking at the actual parse tree SQLite ends up with just prior to its VDBE code generation. __________________________________________________ Do You Yahoo!? Tired of spam? Yahoo! Mail has the best spam protection around http://mail.yahoo.com
Index: src/parse.y =================================================================== RCS file: /sqlite/sqlite/src/parse.y,v retrieving revision 1.206 diff -u -3 -p -r1.206 parse.y --- src/parse.y 11 Jul 2006 10:42:36 -0000 1.206 +++ src/parse.y 24 Jul 2006 03:15:53 -0000 @@ -366,6 +366,7 @@ cmd ::= DROP VIEW ifexists(E) fullname(X //////////////////////// The SELECT statement ///////////////////////////////// // cmd ::= select(X). { + sqlite3SelectDump(pParse, X); sqlite3Select(pParse, X, SRT_Callback, 0, 0, 0, 0, 0); sqlite3SelectDelete(X); } Index: src/select.c =================================================================== RCS file: /sqlite/sqlite/src/select.c,v retrieving revision 1.319 diff -u -3 -p -r1.319 select.c --- src/select.c 11 Jul 2006 13:15:08 -0000 1.319 +++ src/select.c 24 Jul 2006 03:15:54 -0000 @@ -17,6 +17,545 @@ #include "sqliteInt.h" +static int g_printNULLs = 0; /* set to 1 for more verbose output */ + +static const char g_Indent[] = " " + " " + " " + " "; +#define g_Indent_length (sizeof(g_Indent)/sizeof(g_Indent[0])) + +static void dumpSelect(Parse*, int level, const char* name, Select* p); +static void dumpExpr(Parse*, int level, const char* name, Expr* p); +static void dumpExprList(Parse*, int level, const char* name, ExprList* p); + +static const char* indent(int level) { + if (level <= 0) return ""; + if (level > 100) { printf("\nfatal\n"); exit(1); } + return g_Indent + (g_Indent_length-1) - level*2; +} + +static void +dumpInt(Parse* pParse, int level, const char* name, int value) { + printf("%s%s: %d\n", indent(level), name, value); +} + +static const char* op2str(int op) { + switch (op) { + case TK_SEMI: return "TK_SEMI"; + case TK_EXPLAIN: return "TK_EXPLAIN"; + case TK_QUERY: return "TK_QUERY"; + case TK_PLAN: return "TK_PLAN"; + case TK_BEGIN: return "TK_BEGIN"; + case TK_TRANSACTION: return "TK_TRANSACTION"; + case TK_DEFERRED: return "TK_DEFERRED"; + case TK_IMMEDIATE: return "TK_IMMEDIATE"; + case TK_EXCLUSIVE: return "TK_EXCLUSIVE"; + case TK_COMMIT: return "TK_COMMIT"; + case TK_END: return "TK_END"; + case TK_ROLLBACK: return "TK_ROLLBACK"; + case TK_CREATE: return "TK_CREATE"; + case TK_TABLE: return "TK_TABLE"; + case TK_IF: return "TK_IF"; + case TK_NOT: return "TK_NOT"; + case TK_EXISTS: return "TK_EXISTS"; + case TK_TEMP: return "TK_TEMP"; + case TK_LP: return "TK_LP"; + case TK_RP: return "TK_RP"; + case TK_AS: return "TK_AS"; + case TK_COMMA: return "TK_COMMA"; + case TK_ID: return "TK_ID"; + case TK_ABORT: return "TK_ABORT"; + case TK_AFTER: return "TK_AFTER"; + case TK_ANALYZE: return "TK_ANALYZE"; + case TK_ASC: return "TK_ASC"; + case TK_ATTACH: return "TK_ATTACH"; + case TK_BEFORE: return "TK_BEFORE"; + case TK_CASCADE: return "TK_CASCADE"; + case TK_CAST: return "TK_CAST"; + case TK_CONFLICT: return "TK_CONFLICT"; + case TK_DATABASE: return "TK_DATABASE"; + case TK_DESC: return "TK_DESC"; + case TK_DETACH: return "TK_DETACH"; + case TK_EACH: return "TK_EACH"; + case TK_FAIL: return "TK_FAIL"; + case TK_FOR: return "TK_FOR"; + case TK_IGNORE: return "TK_IGNORE"; + case TK_INITIALLY: return "TK_INITIALLY"; + case TK_INSTEAD: return "TK_INSTEAD"; + case TK_LIKE_KW: return "TK_LIKE_KW"; + case TK_MATCH: return "TK_MATCH"; + case TK_KEY: return "TK_KEY"; + case TK_OF: return "TK_OF"; + case TK_OFFSET: return "TK_OFFSET"; + case TK_PRAGMA: return "TK_PRAGMA"; + case TK_RAISE: return "TK_RAISE"; + case TK_REPLACE: return "TK_REPLACE"; + case TK_RESTRICT: return "TK_RESTRICT"; + case TK_ROW: return "TK_ROW"; + case TK_STATEMENT: return "TK_STATEMENT"; + case TK_TRIGGER: return "TK_TRIGGER"; + case TK_VACUUM: return "TK_VACUUM"; + case TK_VIEW: return "TK_VIEW"; + case TK_VIRTUAL: return "TK_VIRTUAL"; + case TK_REINDEX: return "TK_REINDEX"; + case TK_RENAME: return "TK_RENAME"; + case TK_CTIME_KW: return "TK_CTIME_KW"; + case TK_ANY: return "TK_ANY"; + case TK_OR: return "TK_OR"; + case TK_AND: return "TK_AND"; + case TK_IS: return "TK_IS"; + case TK_BETWEEN: return "TK_BETWEEN"; + case TK_IN: return "TK_IN"; + case TK_ISNULL: return "TK_ISNULL"; + case TK_NOTNULL: return "TK_NOTNULL"; + case TK_NE: return "TK_NE"; + case TK_EQ: return "TK_EQ"; + case TK_GT: return "TK_GT"; + case TK_LE: return "TK_LE"; + case TK_LT: return "TK_LT"; + case TK_GE: return "TK_GE"; + case TK_ESCAPE: return "TK_ESCAPE"; + case TK_BITAND: return "TK_BITAND"; + case TK_BITOR: return "TK_BITOR"; + case TK_LSHIFT: return "TK_LSHIFT"; + case TK_RSHIFT: return "TK_RSHIFT"; + case TK_PLUS: return "TK_PLUS"; + case TK_MINUS: return "TK_MINUS"; + case TK_STAR: return "TK_STAR"; + case TK_SLASH: return "TK_SLASH"; + case TK_REM: return "TK_REM"; + case TK_CONCAT: return "TK_CONCAT"; + case TK_UMINUS: return "TK_UMINUS"; + case TK_UPLUS: return "TK_UPLUS"; + case TK_BITNOT: return "TK_BITNOT"; + case TK_STRING: return "TK_STRING"; + case TK_JOIN_KW: return "TK_JOIN_KW"; + case TK_CONSTRAINT: return "TK_CONSTRAINT"; + case TK_DEFAULT: return "TK_DEFAULT"; + case TK_NULL: return "TK_NULL"; + case TK_PRIMARY: return "TK_PRIMARY"; + case TK_UNIQUE: return "TK_UNIQUE"; + case TK_CHECK: return "TK_CHECK"; + case TK_REFERENCES: return "TK_REFERENCES"; + case TK_COLLATE: return "TK_COLLATE"; + case TK_AUTOINCR: return "TK_AUTOINCR"; + case TK_ON: return "TK_ON"; + case TK_DELETE: return "TK_DELETE"; + case TK_UPDATE: return "TK_UPDATE"; + case TK_INSERT: return "TK_INSERT"; + case TK_SET: return "TK_SET"; + case TK_DEFERRABLE: return "TK_DEFERRABLE"; + case TK_FOREIGN: return "TK_FOREIGN"; + case TK_DROP: return "TK_DROP"; + case TK_UNION: return "TK_UNION"; + case TK_ALL: return "TK_ALL"; + case TK_EXCEPT: return "TK_EXCEPT"; + case TK_INTERSECT: return "TK_INTERSECT"; + case TK_SELECT: return "TK_SELECT"; + case TK_DISTINCT: return "TK_DISTINCT"; + case TK_DOT: return "TK_DOT"; + case TK_FROM: return "TK_FROM"; + case TK_JOIN: return "TK_JOIN"; + case TK_USING: return "TK_USING"; + case TK_ORDER: return "TK_ORDER"; + case TK_BY: return "TK_BY"; + case TK_GROUP: return "TK_GROUP"; + case TK_HAVING: return "TK_HAVING"; + case TK_LIMIT: return "TK_LIMIT"; + case TK_WHERE: return "TK_WHERE"; + case TK_INTO: return "TK_INTO"; + case TK_VALUES: return "TK_VALUES"; + case TK_INTEGER: return "TK_INTEGER"; + case TK_FLOAT: return "TK_FLOAT"; + case TK_BLOB: return "TK_BLOB"; + case TK_REGISTER: return "TK_REGISTER"; + case TK_VARIABLE: return "TK_VARIABLE"; + case TK_CASE: return "TK_CASE"; + case TK_WHEN: return "TK_WHEN"; + case TK_THEN: return "TK_THEN"; + case TK_ELSE: return "TK_ELSE"; + case TK_INDEX: return "TK_INDEX"; + case TK_ALTER: return "TK_ALTER"; + case TK_TO: return "TK_TO"; + case TK_ADD: return "TK_ADD"; + case TK_COLUMNKW: return "TK_COLUMNKW"; + case TK_TO_TEXT: return "TK_TO_TEXT"; + case TK_TO_BLOB: return "TK_TO_BLOB"; + case TK_TO_NUMERIC: return "TK_TO_NUMERIC"; + case TK_TO_INT: return "TK_TO_INT"; + case TK_TO_REAL: return "TK_TO_REAL"; + case TK_END_OF_FILE: return "TK_END_OF_FILE"; + case TK_ILLEGAL: return "TK_ILLEGAL"; + case TK_SPACE: return "TK_SPACE"; + case TK_UNCLOSED_STRING: return "TK_UNCLOSED_STRING"; + case TK_COMMENT: return "TK_COMMENT"; + case TK_FUNCTION: return "TK_FUNCTION"; + case TK_COLUMN: return "TK_COLUMN"; + case TK_AGG_FUNCTION: return "TK_AGG_FUNCTION"; + case TK_AGG_COLUMN: return "TK_AGG_COLUMN"; + case TK_CONST_FUNC: return "TK_CONST_FUNC"; + } + return "unknown op"; +} + +static void +dumpOp(Parse* pParse, int level, const char* name, int op) { + printf("%s%s: %s\n", indent(level), name, op2str(op)); +} + +static void +dumpUnsigned(Parse* pParse, int level, const char* name, unsigned value) { + printf("%s%s: %u\n", indent(level), name, value); +} + +static void +dumpFlag(Parse* pParse, int level, const char* name, unsigned value) { + if (g_printNULLs && value) + printf("%s%s: %u\n", indent(level), name, value); +} + +static void +dumpString(Parse* pParse, int level, const char* name, const char* p) { + if (!p) { + if (g_printNULLs) printf("%s%s: NULL\n", indent(level), name); + return; + } + printf("%s%s: %s\n", indent(level), name, p); +} + +static void +dumpPointer(Parse* pParse, int level, const char* name, void* p) { + if (!p) { + if (g_printNULLs) printf("%s%s: NULL\n", indent(level), name); + return; + } + printf("%s%s: %p\n", indent(level), name, p); +} + +static void +dumpChar(Parse* pParse, int level, const char* name, char p) { + if (!p) { + printf("%s%s: NIL\n", indent(level), name); + return; + } + printf("%s%s: %c\n", indent(level), name, p); +} + +static const char* GetAffinityString(char aff) { + switch (aff) { + case SQLITE_AFF_TEXT: return "SQLITE_AFF_TEXT"; + case SQLITE_AFF_NONE: return "SQLITE_AFF_NONE"; + case SQLITE_AFF_NUMERIC: return "SQLITE_AFF_NUMERIC"; + case SQLITE_AFF_INTEGER: return "SQLITE_AFF_INTEGER"; + case SQLITE_AFF_REAL: return "SQLITE_AFF_REAL"; + } + return "NULL"; +} + +static void +dumpAffinity(Parse* pParse, int level, const char* name, char p) { + if (!p) { + if (g_printNULLs) printf("%s%s: NIL\n", indent(level), name); + return; + } + printf("%s%s: %s\n", indent(level), name, GetAffinityString(p)); +} + +static void +dumpExprFlags(Parse* pParse, int level, const char* name, unsigned value) { + char buf[512]; + if (value == EP_Resolved) return; + buf[0] = 0; + if (value & EP_FromJoin) { strcat(buf, "EP_FromJoin "); } + if (value & EP_Agg) { strcat(buf, "EP_Agg "); } + if (value & EP_Resolved) { strcat(buf, "EP_Resolved "); } + if (value & EP_Error) { strcat(buf, "EP_Error "); } + if (value & EP_Distinct) { strcat(buf, "EP_Distinct "); } + if (value & EP_VarSelect) { strcat(buf, "EP_VarSelect "); } + if (value & EP_Dequoted) { strcat(buf, "EP_Dequoted "); } + if (value & EP_InfixFunc) { strcat(buf, "EP_InfixFunc "); } + printf("%s%s: %s\n", indent(level), name, buf); +} + +static void +dumpToken(Parse* pParse, int level, const char* name, Token p) { + if (!p.z) { + /*if (g_printNULLs) printf("%s%s: NULL\n", indent(level), name);*/ + if (g_printNULLs) printf("%s%s: NULL\n", indent(level), name); + return; + } +#if 1 + /* not verbose */ + printf("%s%s: {%-.*s}\n", indent(level), name, p.n, p.z); +#else + /* verbose */ + printf("%s%s: {\n", indent(level), name); + ++level; + dumpUnsigned(pParse, level, "n", p.n); + dumpFlag(pParse, level, "dyn", p.dyn); + printf("%sz: {%-.*s}\n", indent(level), p.n, p.z); + --level; + printf("%s}\n", indent(level)); +#endif +} + +static void +dumpTable(Parse* pParse, int level, const char* name, Table* p) { + if (!p) { + if (g_printNULLs) printf("%s%s: NULL\n", indent(level), name); + return; + } + printf("%s%s: %s\n", indent(level), name, p->zName); + ++level; + /*dumpString(pParse, level, "zName", p);*/ +} + +static const char* GetCollSeq(u8 type) { + switch (type) { + case SQLITE_COLL_BINARY: return "SQLITE_COLL_BINARY"; + case SQLITE_COLL_NOCASE: return "SQLITE_COLL_NOCASE"; + case SQLITE_COLL_REVERSE: return "SQLITE_COLL_REVERSE"; + case SQLITE_COLL_USER: return "SQLITE_COLL_USER"; + } + return "unknown"; +} + +static void +dumpCollSeq(Parse* pParse, int level, const char* name, CollSeq* p) { + if (p && p->xCmp) { + /* BINARY is the typical case, so no need to print it always */ + if (p->type == SQLITE_COLL_BINARY) return; + + printf("%s%s: {\n", indent(level), name); + ++level; + dumpString(pParse, level, "zName", p->zName); + dumpUnsigned(pParse, level, "enc", p->enc); + dumpString(pParse, level, "type", GetCollSeq(p->type)); + dumpPointer(pParse, level, "pUser", p->pUser); + dumpPointer(pParse, level, "xCmp", p->xCmp); + --level; + printf("%s}\n", indent(level)); + } +} + +static void +dumpExpr(Parse* pParse, int level, const char* name, Expr* p) { + if (!p) { + if (g_printNULLs) printf("%s%s: NULL\n", indent(level), name); + return; + } + printf("%s%s: {\n", indent(level), name, p); + ++level; + dumpOp(pParse, level, "op", p->op); + dumpToken(pParse, level, "token", p->token); + dumpToken(pParse, level, "span", p->span); + dumpAffinity(pParse, level, "affinity", p->affinity); + dumpExprFlags(pParse, level, "flags", p->flags); + if (p->op == TK_COLUMN) dumpInt(pParse, level, "iTable", p->iTable); + if (p->op == TK_COLUMN) dumpInt(pParse, level, "iColumn", p->iColumn); + dumpPointer(pParse, level, "pAggInfo", p->pAggInfo); + if (p->iAgg != -1) dumpInt(pParse, level, "iAgg", p->iAgg); + if (p->flags & EP_FromJoin) + dumpInt(pParse, level, "iRightJoinTable", p->iRightJoinTable); + dumpCollSeq(pParse, level, "pColl", p->pColl); + dumpExpr(pParse, level, "pLeft", p->pLeft); + dumpExpr(pParse, level, "pRight", p->pRight); + dumpExprList(pParse, level, "pList", p->pList); + dumpPointer(pParse, level, "pSelect", p->pSelect); + dumpTable(pParse, level, "pTab", p->pTab); + /* dumpPointer(pParse, level, "pSchema", p->pSchema); */ + --level; + printf("%s}\n", indent(level)); +} + +static void +dumpJointype(Parse* pParse, int level, const char* name, unsigned jointype) { + char buf[200]; + if (!jointype) return; + buf[0] = 0; + if (jointype & JT_INNER) { strcat(buf, "JT_INNER "); } + if (jointype & JT_CROSS) { strcat(buf, "JT_CROSS "); } + if (jointype & JT_NATURAL) { strcat(buf, "JT_NATURAL "); } + if (jointype & JT_LEFT) { strcat(buf, "JT_LEFT "); } + if (jointype & JT_RIGHT) { strcat(buf, "JT_RIGHT "); } + if (jointype & JT_OUTER) { strcat(buf, "JT_OUTER "); } + printf("%s%s: %s\n", indent(level), name, buf); +} + +static void +dumpIdList_item(Parse* pParse, int level, const char* name, + struct IdList_item* p) { + if (!p) { + if (g_printNULLs) printf("%s%s: NULL\n", indent(level), name); + return; + } + printf("%s%s: {\n", indent(level), name); + ++level; + dumpString(pParse, level, "zName", p->zName); + /* dumpInt(pParse, level, "idx", p->idx); not used in SELECT */ + --level; + printf("%s}\n", indent(level)); +} + +static void +dumpIdList(Parse* pParse, int level, const char* name, IdList* p) { + int i; + if (!p) { + if (g_printNULLs) printf("%s%s: NULL\n", indent(level), name); + return; + } + if (p->nId <= 0) return; + printf("%s%s: {\n", indent(level), name); + ++level; + /* dumpInt(pParse, level, "nAlloc", p->nAlloc); */ + /* dumpInt(pParse, level, "nId", p->nId); */ + for (i = 0; i < p->nId; ++i) { + char buf[100]; + sprintf(buf, "a[%d]", i); + dumpIdList_item(pParse, level, buf, &p->a[i]); + } + --level; + printf("%s}\n", indent(level)); +} + +static void +dumpHex(Parse* pParse, int level, const char* name, unsigned value) { + printf("%s%s: 0x%08x\n", indent(level), name, value); +} + +static void +dumpSrcList_item(Parse* pParse, int level, const char* name, + struct SrcList_item* p) { + if (!p) { + if (g_printNULLs) printf("%s%s: NULL\n", indent(level), name); + return; + } + printf("%s%s: {\n", indent(level), name); + ++level; + dumpString(pParse, level, "zDatabase", p->zDatabase); + dumpString(pParse, level, "zName", p->zName); + dumpString(pParse, level, "zAlias", p->zAlias); + dumpFlag(pParse, level, "isPopulated", p->isPopulated); + /* if (p->iCursor != -1) dumpInt(pParse, level, "iCursor", p->iCursor); */ + dumpInt(pParse, level, "iCursor", p->iCursor); + if (p->colUsed) dumpHex(pParse, level, "colUsed", p->colUsed); + dumpTable(pParse, level, "pTab", p->pTab); + dumpSelect(pParse, level, "pSelect", p->pSelect); + dumpJointype(pParse, level, "jointype", p->jointype); + dumpExpr(pParse, level, "pOn", p->pOn); + dumpIdList(pParse, level, "pUsing", p->pUsing); /* converted to pWhere */ + --level; + printf("%s}\n", indent(level)); +} + +static void +dumpSrcList(Parse* pParse, int level, const char* name, SrcList* p) { + int i; + if (!p) { + if (g_printNULLs) printf("%s%s: NULL\n", indent(level), name); + return; + } + if (p->nSrc <= 0) return; + printf("%s%s: {\n", indent(level), name); + ++level; + /* dumpInt(pParse, level, "nAlloc", p->nAlloc); */ + /* dumpInt(pParse, level, "nSrc", p->nSrc); */ + for (i = 0; i < p->nSrc; ++i) { + char buf[200]; + sprintf(buf, "a[%d]", i); + dumpSrcList_item(pParse, level, buf, &p->a[i]); + } + --level; + printf("%s}\n", indent(level)); +} + +static void +dumpSortOrder(Parse* pParse, int level, const char* name, unsigned value) { + if (g_printNULLs && value) + printf("%s%s: %s\n", indent(level), name, (value ? "DESC" : "ASC")); +} + +static void +dumpExprList_item(Parse* pParse, int level, const char* name, + struct ExprList_item* p) { + if (!p) { + if (g_printNULLs) printf("%s%s: NULL\n", indent(level), name); + return; + } + printf("%s%s: {\n", indent(level), name); + ++level; + dumpString(pParse, level, "zName", p->zName); + dumpSortOrder(pParse, level, "sortOrder", p->sortOrder); + dumpFlag(pParse, level, "isAgg", p->isAgg); + dumpFlag(pParse, level, "done", p->done); + dumpExpr(pParse, level, "pExpr", p->pExpr); + --level; + printf("%s%}\n", indent(level)); +} + +static void +dumpExprList(Parse* pParse, int level, const char* name, ExprList* p) { + int i; + if (!p) { + if (g_printNULLs) printf("%s%s: NULL\n", indent(level), name); + return; + } + if (p->nExpr <= 0) return; + printf("%s%s: {\n", indent(level), name); + ++level; + /* dumpUnsigned(pParse, level, "iECursor", p->iECursor); */ + /* dumpUnsigned(pParse, level, "nAlloc", p->nAlloc); */ + /* dumpUnsigned(pParse, level, "nExpr", p->nExpr); */ + for (i = 0; i < p->nExpr; ++i) { + char buf[200]; + sprintf(buf, "a[%d]", i); + dumpExprList_item(pParse, level, buf, &p->a[i]); + } + --level; + printf("%s}\n", indent(level)); +} + +static void +dumpSelect(Parse* pParse, int level, const char* name, Select* p) { + if (pParse && p && sqlite3SelectResolve(pParse, p, 0)){ return; } + if (!p) { + if (g_printNULLs) printf("%s%s: NULL\n", indent(level), name); + return; + } + printf("%s%s: {\n", indent(level), name); + level++; + dumpOp(pParse, level, "op", p->op); + dumpFlag(pParse, level, "isDistinct", p->isDistinct); + dumpFlag(pParse, level, "isResolved", p->isResolved); + dumpFlag(pParse, level, "isAgg", p->isAgg); + dumpFlag(pParse, level, "usesEphm", p->usesEphm); + dumpExprList(pParse, level, "pEList", p->pEList); + dumpFlag(pParse, level, "disallowOrderBy", p->disallowOrderBy); + dumpSrcList(pParse, level, "pSrc", p->pSrc); + dumpExpr(pParse, level, "pWhere", p->pWhere); + dumpExprList(pParse, level, "pGroupBy", p->pGroupBy); + dumpExpr(pParse, level, "pHaving", p->pHaving); + dumpExprList(pParse, level, "pOrderBy", p->pOrderBy); + dumpSelect(pParse, level, "pPrior", p->pPrior); + dumpPointer(pParse, level, "pRightmost", p->pRightmost); + dumpExpr(pParse, level, "pLimit", p->pLimit); + dumpExpr(pParse, level, "pOffset", p->pOffset); + if (p->iLimit != -1) dumpInt(pParse, level, "iLimit", p->iLimit); + if (p->iOffset != -1) dumpInt(pParse, level, "iOffset", p->iOffset); + level--; + printf("%s}\n", indent(level)); +} + +int sqlite3SelectDump(Parse *pParse, Select *p) { + char buf[256]; + printf("sqlite3SelectDump Parse=%p, Select=%p\n", pParse, p); + sprintf(buf, "Select %p", p); + dumpSelect(pParse, 0, buf, p); + return 0; +} + + /* ** Delete all the content of a Select structure but do not deallocate ** the select structure itself. @@ -2304,6 +2843,10 @@ static int flattenSubquery( ** success. */ sqlite3SelectDelete(pSub); + + printf("\nSelect tree after successful flattenSubquery():\n"); + sqlite3SelectDump(NULL, p); + return 1; } #endif /* SQLITE_OMIT_VIEW */ Index: src/sqliteInt.h =================================================================== RCS file: /sqlite/sqlite/src/sqliteInt.h,v retrieving revision 1.521 diff -u -3 -p -r1.521 sqliteInt.h --- src/sqliteInt.h 11 Jul 2006 14:17:52 -0000 1.521 +++ src/sqliteInt.h 24 Jul 2006 03:15:54 -0000 @@ -1621,6 +1621,7 @@ void sqlite3DropIndex(Parse*, SrcList*, void sqlite3AddKeyType(Vdbe*, ExprList*); void sqlite3AddIdxKeyType(Vdbe*, Index*); int sqlite3Select(Parse*, Select*, int, int, Select*, int, int*, char *aff); +int sqlite3SelectDump(Parse*, Select*); Select *sqlite3SelectNew(ExprList*,SrcList*,Expr*,ExprList*,Expr*,ExprList*, int,Expr*,Expr*); void sqlite3SelectDelete(Select*);