Rewrite parser's handling of INSERT ... SELECT so that processing

of the SELECT part of the statement is just like a plain SELECT.  All
INSERT-specific processing happens after the SELECT parsing is done.
This eliminates many problems, e.g. INSERT ... SELECT ... GROUP BY using
the wrong column labels.  Ensure that DEFAULT clauses are coerced to
the target column type, whether or not stored clause produces the right
type.  Substantial cleanup of parser's array support.
This commit is contained in:
Tom Lane 1999-07-19 00:26:20 +00:00
parent c981442772
commit 7f76eab140
12 changed files with 877 additions and 1339 deletions

View File

@ -7,7 +7,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/executor/execQual.c,v 1.57 1999/07/17 20:16:57 momjian Exp $
* $Header: /cvsroot/pgsql/src/backend/executor/execQual.c,v 1.58 1999/07/19 00:26:15 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -86,31 +86,44 @@ ExecEvalArrayRef(ArrayRef *arrayRef,
bool *isNull,
bool *isDone)
{
bool dummy;
ArrayType *array_scanner;
List *elt;
int i = 0,
j = 0;
ArrayType *array_scanner;
List *upperIndexpr,
*lowerIndexpr;
Node *assgnexpr;
List *elt;
IntArray upper,
lower;
int *lIndex;
char *dataPtr;
bool dummy;
*isNull = false;
array_scanner = (ArrayType *) ExecEvalExpr(arrayRef->refexpr,
econtext,
isNull,
isDone);
if (*isNull)
return (Datum) NULL;
upperIndexpr = arrayRef->refupperindexpr;
foreach(elt, upperIndexpr)
if (arrayRef->refexpr != NULL)
{
array_scanner = (ArrayType *) ExecEvalExpr(arrayRef->refexpr,
econtext,
isNull,
isDone);
if (*isNull)
return (Datum) NULL;
}
else
{
/* Null refexpr indicates we are doing an INSERT into an array column.
* For now, we just take the refassgnexpr (which the parser will have
* ensured is an array value) and return it as-is, ignoring any
* subscripts that may have been supplied in the INSERT column list.
* This is a kluge, but it's not real clear what the semantics ought
* to be...
*/
array_scanner = NULL;
}
foreach(elt, arrayRef->refupperindexpr)
{
if (i >= MAXDIM)
elog(ERROR, "ExecEvalArrayRef: can only handle %d dimensions",
MAXDIM);
upper.indx[i++] = (int32) ExecEvalExpr((Node *) lfirst(elt),
econtext,
isNull,
@ -119,12 +132,14 @@ ExecEvalArrayRef(ArrayRef *arrayRef,
return (Datum) NULL;
}
lowerIndexpr = arrayRef->reflowerindexpr;
lIndex = NULL;
if (lowerIndexpr != NIL)
if (arrayRef->reflowerindexpr != NIL)
{
foreach(elt, lowerIndexpr)
foreach(elt, arrayRef->reflowerindexpr)
{
if (j >= MAXDIM)
elog(ERROR, "ExecEvalArrayRef: can only handle %d dimensions",
MAXDIM);
lower.indx[j++] = (int32) ExecEvalExpr((Node *) lfirst(elt),
econtext,
isNull,
@ -137,30 +152,42 @@ ExecEvalArrayRef(ArrayRef *arrayRef,
"ExecEvalArrayRef: upper and lower indices mismatch");
lIndex = lower.indx;
}
assgnexpr = arrayRef->refassgnexpr;
if (assgnexpr != NULL)
else
{
dataPtr = (char *) ExecEvalExpr((Node *)
assgnexpr, econtext,
isNull, &dummy);
lIndex = NULL;
}
if (arrayRef->refassgnexpr != NULL)
{
Datum sourceData = ExecEvalExpr(arrayRef->refassgnexpr,
econtext,
isNull,
&dummy);
if (*isNull)
return (Datum) NULL;
execConstByVal = arrayRef->refelembyval;
execConstLen = arrayRef->refelemlength;
if (array_scanner == NULL)
return sourceData; /* XXX do something else? */
if (lIndex == NULL)
return (Datum) array_set(array_scanner, i, upper.indx, dataPtr,
return (Datum) array_set(array_scanner, i, upper.indx,
(char *) sourceData,
arrayRef->refelembyval,
arrayRef->refelemlength,
arrayRef->refattrlength, isNull);
return (Datum) array_assgn(array_scanner, i, upper.indx,
lower.indx,
(ArrayType *) dataPtr,
(ArrayType *) sourceData,
arrayRef->refelembyval,
arrayRef->refelemlength, isNull);
}
execConstByVal = arrayRef->refelembyval;
execConstLen = arrayRef->refelemlength;
if (lIndex == NULL)
return (Datum) array_ref(array_scanner, i, upper.indx,
arrayRef->refelembyval,

View File

@ -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...)
*----------
*/
/* set up a range table --- note INSERT target is not in it yet */
makeRangeTable(pstate, stmt->fromClause, &fromQual);
qry->uniqueFlag = stmt->unique;
/* fix the target list */
icolumns = pstate->p_insert_columns = makeTargetNames(pstate, stmt->cols);
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;
qry->qual = transformWhereClause(pstate, stmt->whereClause, fromQual);
/*
* 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);
/*
* 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.)
*/
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) /* 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,15 +1023,20 @@ 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;
}

View File

@ -7,12 +7,13 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/parser/parse_clause.c,v 1.41 1999/07/17 20:17:23 momjian Exp $
* $Header: /cvsroot/pgsql/src/backend/parser/parse_clause.c,v 1.42 1999/07/19 00:26:19 tgl Exp $
*
*-------------------------------------------------------------------------
*/
#include "postgres.h"
#include "access/heapam.h"
#include "nodes/relation.h"
#include "parse.h"
@ -29,35 +30,40 @@
static char *clauseText[] = {"ORDER", "GROUP"};
static TargetEntry *
findTargetlistEntry(ParseState *pstate, Node *node, List *tlist, int clause);
static TargetEntry *findTargetlistEntry(ParseState *pstate, Node *node,
List *tlist, int clause);
static void parseFromClause(ParseState *pstate, List *frmList, Node **qual);
static char *transformTableEntry(ParseState *pstate, RangeVar *r);
#ifdef ENABLE_OUTER_JOINS
Node *transformUsingClause(ParseState *pstate, List *onList, char *lname, char *rname);
static Node *transformUsingClause(ParseState *pstate, List *onList,
char *lname, char *rname);
#endif
static char *transformTableEntry(ParseState *pstate, RangeVar *r);
/*
* makeRangeTable -
* make a range table with the specified relation (optional) and the
* from_clause.
* Build the initial range table from the FROM clause.
*/
void
makeRangeTable(ParseState *pstate, char *relname, List *frmList, Node **qual)
makeRangeTable(ParseState *pstate, List *frmList, Node **qual)
{
/* Currently, nothing to do except this: */
parseFromClause(pstate, frmList, qual);
}
/*
* setTargetTable
* Add the target relation of INSERT or UPDATE to the range table,
* and make the special links to it in the ParseState.
*/
void
setTargetTable(ParseState *pstate, char *relname)
{
RangeTblEntry *rte;
int sublevels_up;
parseFromClause(pstate, frmList, qual);
if (relname == NULL)
return;
if ((refnameRangeTablePosn(pstate, relname, &sublevels_up) == 0)
|| (sublevels_up != 0))
rte = addRangeTableEntry(pstate, relname, relname, FALSE, FALSE);
@ -136,7 +142,7 @@ makeAttr(char *relname, char *attname)
/* transformUsingClause()
* Take an ON or USING clause from a join expression and expand if necessary.
*/
Node *
static Node *
transformUsingClause(ParseState *pstate, List *onList, char *lname, char *rname)
{
A_Expr *expr = NULL;
@ -295,7 +301,8 @@ parseFromClause(ParseState *pstate, List *frmList, Node **qual)
if (IsA(j->quals, List))
j->quals = lcons(transformUsingClause(pstate, (List *) j->quals, lname, rname), NIL);
Assert(qual != NULL);
if (qual == NULL)
elog(ERROR, "JOIN/ON not supported in this context");
if (*qual == NULL)
*qual = lfirst(j->quals);
@ -329,145 +336,111 @@ parseFromClause(ParseState *pstate, List *frmList, Node **qual)
}
}
/*
* findTargetlistEntry -
* returns the Resdom in the target list matching the specified varname
* and range. If none exist one is created.
*
* Rewritten for ver 6.4 to handle expressions in the GROUP/ORDER BY clauses.
* - daveh@insightdist.com 1998-07-31
* Returns the targetlist entry matching the given (untransformed) node.
* If no matching entry exists, one is created and appended to the target
* list as a "resjunk" node.
*
* node the ORDER BY or GROUP BY expression to be matched
* tlist the existing target list (NB: this cannot be NIL, which is a
* good thing since we'd be unable to append to it...)
* clause identifies clause type for error messages.
*/
static TargetEntry *
findTargetlistEntry(ParseState *pstate, Node *node, List *tlist, int clause)
{
List *l;
int rtable_pos = 0,
target_pos = 0,
targetlist_pos = 0;
TargetEntry *target_result = NULL;
Value *val = NULL;
char *relname = NULL;
char *name = NULL;
Node *expr = NULL;
int relCnt = 0;
List *tl;
Node *expr;
/* Pull out some values before looping thru target list */
switch (nodeTag(node))
/*----------
* Handle two special cases as mandated by the SQL92 spec:
*
* 1. ORDER/GROUP BY ColumnName
* For a bare identifier, we search for a matching column name
* in the existing target list. Multiple matches are an error
* unless they refer to identical values; for example,
* we allow SELECT a, a FROM table ORDER BY a
* but not SELECT a AS b, b FROM table ORDER BY b
* If no match is found, we fall through and treat the identifier
* as an expression.
*
* 2. ORDER/GROUP BY IntegerConstant
* This means to use the n'th item in the existing target list.
* Note that it would make no sense to order/group by an actual
* constant, so this does not create a conflict with our extension
* to order/group by an expression.
*
* Note that pre-existing resjunk targets must not be used in either case.
*----------
*/
if (IsA(node, Ident) && ((Ident *) node)->indirection == NIL)
{
case T_Attr:
relname = ((Attr *) node)->relname;
val = (Value *) lfirst(((Attr *) node)->attrs);
name = strVal(val);
rtable_pos = refnameRangeTablePosn(pstate, relname, NULL);
relCnt = length(pstate->p_rtable);
break;
char *name = ((Ident *) node)->name;
foreach(tl, tlist)
{
TargetEntry *tle = (TargetEntry *) lfirst(tl);
Resdom *resnode = tle->resdom;
case T_Ident:
name = ((Ident *) node)->name;
relCnt = length(pstate->p_rtable);
break;
if (!resnode->resjunk &&
strcmp(resnode->resname, name) == 0)
{
if (target_result != NULL)
{
if (! equal(target_result->expr, tle->expr))
elog(ERROR, "%s BY '%s' is ambiguous",
clauseText[clause], name);
}
else
target_result = tle;
/* Stay in loop to check for ambiguity */
}
}
if (target_result != NULL)
return target_result; /* return the first match */
}
if (IsA(node, A_Const))
{
Value *val = &((A_Const *) node)->val;
int targetlist_pos = 0;
int target_pos;
case T_A_Const:
val = &((A_Const *) node)->val;
if (nodeTag(val) != T_Integer)
elog(ERROR, "Non-integer constant in %s BY", clauseText[clause]);
target_pos = intVal(val);
foreach(tl, tlist)
{
TargetEntry *tle = (TargetEntry *) lfirst(tl);
Resdom *resnode = tle->resdom;
if (nodeTag(val) != T_Integer)
elog(ERROR, "Illegal Constant in %s BY", clauseText[clause]);
target_pos = intVal(val);
break;
case T_FuncCall:
case T_A_Expr:
expr = transformExpr(pstate, node, EXPR_COLUMN_FIRST);
break;
default:
elog(ERROR, "Illegal %s BY node = %d", clauseText[clause], nodeTag(node));
if (!resnode->resjunk)
{
if (++targetlist_pos == target_pos)
return tle; /* return the unique match */
}
}
elog(ERROR, "%s BY position %d is not in target list",
clauseText[clause], target_pos);
}
/*
* Loop through target entries and try to match to node
* Otherwise, we have an expression (this is a Postgres extension
* not found in SQL92). Convert the untransformed node to a
* transformed expression, and search for a match in the tlist.
* NOTE: it doesn't really matter whether there is more than one
* match. Also, we are willing to match a resjunk target here,
* though the above cases must ignore resjunk targets.
*/
foreach(l, tlist)
expr = transformExpr(pstate, node, EXPR_COLUMN_FIRST);
foreach(tl, tlist)
{
TargetEntry *target = (TargetEntry *) lfirst(l);
Resdom *resnode = target->resdom;
Var *var = (Var *) target->expr;
char *resname = resnode->resname;
int test_rtable_pos = var->varno;
TargetEntry *tle = (TargetEntry *) lfirst(tl);
++targetlist_pos;
switch (nodeTag(node))
{
case T_Attr:
if (strcmp(resname, name) == 0 && rtable_pos == test_rtable_pos)
{
/*
* Check for only 1 table & ORDER BY -ambiguity does
* not matter here
*/
if (clause == ORDER_CLAUSE && relCnt == 1)
return target;
if (target_result != NULL)
elog(ERROR, "%s BY '%s' is ambiguous", clauseText[clause], name);
else
target_result = target;
/* Stay in loop to check for ambiguity */
}
break;
case T_Ident:
if (strcmp(resname, name) == 0)
{
/*
* Check for only 1 table & ORDER BY -ambiguity does
* not matter here
*/
if (clause == ORDER_CLAUSE && relCnt == 1)
return target;
if (target_result != NULL)
elog(ERROR, "%s BY '%s' is ambiguous", clauseText[clause], name);
else
target_result = target;
/* Stay in loop to check for ambiguity */
}
break;
case T_A_Const:
if (target_pos == targetlist_pos)
{
/* Can't be ambigious and we got what we came for */
return target;
}
break;
case T_FuncCall:
case T_A_Expr:
if (equal(expr, target->expr))
{
/*
* Check for only 1 table & ORDER BY -ambiguity does
* not matter here
*/
if (clause == ORDER_CLAUSE)
return target;
if (target_result != NULL)
elog(ERROR, "GROUP BY has ambiguous expression");
else
target_result = target;
}
break;
default:
elog(ERROR, "Illegal %s BY node = %d", clauseText[clause], nodeTag(node));
}
if (equal(expr, tle->expr))
return tle;
}
/*
@ -475,51 +448,13 @@ findTargetlistEntry(ParseState *pstate, Node *node, List *tlist, int clause)
* the end of the target list. This target is set to be resjunk =
* TRUE so that it will not be projected into the final tuple.
*/
if (target_result == NULL)
{
switch (nodeTag(node))
{
case T_Attr:
target_result = MakeTargetEntryIdent(pstate, node,
&((Attr *) node)->relname, NULL,
((Attr *) node)->relname, true);
lappend(tlist, target_result);
break;
case T_Ident:
target_result = MakeTargetEntryIdent(pstate, node,
&((Ident *) node)->name, NULL,
((Ident *) node)->name, true);
lappend(tlist, target_result);
break;
case T_A_Const:
/*
* If we got this far, then must have been an out-of-range
* column number
*/
elog(ERROR, "%s BY position %d is not in target list", clauseText[clause], target_pos);
break;
case T_FuncCall:
case T_A_Expr:
target_result = MakeTargetEntryExpr(pstate, "resjunk", expr, false, true);
lappend(tlist, target_result);
break;
default:
elog(ERROR, "Illegal %s BY node = %d", clauseText[clause], nodeTag(node));
break;
}
}
target_result = transformTargetEntry(pstate, node, expr, NULL, true);
lappend(tlist, target_result);
return target_result;
}
/*
* transformGroupClause -
* transform a Group By clause
@ -529,55 +464,42 @@ List *
transformGroupClause(ParseState *pstate, List *grouplist, List *targetlist)
{
List *glist = NIL,
*gl = NIL;
*gl,
*othergl;
int nextgroupref = 1;
while (grouplist != NIL)
foreach(gl, grouplist)
{
GroupClause *grpcl = makeNode(GroupClause);
TargetEntry *restarget;
Resdom *resdom;
restarget = findTargetlistEntry(pstate, lfirst(grouplist), targetlist, GROUP_CLAUSE);
restarget = findTargetlistEntry(pstate, lfirst(gl),
targetlist, GROUP_CLAUSE);
resdom = restarget->resdom;
grpcl->grpOpoid = oprid(oper("<",
resdom->restype,
resdom->restype, false));
if (glist == NIL)
/* avoid making duplicate grouplist entries */
foreach(othergl, glist)
{
int groupref = length(glist) + 1;
GroupClause *gcl = (GroupClause *) lfirst(othergl);
restarget->resdom->resgroupref = groupref;
grpcl->tleGroupref = groupref;
gl = glist = lcons(grpcl, NIL);
if (equal(get_groupclause_expr(gcl, targetlist),
restarget->expr))
break;
}
else
if (othergl == NIL) /* not in grouplist already */
{
List *i;
GroupClause *grpcl = makeNode(GroupClause);
foreach(i, glist)
{
GroupClause *gcl = (GroupClause *) lfirst(i);
grpcl->tleGroupref = nextgroupref++;
resdom->resgroupref = grpcl->tleGroupref;
if (equal(get_groupclause_expr(gcl, targetlist),
restarget->expr))
break;
}
if (i == NIL) /* not in grouplist already */
{
int groupref = length(glist) + 1;
grpcl->grpOpoid = oprid(oper("<",
resdom->restype,
resdom->restype, false));
restarget->resdom->resgroupref = groupref;
grpcl->tleGroupref = groupref;
lnext(gl) = lcons(grpcl, NIL);
gl = lnext(gl);
}
else
pfree(grpcl); /* get rid of this */
glist = lappend(glist, grpcl);
}
grouplist = lnext(grouplist);
}
return glist;
@ -604,7 +526,8 @@ transformSortClause(ParseState *pstate,
TargetEntry *restarget;
Resdom *resdom;
restarget = findTargetlistEntry(pstate, sortby->node, targetlist, ORDER_CLAUSE);
restarget = findTargetlistEntry(pstate, sortby->node,
targetlist, ORDER_CLAUSE);
sortcl->resdom = resdom = restarget->resdom;

View File

@ -7,7 +7,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/parser/parse_expr.c,v 1.54 1999/07/17 20:17:23 momjian Exp $
* $Header: /cvsroot/pgsql/src/backend/parser/parse_expr.c,v 1.55 1999/07/19 00:26:19 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -30,7 +30,7 @@ static Node *parser_typecast(Value *expr, TypeName *typename, int32 atttypmod);
static Node *transformAttr(ParseState *pstate, Attr *att, int precedence);
static Node *transformIdent(ParseState *pstate, Ident *ident, int precedence);
static Node *transformIndirection(ParseState *pstate, Node *basenode,
List *indirection, int precedence);
List *indirection);
/*
* transformExpr -
@ -81,7 +81,7 @@ transformExpr(ParseState *pstate, Node *expr, int precedence)
param->paramtype = (Oid) toid;
param->param_tlist = (List *) NULL;
result = transformIndirection(pstate, (Node *) param,
pno->indirection, precedence);
pno->indirection);
break;
}
case T_A_Expr:
@ -467,37 +467,12 @@ transformExpr(ParseState *pstate, Node *expr, int precedence)
}
static Node *
transformIndirection(ParseState *pstate, Node *basenode,
List *indirection, int precedence)
transformIndirection(ParseState *pstate, Node *basenode, List *indirection)
{
List *idx;
if (indirection == NIL)
return basenode;
foreach (idx, indirection)
{
A_Indices *ai = (A_Indices *) lfirst(idx);
Node *lexpr = NULL,
*uexpr;
/* uidx is always present, but lidx might be null */
if (ai->lidx != NULL)
{
lexpr = transformExpr(pstate, ai->lidx, precedence);
if (exprType(lexpr) != INT4OID)
elog(ERROR, "array index expressions must be int4's");
}
uexpr = transformExpr(pstate, ai->uidx, precedence);
if (exprType(uexpr) != INT4OID)
elog(ERROR, "array index expressions must be int4's");
ai->lidx = lexpr;
ai->uidx = uexpr;
/*
* note we reuse the list of A_Indices nodes, make sure
* we don't free them! Otherwise, make a new list here
*/
}
return (Node *) make_array_ref(basenode, indirection);
return (Node *) transformArraySubscripts(pstate, basenode,
indirection, false, NULL);
}
static Node *
@ -505,11 +480,9 @@ transformAttr(ParseState *pstate, Attr *att, int precedence)
{
Node *basenode;
/* what if att->attrs == "*"? */
basenode = ParseNestedFuncOrColumn(pstate, att, &pstate->p_last_resno,
precedence);
return transformIndirection(pstate, basenode,
att->indirection, precedence);
return transformIndirection(pstate, basenode, att->indirection);
}
static Node *
@ -555,7 +528,7 @@ transformIdent(ParseState *pstate, Ident *ident, int precedence)
Oid
exprType(Node *expr)
{
Oid type = (Oid) 0;
Oid type = (Oid) InvalidOid;
if (!expr)
return type;
@ -622,6 +595,42 @@ exprType(Node *expr)
return type;
}
/*
* exprTypmod -
* returns the type-specific attrmod of the expression, if it can be
* determined. In most cases, it can't and we return -1.
*/
int32
exprTypmod(Node *expr)
{
if (!expr)
return -1;
switch (nodeTag(expr))
{
case T_Var:
return ((Var *) expr)->vartypmod;
case T_Const:
{
/* Be smart about string constants... */
Const *con = (Const *) expr;
switch (con->consttype)
{
case BPCHAROID:
if (! con->constisnull)
return VARSIZE(DatumGetPointer(con->constvalue));
break;
default:
break;
}
}
break;
default:
break;
}
return -1;
}
static Node *
parser_typecast(Value *expr, TypeName *typename, int32 atttypmod)
{

View File

@ -7,7 +7,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/parser/parse_node.c,v 1.28 1999/07/17 20:17:24 momjian Exp $
* $Header: /cvsroot/pgsql/src/backend/parser/parse_node.c,v 1.29 1999/07/19 00:26:19 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -24,6 +24,7 @@
#include "parser/parse_node.h"
#include "parser/parse_oper.h"
#include "parser/parse_relation.h"
#include "parser/parse_target.h"
#include "parser/parse_type.h"
#include "utils/builtins.h"
#include "utils/lsyscache.h"
@ -222,164 +223,184 @@ make_var(ParseState *pstate, Oid relid, char *refname,
}
/*
* make_array_ref() -- Make an array reference node.
* transformArraySubscripts()
* Transform array subscripting. This is used for both
* array fetch and array assignment.
*
* Array references can hang off of arbitrary nested dot (or
* function invocation) expressions. This routine takes a
* tree generated by ParseFunc() and an array index and
* generates a new array reference tree. We do some simple
* typechecking to be sure the dereference is valid in the
* type system, but we don't do any bounds checking here.
* In an array fetch, we are given a source array value and we produce an
* expression that represents the result of extracting a single array element
* or an array slice.
*
* indirection is a list of A_Indices
* In an array assignment, we are given a destination array value plus a
* source value that is to be assigned to a single element or a slice of
* that array. We produce an expression that represents the new array value
* with the source data inserted into the right part of the array.
*
* pstate Parse state
* arrayBase Already-transformed expression for the array as a whole
* indirection Untransformed list of subscripts (must not be NIL)
* forceSlice If true, treat subscript as array slice in all cases
* assignFrom NULL for array fetch, else transformed expression for source.
*/
ArrayRef *
make_array_ref(Node *expr,
List *indirection)
ArrayRef *
transformArraySubscripts(ParseState *pstate,
Node *arrayBase,
List *indirection,
bool forceSlice,
Node *assignFrom)
{
Oid typearray;
Oid typearray,
typeelement,
typeresult;
HeapTuple type_tuple;
Form_pg_type type_struct_array,
type_struct_element;
ArrayRef *aref;
Oid reftype;
bool isSlice = forceSlice;
List *upperIndexpr = NIL;
List *lowerIndexpr = NIL;
List *idx;
ArrayRef *aref;
typearray = exprType(expr);
/* Get the type tuple for the array */
typearray = exprType(arrayBase);
type_tuple = SearchSysCacheTuple(TYPOID,
ObjectIdGetDatum(typearray),
0, 0, 0);
if (!HeapTupleIsValid(type_tuple))
elog(ERROR, "make_array_ref: Cache lookup failed for type %u\n",
elog(ERROR, "transformArraySubscripts: Cache lookup failed for array type %u",
typearray);
/* get the array type struct from the type tuple */
type_struct_array = (Form_pg_type) GETSTRUCT(type_tuple);
if (type_struct_array->typelem == InvalidOid)
elog(ERROR, "make_array_ref: type %s is not an array",
typeelement = type_struct_array->typelem;
if (typeelement == InvalidOid)
elog(ERROR, "transformArraySubscripts: type %s is not an array",
type_struct_array->typname);
/* get the type tuple for the element type */
/* Get the type tuple for the array element type */
type_tuple = SearchSysCacheTuple(TYPOID,
ObjectIdGetDatum(type_struct_array->typelem),
ObjectIdGetDatum(typeelement),
0, 0, 0);
if (!HeapTupleIsValid(type_tuple))
elog(ERROR, "make_array_ref: Cache lookup failed for type %u\n",
typearray);
elog(ERROR, "transformArraySubscripts: Cache lookup failed for array element type %u",
typeelement);
type_struct_element = (Form_pg_type) GETSTRUCT(type_tuple);
while (indirection != NIL)
{
A_Indices *ind = lfirst(indirection);
if (ind->lidx)
/*
* XXX assumes all lower indices non null in this case
*/
lowerIndexpr = lappend(lowerIndexpr, ind->lidx);
upperIndexpr = lappend(upperIndexpr, ind->uidx);
indirection = lnext(indirection);
}
aref = makeNode(ArrayRef);
aref->refattrlength = type_struct_array->typlen;
aref->refelemlength = type_struct_element->typlen;
aref->refelemtype = type_struct_array->typelem;
aref->refelembyval = type_struct_element->typbyval;
aref->refupperindexpr = upperIndexpr;
aref->reflowerindexpr = lowerIndexpr;
aref->refexpr = expr;
aref->refassgnexpr = NULL;
if (lowerIndexpr == NIL) /* accessing a single array element */
reftype = aref->refelemtype;
else
/* request to clip a part of the array, the result is another array */
reftype = typearray;
/*
* we change it to reflect the true type; since the original
* refelemtype doesn't seem to get used anywhere. - ay 10/94
* A list containing only single subscripts refers to a single array
* element. If any of the items are double subscripts (lower:upper),
* then the subscript expression means an array slice operation.
* In this case, we supply a default lower bound of 1 for any items
* that contain only a single subscript.
* The forceSlice parameter forces us to treat the operation as a
* slice, even if no lower bounds are mentioned. Otherwise,
* we have to prescan the indirection list to see if there are any
* double subscripts.
*/
aref->refelemtype = reftype;
if (! isSlice)
{
foreach (idx, indirection)
{
A_Indices *ai = (A_Indices *) lfirst(idx);
if (ai->lidx != NULL)
{
isSlice = true;
break;
}
}
}
return aref;
}
/* The type represented by the subscript expression is the element type
* if we are fetching a single element, but it is the same as the array
* type if we are fetching a slice or storing.
*/
if (isSlice || assignFrom != NULL)
typeresult = typearray;
else
typeresult = typeelement;
/*
* Transform the subscript expressions.
*/
foreach (idx, indirection)
{
A_Indices *ai = (A_Indices *) lfirst(idx);
Node *subexpr;
/* make_array_set()
*/
ArrayRef *
make_array_set(Expr *target_expr,
List *upperIndexpr,
List *lowerIndexpr,
Expr *expr)
{
Oid typearray;
HeapTuple type_tuple;
Form_pg_type type_struct_array;
Form_pg_type type_struct_element;
ArrayRef *aref;
Oid reftype;
if (isSlice)
{
if (ai->lidx)
{
subexpr = transformExpr(pstate, ai->lidx, EXPR_COLUMN_FIRST);
/* If it's not int4 already, try to coerce */
subexpr = CoerceTargetExpr(pstate, subexpr,
exprType(subexpr), INT4OID);
if (subexpr == NULL)
elog(ERROR, "array index expressions must be integers");
}
else
{
/* Make a constant 1 */
subexpr = (Node *) makeConst(INT4OID,
sizeof(int32),
Int32GetDatum(1),
false,
true, /* pass by value */
false,
false);
}
lowerIndexpr = lappend(lowerIndexpr, subexpr);
}
subexpr = transformExpr(pstate, ai->uidx, EXPR_COLUMN_FIRST);
/* If it's not int4 already, try to coerce */
subexpr = CoerceTargetExpr(pstate, subexpr,
exprType(subexpr), INT4OID);
if (subexpr == NULL)
elog(ERROR, "array index expressions must be integers");
upperIndexpr = lappend(upperIndexpr, subexpr);
}
typearray = exprType((Node *) target_expr);
/*
* If doing an array store, coerce the source value to the right type.
*/
if (assignFrom != NULL)
{
Oid typesource = exprType(assignFrom);
Oid typeneeded = isSlice ? typearray : typeelement;
type_tuple = SearchSysCacheTuple(TYPOID,
ObjectIdGetDatum(typearray),
0, 0, 0);
if (!HeapTupleIsValid(type_tuple))
elog(ERROR, "make_array_ref: Cache lookup failed for type %u\n",
typearray);
/* get the array type struct from the type tuple */
type_struct_array = (Form_pg_type) GETSTRUCT(type_tuple);
if (type_struct_array->typelem == InvalidOid)
elog(ERROR, "make_array_ref: type %s is not an array",
type_struct_array->typname);
/* get the type tuple for the element type */
type_tuple = SearchSysCacheTuple(TYPOID,
ObjectIdGetDatum(type_struct_array->typelem),
0, 0, 0);
if (!HeapTupleIsValid(type_tuple))
elog(ERROR, "make_array_ref: Cache lookup failed for type %u\n",
typearray);
type_struct_element = (Form_pg_type) GETSTRUCT(type_tuple);
if (typesource != InvalidOid)
{
if (typesource != typeneeded)
{
assignFrom = CoerceTargetExpr(pstate, assignFrom,
typesource, typeneeded);
if (assignFrom == NULL)
elog(ERROR, "Array assignment requires type '%s'"
" but expression is of type '%s'"
"\n\tYou will need to rewrite or cast the expression",
typeidTypeName(typeneeded),
typeidTypeName(typesource));
}
}
}
/*
* Ready to build the ArrayRef node.
*/
aref = makeNode(ArrayRef);
aref->refattrlength = type_struct_array->typlen;
aref->refelemlength = type_struct_element->typlen;
aref->refelemtype = type_struct_array->typelem;
aref->refelemtype = typeresult; /* XXX should save element type too */
aref->refelembyval = type_struct_element->typbyval;
aref->refupperindexpr = upperIndexpr;
aref->reflowerindexpr = lowerIndexpr;
aref->refexpr = (Node *) target_expr;
aref->refassgnexpr = (Node *) expr;
/* accessing a single array element? */
if (lowerIndexpr == NIL)
reftype = aref->refelemtype;
/* otherwise, request to set a part of the array, by another array */
else
reftype = typearray;
aref->refelemtype = reftype;
aref->refexpr = arrayBase;
aref->refassgnexpr = assignFrom;
return aref;
}
/*
*
* make_const -
*
* - takes a lispvalue, (as returned to the yacc routine by the lexer)

View File

@ -7,7 +7,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/parser/parse_relation.c,v 1.25 1999/07/17 20:17:25 momjian Exp $
* $Header: /cvsroot/pgsql/src/backend/parser/parse_relation.c,v 1.26 1999/07/19 00:26:20 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -25,10 +25,8 @@
#include "utils/builtins.h"
#include "utils/lsyscache.h"
static void checkTargetTypes(ParseState *pstate, char *target_colname,
char *refname, char *colname);
struct
static struct
{
char *field;
int code;
@ -97,7 +95,6 @@ refnameRangeTablePosn(ParseState *pstate, char *refname, int *sublevels_up)
int index;
List *temp;
if (sublevels_up)
*sublevels_up = 0;
@ -175,7 +172,7 @@ colnameRangeTableEntry(ParseState *pstate, char *colname)
/*
* put new entry in pstate p_rtable structure, or return pointer
* if pstate null
*/
*/
RangeTblEntry *
addRangeTableEntry(ParseState *pstate,
char *relname,
@ -239,38 +236,31 @@ addRangeTableEntry(ParseState *pstate,
List *
expandAll(ParseState *pstate, char *relname, char *refname, int *this_resno)
{
List *te_list = NIL;
RangeTblEntry *rte;
Relation rel;
List *te_tail = NIL,
*te_head = NIL;
Var *varnode;
int varattno,
maxattrs;
RangeTblEntry *rte;
rte = refnameRangeTableEntry(pstate, refname);
if (rte == NULL)
rte = addRangeTableEntry(pstate, relname, refname, FALSE, FALSE);
rel = heap_open(rte->relid);
if (rel == NULL)
elog(ERROR, "Unable to expand all -- heap_open failed on %s",
rte->refname);
maxattrs = RelationGetNumberOfAttributes(rel);
for (varattno = 0; varattno <= maxattrs - 1; varattno++)
for (varattno = 0; varattno < maxattrs; varattno++)
{
char *attrname;
char *resname = NULL;
Var *varnode;
TargetEntry *te = makeNode(TargetEntry);
attrname = pstrdup((rel->rd_att->attrs[varattno]->attname).data);
varnode = (Var *) make_var(pstate, rte->relid, refname, attrname);
handleTargetColname(pstate, &resname, refname, attrname);
if (resname != NULL)
attrname = resname;
attrname = pstrdup(rel->rd_att->attrs[varattno]->attname.data);
varnode = make_var(pstate, rte->relid, refname, attrname);
/*
* Even if the elements making up a set are complex, the set
@ -285,15 +275,12 @@ expandAll(ParseState *pstate, char *relname, char *refname, int *this_resno)
(Oid) 0,
false);
te->expr = (Node *) varnode;
if (te_head == NIL)
te_head = te_tail = lcons(te, NIL);
else
te_tail = lappend(te_tail, te);
te_list = lappend(te_list, te);
}
heap_close(rel);
return te_head;
return te_list;
}
/*
@ -378,102 +365,3 @@ attnumTypeId(Relation rd, int attid)
*/
return rd->rd_att->attrs[attid - 1]->atttypid;
}
/* handleTargetColname()
* Use column names from insert.
*/
void
handleTargetColname(ParseState *pstate, char **resname,
char *refname, char *colname)
{
if (pstate->p_is_insert)
{
if (pstate->p_insert_columns != NIL)
{
Ident *id = lfirst(pstate->p_insert_columns);
*resname = id->name;
pstate->p_insert_columns = lnext(pstate->p_insert_columns);
}
else
elog(ERROR, "INSERT has more expressions than target columns");
}
if (pstate->p_is_insert || pstate->p_is_update)
checkTargetTypes(pstate, *resname, refname, colname);
}
/* checkTargetTypes()
* Checks value and target column types.
*/
static void
checkTargetTypes(ParseState *pstate, char *target_colname,
char *refname, char *colname)
{
Oid attrtype_id,
attrtype_target;
int resdomno_id,
resdomno_target;
RangeTblEntry *rte;
if (target_colname == NULL || colname == NULL)
return;
if (refname != NULL)
rte = refnameRangeTableEntry(pstate, refname);
else
{
rte = colnameRangeTableEntry(pstate, colname);
if (rte == (RangeTblEntry *) NULL)
elog(ERROR, "Attribute %s not found", colname);
refname = rte->refname;
}
/*
if (pstate->p_is_insert && rte == pstate->p_target_rangetblentry)
elog(ERROR, "'%s' not available in this context", colname);
*/
resdomno_id = get_attnum(rte->relid, colname);
attrtype_id = get_atttype(rte->relid, resdomno_id);
resdomno_target = attnameAttNum(pstate->p_target_relation, target_colname);
attrtype_target = attnumTypeId(pstate->p_target_relation, resdomno_target);
#ifdef NOT_USED
if ((attrtype_id != attrtype_target)
|| (get_atttypmod(rte->relid, resdomno_id) !=
get_atttypmod(pstate->p_target_relation->rd_id, resdomno_target)))
{
if (can_coerce_type(1, &attrtype_id, &attrtype_target))
{
Node *expr = coerce_type(pstate, expr, attrtype_id,
attrtype_target,
get_atttypmod(pstate->p_target_relation->rd_id, resdomno_target));
elog(ERROR, "Type %s(%d) can be coerced to match target column %s(%d)",
colname, get_atttypmod(rte->relid, resdomno_id),
target_colname, get_atttypmod(pstate->p_target_relation->rd_id, resdomno_target));
}
else
{
elog(ERROR, "Type or size of %s(%d) does not match target column %s(%d)",
colname, get_atttypmod(rte->relid, resdomno_id),
target_colname, get_atttypmod(pstate->p_target_relation->rd_id, resdomno_target));
}
}
#else
if (attrtype_id != attrtype_target)
elog(ERROR, "Type of '%s' does not match target column '%s'",
colname, target_colname);
if (attrtype_id == BPCHAROID &&
get_atttypmod(rte->relid, resdomno_id) !=
get_atttypmod(pstate->p_target_relation->rd_id, resdomno_target))
elog(ERROR, "Length of '%s' is not equal to the length of target column '%s'",
colname, target_colname);
if (attrtype_id == VARCHAROID &&
get_atttypmod(rte->relid, resdomno_id) >
get_atttypmod(pstate->p_target_relation->rd_id, resdomno_target))
elog(ERROR, "Length of '%s' is longer than length of target column '%s'",
colname, target_colname);
#endif
}

File diff suppressed because it is too large Load Diff

View File

@ -6,7 +6,7 @@
*
* Copyright (c) 1994, Regents of the University of California
*
* $Id: parse_clause.h,v 1.11 1999/07/15 23:04:01 momjian Exp $
* $Id: parse_clause.h,v 1.12 1999/07/19 00:26:16 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -15,8 +15,10 @@
#include "parser/parse_node.h"
extern void makeRangeTable(ParseState *pstate, char *relname, List *frmList, Node **qual);
extern Node *transformWhereClause(ParseState *pstate, Node *where, Node *using);
extern void makeRangeTable(ParseState *pstate, List *frmList, Node **qual);
extern void setTargetTable(ParseState *pstate, char *relname);
extern Node *transformWhereClause(ParseState *pstate, Node *where,
Node *using);
extern List *transformGroupClause(ParseState *pstate, List *grouplist,
List *targetlist);
extern List *transformSortClause(ParseState *pstate,

View File

@ -1,12 +1,12 @@
/*-------------------------------------------------------------------------
*
* parse_exer.h
* parse_expr.h
*
*
*
* Copyright (c) 1994, Regents of the University of California
*
* $Id: parse_expr.h,v 1.13 1999/07/15 23:04:02 momjian Exp $
* $Id: parse_expr.h,v 1.14 1999/07/19 00:26:16 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -16,8 +16,12 @@
#include "parser/parse_node.h"
#include "parser/parse_type.h"
#define EXPR_COLUMN_FIRST 1
#define EXPR_RELATION_FIRST 2
extern Node *transformExpr(ParseState *pstate, Node *expr, int precedence);
extern Oid exprType(Node *expr);
extern int32 exprTypmod(Node *expr);
extern Node *parser_typecast2(Node *expr, Oid exprType, Type tp, int32 attypmod);
#endif /* PARSE_EXPR_H */

View File

@ -5,7 +5,7 @@
*
* Copyright (c) 1994, Regents of the University of California
*
* $Id: parse_node.h,v 1.14 1999/07/15 23:04:02 momjian Exp $
* $Id: parse_node.h,v 1.15 1999/07/19 00:26:17 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -20,7 +20,6 @@ typedef struct ParseState
{
int p_last_resno;
List *p_rtable;
List *p_insert_columns;
struct ParseState *parentParseState;
bool p_hasAggs;
bool p_hasSubLinks;
@ -36,12 +35,11 @@ extern ParseState *make_parsestate(ParseState *parentParseState);
extern Expr *make_op(char *opname, Node *ltree, Node *rtree);
extern Var *make_var(ParseState *pstate, Oid relid, char *refname,
char *attrname);
extern ArrayRef *make_array_ref(Node *expr,
List *indirection);
extern ArrayRef *make_array_set(Expr *target_expr,
List *upperIndexpr,
List *lowerIndexpr,
Expr *expr);
extern ArrayRef *transformArraySubscripts(ParseState *pstate,
Node *arrayBase,
List *indirection,
bool forceSlice,
Node *assignFrom);
extern Const *make_const(Value *value);
#endif /* PARSE_NODE_H */

View File

@ -1,17 +1,17 @@
/*-------------------------------------------------------------------------
*
* parse_query.h
* prototypes for parse_query.c.
* parse_relation.h
* prototypes for parse_relation.c.
*
*
* Copyright (c) 1994, Regents of the University of California
*
* $Id: parse_relation.h,v 1.11 1999/07/15 23:04:03 momjian Exp $
* $Id: parse_relation.h,v 1.12 1999/07/19 00:26:17 tgl Exp $
*
*-------------------------------------------------------------------------
*/
#ifndef PARSE_QUERY_H
#define PARSE_RANGE_H
#ifndef PARSE_RELATION_H
#define PARSE_RELATION_H
#include "parser/parse_node.h"
@ -30,7 +30,5 @@ extern int attnameAttNum(Relation rd, char *a);
extern bool attnameIsSet(Relation rd, char *name);
extern int attnumAttNelems(Relation rd, int attid);
extern Oid attnumTypeId(Relation rd, int attid);
extern void handleTargetColname(ParseState *pstate, char **resname,
char *refname, char *colname);
#endif /* PARSE_RANGE_H */
#endif /* PARSE_RELATION_H */

View File

@ -6,7 +6,7 @@
*
* Copyright (c) 1994, Regents of the University of California
*
* $Id: parse_target.h,v 1.14 1999/07/15 23:04:03 momjian Exp $
* $Id: parse_target.h,v 1.15 1999/07/19 00:26:18 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -15,23 +15,14 @@
#include "parser/parse_node.h"
#define EXPR_COLUMN_FIRST 1
#define EXPR_RELATION_FIRST 2
extern List *transformTargetList(ParseState *pstate, List *targetlist);
extern List *makeTargetNames(ParseState *pstate, List *cols);
extern TargetEntry *MakeTargetEntryIdent(ParseState *pstate,
Node *node,
char **resname,
char *refname,
char *colname,
bool resjunk);
extern TargetEntry *transformTargetEntry(ParseState *pstate,
Node *node, Node *expr,
char *colname, bool resjunk);
extern void updateTargetListEntry(ParseState *pstate, TargetEntry *tle,
char *colname, List *indirection);
extern Node *CoerceTargetExpr(ParseState *pstate, Node *expr,
Oid type_id, Oid attrtype);
TargetEntry *MakeTargetEntryExpr(ParseState *pstate,
char *colname,
Node *expr,
List *arrayRef,
bool resjunk);
Oid type_id, Oid attrtype);
extern List *makeTargetNames(ParseState *pstate, List *cols);
#endif /* PARSE_TARGET_H */