*
* Copyright (c) 1994, Regents of the University of California
*
- * $Id: analyze.c,v 1.113 1999/07/15 22:39:33 momjian Exp $
+ * $Id: analyze.c,v 1.120 1999/10/03 23:55:30 tgl Exp $
*
*-------------------------------------------------------------------------
*/
-#include <stdio.h>
-#include <stdlib.h>
-#include <stdarg.h>
-#include <string.h>
-
#include "postgres.h"
+
#include "access/heapam.h"
+#include "catalog/pg_type.h"
#include "nodes/makefuncs.h"
+#include "parse.h"
#include "parser/analyze.h"
#include "parser/parse_agg.h"
#include "parser/parse_clause.h"
#include "parser/parse_relation.h"
#include "parser/parse_target.h"
-#include "catalog/pg_type.h"
-#include "parse.h"
-
#include "utils/builtins.h"
static Query *transformStmt(ParseState *pstate, Node *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 -
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);
+ heap_close(pstate->p_target_relation, AccessShareLock);
+ 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);
+ heap_close(pstate->p_target_relation, AccessShareLock);
+ pstate->p_target_relation = NULL;
+ pstate->p_target_rangetblentry = NULL;
extras_before = lnext(extras_before);
}
result = lappend(result,
transformStmt(pstate, lfirst(extras_after)));
if (pstate->p_target_relation != NULL)
- heap_close(pstate->p_target_relation);
+ heap_close(pstate->p_target_relation, AccessShareLock);
+ 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;
result->commandType = CMD_UTILITY;
result->utilityStmt = (Node *) parseTree;
MemoryContextSwitchTo(oldcontext);
- break;
-
}
+ break;
+
case T_ExplainStmt:
{
ExplainStmt *n = (ExplainStmt *) parseTree;
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;
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;
- }
-
- 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);
+ 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;
-
- /* 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;
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.)
*/
- if ((qry->hasAggs == false) && (qry->havingQual != NULL))
+ 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)
{
- elog(ERROR, "SELECT/HAVING requires aggregates to be valid");
- return (Query *) NIL;
+ 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?
+ */
+ rd_att = pstate->p_target_relation->rd_att;
+ if (rd_att->constr && rd_att->constr->num_defval > 0)
+ {
+ 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) /* found TLE 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)
q = makeNode(Query);
q->commandType = CMD_UTILITY;
- elements = stmt->tableElts;
constraints = stmt->constraints;
columns = NIL;
dlist = NIL;
- while (elements != NIL)
+ /*
+ * Run through each primary element in the table creation clause
+ */
+ foreach(elements, stmt->tableElts)
{
element = lfirst(elements);
switch (nodeTag(element))
column = (ColumnDef *) element;
columns = lappend(columns, column);
+ /* Special case SERIAL type? */
if (column->is_sequence)
{
char *sname;
- char *cstring;
+ char *qstring;
+ A_Const *snamenode;
+ FuncCall *funccallnode;
CreateSeqStmt *sequence;
sname = makeObjectName(stmt->relname, column->colname,
"seq");
+ /*
+ * Create an expression tree representing the function
+ * call nextval('"sequencename"')
+ */
+ qstring = palloc(strlen(sname) + 2 + 1);
+ sprintf(qstring, "\"%s\"", sname);
+ snamenode = makeNode(A_Const);
+ snamenode->val.type = T_String;
+ snamenode->val.val.str = qstring;
+ funccallnode = makeNode(FuncCall);
+ funccallnode->funcname = "nextval";
+ funccallnode->args = lcons(snamenode, NIL);
+
constraint = makeNode(Constraint);
constraint->contype = CONSTR_DEFAULT;
constraint->name = sname;
- cstring = palloc(10 + strlen(constraint->name) + 3 + 1);
- strcpy(cstring, "nextval('\"");
- strcat(cstring, constraint->name);
- strcat(cstring, "\"')");
- constraint->def = cstring;
+ constraint->raw_expr = (Node *) funccallnode;
+ constraint->cooked_expr = NULL;
constraint->keys = NULL;
column->constraints = lappend(column->constraints, constraint);
blist = lcons(sequence, NIL);
}
- if (column->constraints != NIL)
+ /* Process column constraints, if any... */
+ foreach(clist, column->constraints)
{
- clist = column->constraints;
- while (clist != NIL)
+ constraint = lfirst(clist);
+ switch (constraint->contype)
{
- constraint = lfirst(clist);
- switch (constraint->contype)
- {
- case CONSTR_NULL:
-
- /*
- * We should mark this explicitly, so we
- * can tell if NULL and NOT NULL are both
- * specified
- */
- if (column->is_not_null)
- elog(ERROR, "CREATE TABLE/(NOT) NULL conflicting declaration"
- " for '%s.%s'", stmt->relname, column->colname);
- column->is_not_null = FALSE;
- break;
-
- case CONSTR_NOTNULL:
- if (column->is_not_null)
- elog(ERROR, "CREATE TABLE/NOT NULL already specified"
- " for '%s.%s'", stmt->relname, column->colname);
- column->is_not_null = TRUE;
- break;
-
- case CONSTR_DEFAULT:
- if (column->defval != NULL)
- elog(ERROR, "CREATE TABLE/DEFAULT multiple values specified"
- " for '%s.%s'", stmt->relname, column->colname);
- column->defval = constraint->def;
- break;
-
- case CONSTR_PRIMARY:
- if (constraint->name == NULL)
- constraint->name = makeObjectName(stmt->relname, NULL, "pkey");
- if (constraint->keys == NIL)
- constraint->keys = lappend(constraint->keys, column);
- dlist = lappend(dlist, constraint);
- break;
-
- case CONSTR_UNIQUE:
- if (constraint->name == NULL)
- constraint->name = makeObjectName(stmt->relname, column->colname, "key");
- if (constraint->keys == NIL)
- constraint->keys = lappend(constraint->keys, column);
- dlist = lappend(dlist, constraint);
- break;
-
- case CONSTR_CHECK:
- constraints = lappend(constraints, constraint);
- if (constraint->name == NULL)
- constraint->name = makeObjectName(stmt->relname, column->colname, NULL);
- break;
-
- default:
- elog(ERROR, "parser: unrecognized constraint (internal error)", NULL);
- break;
- }
- clist = lnext(clist);
+ case CONSTR_NULL:
+
+ /*
+ * We should mark this explicitly, so we
+ * can tell if NULL and NOT NULL are both
+ * specified
+ */
+ if (column->is_not_null)
+ elog(ERROR, "CREATE TABLE/(NOT) NULL conflicting declaration"
+ " for '%s.%s'", stmt->relname, column->colname);
+ column->is_not_null = FALSE;
+ break;
+
+ case CONSTR_NOTNULL:
+ if (column->is_not_null)
+ elog(ERROR, "CREATE TABLE/NOT NULL already specified"
+ " for '%s.%s'", stmt->relname, column->colname);
+ column->is_not_null = TRUE;
+ break;
+
+ case CONSTR_DEFAULT:
+ if (column->raw_default != NULL)
+ elog(ERROR, "CREATE TABLE/DEFAULT multiple values specified"
+ " for '%s.%s'", stmt->relname, column->colname);
+ column->raw_default = constraint->raw_expr;
+ Assert(constraint->cooked_expr == NULL);
+ break;
+
+ case CONSTR_PRIMARY:
+ if (constraint->name == NULL)
+ constraint->name = makeObjectName(stmt->relname, NULL, "pkey");
+ if (constraint->keys == NIL)
+ constraint->keys = lappend(constraint->keys, column);
+ dlist = lappend(dlist, constraint);
+ break;
+
+ case CONSTR_UNIQUE:
+ if (constraint->name == NULL)
+ constraint->name = makeObjectName(stmt->relname, column->colname, "key");
+ if (constraint->keys == NIL)
+ constraint->keys = lappend(constraint->keys, column);
+ dlist = lappend(dlist, constraint);
+ break;
+
+ case CONSTR_CHECK:
+ if (constraint->name == NULL)
+ constraint->name = makeObjectName(stmt->relname, column->colname, NULL);
+ constraints = lappend(constraints, constraint);
+ break;
+
+ default:
+ elog(ERROR, "parser: unrecognized constraint (internal error)", NULL);
+ break;
}
}
break;
case CONSTR_NOTNULL:
case CONSTR_DEFAULT:
- elog(ERROR, "parser: illegal context for constraint (internal error)", NULL);
+ elog(ERROR, "parser: illegal context for constraint (internal error)");
break;
default:
- elog(ERROR, "parser: unrecognized constraint (internal error)", NULL);
+ elog(ERROR, "parser: unrecognized constraint (internal error)");
break;
}
break;
default:
- elog(ERROR, "parser: unrecognized node (internal error)", NULL);
+ elog(ERROR, "parser: unrecognized node (internal error)");
}
-
- elements = lnext(elements);
}
stmt->tableElts = columns;
* or if a SERIAL column was defined along with a table PRIMARY KEY constraint.
* - thomas 1999-05-11
*/
- if ((pkey != NULL) && (length(lfirst(pkey->indexParams)) == 1))
+ if (pkey != NULL)
{
dlist = ilist;
ilist = NIL;
while (dlist != NIL)
{
- int keep = TRUE;
+ List *pcols, *icols;
+ int plen, ilen;
+ int keep = TRUE;
index = lfirst(dlist);
+ pcols = pkey->indexParams;
+ icols = index->indexParams;
- /*
- * has a single column argument, so might be a conflicting
- * index...
- */
- if ((index != pkey)
- && (length(index->indexParams) == 1))
+ plen = length(pcols);
+ ilen = length(icols);
+
+ /* Not the same as the primary key? Then we should look... */
+ if ((index != pkey) && (ilen == plen))
{
- char *pname = ((IndexElem *) lfirst(index->indexParams))->name;
- char *iname = ((IndexElem *) lfirst(index->indexParams))->name;
+ keep = FALSE;
+ while ((pcols != NIL) && (icols != NIL))
+ {
+ IndexElem *pcol = lfirst(pcols);
+ IndexElem *icol = lfirst(icols);
+ char *pname = pcol->name;
+ char *iname = icol->name;
- /* same names? then don't keep... */
- keep = (strcmp(iname, pname) != 0);
+ /* different names? then no match... */
+ if (strcmp(iname, pname) != 0)
+ {
+ keep = TRUE;
+ break;
+ }
+ pcols = lnext(pcols);
+ icols = lnext(icols);
+ }
}
if (keep)
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;
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,
- NIL,
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
*/
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);
transformUpdateStmt(ParseState *pstate, UpdateStmt *stmt)
{
Query *qry = makeNode(Query);
+ List *origTargetList;
+ List *tl;
qry->commandType = CMD_UPDATE;
pstate->p_is_update = true;
* 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;
}