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.c345
1 files changed, 204 insertions, 141 deletions
diff --git a/src/backend/parser/analyze.c b/src/backend/parser/analyze.c
index 40a66b48738..d4a1ef57ef8 100644
--- a/src/backend/parser/analyze.c
+++ b/src/backend/parser/analyze.c
@@ -5,12 +5,11 @@
*
* Copyright (c) 1994, Regents of the University of California
*
- * $Id: analyze.c,v 1.115 1999/07/17 20:17:19 momjian Exp $
+ * $Id: analyze.c,v 1.116 1999/07/19 00:26:18 tgl Exp $
*
*-------------------------------------------------------------------------
*/
-
#include "postgres.h"
#include "access/heapam.h"
@@ -38,8 +37,10 @@ static Query *transformCreateStmt(ParseState *pstate, CreateStmt *stmt);
static void transformForUpdate(Query *qry, List *forUpdate);
void CheckSelectForUpdate(Query *qry);
-List *extras_before = NIL;
-List *extras_after = NIL;
+/* kluge to return extra info from transformCreateStmt() */
+static List *extras_before;
+static List *extras_after;
+
/*
* parse_analyze -
@@ -58,17 +59,23 @@ parse_analyze(List *pl, ParseState *parentParseState)
while (pl != NIL)
{
+ 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);
+ pstate->p_target_relation = NULL;
+ pstate->p_target_rangetblentry = NULL;
while (extras_before != NIL)
{
result = lappend(result,
- transformStmt(pstate, lfirst(extras_before)));
+ transformStmt(pstate, lfirst(extras_before)));
if (pstate->p_target_relation != NULL)
heap_close(pstate->p_target_relation);
+ pstate->p_target_relation = NULL;
+ pstate->p_target_rangetblentry = NULL;
extras_before = lnext(extras_before);
}
@@ -80,11 +87,13 @@ parse_analyze(List *pl, ParseState *parentParseState)
transformStmt(pstate, lfirst(extras_after)));
if (pstate->p_target_relation != NULL)
heap_close(pstate->p_target_relation);
+ pstate->p_target_relation = NULL;
+ pstate->p_target_rangetblentry = NULL;
extras_after = lnext(extras_after);
}
- pl = lnext(pl);
pfree(pstate);
+ pl = lnext(pl);
}
return result;
@@ -148,9 +157,9 @@ transformStmt(ParseState *pstate, Node *parseTree)
result->commandType = CMD_UTILITY;
result->utilityStmt = (Node *) parseTree;
MemoryContextSwitchTo(oldcontext);
- break;
-
}
+ break;
+
case T_ExplainStmt:
{
ExplainStmt *n = (ExplainStmt *) parseTree;
@@ -215,7 +224,8 @@ transformDeleteStmt(ParseState *pstate, DeleteStmt *stmt)
qry->commandType = CMD_DELETE;
/* set up a range table */
- makeRangeTable(pstate, stmt->relname, NULL, NULL);
+ makeRangeTable(pstate, NULL, NULL);
+ setTargetTable(pstate, stmt->relname);
qry->uniqueFlag = NULL;
@@ -240,135 +250,70 @@ transformDeleteStmt(ParseState *pstate, DeleteStmt *stmt)
static Query *
transformInsertStmt(ParseState *pstate, InsertStmt *stmt)
{
- Query *qry = makeNode(Query); /* make a new query tree */
+ Query *qry = makeNode(Query);
+ Node *fromQual;
List *icolumns;
+ List *tl;
+ TupleDesc rd_att;
qry->commandType = CMD_INSERT;
pstate->p_is_insert = true;
- /* set up a range table */
- makeRangeTable(pstate, stmt->relname, stmt->fromClause, NULL);
+ /*----------
+ * Initial processing steps are just like SELECT, which should not
+ * be surprising, since we may be handling an INSERT ... SELECT.
+ * It is important that we finish processing all the SELECT subclauses
+ * before we start doing any INSERT-specific processing; otherwise
+ * the behavior of SELECT within INSERT might be different from a
+ * stand-alone SELECT. (Indeed, Postgres up through 6.5 had bugs of
+ * just that nature...)
+ *----------
+ */
- qry->uniqueFlag = stmt->unique;
+ /* set up a range table --- note INSERT target is not in it yet */
+ makeRangeTable(pstate, stmt->fromClause, &fromQual);
- /* fix the target list */
- icolumns = pstate->p_insert_columns = makeTargetNames(pstate, stmt->cols);
+ qry->uniqueFlag = stmt->unique;
qry->targetList = transformTargetList(pstate, stmt->targetList);
- /* DEFAULT handling */
- if (length(qry->targetList) < pstate->p_target_relation->rd_att->natts &&
- pstate->p_target_relation->rd_att->constr &&
- pstate->p_target_relation->rd_att->constr->num_defval > 0)
- {
- Form_pg_attribute *att = pstate->p_target_relation->rd_att->attrs;
- AttrDefault *defval = pstate->p_target_relation->rd_att->constr->defval;
- int ndef = pstate->p_target_relation->rd_att->constr->num_defval;
-
- /*
- * if stmt->cols == NIL then makeTargetNames returns list of all
- * attrs. May have to shorten icolumns list...
- */
- if (stmt->cols == NIL)
- {
- List *extrl;
- int i = length(qry->targetList);
-
- foreach(extrl, icolumns)
- {
-
- /*
- * decrements first, so if we started with zero items it
- * will now be negative
- */
- if (--i <= 0)
- break;
- }
-
- /*
- * this an index into the targetList, so make sure we had one
- * to start...
- */
- if (i >= 0)
- {
- freeList(lnext(extrl));
- lnext(extrl) = NIL;
- }
- else
- icolumns = NIL;
- }
+ qry->qual = transformWhereClause(pstate, stmt->whereClause, fromQual);
- while (ndef-- > 0)
- {
- List *tl;
- Ident *id;
- TargetEntry *te;
-
- foreach(tl, icolumns)
- {
- id = (Ident *) lfirst(tl);
- if (namestrcmp(&(att[defval[ndef].adnum - 1]->attname), id->name) == 0)
- break;
- }
- if (tl != NIL) /* something given for this attr */
- continue;
-
- /*
- * Nothing given for this attr with DEFAULT expr, so add new
- * TargetEntry to qry->targetList. Note, that we set resno to
- * defval[ndef].adnum: it's what
- * transformTargetList()->make_targetlist_expr() does for
- * INSERT ... SELECT. But for INSERT ... VALUES
- * pstate->p_last_resno is used. It doesn't matter for
- * "normal" using (planner creates proper target list in
- * preptlist.c), but may break RULEs in some way. It seems
- * better to create proper target list here...
- */
- te = makeTargetEntry(makeResdom(defval[ndef].adnum,
- att[defval[ndef].adnum - 1]->atttypid,
- att[defval[ndef].adnum - 1]->atttypmod,
- pstrdup(nameout(&(att[defval[ndef].adnum - 1]->attname))),
- 0, 0, false),
- (Node *) stringToNode(defval[ndef].adbin));
- qry->targetList = lappend(qry->targetList, te);
- }
- }
-
- /* fix where clause */
- qry->qual = transformWhereClause(pstate, stmt->whereClause, NULL);
-
- /*
- * The havingQual has a similar meaning as "qual" in the where
- * statement. So we can easily use the code from the "where clause"
- * with some additional traversals done in
- * .../optimizer/plan/planner.c
+ /* Initial processing of HAVING clause is just like WHERE clause.
+ * Additional work will be done in optimizer/plan/planner.c.
*/
qry->havingQual = transformWhereClause(pstate, stmt->havingClause, NULL);
- qry->hasSubLinks = pstate->p_hasSubLinks;
-
- /* now the range table will not change */
- qry->rtable = pstate->p_rtable;
- qry->resultRelation = refnameRangeTablePosn(pstate, stmt->relname, NULL);
-
qry->groupClause = transformGroupClause(pstate,
stmt->groupClause,
qry->targetList);
- /* fix order clause */
+ /* An InsertStmt has no sortClause, but we still call
+ * transformSortClause because it also handles uniqueFlag.
+ */
qry->sortClause = transformSortClause(pstate,
NIL,
NIL,
qry->targetList,
qry->uniqueFlag);
+ qry->hasSubLinks = pstate->p_hasSubLinks;
qry->hasAggs = pstate->p_hasAggs;
if (pstate->p_hasAggs || qry->groupClause)
parseCheckAggregates(pstate, qry);
/*
+ * If there is a havingQual but there are no aggregates, then there is
+ * something wrong with the query because HAVING must contain
+ * aggregates in its expressions! Otherwise the query could have been
+ * formulated using the WHERE clause.
+ */
+ if (qry->havingQual && ! qry->hasAggs)
+ elog(ERROR, "SELECT/HAVING requires aggregates to be valid");
+
+ /*
* The INSERT INTO ... SELECT ... could have a UNION in child, so
- * unionClause may be false ,
+ * unionClause may be false
*/
qry->unionall = stmt->unionall;
@@ -380,15 +325,98 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt)
qry->intersectClause = stmt->intersectClause;
/*
- * If there is a havingQual but there are no aggregates, then there is
- * something wrong with the query because having must contain
- * aggregates in its expressions! Otherwise the query could have been
- * formulated using the where clause.
+ * Now we are done with SELECT-like processing, and can get on with
+ * transforming the target list to match the INSERT target columns.
+ *
+ * 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.)
+ */
+ setTargetTable(pstate, stmt->relname);
+
+ /* now the range table will not change */
+ qry->rtable = pstate->p_rtable;
+ qry->resultRelation = refnameRangeTablePosn(pstate, stmt->relname, NULL);
+
+ /* Prepare to assign non-conflicting resnos to resjunk attributes */
+ if (pstate->p_last_resno <= pstate->p_target_relation->rd_rel->relnatts)
+ pstate->p_last_resno = pstate->p_target_relation->rd_rel->relnatts + 1;
+
+ /* Validate stmt->cols list, or build default list if no list given */
+ icolumns = makeTargetNames(pstate, stmt->cols);
+
+ /* Prepare non-junk columns for assignment to target table */
+ foreach(tl, qry->targetList)
+ {
+ TargetEntry *tle = (TargetEntry *) lfirst(tl);
+ Resdom *resnode = tle->resdom;
+ Ident *id;
+
+ if (resnode->resjunk)
+ {
+ /* Resjunk nodes need no additional processing, but be sure they
+ * have names and resnos that do not match any target columns;
+ * else rewriter or planner might get confused.
+ */
+ resnode->resname = "?resjunk?";
+ resnode->resno = (AttrNumber) pstate->p_last_resno++;
+ continue;
+ }
+ if (icolumns == NIL)
+ elog(ERROR, "INSERT has more expressions than target columns");
+ id = (Ident *) lfirst(icolumns);
+ updateTargetListEntry(pstate, tle, id->name, id->indirection);
+ icolumns = lnext(icolumns);
+ }
+
+ /*
+ * Add targetlist items to assign DEFAULT values to any columns that
+ * have defaults and were not assigned to by the user.
+ * XXX wouldn't it make more sense to do this further downstream,
+ * after the rule rewriter?
*/
- if ((qry->hasAggs == false) && (qry->havingQual != NULL))
+ rd_att = pstate->p_target_relation->rd_att;
+ if (rd_att->constr && rd_att->constr->num_defval > 0)
{
- elog(ERROR, "SELECT/HAVING requires aggregates to be valid");
- return (Query *) NIL;
+ Form_pg_attribute *att = rd_att->attrs;
+ AttrDefault *defval = rd_att->constr->defval;
+ int ndef = rd_att->constr->num_defval;
+
+ while (ndef-- > 0)
+ {
+ Form_pg_attribute thisatt = att[defval[ndef].adnum - 1];
+ TargetEntry *te;
+
+ foreach(tl, qry->targetList)
+ {
+ TargetEntry *tle = (TargetEntry *) lfirst(tl);
+ Resdom *resnode = tle->resdom;
+
+ if (resnode->resjunk)
+ continue; /* ignore resjunk nodes */
+ if (namestrcmp(&(thisatt->attname), resnode->resname) == 0)
+ break;
+ }
+ if (tl != NIL) /* something given for this attr */
+ continue;
+ /*
+ * No user-supplied value, so add a targetentry with DEFAULT expr
+ * and correct data for the target column.
+ */
+ te = makeTargetEntry(
+ makeResdom(defval[ndef].adnum,
+ thisatt->atttypid,
+ thisatt->atttypmod,
+ pstrdup(nameout(&(thisatt->attname))),
+ 0, 0, false),
+ stringToNode(defval[ndef].adbin));
+ qry->targetList = lappend(qry->targetList, te);
+ /*
+ * Make sure the value is coerced to the target column type
+ * (might not be right type if it's not a constant!)
+ */
+ updateTargetListEntry(pstate, te, te->resdom->resname, NIL);
+ }
}
if (stmt->forUpdate != NULL)
@@ -963,12 +991,12 @@ static Query *
transformSelectStmt(ParseState *pstate, SelectStmt *stmt)
{
Query *qry = makeNode(Query);
- Node *qual;
+ Node *fromQual;
qry->commandType = CMD_SELECT;
/* set up a range table */
- makeRangeTable(pstate, NULL, stmt->fromClause, &qual);
+ makeRangeTable(pstate, stmt->fromClause, &fromQual);
qry->uniqueFlag = stmt->unique;
@@ -978,16 +1006,16 @@ transformSelectStmt(ParseState *pstate, SelectStmt *stmt)
qry->targetList = transformTargetList(pstate, stmt->targetList);
- qry->qual = transformWhereClause(pstate, stmt->whereClause, qual);
+ qry->qual = transformWhereClause(pstate, stmt->whereClause, fromQual);
- /*
- * The havingQual has a similar meaning as "qual" in the where
- * statement. So we can easily use the code from the "where clause"
- * with some additional traversals done in optimizer/plan/planner.c
+ /* Initial processing of HAVING clause is just like WHERE clause.
+ * Additional work will be done in optimizer/plan/planner.c.
*/
qry->havingQual = transformWhereClause(pstate, stmt->havingClause, NULL);
- qry->hasSubLinks = pstate->p_hasSubLinks;
+ qry->groupClause = transformGroupClause(pstate,
+ stmt->groupClause,
+ qry->targetList);
qry->sortClause = transformSortClause(pstate,
stmt->sortClause,
@@ -995,16 +1023,21 @@ transformSelectStmt(ParseState *pstate, SelectStmt *stmt)
qry->targetList,
qry->uniqueFlag);
- qry->groupClause = transformGroupClause(pstate,
- stmt->groupClause,
- qry->targetList);
- qry->rtable = pstate->p_rtable;
-
+ qry->hasSubLinks = pstate->p_hasSubLinks;
qry->hasAggs = pstate->p_hasAggs;
if (pstate->p_hasAggs || qry->groupClause)
parseCheckAggregates(pstate, qry);
/*
+ * If there is a havingQual but there are no aggregates, then there is
+ * something wrong with the query because HAVING must contain
+ * aggregates in its expressions! Otherwise the query could have been
+ * formulated using the WHERE clause.
+ */
+ if (qry->havingQual && ! qry->hasAggs)
+ elog(ERROR, "SELECT/HAVING requires aggregates to be valid");
+
+ /*
* The INSERT INTO ... SELECT ... could have a UNION in child, so
* unionClause may be false
*/
@@ -1017,17 +1050,7 @@ transformSelectStmt(ParseState *pstate, SelectStmt *stmt)
qry->unionClause = stmt->unionClause;
qry->intersectClause = stmt->intersectClause;
- /*
- * If there is a havingQual but there are no aggregates, then there is
- * something wrong with the query because having must contain
- * aggregates in its expressions! Otherwise the query could have been
- * formulated using the where clause.
- */
- if ((qry->hasAggs == false) && (qry->havingQual != NULL))
- {
- elog(ERROR, "SELECT/HAVING requires aggregates to be valid");
- return (Query *) NIL;
- }
+ qry->rtable = pstate->p_rtable;
if (stmt->forUpdate != NULL)
transformForUpdate(qry, stmt->forUpdate);
@@ -1044,6 +1067,8 @@ static Query *
transformUpdateStmt(ParseState *pstate, UpdateStmt *stmt)
{
Query *qry = makeNode(Query);
+ List *origTargetList;
+ List *tl;
qry->commandType = CMD_UPDATE;
pstate->p_is_update = true;
@@ -1052,21 +1077,59 @@ 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.
*/
- makeRangeTable(pstate, stmt->relname, stmt->fromClause, NULL);
+ makeRangeTable(pstate, stmt->fromClause, NULL);
+ setTargetTable(pstate, stmt->relname);
qry->targetList = transformTargetList(pstate, stmt->targetList);
qry->qual = transformWhereClause(pstate, stmt->whereClause, NULL);
+
qry->hasSubLinks = pstate->p_hasSubLinks;
qry->rtable = pstate->p_rtable;
-
qry->resultRelation = refnameRangeTablePosn(pstate, stmt->relname, NULL);
qry->hasAggs = pstate->p_hasAggs;
if (pstate->p_hasAggs)
parseCheckAggregates(pstate, qry);
+ /*
+ * Now we are done with SELECT-like processing, and can get on with
+ * transforming the target list to match the UPDATE target columns.
+ */
+
+ /* Prepare to assign non-conflicting resnos to resjunk attributes */
+ if (pstate->p_last_resno <= pstate->p_target_relation->rd_rel->relnatts)
+ pstate->p_last_resno = pstate->p_target_relation->rd_rel->relnatts + 1;
+
+ /* Prepare non-junk columns for assignment to target table */
+ origTargetList = stmt->targetList;
+ foreach(tl, qry->targetList)
+ {
+ TargetEntry *tle = (TargetEntry *) lfirst(tl);
+ Resdom *resnode = tle->resdom;
+ ResTarget *origTarget;
+
+ if (resnode->resjunk)
+ {
+ /* Resjunk nodes need no additional processing, but be sure they
+ * have names and resnos that do not match any target columns;
+ * else rewriter or planner might get confused.
+ */
+ resnode->resname = "?resjunk?";
+ resnode->resno = (AttrNumber) pstate->p_last_resno++;
+ continue;
+ }
+ if (origTargetList == NIL)
+ elog(ERROR, "UPDATE target count mismatch --- internal error");
+ origTarget = (ResTarget *) lfirst(origTargetList);
+ updateTargetListEntry(pstate, tle,
+ origTarget->name, origTarget->indirection);
+ origTargetList = lnext(origTargetList);
+ }
+ if (origTargetList != NIL)
+ elog(ERROR, "UPDATE target count mismatch --- internal error");
+
return (Query *) qry;
}