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.c409
1 files changed, 193 insertions, 216 deletions
diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c
index 571854d446c..26ebe21c4a2 100644
--- a/src/backend/utils/adt/ruleutils.c
+++ b/src/backend/utils/adt/ruleutils.c
@@ -1,9 +1,9 @@
/**********************************************************************
- * get_ruledef.c - Function to get a rules definition text
- * out of its tuple
+ * ruleutils.c - Functions to convert stored expressions/querytrees
+ * back to source text
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/utils/adt/ruleutils.c,v 1.60 2000/09/12 04:15:58 momjian Exp $
+ * $Header: /cvsroot/pgsql/src/backend/utils/adt/ruleutils.c,v 1.61 2000/09/12 21:07:05 tgl Exp $
*
* This software is copyrighted by Jan Wieck - Hamburg.
*
@@ -43,6 +43,7 @@
#include "catalog/pg_index.h"
#include "catalog/pg_operator.h"
#include "catalog/pg_shadow.h"
+#include "commands/view.h"
#include "executor/spi.h"
#include "lib/stringinfo.h"
#include "optimizer/clauses.h"
@@ -50,8 +51,8 @@
#include "parser/keywords.h"
#include "parser/parse_expr.h"
#include "parser/parsetree.h"
+#include "rewrite/rewriteManip.h"
#include "utils/lsyscache.h"
-#include "commands/view.h"
/* ----------
@@ -65,12 +66,6 @@ typedef struct
bool varprefix; /* TRUE to print prefixes on Vars */
} deparse_context;
-typedef struct
-{
- Index rt_index;
- int levelsup;
-} check_if_rte_used_context;
-
/* ----------
* Global data
@@ -108,13 +103,13 @@ static void get_func_expr(Expr *expr, deparse_context *context);
static void get_tle_expr(TargetEntry *tle, deparse_context *context);
static void get_const_expr(Const *constval, deparse_context *context);
static void get_sublink_expr(Node *node, deparse_context *context);
+static void get_from_clause(Query *query, deparse_context *context);
+static void get_from_clause_item(Node *jtnode, Query *query,
+ deparse_context *context);
static bool tleIsArrayAssign(TargetEntry *tle);
static char *quote_identifier(char *ident);
static char *get_relation_name(Oid relid);
static char *get_attribute_name(Oid relid, int2 attnum);
-static bool check_if_rte_used(Node *node, Index rt_index, int levelsup);
-static bool check_if_rte_used_walker(Node *node,
- check_if_rte_used_context *context);
#define only_marker(rte) ((rte)->inh ? "" : "ONLY ")
@@ -230,13 +225,13 @@ pg_get_viewdef(PG_FUNCTION_ARGS)
Name vname = PG_GETARG_NAME(0);
text *ruledef;
Datum args[1];
- char nulls[2];
+ char nulls[1];
int spirc;
HeapTuple ruletup;
TupleDesc rulettc;
StringInfoData buf;
int len;
- char *name;
+ char *name;
/* ----------
* We need the view name somewhere deep down
@@ -276,7 +271,6 @@ pg_get_viewdef(PG_FUNCTION_ARGS)
name = MakeRetrieveViewRuleName(rulename);
args[0] = PointerGetDatum(name);
nulls[0] = ' ';
- nulls[1] = '\0';
spirc = SPI_execp(plan_getview, args, nulls, 1);
if (spirc != SPI_OK_SELECT)
elog(ERROR, "failed to get pg_rewrite tuple for view %s", rulename);
@@ -883,61 +877,9 @@ get_select_query_def(Query *query, deparse_context *context)
{
StringInfo buf = context->buf;
char *sep;
- TargetEntry *tle;
- RangeTblEntry *rte;
- bool *rt_used;
- int rt_length;
- int rt_numused = 0;
- bool rt_constonly = TRUE;
- int i;
List *l;
/* ----------
- * First we need to know which and how many of the
- * range table entries in the query are used in the target list
- * or queries qualification
- * ----------
- */
- rt_length = length(query->rtable);
- rt_used = palloc(sizeof(bool) * rt_length);
- for (i = 0; i < rt_length; i++)
- {
- if (check_if_rte_used((Node *) (query->targetList), i + 1, 0) ||
- check_if_rte_used(query->qual, i + 1, 0) ||
- check_if_rte_used(query->havingQual, i + 1, 0))
- {
- rt_used[i] = TRUE;
- rt_numused++;
- }
- else
- rt_used[i] = FALSE;
- }
-
- /* ----------
- * Now check if any of the used rangetable entries is different
- * from *NEW* and *OLD*. If so we must provide the FROM clause
- * later.
- * ----------
- */
- i = 0;
- foreach(l, query->rtable)
- {
- if (!rt_used[i++])
- continue;
-
- rte = (RangeTblEntry *) lfirst(l);
- if (rte->ref == NULL)
- continue;
- if (strcmp(rte->ref->relname, "*NEW*") == 0)
- continue;
- if (strcmp(rte->ref->relname, "*OLD*") == 0)
- continue;
-
- rt_constonly = FALSE;
- break;
- }
-
- /* ----------
* Build up the query string - first we say SELECT
* ----------
*/
@@ -947,9 +889,9 @@ get_select_query_def(Query *query, deparse_context *context)
sep = " ";
foreach(l, query->targetList)
{
+ TargetEntry *tle = (TargetEntry *) lfirst(l);
bool tell_as = false;
- tle = (TargetEntry *) lfirst(l);
appendStringInfo(buf, sep);
sep = ", ";
@@ -962,6 +904,7 @@ get_select_query_def(Query *query, deparse_context *context)
else
{
Var *var = (Var *) (tle->expr);
+ RangeTblEntry *rte;
char *attname;
rte = get_rte_for_var(var, context);
@@ -975,60 +918,8 @@ get_select_query_def(Query *query, deparse_context *context)
quote_identifier(tle->resdom->resname));
}
- /* If we need other tables than *NEW* or *OLD* add the FROM clause */
- if (!rt_constonly && rt_numused > 0)
- {
- sep = " FROM ";
- i = 0;
- foreach(l, query->rtable)
- {
- if (rt_used[i++])
- {
- rte = (RangeTblEntry *) lfirst(l);
-
- if (rte->ref == NULL)
- continue;
- if (strcmp(rte->ref->relname, "*NEW*") == 0)
- continue;
- if (strcmp(rte->ref->relname, "*OLD*") == 0)
- continue;
-
- appendStringInfo(buf, sep);
- sep = ", ";
- appendStringInfo(buf, "%s%s",
- only_marker(rte),
- quote_identifier(rte->relname));
-
- /*
- * NOTE: SQL92 says you can't write column aliases unless
- * you write a table alias --- so, if there's an alias
- * list, make sure we emit a table alias even if it's the
- * same as the table's real name.
- */
- if ((rte->ref != NULL)
- && ((strcmp(rte->relname, rte->ref->relname) != 0)
- || (rte->ref->attrs != NIL)))
- {
- appendStringInfo(buf, " %s",
- quote_identifier(rte->ref->relname));
- if (rte->ref->attrs != NIL)
- {
- List *col;
-
- appendStringInfo(buf, " (");
- foreach(col, rte->ref->attrs)
- {
- if (col != rte->ref->attrs)
- appendStringInfo(buf, ", ");
- appendStringInfo(buf, "%s",
- quote_identifier(strVal(lfirst(col))));
- }
- appendStringInfoChar(buf, ')');
- }
- }
- }
- }
- }
+ /* Add the FROM clause if needed */
+ get_from_clause(query, context);
/* Add the WHERE clause if given */
if (query->qual != NULL)
@@ -1066,52 +957,32 @@ get_insert_query_def(Query *query, deparse_context *context)
{
StringInfo buf = context->buf;
char *sep;
- TargetEntry *tle;
- RangeTblEntry *rte;
- bool *rt_used;
- int rt_length;
- int rt_numused = 0;
bool rt_constonly = TRUE;
+ RangeTblEntry *rte;
int i;
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.
+ * otherwise an INSERT ... SELECT. (Pretty klugy ... fix this
+ * when we redesign querytrees!)
* ----------
*/
- rt_length = length(query->rtable);
- rt_used = palloc(sizeof(bool) * rt_length);
- for (i = 0; i < rt_length; i++)
- {
- if (check_if_rte_used((Node *) (query->targetList), i + 1, 0) ||
- check_if_rte_used(query->qual, i + 1, 0) ||
- check_if_rte_used(query->havingQual, i + 1, 0))
- {
- rt_used[i] = TRUE;
- rt_numused++;
- }
- else
- rt_used[i] = FALSE;
- }
-
i = 0;
foreach(l, query->rtable)
{
- if (!rt_used[i++])
- continue;
-
rte = (RangeTblEntry *) lfirst(l);
- if (rte->ref == NULL)
- continue;
- if (strcmp(rte->ref->relname, "*NEW*") == 0)
+ i++;
+ if (strcmp(rte->eref->relname, "*NEW*") == 0)
continue;
- if (strcmp(rte->ref->relname, "*OLD*") == 0)
+ if (strcmp(rte->eref->relname, "*OLD*") == 0)
continue;
-
- rt_constonly = FALSE;
- break;
+ if (rangeTableEntry_used((Node *) query, i, 0))
+ {
+ rt_constonly = FALSE;
+ break;
+ }
}
/* ----------
@@ -1122,11 +993,11 @@ get_insert_query_def(Query *query, deparse_context *context)
appendStringInfo(buf, "INSERT INTO %s",
quote_identifier(rte->relname));
- /* Add the target list */
+ /* Add the insert-column-names list */
sep = " (";
foreach(l, query->targetList)
{
- tle = (TargetEntry *) lfirst(l);
+ TargetEntry *tle = (TargetEntry *) lfirst(l);
appendStringInfo(buf, sep);
sep = ", ";
@@ -1141,7 +1012,7 @@ get_insert_query_def(Query *query, deparse_context *context)
sep = "";
foreach(l, query->targetList)
{
- tle = (TargetEntry *) lfirst(l);
+ TargetEntry *tle = (TargetEntry *) lfirst(l);
appendStringInfo(buf, sep);
sep = ", ";
@@ -1195,6 +1066,9 @@ get_update_query_def(Query *query, deparse_context *context)
get_tle_expr(tle, context);
}
+ /* Add the FROM clause if needed */
+ get_from_clause(query, context);
+
/* Finally add a WHERE clause if given */
if (query->qual != NULL)
{
@@ -1281,16 +1155,13 @@ get_rule_expr(Node *node, deparse_context *context)
if (context->varprefix)
{
- if (rte->ref == NULL)
- appendStringInfo(buf, "%s.",
- quote_identifier(rte->relname));
- else if (strcmp(rte->ref->relname, "*NEW*") == 0)
+ if (strcmp(rte->eref->relname, "*NEW*") == 0)
appendStringInfo(buf, "new.");
- else if (strcmp(rte->ref->relname, "*OLD*") == 0)
+ else if (strcmp(rte->eref->relname, "*OLD*") == 0)
appendStringInfo(buf, "old.");
else
appendStringInfo(buf, "%s.",
- quote_identifier(rte->ref->relname));
+ quote_identifier(rte->eref->relname));
}
appendStringInfo(buf, "%s",
quote_identifier(get_attribute_name(rte->relid,
@@ -1860,6 +1731,165 @@ get_sublink_expr(Node *node, deparse_context *context)
appendStringInfoChar(buf, ')');
}
+
+/* ----------
+ * get_from_clause - Parse back a FROM clause
+ * ----------
+ */
+static void
+get_from_clause(Query *query, deparse_context *context)
+{
+ StringInfo buf = context->buf;
+ char *sep;
+ List *l;
+
+ /*
+ * We use the query's jointree as a guide to what to print. However,
+ * we must ignore auto-added RTEs that are marked not inFromCl.
+ * Also ignore the rule pseudo-RTEs for NEW and OLD.
+ */
+ sep = " FROM ";
+
+ foreach(l, query->jointree)
+ {
+ Node *jtnode = (Node *) lfirst(l);
+
+ if (IsA(jtnode, RangeTblRef))
+ {
+ int varno = ((RangeTblRef *) jtnode)->rtindex;
+ RangeTblEntry *rte = rt_fetch(varno, query->rtable);
+
+ if (!rte->inFromCl)
+ continue;
+ if (strcmp(rte->eref->relname, "*NEW*") == 0)
+ continue;
+ if (strcmp(rte->eref->relname, "*OLD*") == 0)
+ continue;
+ }
+
+ appendStringInfo(buf, sep);
+ get_from_clause_item(jtnode, query, context);
+ sep = ", ";
+ }
+}
+
+static void
+get_from_clause_item(Node *jtnode, Query *query, deparse_context *context)
+{
+ StringInfo buf = context->buf;
+
+ if (IsA(jtnode, RangeTblRef))
+ {
+ int varno = ((RangeTblRef *) jtnode)->rtindex;
+ RangeTblEntry *rte = rt_fetch(varno, query->rtable);
+
+ appendStringInfo(buf, "%s%s",
+ only_marker(rte),
+ quote_identifier(rte->relname));
+ if (rte->alias != NULL)
+ {
+ appendStringInfo(buf, " %s",
+ quote_identifier(rte->alias->relname));
+ if (rte->alias->attrs != NIL)
+ {
+ List *col;
+
+ appendStringInfo(buf, " (");
+ foreach(col, rte->alias->attrs)
+ {
+ if (col != rte->alias->attrs)
+ appendStringInfo(buf, ", ");
+ appendStringInfo(buf, "%s",
+ quote_identifier(strVal(lfirst(col))));
+ }
+ appendStringInfoChar(buf, ')');
+ }
+ }
+ }
+ else if (IsA(jtnode, JoinExpr))
+ {
+ JoinExpr *j = (JoinExpr *) jtnode;
+
+ appendStringInfoChar(buf, '(');
+ get_from_clause_item(j->larg, query, context);
+ if (j->isNatural)
+ appendStringInfo(buf, " NATURAL");
+ switch (j->jointype)
+ {
+ case JOIN_INNER:
+ if (j->quals)
+ appendStringInfo(buf, " JOIN ");
+ else
+ appendStringInfo(buf, " CROSS JOIN ");
+ break;
+ case JOIN_LEFT:
+ appendStringInfo(buf, " LEFT JOIN ");
+ break;
+ case JOIN_FULL:
+ appendStringInfo(buf, " FULL JOIN ");
+ break;
+ case JOIN_RIGHT:
+ appendStringInfo(buf, " RIGHT JOIN ");
+ break;
+ case JOIN_UNION:
+ appendStringInfo(buf, " UNION JOIN ");
+ break;
+ default:
+ elog(ERROR, "get_from_clause_item: unknown join type %d",
+ (int) j->jointype);
+ }
+ get_from_clause_item(j->rarg, query, context);
+ if (! j->isNatural)
+ {
+ if (j->using)
+ {
+ List *col;
+
+ appendStringInfo(buf, " USING (");
+ foreach(col, j->using)
+ {
+ if (col != j->using)
+ appendStringInfo(buf, ", ");
+ appendStringInfo(buf, "%s",
+ quote_identifier(strVal(lfirst(col))));
+ }
+ appendStringInfoChar(buf, ')');
+ }
+ else if (j->quals)
+ {
+ appendStringInfo(buf, " ON (");
+ get_rule_expr(j->quals, context);
+ appendStringInfoChar(buf, ')');
+ }
+ }
+ appendStringInfoChar(buf, ')');
+ /* Yes, it's correct to put alias after the right paren ... */
+ if (j->alias != NULL)
+ {
+ appendStringInfo(buf, " %s",
+ quote_identifier(j->alias->relname));
+ if (j->alias->attrs != NIL)
+ {
+ List *col;
+
+ appendStringInfo(buf, " (");
+ foreach(col, j->alias->attrs)
+ {
+ if (col != j->alias->attrs)
+ appendStringInfo(buf, ", ");
+ appendStringInfo(buf, "%s",
+ quote_identifier(strVal(lfirst(col))));
+ }
+ appendStringInfoChar(buf, ')');
+ }
+ }
+ }
+ else
+ elog(ERROR, "get_from_clause_item: unexpected node type %d",
+ nodeTag(jtnode));
+}
+
+
/* ----------
* tleIsArrayAssign - check for array assignment
* ----------
@@ -1990,56 +2020,3 @@ get_attribute_name(Oid relid, int2 attnum)
attStruct = (Form_pg_attribute) GETSTRUCT(atttup);
return pstrdup(NameStr(attStruct->attname));
}
-
-
-/* ----------
- * check_if_rte_used
- * Check a targetlist or qual to see if a given rangetable entry
- * is used in it
- * ----------
- */
-static bool
-check_if_rte_used(Node *node, Index rt_index, int levelsup)
-{
- check_if_rte_used_context context;
-
- context.rt_index = rt_index;
- context.levelsup = levelsup;
- return check_if_rte_used_walker(node, &context);
-}
-
-static bool
-check_if_rte_used_walker(Node *node,
- check_if_rte_used_context *context)
-{
- if (node == NULL)
- return false;
- if (IsA(node, Var))
- {
- Var *var = (Var *) node;
-
- return var->varno == context->rt_index &&
- var->varlevelsup == context->levelsup;
- }
- if (IsA(node, SubLink))
- {
- SubLink *sublink = (SubLink *) node;
- Query *query = (Query *) sublink->subselect;
-
- /* Recurse into subquery; expression_tree_walker will not */
- if (check_if_rte_used((Node *) (query->targetList),
- context->rt_index, context->levelsup + 1) ||
- check_if_rte_used(query->qual,
- context->rt_index, context->levelsup + 1) ||
- check_if_rte_used(query->havingQual,
- context->rt_index, context->levelsup + 1))
- return true;
-
- /*
- * fall through to let expression_tree_walker examine lefthand
- * args
- */
- }
- return expression_tree_walker(node, check_if_rte_used_walker,
- (void *) context);
-}