aboutsummaryrefslogtreecommitdiff
path: root/src/backend/utils/adt/ruleutils.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/utils/adt/ruleutils.c')
-rw-r--r--src/backend/utils/adt/ruleutils.c212
1 files changed, 190 insertions, 22 deletions
diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c
index 64d0c3a820a..4d0fa04bdf3 100644
--- a/src/backend/utils/adt/ruleutils.c
+++ b/src/backend/utils/adt/ruleutils.c
@@ -3,7 +3,7 @@
* back to source text
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/utils/adt/ruleutils.c,v 1.64 2000/09/29 18:21:37 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/utils/adt/ruleutils.c,v 1.65 2000/10/05 19:11:34 tgl Exp $
*
* This software is copyrighted by Jan Wieck - Hamburg.
*
@@ -97,6 +97,10 @@ static void get_select_query_def(Query *query, deparse_context *context);
static void get_insert_query_def(Query *query, deparse_context *context);
static void get_update_query_def(Query *query, deparse_context *context);
static void get_delete_query_def(Query *query, deparse_context *context);
+static void get_basic_select_query(Query *query, deparse_context *context);
+static void get_setop_query(Node *setOp, Query *query,
+ deparse_context *context, bool toplevel);
+static bool simple_distinct(List *distinctClause, List *targetList);
static RangeTblEntry *get_rte_for_var(Var *var, deparse_context *context);
static void get_rule_expr(Node *node, deparse_context *context);
static void get_func_expr(Expr *expr, deparse_context *context);
@@ -876,6 +880,63 @@ static void
get_select_query_def(Query *query, deparse_context *context)
{
StringInfo buf = context->buf;
+ bool shortform_orderby;
+ char *sep;
+ List *l;
+
+ /* ----------
+ * If the Query node has a setOperations tree, then it's the top
+ * level of a UNION/INTERSECT/EXCEPT query; only the ORDER BY field
+ * is interesting in the top query itself.
+ * ----------
+ */
+ if (query->setOperations)
+ {
+ get_setop_query(query->setOperations, query, context, true);
+ /* ORDER BY clauses must be simple in this case */
+ shortform_orderby = true;
+ }
+ else
+ {
+ get_basic_select_query(query, context);
+ shortform_orderby = false;
+ }
+
+ /* Add the ORDER BY clause if given */
+ if (query->sortClause != NIL)
+ {
+ appendStringInfo(buf, " ORDER BY ");
+ sep = "";
+ foreach(l, query->sortClause)
+ {
+ SortClause *srt = (SortClause *) lfirst(l);
+ TargetEntry *sorttle;
+ char *opname;
+
+ sorttle = get_sortgroupclause_tle(srt,
+ query->targetList);
+ appendStringInfo(buf, sep);
+ if (shortform_orderby)
+ appendStringInfo(buf, "%d", sorttle->resdom->resno);
+ else
+ get_rule_expr(sorttle->expr, context);
+ opname = get_opname(srt->sortop);
+ if (strcmp(opname, "<") != 0)
+ {
+ if (strcmp(opname, ">") == 0)
+ appendStringInfo(buf, " DESC");
+ else
+ appendStringInfo(buf, " USING %s", opname);
+ }
+ sep = ", ";
+ }
+ }
+}
+
+static void
+get_basic_select_query(Query *query, deparse_context *context)
+{
+ StringInfo buf = context->buf;
char *sep;
List *l;
@@ -885,6 +946,32 @@ get_select_query_def(Query *query, deparse_context *context)
*/
appendStringInfo(buf, "SELECT");
+ /* Add the DISTINCT clause if given */
+ if (query->distinctClause != NIL)
+ {
+ if (simple_distinct(query->distinctClause, query->targetList))
+ {
+ appendStringInfo(buf, " DISTINCT");
+ }
+ else
+ {
+ appendStringInfo(buf, " DISTINCT ON (");
+ sep = "";
+ foreach(l, query->distinctClause)
+ {
+ SortClause *srt = (SortClause *) lfirst(l);
+ Node *sortexpr;
+
+ sortexpr = get_sortgroupclause_expr(srt,
+ query->targetList);
+ appendStringInfo(buf, sep);
+ get_rule_expr(sortexpr, context);
+ sep = ", ";
+ }
+ appendStringInfo(buf, ")");
+ }
+ }
+
/* Then we tell what to select (the targetlist) */
sep = " ";
foreach(l, query->targetList)
@@ -931,7 +1018,7 @@ get_select_query_def(Query *query, deparse_context *context)
get_rule_expr(query->jointree->quals, context);
}
- /* Add the GROUP BY CLAUSE */
+ /* Add the GROUP BY clause if given */
if (query->groupClause != NULL)
{
appendStringInfo(buf, " GROUP BY ");
@@ -948,6 +1035,94 @@ get_select_query_def(Query *query, deparse_context *context)
sep = ", ";
}
}
+
+ /* Add the HAVING clause if given */
+ if (query->havingQual != NULL)
+ {
+ appendStringInfo(buf, " HAVING ");
+ get_rule_expr(query->havingQual, context);
+ }
+}
+
+static void
+get_setop_query(Node *setOp, Query *query, deparse_context *context,
+ bool toplevel)
+{
+ StringInfo buf = context->buf;
+
+ if (IsA(setOp, RangeTblRef))
+ {
+ RangeTblRef *rtr = (RangeTblRef *) setOp;
+ RangeTblEntry *rte = rt_fetch(rtr->rtindex, query->rtable);
+ Query *subquery = rte->subquery;
+
+ Assert(subquery != NULL);
+ get_query_def(subquery, buf, context->rangetables);
+ }
+ else if (IsA(setOp, SetOperationStmt))
+ {
+ SetOperationStmt *op = (SetOperationStmt *) setOp;
+
+ /* Must suppress parens at top level of a setop tree because
+ * of grammar limitations...
+ */
+ if (! toplevel)
+ appendStringInfo(buf, "(");
+ get_setop_query(op->larg, query, context, false);
+ switch (op->op)
+ {
+ case SETOP_UNION:
+ appendStringInfo(buf, " UNION ");
+ break;
+ case SETOP_INTERSECT:
+ appendStringInfo(buf, " INTERSECT ");
+ break;
+ case SETOP_EXCEPT:
+ appendStringInfo(buf, " EXCEPT ");
+ break;
+ default:
+ elog(ERROR, "get_setop_query: unexpected set op %d",
+ (int) op->op);
+ }
+ if (op->all)
+ appendStringInfo(buf, "ALL ");
+ get_setop_query(op->rarg, query, context, false);
+ if (! toplevel)
+ appendStringInfo(buf, ")");
+ }
+ else
+ {
+ elog(ERROR, "get_setop_query: unexpected node %d",
+ (int) nodeTag(setOp));
+ }
+}
+
+/*
+ * Detect whether a DISTINCT list can be represented as just DISTINCT
+ * or needs DISTINCT ON. It's simple if it contains exactly the nonjunk
+ * targetlist items.
+ */
+static bool
+simple_distinct(List *distinctClause, List *targetList)
+{
+ while (targetList)
+ {
+ TargetEntry *tle = (TargetEntry *) lfirst(targetList);
+
+ if (! tle->resdom->resjunk)
+ {
+ if (distinctClause == NIL)
+ return false;
+ if (((SortClause *) lfirst(distinctClause))->tleSortGroupRef !=
+ tle->resdom->ressortgroupref)
+ return false;
+ distinctClause = lnext(distinctClause);
+ }
+ targetList = lnext(targetList);
+ }
+ if (distinctClause != NIL)
+ return false;
+ return true;
}
@@ -959,33 +1134,24 @@ static void
get_insert_query_def(Query *query, deparse_context *context)
{
StringInfo buf = context->buf;
- char *sep;
- bool rt_constonly = TRUE;
+ RangeTblEntry *select_rte = NULL;
RangeTblEntry *rte;
- int i;
+ char *sep;
List *l;
/* ----------
- * We need to know if other tables than *NEW* or *OLD*
- * are used in the query. If not, it's an INSERT ... VALUES,
- * otherwise an INSERT ... SELECT. (Pretty klugy ... fix this
- * when we redesign querytrees!)
+ * If it's an INSERT ... SELECT there will be a single subquery RTE
+ * for the SELECT.
* ----------
*/
- i = 0;
foreach(l, query->rtable)
{
rte = (RangeTblEntry *) lfirst(l);
- i++;
- if (strcmp(rte->eref->relname, "*NEW*") == 0)
- continue;
- if (strcmp(rte->eref->relname, "*OLD*") == 0)
+ if (rte->subquery == NULL)
continue;
- if (rangeTableEntry_used((Node *) query, i, 0))
- {
- rt_constonly = FALSE;
- break;
- }
+ if (select_rte)
+ elog(ERROR, "get_insert_query_def: too many RTEs in INSERT!");
+ select_rte = rte;
}
/* ----------
@@ -1012,7 +1178,7 @@ get_insert_query_def(Query *query, deparse_context *context)
appendStringInfo(buf, ") ");
/* Add the VALUES or the SELECT */
- if (rt_constonly && query->jointree->quals == NULL)
+ if (select_rte == NULL)
{
appendStringInfo(buf, "VALUES (");
sep = "";
@@ -1030,7 +1196,9 @@ get_insert_query_def(Query *query, deparse_context *context)
appendStringInfoChar(buf, ')');
}
else
- get_select_query_def(query, context);
+ {
+ get_query_def(select_rte->subquery, buf, NIL);
+ }
}
@@ -1809,7 +1977,7 @@ get_from_clause_item(Node *jtnode, Query *query, deparse_context *context)
/* Subquery RTE */
Assert(rte->subquery != NULL);
appendStringInfoChar(buf, '(');
- get_query_def(rte->subquery, buf, NIL);
+ get_query_def(rte->subquery, buf, context->rangetables);
appendStringInfoChar(buf, ')');
}
if (rte->alias != NULL)