diff options
author | Tom Lane <tgl@sss.pgh.pa.us> | 2014-04-30 13:26:26 -0400 |
---|---|---|
committer | Tom Lane <tgl@sss.pgh.pa.us> | 2014-04-30 13:26:26 -0400 |
commit | 41de93c53aa941167d445ecb7d91d58829adcc92 (patch) | |
tree | d0885b5216a00a4d89805c49fc3ecaecfdeb93cf /src/backend/utils/adt/ruleutils.c | |
parent | 0601cb54dac14d979d726ab2ebeda251ae36e857 (diff) | |
download | postgresql-41de93c53aa941167d445ecb7d91d58829adcc92.tar.gz postgresql-41de93c53aa941167d445ecb7d91d58829adcc92.zip |
Reduce indentation/parenthesization of set operations in rule/view dumps.
A query such as "SELECT x UNION SELECT y UNION SELECT z UNION ..."
produces a left-deep nested parse tree, which we formerly showed in its
full nested glory and with all the possible parentheses. This does little
for readability, though, and long UNION lists resulting in excessive
indentation are common. Instead, let's omit parentheses and indent all
the subqueries at the same level in such cases.
This patch skips indentation/parenthesization whenever the lefthand input
of a SetOperationStmt is another SetOperationStmt of the same kind and
ALL/DISTINCT property. We could teach the code the exact syntactic
precedence of set operations and thereby avoid parenthesization in some
more cases, but it's not clear that that'd be a readability win: it seems
better to parenthesize if the set operation changes. (As an example,
if there's one UNION in a long list of UNION ALL, it now stands out like
a sore thumb, which seems like a good thing.)
Back-patch to 9.3. This completes our response to a complaint from Greg
Stark that since commit 62e666400d there's a performance problem in pg_dump
for views containing long UNION sequences (or other types of deeply nested
constructs). The previous commit 0601cb54dac14d979d726ab2ebeda251ae36e857
handles the general problem, but this one makes the specific case of UNION
lists look a lot nicer.
Diffstat (limited to 'src/backend/utils/adt/ruleutils.c')
-rw-r--r-- | src/backend/utils/adt/ruleutils.c | 81 |
1 files changed, 54 insertions, 27 deletions
diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c index 5ed762cddb6..957f0fc7d9f 100644 --- a/src/backend/utils/adt/ruleutils.c +++ b/src/backend/utils/adt/ruleutils.c @@ -4714,42 +4714,59 @@ get_setop_query(Node *setOp, Query *query, deparse_context *context, else if (IsA(setOp, SetOperationStmt)) { SetOperationStmt *op = (SetOperationStmt *) setOp; - - if (PRETTY_INDENT(context)) - { - context->indentLevel += PRETTYINDENT_STD; - appendStringInfoSpaces(buf, PRETTYINDENT_STD); - } + int subindent; /* - * We force parens whenever nesting two SetOperationStmts. There are - * some cases in which parens are needed around a leaf query too, but - * those are more easily handled at the next level down (see code - * above). + * We force parens when nesting two SetOperationStmts, except when the + * lefthand input is another setop of the same kind. Syntactically, + * we could omit parens in rather more cases, but it seems best to use + * parens to flag cases where the setop operator changes. If we use + * parens, we also increase the indentation level for the child query. + * + * There are some cases in which parens are needed around a leaf query + * too, but those are more easily handled at the next level down (see + * code above). */ - need_paren = !IsA(op->larg, RangeTblRef); + if (IsA(op->larg, SetOperationStmt)) + { + SetOperationStmt *lop = (SetOperationStmt *) op->larg; + + if (op->op == lop->op && op->all == lop->all) + need_paren = false; + else + need_paren = true; + } + else + need_paren = false; if (need_paren) + { appendStringInfoChar(buf, '('); + subindent = PRETTYINDENT_STD; + appendContextKeyword(context, "", subindent, 0, 0); + } + else + subindent = 0; + get_setop_query(op->larg, query, context, resultDesc); - if (need_paren) - appendStringInfoChar(buf, ')'); - if (!PRETTY_INDENT(context)) + if (need_paren) + appendContextKeyword(context, ") ", -subindent, 0, 0); + else if (PRETTY_INDENT(context)) + appendContextKeyword(context, "", -subindent, 0, 0); + else appendStringInfoChar(buf, ' '); + switch (op->op) { case SETOP_UNION: - appendContextKeyword(context, "UNION ", - -PRETTYINDENT_STD, PRETTYINDENT_STD, 0); + appendStringInfoString(buf, "UNION "); break; case SETOP_INTERSECT: - appendContextKeyword(context, "INTERSECT ", - -PRETTYINDENT_STD, PRETTYINDENT_STD, 0); + appendStringInfoString(buf, "INTERSECT "); break; case SETOP_EXCEPT: - appendContextKeyword(context, "EXCEPT ", - -PRETTYINDENT_STD, PRETTYINDENT_STD, 0); + appendStringInfoString(buf, "EXCEPT "); break; default: elog(ERROR, "unrecognized set op: %d", @@ -4758,19 +4775,29 @@ get_setop_query(Node *setOp, Query *query, deparse_context *context, if (op->all) appendStringInfoString(buf, "ALL "); - if (PRETTY_INDENT(context)) - appendContextKeyword(context, "", 0, 0, 0); - - need_paren = !IsA(op->rarg, RangeTblRef); + /* Always parenthesize if RHS is another setop */ + need_paren = IsA(op->rarg, SetOperationStmt); + /* + * The indentation code here is deliberately a bit different from that + * for the lefthand input, because we want the line breaks in + * different places. + */ if (need_paren) + { appendStringInfoChar(buf, '('); + subindent = PRETTYINDENT_STD; + } + else + subindent = 0; + appendContextKeyword(context, "", subindent, 0, 0); + get_setop_query(op->rarg, query, context, resultDesc); - if (need_paren) - appendStringInfoChar(buf, ')'); if (PRETTY_INDENT(context)) - context->indentLevel -= PRETTYINDENT_STD; + context->indentLevel -= subindent; + if (need_paren) + appendContextKeyword(context, ")", 0, 0, 0); } else { |