sqlite3TreeViewPop(pView);
}
+/****************************************************************************
+** Experimental (demonstration-only) routines for printing a AST as a
+** relation:
+**
+** CREATE TABLE AstNode(
+** nodeId INTEGER PRIMARY KEY,
+** parentId INTEGER REFERENCES AstNode,
+** name TEXT,
+** value ANY
+** );
+*/
+
+#define ASTVT_NULL 0
+#define ASTVT_TEXT 1
+#define ASTVT_INT 2
+
+/*
+** Output a single row of the AstNode relation
+*/
+static int sqlite3AstOneRow(
+ int *pNodeCnt,
+ int parentId,
+ const char *zName,
+ int eValtype,
+ int iValue,
+ const char *zValue
+){
+ int nodeId = ++(*pNodeCnt);
+ printf("%d,", nodeId);
+ if( parentId ){
+ printf("%d,", parentId);
+ }else{
+ printf("NULL,");
+ }
+ printf("'%s',", zName);
+ switch( eValtype ){
+ case ASTVT_INT: printf("%d\n", iValue); break;
+ case ASTVT_TEXT: printf("'%s'\n", zValue); break;
+ default: printf("NULL\n"); break;
+ }
+ return nodeId;
+}
+static int sqlite3AstOneRowInt(
+ int *pNodeCnt,
+ int parentId,
+ const char *zName,
+ int iValue
+){
+ return sqlite3AstOneRow(pNodeCnt,parentId,zName,ASTVT_INT,iValue,0);
+}
+static int sqlite3AstOneRowText(
+ int *pNodeCnt,
+ int parentId,
+ const char *zName,
+ const char *zValue
+){
+ return sqlite3AstOneRow(pNodeCnt,parentId,zName,ASTVT_TEXT,0,zValue);
+}
+static int sqlite3AstOneRowNull(
+ int *pNodeCnt,
+ int parentId,
+ const char *zName
+){
+ return sqlite3AstOneRow(pNodeCnt,parentId,zName,ASTVT_NULL,0,0);
+}
+static int sqlite3AstOneRowOp(
+ int *pNodeCnt,
+ int parentId,
+ const char *zName,
+ const Expr *pExpr
+){
+ char zFlg[30];
+ sqlite3_snprintf(sizeof(zFlg),zFlg,"0x%6x",pExpr->flags);
+ return sqlite3AstOneRow(pNodeCnt,parentId,zName,ASTVT_TEXT,0,zFlg);
+}
+
+
+void sqlite3AstExpr(const Expr *pExpr, int *pNodeCnt, int iParentId){
+ int iNodeId;
+ const char *zBinOp = 0;
+ const char *zUniOp = 0;
+
+ if( pExpr==0 ) return;
+ switch( pExpr->op ){
+ case TK_AGG_COLUMN: {
+ iNodeId = sqlite3AstOneRowOp(pNodeCnt, iParentId, "agg_column", pExpr);
+ sqlite3AstOneRowInt(pNodeCnt, iNodeId, "table", pExpr->iTable);
+ sqlite3AstOneRowInt(pNodeCnt, iNodeId, "column", pExpr->iColumn);
+ break;
+ }
+ case TK_COLUMN: {
+ iNodeId = sqlite3AstOneRowOp(pNodeCnt, iParentId, "column", pExpr);
+ if( pExpr->iTable>=0 ){
+ sqlite3AstOneRowInt(pNodeCnt, iNodeId, "table", pExpr->iTable);
+ }
+ sqlite3AstOneRowInt(pNodeCnt, iNodeId, "column", pExpr->iColumn);
+ break;
+ }
+ case TK_INTEGER: {
+ if( pExpr->flags & EP_IntValue ){
+ iNodeId = sqlite3AstOneRowInt(pNodeCnt, iParentId,
+ "integer", pExpr->u.iValue);
+ }else{
+ iNodeId = sqlite3AstOneRowText(pNodeCnt, iParentId,
+ "integer", pExpr->u.zToken);
+ }
+ break;
+ }
+#ifndef SQLITE_OMIT_FLOATING_POINT
+ case TK_FLOAT: {
+ iNodeId = sqlite3AstOneRowText(pNodeCnt, iParentId,
+ "float", pExpr->u.zToken);
+ break;
+ }
+#endif
+ case TK_STRING: {
+ iNodeId = sqlite3AstOneRowText(pNodeCnt, iParentId,
+ "string", pExpr->u.zToken);
+ break;
+ }
+ case TK_NULL: {
+ iNodeId = sqlite3AstOneRowNull(pNodeCnt, iParentId, "null");
+ break;
+ }
+ case TK_TRUEFALSE: {
+ iNodeId = sqlite3AstOneRowNull(pNodeCnt, iParentId,
+ sqlite3ExprTruthValue(pExpr) ? "true" : "false");
+ break;
+ }
+#ifndef SQLITE_OMIT_BLOB_LITERAL
+ case TK_BLOB: {
+ iNodeId = sqlite3AstOneRowText(pNodeCnt, iParentId, "blob",
+ pExpr->u.zToken);
+ break;
+ }
+#endif
+ case TK_VARIABLE: {
+ iNodeId = sqlite3AstOneRowOp(pNodeCnt, iParentId, "variable", pExpr);
+ sqlite3AstOneRowText(pNodeCnt, iNodeId, "name", pExpr->u.zToken);
+ sqlite3AstOneRowInt(pNodeCnt, iNodeId, "column", pExpr->iColumn);
+ break;
+ }
+ case TK_REGISTER: {
+ iNodeId = sqlite3AstOneRowOp(pNodeCnt, iParentId, "register", pExpr);
+ sqlite3AstOneRowInt(pNodeCnt, iNodeId, "registerNumber",
+ pExpr->iTable);
+ break;
+ }
+ case TK_ID: {
+ iNodeId = sqlite3AstOneRowOp(pNodeCnt, iParentId, "id", pExpr);
+ sqlite3AstOneRowText(pNodeCnt, iNodeId, "Name", pExpr->u.zToken);
+ break;
+ }
+ 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_DOT: zBinOp = "DOT"; break;
+
+ case TK_CAST: zUniOp = "CAST"; 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_IF_NULL_ROW: zUniOp = "IFNULLROW"; break;
+
+ case TK_TRUTH: {
+ int x;
+ const char *azOp[] = {
+ "IS-FALSE", "IS-TRUE", "IS-NOT-FALSE", "IS-NOT-TRUE"
+ };
+ assert( pExpr->op2==TK_IS || pExpr->op2==TK_ISNOT );
+ assert( pExpr->pRight );
+ assert( pExpr->pRight->op==TK_TRUEFALSE );
+ x = (pExpr->op2==TK_ISNOT)*2 + sqlite3ExprTruthValue(pExpr->pRight);
+ zUniOp = azOp[x];
+ break;
+ }
+
+ case TK_SPAN: {
+ iNodeId = sqlite3AstOneRowText(pNodeCnt, iParentId, "span",
+ pExpr->u.zToken);
+ sqlite3AstExpr(pExpr->pLeft, pNodeCnt, iNodeId);
+ break;
+ }
+
+ case TK_COLLATE: {
+ iNodeId = sqlite3AstOneRowText(pNodeCnt, iParentId, "collate",
+ pExpr->u.zToken);
+ sqlite3AstExpr(pExpr->pLeft, pNodeCnt, iNodeId);
+ break;
+ }
+
+ case TK_AGG_FUNCTION:
+ case TK_FUNCTION: {
+ ExprList *pFarg; /* List of function arguments */
+ if( ExprHasProperty(pExpr, EP_TokenOnly) ){
+ pFarg = 0;
+ }else{
+ pFarg = pExpr->x.pList;
+ }
+ if( pExpr->op==TK_AGG_FUNCTION ){
+ char zId[20];
+ sqlite3_snprintf(sizeof(zId),zId,"agg_function%d", pExpr->op2);
+ iNodeId = sqlite3AstOneRowText(pNodeCnt, iParentId, zId,
+ pExpr->u.zToken);
+ }else{
+ iNodeId = sqlite3AstOneRowText(pNodeCnt, iParentId, "function",
+ pExpr->u.zToken);
+ }
+ if( pFarg ){
+ sqlite3AstExprList(pFarg, pNodeCnt, iNodeId, 0);
+ }
+ break;
+ }
+#ifndef SQLITE_OMIT_SUBQUERY
+ case TK_EXISTS: {
+ iNodeId = sqlite3AstOneRowOp(pNodeCnt, iParentId, "exists-expr", pExpr);
+ sqlite3AstSelect(pExpr->x.pSelect, pNodeCnt, iNodeId);
+ break;
+ }
+ case TK_SELECT: {
+ iNodeId = sqlite3AstOneRowOp(pNodeCnt, iParentId, "select-expr", pExpr);
+ sqlite3AstSelect(pExpr->x.pSelect, pNodeCnt, iNodeId);
+ break;
+ }
+ case TK_IN: {
+ int id;
+ iNodeId = sqlite3AstOneRowOp(pNodeCnt, iParentId, "in", pExpr);
+ id = sqlite3AstOneRowNull(pNodeCnt, iNodeId, "left");
+ sqlite3AstExpr(pExpr->pLeft, pNodeCnt, id);
+ id = sqlite3AstOneRowNull(pNodeCnt, iNodeId, "right");
+ if( ExprHasProperty(pExpr, EP_xIsSelect) ){
+ sqlite3AstSelect(pExpr->x.pSelect, pNodeCnt, id);
+ }else{
+ sqlite3AstExprList(pExpr->x.pList, pNodeCnt, id, 0);
+ }
+ 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: {
+ int id;
+ Expr *pX = pExpr->pLeft;
+ Expr *pY = pExpr->x.pList->a[0].pExpr;
+ Expr *pZ = pExpr->x.pList->a[1].pExpr;
+ iNodeId = sqlite3AstOneRowOp(pNodeCnt, iParentId, "between", pExpr);
+ id = sqlite3AstOneRowNull(pNodeCnt, iNodeId, "left");
+ sqlite3AstExpr(pX, pNodeCnt, id);
+ id = sqlite3AstOneRowNull(pNodeCnt, iNodeId, "lower");
+ sqlite3AstExpr(pY, pNodeCnt, id);
+ id = sqlite3AstOneRowNull(pNodeCnt, iNodeId, "upper");
+ sqlite3AstExpr(pZ, pNodeCnt, id);
+ 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.
+ */
+ iNodeId = sqlite3AstOneRowOp(pNodeCnt, iParentId,
+ pExpr->iTable ? "new-column" : "old-column", pExpr);
+ sqlite3AstOneRowInt(pNodeCnt, iNodeId, "column", pExpr->iColumn);
+ break;
+ }
+ case TK_CASE: {
+ int id;
+ iNodeId = sqlite3AstOneRowOp(pNodeCnt, iParentId, "case", pExpr);
+ id = sqlite3AstOneRowNull(pNodeCnt, iNodeId, "left");
+ sqlite3AstExpr(pExpr->pLeft, pNodeCnt, id);
+ id = sqlite3AstOneRowNull(pNodeCnt, iNodeId, "list");
+ sqlite3AstExprList(pExpr->x.pList, pNodeCnt, id, 0);
+ break;
+ }
+ 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;
+ }
+ iNodeId = sqlite3AstOneRowOp(pNodeCnt, iParentId, "raise", pExpr);
+ sqlite3AstOneRowText(pNodeCnt, iNodeId, "type", zType);
+ sqlite3AstOneRowText(pNodeCnt, iNodeId, "arg", pExpr->u.zToken);
+ break;
+ }
+ case TK_MATCH: {
+ iNodeId = sqlite3AstOneRowOp(pNodeCnt, iParentId, "match", pExpr);
+ sqlite3AstOneRowInt(pNodeCnt, iNodeId, "table", pExpr->iTable);
+ sqlite3AstOneRowInt(pNodeCnt, iNodeId, "column", pExpr->iColumn);
+ break;
+ }
+ case TK_VECTOR: {
+ iNodeId = sqlite3AstOneRowOp(pNodeCnt, iParentId, "vector", pExpr);
+ sqlite3AstExprList(pExpr->x.pList, pNodeCnt, iNodeId, 0);
+ break;
+ }
+ case TK_SELECT_COLUMN: {
+ iNodeId = sqlite3AstOneRowInt(pNodeCnt, iParentId, "select-column",
+ pExpr->iColumn);
+ sqlite3AstSelect(pExpr->pLeft->x.pSelect, pNodeCnt, iNodeId);
+ break;
+ }
+ default: {
+ sqlite3AstOneRowInt(pNodeCnt, iParentId, "unknown", pExpr->op);
+ break;
+ }
+ }
+ if( zBinOp ){
+ iNodeId = sqlite3AstOneRowOp(pNodeCnt, iParentId, zBinOp, pExpr);
+ if( pExpr->pLeft ){
+ int id = sqlite3AstOneRowNull(pNodeCnt, iNodeId, "left");
+ sqlite3AstExpr(pExpr->pLeft, pNodeCnt, id);
+ }
+ if( pExpr->pRight ){
+ int id = sqlite3AstOneRowNull(pNodeCnt, iNodeId, "right");
+ sqlite3AstExpr(pExpr->pRight, pNodeCnt, id);
+ }
+ }
+ if( zUniOp ){
+ iNodeId = sqlite3AstOneRowOp(pNodeCnt, iParentId, zUniOp, pExpr);
+ if( pExpr->pLeft ){
+ sqlite3AstExpr(pExpr->pLeft, pNodeCnt, iNodeId);
+ }
+ }
+}
+
+void sqlite3AstExprList(
+ const ExprList *pList,
+ int *pNodeCnt,
+ int iParentId,
+ int showSortOrder
+){
+ int i;
+ if( pList==0 ) return;
+ for(i=0; i<pList->nExpr; i++){
+ const struct ExprList_item *pItem = pList->a + i;
+ int id = sqlite3AstOneRowNull(pNodeCnt, iParentId, "listitem");
+ if( pItem->zName && pItem->zName[0] ){
+ sqlite3AstOneRowText(pNodeCnt, id, "name", pItem->zName);
+ }
+ if( showSortOrder && pItem->sortOrder ){
+ sqlite3AstOneRowInt(pNodeCnt, id, "desc", 1);
+ }
+ sqlite3AstExpr(pList->a[i].pExpr, pNodeCnt, id);
+ }
+}
+
+
+/*
+** Generate a human-readable description of a WITH clause.
+*/
+void sqlite3AstWith(const With *pWith, int *pNodeCnt, int iParentId){
+ int i;
+ if( pWith==0 ) return;
+ if( pWith->nCte==0 ) return;
+ if( pWith && pWith->nCte>0 ){
+ int iNodeId = sqlite3AstOneRowNull(pNodeCnt, iParentId, "WITH");
+ for(i=0; i<pWith->nCte; i++){
+ const struct Cte *pCte = &pWith->a[i];
+ int id = sqlite3AstOneRowNull(pNodeCnt, iNodeId, "with-cte");
+ sqlite3AstOneRowText(pNodeCnt, id, "name", pCte->zName);
+ if( pCte->pCols && pCte->pCols->nExpr>0 ){
+ int x = sqlite3AstOneRowNull(pNodeCnt, iNodeId, "column-list");
+ sqlite3AstExprList(pCte->pCols, pNodeCnt, x, 0);
+ }
+ sqlite3AstSelect(pCte->pSelect, pNodeCnt, iNodeId);
+ }
+ }
+}
+
+
+/*
+** Generate a human-readable description of a Select object.
+*/
+void sqlite3AstSelect(const Select *p, int *pNodeCnt, int iParentId){
+ if( p==0 ) return;
+ if( p->pWith ){
+ sqlite3AstWith(p->pWith, pNodeCnt, iParentId);
+ }
+ do{
+ int iNodeId = sqlite3AstOneRowNull(pNodeCnt, iParentId, "SELECT");
+ int id = sqlite3AstOneRowNull(pNodeCnt, iNodeId, "result-set");
+ sqlite3AstExprList(p->pEList, pNodeCnt, id, 0);
+ if( p->pSrc && p->pSrc->nSrc ){
+ int i;
+ for(i=0; i<p->pSrc->nSrc; i++){
+ struct SrcList_item *pItem = &p->pSrc->a[i];
+ int iFrom = sqlite3AstOneRowInt(pNodeCnt, iNodeId,
+ "from-item", pItem->iCursor);
+ if( pItem->zDatabase ){
+ sqlite3AstOneRowText(pNodeCnt, iFrom, "database", pItem->zDatabase);
+ }
+ if( pItem->zName ){
+ sqlite3AstOneRowText(pNodeCnt, iFrom, "name", pItem->zName);
+ }
+ if( pItem->pTab ){
+ sqlite3AstOneRowText(pNodeCnt, iFrom, "tabname", pItem->pTab->zName);
+ }
+ if( pItem->zAlias ){
+ sqlite3AstOneRowText(pNodeCnt, iFrom, "as", pItem->zAlias);
+ }
+ if( pItem->fg.jointype & JT_LEFT ){
+ sqlite3AstOneRowInt(pNodeCnt, iFrom, "left-join", 1);
+ }
+ if( pItem->pSelect ){
+ sqlite3AstSelect(pItem->pSelect, pNodeCnt, iFrom);
+ }
+ if( pItem->fg.isTabFunc ){
+ int x = sqlite3AstOneRowNull(pNodeCnt, iFrom, "func-args");
+ sqlite3AstExprList(pItem->u1.pFuncArg, pNodeCnt, x, 0);
+ }
+ }
+ }
+ if( p->pWhere ){
+ id = sqlite3AstOneRowNull(pNodeCnt, iNodeId, "where");
+ sqlite3AstExpr(p->pWhere, pNodeCnt, id);
+ }
+ if( p->pGroupBy ){
+ id = sqlite3AstOneRowNull(pNodeCnt, iNodeId, "groupby");
+ sqlite3AstExprList(p->pGroupBy, pNodeCnt, id, 0);
+ }
+ if( p->pHaving ){
+ id = sqlite3AstOneRowNull(pNodeCnt, iNodeId, "having");
+ sqlite3AstExpr(p->pHaving, pNodeCnt, id);
+ }
+ if( p->pOrderBy ){
+ id = sqlite3AstOneRowNull(pNodeCnt, iNodeId, "orderby");
+ sqlite3AstExprList(p->pOrderBy, pNodeCnt, id, 1);
+ }
+ if( p->pLimit ){
+ id = sqlite3AstOneRowNull(pNodeCnt, iNodeId, "limit");
+ sqlite3AstExpr(p->pLimit->pLeft, pNodeCnt, id);
+ if( p->pLimit->pRight ){
+ id = sqlite3AstOneRowNull(pNodeCnt, iNodeId, "offset");
+ sqlite3AstExpr(p->pLimit->pRight, pNodeCnt, id);
+ }
+ }
+ if( p->pPrior ){
+ const char *zOp = "UNION";
+ switch( p->op ){
+ case TK_ALL: zOp = "UNION ALL"; break;
+ case TK_INTERSECT: zOp = "INTERSECT"; break;
+ case TK_EXCEPT: zOp = "EXCEPT"; break;
+ }
+ iParentId = sqlite3AstOneRowNull(pNodeCnt, iNodeId, zOp);
+ }
+ p = p->pPrior;
+ }while( p!=0 );
+}
+
#endif /* SQLITE_DEBUG */