aboutsummaryrefslogtreecommitdiff
path: root/src/backend/parser/analyze.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/parser/analyze.c')
-rw-r--r--src/backend/parser/analyze.c242
1 files changed, 171 insertions, 71 deletions
diff --git a/src/backend/parser/analyze.c b/src/backend/parser/analyze.c
index fe21804a2c4..0165ef15c21 100644
--- a/src/backend/parser/analyze.c
+++ b/src/backend/parser/analyze.c
@@ -6,7 +6,7 @@
* Portions Copyright (c) 1996-2000, PostgreSQL, Inc
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $Id: analyze.c,v 1.156 2000/08/29 04:20:44 momjian Exp $
+ * $Id: analyze.c,v 1.157 2000/09/12 21:07:00 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -25,6 +25,7 @@
#include "parser/parse_relation.h"
#include "parser/parse_target.h"
#include "parser/parse_type.h"
+#include "rewrite/rewriteManip.h"
#include "utils/builtins.h"
#include "utils/fmgroids.h"
#include "utils/relcache.h"
@@ -54,6 +55,8 @@ static void transformConstraintAttrs(List *constraintList);
static void transformColumnType(ParseState *pstate, ColumnDef *column);
static void transformFkeyCheckAttrs(FkConstraint *fkconstraint);
+static void release_pstate_resources(ParseState *pstate);
+
/* kluge to return extra info from transformCreateStmt() */
static List *extras_before;
static List *extras_after;
@@ -71,28 +74,22 @@ List *
parse_analyze(List *pl, ParseState *parentParseState)
{
List *result = NIL;
- ParseState *pstate;
- Query *parsetree;
while (pl != NIL)
{
+ ParseState *pstate = make_parsestate(parentParseState);
+ Query *parsetree;
+
extras_before = extras_after = NIL;
- pstate = make_parsestate(parentParseState);
parsetree = transformStmt(pstate, lfirst(pl));
- if (pstate->p_target_relation != NULL)
- heap_close(pstate->p_target_relation, AccessShareLock);
- pstate->p_target_relation = NULL;
- pstate->p_target_rangetblentry = NULL;
+ release_pstate_resources(pstate);
while (extras_before != NIL)
{
result = lappend(result,
- transformStmt(pstate, lfirst(extras_before)));
- if (pstate->p_target_relation != NULL)
- heap_close(pstate->p_target_relation, AccessShareLock);
- pstate->p_target_relation = NULL;
- pstate->p_target_rangetblentry = NULL;
+ transformStmt(pstate, lfirst(extras_before)));
+ release_pstate_resources(pstate);
extras_before = lnext(extras_before);
}
@@ -102,10 +99,7 @@ parse_analyze(List *pl, ParseState *parentParseState)
{
result = lappend(result,
transformStmt(pstate, lfirst(extras_after)));
- if (pstate->p_target_relation != NULL)
- heap_close(pstate->p_target_relation, AccessShareLock);
- pstate->p_target_relation = NULL;
- pstate->p_target_rangetblentry = NULL;
+ release_pstate_resources(pstate);
extras_after = lnext(extras_after);
}
@@ -116,6 +110,15 @@ parse_analyze(List *pl, ParseState *parentParseState)
return result;
}
+static void
+release_pstate_resources(ParseState *pstate)
+{
+ if (pstate->p_target_relation != NULL)
+ heap_close(pstate->p_target_relation, AccessShareLock);
+ pstate->p_target_relation = NULL;
+ pstate->p_target_rangetblentry = NULL;
+}
+
/*
* transformStmt -
* transform a Parse tree. If it is an optimizable statement, turn it
@@ -176,11 +179,11 @@ transformStmt(ParseState *pstate, Node *parseTree)
Resdom *rd;
id = nth(i, n->aliases);
- Assert(nodeTag(id) == T_Ident);
+ Assert(IsA(id, Ident));
te = nth(i, targetList);
- Assert(nodeTag(te) == T_TargetEntry);
+ Assert(IsA(te, TargetEntry));
rd = te->resdom;
- Assert(nodeTag(rd) == T_Resdom);
+ Assert(IsA(rd, Resdom));
rd->resname = pstrdup(id->name);
}
}
@@ -290,15 +293,17 @@ transformDeleteStmt(ParseState *pstate, DeleteStmt *stmt)
qry->commandType = CMD_DELETE;
/* set up a range table */
- makeRangeTable(pstate, NULL);
- setTargetTable(pstate, stmt->relname, stmt->inh);
+ makeRangeTable(pstate, NIL);
+ setTargetTable(pstate, stmt->relname, stmt->inh, true);
qry->distinctClause = NIL;
/* fix where clause */
qry->qual = transformWhereClause(pstate, stmt->whereClause);
+ /* done building the rtable */
qry->rtable = pstate->p_rtable;
+ qry->jointree = pstate->p_jointree;
qry->resultRelation = refnameRangeTablePosn(pstate, stmt->relname, NULL);
qry->hasSubLinks = pstate->p_hasSubLinks;
@@ -387,12 +392,14 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt)
*
* In particular, it's time to add the INSERT target to the rangetable.
* (We didn't want it there until now since it shouldn't be visible in
- * the SELECT part.)
+ * the SELECT part.) Note that the INSERT target is NOT added to the
+ * join tree, since we don't want to join over it.
*/
- setTargetTable(pstate, stmt->relname, FALSE);
+ setTargetTable(pstate, stmt->relname, false, false);
/* now the range table will not change */
qry->rtable = pstate->p_rtable;
+ qry->jointree = pstate->p_jointree;
qry->resultRelation = refnameRangeTablePosn(pstate, stmt->relname, NULL);
/* Prepare to assign non-conflicting resnos to resjunk attributes */
@@ -908,7 +915,7 @@ transformCreateStmt(ParseState *pstate, CreateStmt *stmt)
while (dlist != NIL)
{
constraint = lfirst(dlist);
- Assert(nodeTag(constraint) == T_Constraint);
+ Assert(IsA(constraint, Constraint));
Assert((constraint->contype == CONSTR_PRIMARY)
|| (constraint->contype == CONSTR_UNIQUE));
@@ -1427,17 +1434,68 @@ static Query *
transformRuleStmt(ParseState *pstate, RuleStmt *stmt)
{
Query *qry;
- Query *action;
- List *actions;
+ RangeTblEntry *oldrte;
+ RangeTblEntry *newrte;
qry = makeNode(Query);
qry->commandType = CMD_UTILITY;
+ qry->utilityStmt = (Node *) stmt;
+
+ /*
+ * NOTE: 'OLD' must always have a varno equal to 1 and 'NEW'
+ * equal to 2. Set up their RTEs in the main pstate for use
+ * in parsing the rule qualification.
+ */
+ Assert(pstate->p_rtable == NIL);
+ oldrte = addRangeTableEntry(pstate, stmt->object->relname,
+ makeAttr("*OLD*", NULL),
+ false, true);
+ newrte = addRangeTableEntry(pstate, stmt->object->relname,
+ makeAttr("*NEW*", NULL),
+ false, true);
+ /*
+ * They must be in the jointree too for lookup purposes, but only add
+ * the one(s) that are relevant for the current kind of rule. In an
+ * UPDATE rule, quals must refer to OLD.field or NEW.field to be
+ * unambiguous, but there's no need to be so picky for INSERT & DELETE.
+ * (Note we marked the RTEs "inFromCl = true" above to allow unqualified
+ * references to their fields.)
+ */
+ switch (stmt->event)
+ {
+ case CMD_SELECT:
+ addRTEtoJoinTree(pstate, oldrte);
+ break;
+ case CMD_UPDATE:
+ addRTEtoJoinTree(pstate, oldrte);
+ addRTEtoJoinTree(pstate, newrte);
+ break;
+ case CMD_INSERT:
+ addRTEtoJoinTree(pstate, newrte);
+ break;
+ case CMD_DELETE:
+ addRTEtoJoinTree(pstate, oldrte);
+ break;
+ default:
+ elog(ERROR, "transformRuleStmt: unexpected event type %d",
+ (int) stmt->event);
+ break;
+ }
+
+ /* take care of the where clause */
+ stmt->whereClause = transformWhereClause(pstate, stmt->whereClause);
+
+ if (length(pstate->p_rtable) != 2) /* naughty, naughty... */
+ elog(ERROR, "Rule WHERE condition may not contain references to other relations");
+
+ /* save info about sublinks in where clause */
+ qry->hasSubLinks = pstate->p_hasSubLinks;
/*
- * 'instead nothing' rules with a qualification need a query a
+ * 'instead nothing' rules with a qualification need a query
* rangetable so the rewrite handler can add the negated rule
* qualification to the original query. We create a query with the new
- * command type CMD_NOTHING here that is treated special by the
+ * command type CMD_NOTHING here that is treated specially by the
* rewrite system.
*/
if (stmt->actions == NIL)
@@ -1445,54 +1503,95 @@ transformRuleStmt(ParseState *pstate, RuleStmt *stmt)
Query *nothing_qry = makeNode(Query);
nothing_qry->commandType = CMD_NOTHING;
-
- addRangeTableEntry(pstate, stmt->object->relname,
- makeAttr("*OLD*", NULL),
- FALSE, FALSE, FALSE);
- addRangeTableEntry(pstate, stmt->object->relname,
- makeAttr("*NEW*", NULL),
- FALSE, FALSE, FALSE);
-
nothing_qry->rtable = pstate->p_rtable;
+ nothing_qry->jointree = NIL; /* no join actually wanted */
stmt->actions = lappend(NIL, nothing_qry);
}
-
- actions = stmt->actions;
-
- /*
- * transform each statment, like parse_analyze()
- */
- while (actions != NIL)
+ else
{
+ List *actions;
/*
- * NOTE: 'OLD' must always have a varno equal to 1 and 'NEW'
- * equal to 2.
+ * transform each statement, like parse_analyze()
*/
- addRangeTableEntry(pstate, stmt->object->relname,
- makeAttr("*OLD*", NULL),
- FALSE, FALSE, FALSE);
- addRangeTableEntry(pstate, stmt->object->relname,
- makeAttr("*NEW*", NULL),
- FALSE, FALSE, FALSE);
-
- pstate->p_last_resno = 1;
- pstate->p_is_rule = true; /* for expand all */
- pstate->p_hasAggs = false;
-
- action = (Query *) lfirst(actions);
- if (action->commandType != CMD_NOTHING)
- lfirst(actions) = transformStmt(pstate, lfirst(actions));
- actions = lnext(actions);
- }
+ foreach(actions, stmt->actions)
+ {
+ ParseState *sub_pstate = make_parsestate(pstate->parentParseState);
+ Query *sub_qry;
+ bool has_old,
+ has_new;
- /* take care of the where clause */
- stmt->whereClause = transformWhereClause(pstate, stmt->whereClause);
+ /*
+ * Set up OLD/NEW in the rtable for this statement. The entries
+ * are marked not inFromCl because we don't want them to be
+ * referred to by unqualified field names nor "*" in the rule
+ * actions. We don't need to add them to the jointree for
+ * qualified-name lookup, either (see qualifiedNameToVar()).
+ */
+ oldrte = addRangeTableEntry(sub_pstate, stmt->object->relname,
+ makeAttr("*OLD*", NULL),
+ false, false);
+ newrte = addRangeTableEntry(sub_pstate, stmt->object->relname,
+ makeAttr("*NEW*", NULL),
+ false, false);
- qry->hasSubLinks = pstate->p_hasSubLinks;
+ /* Transform the rule action statement */
+ sub_qry = transformStmt(sub_pstate, lfirst(actions));
+
+ /*
+ * Validate action's use of OLD/NEW, qual too
+ */
+ has_old =
+ rangeTableEntry_used((Node *) sub_qry, PRS2_OLD_VARNO, 0) ||
+ rangeTableEntry_used(stmt->whereClause, PRS2_OLD_VARNO, 0);
+ has_new =
+ rangeTableEntry_used((Node *) sub_qry, PRS2_NEW_VARNO, 0) ||
+ rangeTableEntry_used(stmt->whereClause, PRS2_NEW_VARNO, 0);
+
+ switch (stmt->event)
+ {
+ case CMD_SELECT:
+ if (has_old)
+ elog(ERROR, "ON SELECT rule may not use OLD");
+ if (has_new)
+ elog(ERROR, "ON SELECT rule may not use NEW");
+ break;
+ case CMD_UPDATE:
+ /* both are OK */
+ break;
+ case CMD_INSERT:
+ if (has_old)
+ elog(ERROR, "ON INSERT rule may not use OLD");
+ break;
+ case CMD_DELETE:
+ if (has_new)
+ elog(ERROR, "ON DELETE rule may not use NEW");
+ break;
+ default:
+ elog(ERROR, "transformRuleStmt: unexpected event type %d",
+ (int) stmt->event);
+ break;
+ }
+
+ /*
+ * For efficiency's sake, add OLD to the rule action's jointree
+ * only if it was actually referenced in the statement or qual.
+ * NEW is not really a relation and should never be added.
+ */
+ if (has_old)
+ {
+ addRTEtoJoinTree(sub_pstate, oldrte);
+ sub_qry->jointree = sub_pstate->p_jointree;
+ }
+
+ lfirst(actions) = sub_qry;
+
+ release_pstate_resources(sub_pstate);
+ pfree(sub_pstate);
+ }
+ }
- qry->utilityStmt = (Node *) stmt;
return qry;
}
@@ -1558,6 +1657,7 @@ transformSelectStmt(ParseState *pstate, SelectStmt *stmt)
qry->intersectClause = stmt->intersectClause;
qry->rtable = pstate->p_rtable;
+ qry->jointree = pstate->p_jointree;
if (stmt->forUpdate != NULL)
transformForUpdate(qry, stmt->forUpdate);
@@ -1585,17 +1685,17 @@ transformUpdateStmt(ParseState *pstate, UpdateStmt *stmt)
* do this with REPLACE in POSTQUEL so we keep the feature.
*/
makeRangeTable(pstate, stmt->fromClause);
- setTargetTable(pstate, stmt->relname, stmt->inh);
+ setTargetTable(pstate, stmt->relname, stmt->inh, true);
qry->targetList = transformTargetList(pstate, stmt->targetList);
qry->qual = transformWhereClause(pstate, stmt->whereClause);
- qry->hasSubLinks = pstate->p_hasSubLinks;
-
qry->rtable = pstate->p_rtable;
+ qry->jointree = pstate->p_jointree;
qry->resultRelation = refnameRangeTablePosn(pstate, stmt->relname, NULL);
+ qry->hasSubLinks = pstate->p_hasSubLinks;
qry->hasAggs = pstate->p_hasAggs;
if (pstate->p_hasAggs)
parseCheckAggregates(pstate, qry);
@@ -1689,7 +1789,7 @@ transformAlterTableStmt(ParseState *pstate, AlterTableStmt *stmt)
transformColumnType(pstate, (ColumnDef *) stmt->def);
break;
case 'C':
- if (stmt->def && nodeTag(stmt->def) == T_FkConstraint)
+ if (stmt->def && IsA(stmt->def, FkConstraint))
{
CreateTrigStmt *fk_trigger;
List *fk_attr;
@@ -2085,7 +2185,7 @@ transformForUpdate(Query *qry, List *forUpdate)
i++;
}
if (l2 == NULL)
- elog(ERROR, "FOR UPDATE: relation '%s' not found in FROM clause",
+ elog(ERROR, "FOR UPDATE: relation \"%s\" not found in FROM clause",
relname);
}