summaryrefslogtreecommitdiff
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.c179
1 files changed, 107 insertions, 72 deletions
diff --git a/src/backend/parser/analyze.c b/src/backend/parser/analyze.c
index 888f8f8e14..169075c47e 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.158 2000/09/25 12:58:46 momjian Exp $
+ * $Id: analyze.c,v 1.159 2000/09/29 18:21:36 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -56,6 +56,7 @@ static void transformColumnType(ParseState *pstate, ColumnDef *column);
static void transformFkeyCheckAttrs(FkConstraint *fkconstraint);
static void release_pstate_resources(ParseState *pstate);
+static FromExpr *makeFromExpr(List *fromlist, Node *quals);
/* kluge to return extra info from transformCreateStmt() */
static List *extras_before;
@@ -289,6 +290,7 @@ static Query *
transformDeleteStmt(ParseState *pstate, DeleteStmt *stmt)
{
Query *qry = makeNode(Query);
+ Node *qual;
qry->commandType = CMD_DELETE;
@@ -299,17 +301,17 @@ transformDeleteStmt(ParseState *pstate, DeleteStmt *stmt)
qry->distinctClause = NIL;
/* fix where clause */
- qry->qual = transformWhereClause(pstate, stmt->whereClause);
+ qual = transformWhereClause(pstate, stmt->whereClause);
- /* done building the rtable */
+ /* done building the range table and jointree */
qry->rtable = pstate->p_rtable;
- qry->jointree = pstate->p_jointree;
+ qry->jointree = makeFromExpr(pstate->p_joinlist, qual);
qry->resultRelation = refnameRangeTablePosn(pstate, stmt->relname, NULL);
qry->hasSubLinks = pstate->p_hasSubLinks;
qry->hasAggs = pstate->p_hasAggs;
if (pstate->p_hasAggs)
- parseCheckAggregates(pstate, qry);
+ parseCheckAggregates(pstate, qry, qual);
return (Query *) qry;
}
@@ -322,6 +324,7 @@ static Query *
transformInsertStmt(ParseState *pstate, InsertStmt *stmt)
{
Query *qry = makeNode(Query);
+ Node *qual;
List *icolumns;
List *attrnos;
List *attnos;
@@ -348,7 +351,7 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt)
qry->targetList = transformTargetList(pstate, stmt->targetList);
- qry->qual = transformWhereClause(pstate, stmt->whereClause);
+ qual = transformWhereClause(pstate, stmt->whereClause);
/*
* Initial processing of HAVING clause is just like WHERE clause.
@@ -371,7 +374,7 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt)
qry->hasSubLinks = pstate->p_hasSubLinks;
qry->hasAggs = pstate->p_hasAggs;
if (pstate->p_hasAggs || qry->groupClause || qry->havingQual)
- parseCheckAggregates(pstate, qry);
+ parseCheckAggregates(pstate, qry, qual);
/*
* The INSERT INTO ... SELECT ... could have a UNION in child, so
@@ -393,13 +396,13 @@ 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.) Note that the INSERT target is NOT added to the
- * join tree, since we don't want to join over it.
+ * joinlist, since we don't want to join over it.
*/
setTargetTable(pstate, stmt->relname, false, false);
- /* now the range table will not change */
+ /* now the range table and jointree will not change */
qry->rtable = pstate->p_rtable;
- qry->jointree = pstate->p_jointree;
+ qry->jointree = makeFromExpr(pstate->p_joinlist, qual);
qry->resultRelation = refnameRangeTablePosn(pstate, stmt->relname, NULL);
/* Prepare to assign non-conflicting resnos to resjunk attributes */
@@ -715,7 +718,7 @@ transformCreateStmt(ParseState *pstate, CreateStmt *stmt)
snamenode->val.val.str = qstring;
funccallnode = makeNode(FuncCall);
funccallnode->funcname = "nextval";
- funccallnode->args = lcons(snamenode, NIL);
+ funccallnode->args = makeList1(snamenode);
funccallnode->agg_star = false;
funccallnode->agg_distinct = false;
@@ -748,7 +751,7 @@ transformCreateStmt(ParseState *pstate, CreateStmt *stmt)
elog(NOTICE, "CREATE TABLE will create implicit sequence '%s' for SERIAL column '%s.%s'",
sequence->seqname, stmt->relname, column->colname);
- blist = lcons(sequence, NIL);
+ blist = makeList1(sequence);
}
/* Process column constraints, if any... */
@@ -776,7 +779,7 @@ transformCreateStmt(ParseState *pstate, CreateStmt *stmt)
id->isRel = false;
fkconstraint = (FkConstraint *) constraint;
- fkconstraint->fk_attrs = lappend(NIL, id);
+ fkconstraint->fk_attrs = makeList1(id);
fkconstraints = lappend(fkconstraints, constraint);
continue;
@@ -815,7 +818,7 @@ transformCreateStmt(ParseState *pstate, CreateStmt *stmt)
{
key = makeNode(Ident);
key->name = pstrdup(column->colname);
- constraint->keys = lcons(key, NIL);
+ constraint->keys = makeList1(key);
}
dlist = lappend(dlist, constraint);
break;
@@ -827,7 +830,7 @@ transformCreateStmt(ParseState *pstate, CreateStmt *stmt)
{
key = makeNode(Ident);
key->name = pstrdup(column->colname);
- constraint->keys = lcons(key, NIL);
+ constraint->keys = makeList1(key);
}
dlist = lappend(dlist, constraint);
break;
@@ -1453,8 +1456,11 @@ transformRuleStmt(ParseState *pstate, RuleStmt *stmt)
newrte = addRangeTableEntry(pstate, stmt->object->relname,
makeAttr("*NEW*", NULL),
false, true);
+ /* Must override addRangeTableEntry's default access-check flags */
+ oldrte->checkForRead = false;
+ newrte->checkForRead = false;
/*
- * They must be in the jointree too for lookup purposes, but only add
+ * They must be in the joinlist 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.
@@ -1464,17 +1470,17 @@ transformRuleStmt(ParseState *pstate, RuleStmt *stmt)
switch (stmt->event)
{
case CMD_SELECT:
- addRTEtoJoinTree(pstate, oldrte);
+ addRTEtoJoinList(pstate, oldrte);
break;
case CMD_UPDATE:
- addRTEtoJoinTree(pstate, oldrte);
- addRTEtoJoinTree(pstate, newrte);
+ addRTEtoJoinList(pstate, oldrte);
+ addRTEtoJoinList(pstate, newrte);
break;
case CMD_INSERT:
- addRTEtoJoinTree(pstate, newrte);
+ addRTEtoJoinList(pstate, newrte);
break;
case CMD_DELETE:
- addRTEtoJoinTree(pstate, oldrte);
+ addRTEtoJoinList(pstate, oldrte);
break;
default:
elog(ERROR, "transformRuleStmt: unexpected event type %d",
@@ -1504,9 +1510,9 @@ transformRuleStmt(ParseState *pstate, RuleStmt *stmt)
nothing_qry->commandType = CMD_NOTHING;
nothing_qry->rtable = pstate->p_rtable;
- nothing_qry->jointree = NIL; /* no join actually wanted */
+ nothing_qry->jointree = makeFromExpr(NIL, NULL); /* no join wanted */
- stmt->actions = lappend(NIL, nothing_qry);
+ stmt->actions = makeList1(nothing_qry);
}
else
{
@@ -1526,7 +1532,7 @@ transformRuleStmt(ParseState *pstate, RuleStmt *stmt)
* 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
+ * actions. We don't need to add them to the joinlist for
* qualified-name lookup, either (see qualifiedNameToVar()).
*/
oldrte = addRangeTableEntry(sub_pstate, stmt->object->relname,
@@ -1535,6 +1541,8 @@ transformRuleStmt(ParseState *pstate, RuleStmt *stmt)
newrte = addRangeTableEntry(sub_pstate, stmt->object->relname,
makeAttr("*NEW*", NULL),
false, false);
+ oldrte->checkForRead = false;
+ newrte->checkForRead = false;
/* Transform the rule action statement */
sub_qry = transformStmt(sub_pstate, lfirst(actions));
@@ -1581,8 +1589,8 @@ transformRuleStmt(ParseState *pstate, RuleStmt *stmt)
*/
if (has_old)
{
- addRTEtoJoinTree(sub_pstate, oldrte);
- sub_qry->jointree = sub_pstate->p_jointree;
+ addRTEtoJoinList(sub_pstate, oldrte);
+ sub_qry->jointree->fromlist = sub_pstate->p_joinlist;
}
lfirst(actions) = sub_qry;
@@ -1605,6 +1613,7 @@ static Query *
transformSelectStmt(ParseState *pstate, SelectStmt *stmt)
{
Query *qry = makeNode(Query);
+ Node *qual;
qry->commandType = CMD_SELECT;
@@ -1617,7 +1626,7 @@ transformSelectStmt(ParseState *pstate, SelectStmt *stmt)
qry->targetList = transformTargetList(pstate, stmt->targetList);
- qry->qual = transformWhereClause(pstate, stmt->whereClause);
+ qual = transformWhereClause(pstate, stmt->whereClause);
/*
* Initial processing of HAVING clause is just like WHERE clause.
@@ -1641,7 +1650,7 @@ transformSelectStmt(ParseState *pstate, SelectStmt *stmt)
qry->hasSubLinks = pstate->p_hasSubLinks;
qry->hasAggs = pstate->p_hasAggs;
if (pstate->p_hasAggs || qry->groupClause || qry->havingQual)
- parseCheckAggregates(pstate, qry);
+ parseCheckAggregates(pstate, qry, qual);
/*
* The INSERT INTO ... SELECT ... could have a UNION in child, so
@@ -1657,7 +1666,7 @@ transformSelectStmt(ParseState *pstate, SelectStmt *stmt)
qry->intersectClause = stmt->intersectClause;
qry->rtable = pstate->p_rtable;
- qry->jointree = pstate->p_jointree;
+ qry->jointree = makeFromExpr(pstate->p_joinlist, qual);
if (stmt->forUpdate != NULL)
transformForUpdate(qry, stmt->forUpdate);
@@ -1674,6 +1683,7 @@ static Query *
transformUpdateStmt(ParseState *pstate, UpdateStmt *stmt)
{
Query *qry = makeNode(Query);
+ Node *qual;
List *origTargetList;
List *tl;
@@ -1683,22 +1693,27 @@ transformUpdateStmt(ParseState *pstate, UpdateStmt *stmt)
/*
* the FROM clause is non-standard SQL syntax. We used to be able to
* do this with REPLACE in POSTQUEL so we keep the feature.
+ *
+ * Note: it's critical here that we process FROM before adding the
+ * target table to the rtable --- otherwise, if the target is also
+ * used in FROM, we'd fail to notice that it should be marked
+ * checkForRead as well as checkForWrite. See setTargetTable().
*/
makeRangeTable(pstate, stmt->fromClause);
setTargetTable(pstate, stmt->relname, stmt->inh, true);
qry->targetList = transformTargetList(pstate, stmt->targetList);
- qry->qual = transformWhereClause(pstate, stmt->whereClause);
+ qual = transformWhereClause(pstate, stmt->whereClause);
qry->rtable = pstate->p_rtable;
- qry->jointree = pstate->p_jointree;
+ qry->jointree = makeFromExpr(pstate->p_joinlist, qual);
qry->resultRelation = refnameRangeTablePosn(pstate, stmt->relname, NULL);
qry->hasSubLinks = pstate->p_hasSubLinks;
qry->hasAggs = pstate->p_hasAggs;
if (pstate->p_hasAggs)
- parseCheckAggregates(pstate, qry);
+ parseCheckAggregates(pstate, qry, qual);
/*
* Now we are done with SELECT-like processing, and can get on with
@@ -2083,7 +2098,7 @@ A_Expr_to_Expr(Node *ptr, bool *intersect_present)
expr->typeOid = BOOLOID;
expr->opType = OR_EXPR;
- expr->args = makeList(lexpr, rexpr, -1);
+ expr->args = makeList2(lexpr, rexpr);
result = (Node *) expr;
break;
}
@@ -2095,7 +2110,7 @@ A_Expr_to_Expr(Node *ptr, bool *intersect_present)
expr->typeOid = BOOLOID;
expr->opType = AND_EXPR;
- expr->args = makeList(lexpr, rexpr, -1);
+ expr->args = makeList2(lexpr, rexpr);
result = (Node *) expr;
break;
}
@@ -2106,7 +2121,7 @@ A_Expr_to_Expr(Node *ptr, bool *intersect_present)
expr->typeOid = BOOLOID;
expr->opType = NOT_EXPR;
- expr->args = makeList(rexpr, -1);
+ expr->args = makeList1(rexpr);
result = (Node *) expr;
break;
}
@@ -2122,7 +2137,7 @@ A_Expr_to_Expr(Node *ptr, bool *intersect_present)
void
CheckSelectForUpdate(Query *qry)
{
- if (qry->unionClause != NULL)
+ if (qry->unionClause || qry->intersectClause)
elog(ERROR, "SELECT FOR UPDATE is not allowed with UNION/INTERSECT/EXCEPT clause");
if (qry->distinctClause != NIL)
elog(ERROR, "SELECT FOR UPDATE is not allowed with DISTINCT clause");
@@ -2135,61 +2150,70 @@ CheckSelectForUpdate(Query *qry)
static void
transformForUpdate(Query *qry, List *forUpdate)
{
- List *rowMark = NULL;
- RowMark *newrm;
+ List *rowMarks = NIL;
List *l;
+ List *rt;
Index i;
CheckSelectForUpdate(qry);
- if (lfirst(forUpdate) == NULL) /* all tables */
+ if (lfirst(forUpdate) == NULL)
{
- i = 1;
- foreach(l, qry->rtable)
+ /* all tables used in query */
+ i = 0;
+ foreach(rt, qry->rtable)
{
- newrm = makeNode(RowMark);
- newrm->rti = i++;
- newrm->info = ROW_MARK_FOR_UPDATE | ROW_ACL_FOR_UPDATE;
- rowMark = lappend(rowMark, newrm);
+ RangeTblEntry *rte = (RangeTblEntry *) lfirst(rt);
+
+ ++i;
+ if (rte->subquery)
+ {
+ /* FOR UPDATE of subquery is propagated to subquery's rels */
+ transformForUpdate(rte->subquery, makeList1(NULL));
+ }
+ else
+ {
+ rowMarks = lappendi(rowMarks, i);
+ rte->checkForWrite = true;
+ }
}
- qry->rowMark = nconc(qry->rowMark, rowMark);
- return;
}
-
- foreach(l, forUpdate)
+ else
{
- char *relname = lfirst(l);
- List *l2;
-
- i = 1;
- foreach(l2, qry->rtable)
+ /* just the named tables */
+ foreach(l, forUpdate)
{
- if (strcmp(((RangeTblEntry *) lfirst(l2))->eref->relname, relname) == 0)
+ char *relname = lfirst(l);
+
+ i = 0;
+ foreach(rt, qry->rtable)
{
- List *l3;
+ RangeTblEntry *rte = (RangeTblEntry *) lfirst(rt);
- foreach(l3, rowMark)
+ ++i;
+ if (strcmp(rte->eref->relname, relname) == 0)
{
- if (((RowMark *) lfirst(l3))->rti == i) /* duplicate */
- break;
- }
- if (l3 == NULL)
- {
- newrm = makeNode(RowMark);
- newrm->rti = i;
- newrm->info = ROW_MARK_FOR_UPDATE | ROW_ACL_FOR_UPDATE;
- rowMark = lappend(rowMark, newrm);
+ if (rte->subquery)
+ {
+ /* propagate to subquery */
+ transformForUpdate(rte->subquery, makeList1(NULL));
+ }
+ else
+ {
+ if (!intMember(i, rowMarks)) /* avoid duplicates */
+ rowMarks = lappendi(rowMarks, i);
+ rte->checkForWrite = true;
+ }
+ break;
}
- break;
}
- i++;
+ if (rt == NIL)
+ elog(ERROR, "FOR UPDATE: relation \"%s\" not found in FROM clause",
+ relname);
}
- if (l2 == NULL)
- elog(ERROR, "FOR UPDATE: relation \"%s\" not found in FROM clause",
- relname);
}
- qry->rowMark = rowMark;
+ qry->rowMarks = rowMarks;
}
@@ -2452,6 +2476,17 @@ transformConstraintAttrs(List *constraintList)
}
}
+/* Build a FromExpr node */
+static FromExpr *
+makeFromExpr(List *fromlist, Node *quals)
+{
+ FromExpr *f = makeNode(FromExpr);
+
+ f->fromlist = fromlist;
+ f->quals = quals;
+ return f;
+}
+
/*
* Special handling of type definition for a column
*/