From 0b69d8a27c1aedee7e56e6d5a14fe9a7e5c0cda8 Mon Sep 17 00:00:00 2001 From: Tom Lane Date: Sun, 9 May 1999 23:31:47 +0000 Subject: [PATCH] Rearrange top-level rewrite operations so that EXPLAIN works on queries involving UNION, EXCEPT, INTERSECT. --- src/backend/commands/explain.c | 19 ++-- src/backend/rewrite/rewriteHandler.c | 63 +++++++++-- src/backend/tcop/postgres.c | 149 +++++++++++---------------- 3 files changed, 125 insertions(+), 106 deletions(-) diff --git a/src/backend/commands/explain.c b/src/backend/commands/explain.c index a26579270c..6a04417964 100644 --- a/src/backend/commands/explain.c +++ b/src/backend/commands/explain.c @@ -4,7 +4,7 @@ * * Copyright (c) 1994-5, Regents of the University of California * - * $Id: explain.c,v 1.35 1999/04/25 03:19:09 tgl Exp $ + * $Id: explain.c,v 1.36 1999/05/09 23:31:45 tgl Exp $ * */ #include @@ -49,15 +49,18 @@ ExplainQuery(Query *query, bool verbose, CommandDest dest) List *rewritten; List *l; + /* rewriter and planner may not work in aborted state? */ if (IsAbortedTransactionBlockState()) { - char *tag = "*ABORT STATE*"; - - EndCommand(tag, dest); - elog(NOTICE, "(transaction aborted): %s", "queries ignored until END"); + return; + } + /* rewriter and planner will not cope with utility statements */ + if (query->commandType == CMD_UTILITY) + { + elog(NOTICE, "Utility statements have no plan structure"); return; } @@ -67,7 +70,7 @@ ExplainQuery(Query *query, bool verbose, CommandDest dest) /* In the case of an INSTEAD NOTHING, tell at least that */ if (rewritten == NIL) { - elog(NOTICE, "query rewrites to nothing"); + elog(NOTICE, "Query rewrites to nothing"); return; } @@ -88,7 +91,7 @@ ExplainOneQuery(Query *query, bool verbose, CommandDest dest) Plan *plan; ExplainState *es; - /* plan the queries (XXX we've ignored rewrite!!) */ + /* plan the query */ plan = planner(query); /* pg_plan could have failed */ @@ -195,7 +198,7 @@ explain_outNode(StringInfo str, Plan *plan, int indent, ExplainState *es) pname = "Hash"; break; default: - pname = ""; + pname = "???"; break; } diff --git a/src/backend/rewrite/rewriteHandler.c b/src/backend/rewrite/rewriteHandler.c index a9ff8a0a3b..1ba1a5dd56 100644 --- a/src/backend/rewrite/rewriteHandler.c +++ b/src/backend/rewrite/rewriteHandler.c @@ -6,7 +6,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/rewrite/rewriteHandler.c,v 1.37 1999/02/22 05:26:46 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/rewrite/rewriteHandler.c,v 1.38 1999/05/09 23:31:46 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -59,8 +59,6 @@ static void modifyAggrefChangeVarnodes(Node **nodePtr, int rt_index, int new_ind static void modifyAggrefDropQual(Node **nodePtr, Node *orignode, Expr *expr); static SubLink *modifyAggrefMakeSublink(Expr *origexp, Query *parsetree); static void modifyAggrefQual(Node **nodePtr, Query *parsetree); - - static Query *fireRIRrules(Query *parsetree); @@ -2634,12 +2632,12 @@ RewritePreprocessQuery(Query *parsetree) /* - * QueryRewrite - + * BasicQueryRewrite - * rewrite one query via query rewrite system, possibly returning 0 * or many queries */ -List * -QueryRewrite(Query *parsetree) +static List * +BasicQueryRewrite(Query *parsetree) { List *querylist; List *results = NIL; @@ -2672,10 +2670,57 @@ QueryRewrite(Query *parsetree) } return results; } -/***S*I***/ -/* This function takes two targetlists as arguments and checks if the targetlists are compatible - * (i.e. both select for the same number of attributes and the types are compatible + +/* + * QueryRewrite - + * Primary entry point to the query rewriter. + * Rewrite one query via query rewrite system, possibly returning 0 + * or many queries. + * + * NOTE: The code in QueryRewrite was formerly in pg_parse_and_plan(), and was + * moved here so that it would be invoked during EXPLAIN. The division of + * labor between this routine and BasicQueryRewrite is not obviously correct + * ... at least not to me ... tgl 5/99. */ +List * +QueryRewrite(Query *parsetree) +{ + List *rewritten, + *rewritten_item; + + /***S*I***/ + /* Rewrite Union, Intersect and Except Queries + * to normal Union Queries using IN and NOT IN subselects */ + if (parsetree->intersectClause) + parsetree = Except_Intersect_Rewrite(parsetree); + + /* Rewrite basic queries (retrieve, append, delete, replace) */ + rewritten = BasicQueryRewrite(parsetree); + + /* + * Rewrite the UNIONS. + */ + foreach (rewritten_item, rewritten) + { + Query *qry = (Query *) lfirst(rewritten_item); + List *union_result = NIL; + List *union_item; + + foreach (union_item, qry->unionClause) + { + union_result = nconc(union_result, + BasicQueryRewrite((Query *) lfirst(union_item))); + } + qry->unionClause = union_result; + } + + return rewritten; +} + +/***S*I***/ +/* This function takes two targetlists as arguments and checks if the + * targetlists are compatible (i.e. both select for the same number of + * attributes and the types are compatible */ void check_targetlists_are_compatible(List *prev_target, List *current_target) { List *next_target; diff --git a/src/backend/tcop/postgres.c b/src/backend/tcop/postgres.c index f196e51eaf..24b2303ee9 100644 --- a/src/backend/tcop/postgres.c +++ b/src/backend/tcop/postgres.c @@ -7,7 +7,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/tcop/postgres.c,v 1.110 1999/05/03 19:09:54 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/tcop/postgres.c,v 1.111 1999/05/09 23:31:47 tgl Exp $ * * NOTES * this is the "main" module of the postgres backend and @@ -399,6 +399,49 @@ pg_parse_and_plan(char *query_string, /* string to execute */ List *rewritten = NIL; Query *querytree; + if (DebugPrintQuery) + { + if (DebugPrintQuery > 3) + { + /* Print the query string as is if query debug level > 3 */ + TPRINTF(TRACE_QUERY, "query: %s", query_string); + } + else + { + /* Print condensed query string to fit in one log line */ + char buff[MAX_QUERY_SIZE + 1]; + char c, + *s, + *d; + int n, + is_space = 1; + + for (s = query_string, d = buff, n = 0; (c = *s) && (n < MAX_QUERY_SIZE); s++) + { + switch (c) + { + case '\r': + case '\n': + case '\t': + c = ' '; + /* fall through */ + case ' ': + if (is_space) + continue; + is_space = 1; + break; + default: + is_space = 0; + break; + } + *d++ = c; + n++; + } + *d = '\0'; + TPRINTF(TRACE_QUERY, "query: %s", buff); + } + } + /* ---------------- * (1) parse the request string into a list of parse trees * ---------------- @@ -421,84 +464,30 @@ pg_parse_and_plan(char *query_string, /* string to execute */ /* ---------------- * (2) rewrite the queries, as necessary + * + * j counts queries output into new_list; the number of rewritten + * queries can be different from the original number. * ---------------- */ - j = 0; /* counter for the new_list, new_list can - * be longer than old list as a result of - * rewrites */ + j = 0; for (i = 0; i < querytree_list->len; i++) { - List *union_result, - *union_list, - *rewritten_list; - querytree = querytree_list->qtrees[i]; - /***S*I***/ - /* Rewrite Union, Intersect and Except Queries - * to normal Union Queries using IN and NOT IN subselects */ - if(querytree->intersectClause != NIL) - { - querytree = Except_Intersect_Rewrite(querytree); - } - - if (DebugPrintQuery) - { - if (DebugPrintQuery > 3) - { - /* Print the query string as is if query debug level > 3 */ - TPRINTF(TRACE_QUERY, "query: %s", query_string); - } - else - { - /* Print condensed query string to fit in one log line */ - char buff[MAX_QUERY_SIZE + 1]; - char c, - *s, - *d; - int n, - is_space = 1; - - for (s = query_string, d = buff, n = 0; (c = *s) && (n < MAX_QUERY_SIZE); s++) - { - switch (c) - { - case '\r': - case '\n': - case '\t': - c = ' '; - /* fall through */ - case ' ': - if (is_space) - continue; - is_space = 1; - break; - default: - is_space = 0; - break; - } - *d++ = c; - n++; - } - *d = '\0'; - TPRINTF(TRACE_QUERY, "query: %s", buff); - } - } - - /* don't rewrite utilites */ - if (querytree->commandType == CMD_UTILITY) - { - new_list->qtrees[j++] = querytree; - continue; - } - if (DebugPrintParse) { TPRINTF(TRACE_PARSE, "parser outputs:"); nodeDisplay(querytree); } - /* rewrite queries (retrieve, append, delete, replace) */ + /* don't rewrite utilites, just dump 'em into new_list */ + if (querytree->commandType == CMD_UTILITY) + { + new_list->qtrees[j++] = querytree; + continue; + } + + /* rewrite regular queries */ rewritten = QueryRewrite(querytree); if (rewritten != NIL) @@ -506,19 +495,6 @@ pg_parse_and_plan(char *query_string, /* string to execute */ int len, k; - /* - * Rewrite the UNIONS. - */ - foreach(rewritten_list, rewritten) - { - Query *qry = (Query *) lfirst(rewritten_list); - - union_result = NIL; - foreach(union_list, qry->unionClause) - union_result = nconc(union_result, QueryRewrite((Query *) lfirst(union_list))); - qry->unionClause = union_result; - } - len = length(rewritten); if (len == 1) new_list->qtrees[j++] = (Query *) lfirst(rewritten); @@ -530,19 +506,14 @@ pg_parse_and_plan(char *query_string, /* string to execute */ * we allocated one space * for the query */ new_list->qtrees = realloc(new_list->qtrees, - new_list->len * sizeof(Query *)); + new_list->len * sizeof(Query *)); for (k = 0; k < len; k++) new_list->qtrees[j++] = (Query *) nth(k, rewritten); } } } - /* ---------- - * Due to rewriting, the new list could also have been - * shrunk (do instead nothing). Forget obsolete queries - * at the end. - * ---------- - */ + /* Update new_list with correct final length */ new_list->len = j; /* we're done with the original lists, free it */ @@ -657,7 +628,7 @@ pg_parse_and_plan(char *query_string, /* string to execute */ } /* ---------- - * Check if the rewriting had thrown away anything + * Check if the rewriting had thrown away everything * ---------- */ if (querytree_list->len == 0) @@ -1539,7 +1510,7 @@ PostgresMain(int argc, char *argv[], int real_argc, char *real_argv[]) if (!IsUnderPostmaster) { puts("\nPOSTGRES backend interactive interface "); - puts("$Revision: 1.110 $ $Date: 1999/05/03 19:09:54 $\n"); + puts("$Revision: 1.111 $ $Date: 1999/05/09 23:31:47 $\n"); } /* ----------------