mirror of
https://github.com/postgres/postgres.git
synced 2025-11-09 06:21:09 +03:00
Add support for multi-row VALUES clauses as part of INSERT statements
(e.g. "INSERT ... VALUES (...), (...), ...") and elsewhere as allowed by the spec. (e.g. similar to a FROM clause subselect). initdb required. Joe Conway and Tom Lane.
This commit is contained in:
@@ -6,7 +6,7 @@
|
||||
* Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
|
||||
* Portions Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* $PostgreSQL: pgsql/src/backend/parser/analyze.c,v 1.340 2006/07/14 14:52:21 momjian Exp $
|
||||
* $PostgreSQL: pgsql/src/backend/parser/analyze.c,v 1.341 2006/08/02 01:59:46 joe Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -98,10 +98,13 @@ static Query *transformViewStmt(ParseState *pstate, ViewStmt *stmt,
|
||||
static Query *transformDeleteStmt(ParseState *pstate, DeleteStmt *stmt);
|
||||
static Query *transformInsertStmt(ParseState *pstate, InsertStmt *stmt,
|
||||
List **extras_before, List **extras_after);
|
||||
static List *transformInsertRow(ParseState *pstate, List *exprlist,
|
||||
List *stmtcols, List *icolumns, List *attrnos);
|
||||
static Query *transformIndexStmt(ParseState *pstate, IndexStmt *stmt);
|
||||
static Query *transformRuleStmt(ParseState *query, RuleStmt *stmt,
|
||||
List **extras_before, List **extras_after);
|
||||
static Query *transformSelectStmt(ParseState *pstate, SelectStmt *stmt);
|
||||
static Query *transformValuesClause(ParseState *pstate, SelectStmt *stmt);
|
||||
static Query *transformSetOperationStmt(ParseState *pstate, SelectStmt *stmt);
|
||||
static Node *transformSetOperationTree(ParseState *pstate, SelectStmt *stmt);
|
||||
static Query *transformUpdateStmt(ParseState *pstate, UpdateStmt *stmt);
|
||||
@@ -367,12 +370,16 @@ transformStmt(ParseState *pstate, Node *parseTree,
|
||||
break;
|
||||
|
||||
case T_SelectStmt:
|
||||
if (((SelectStmt *) parseTree)->op == SETOP_NONE)
|
||||
result = transformSelectStmt(pstate,
|
||||
(SelectStmt *) parseTree);
|
||||
else
|
||||
result = transformSetOperationStmt(pstate,
|
||||
(SelectStmt *) parseTree);
|
||||
{
|
||||
SelectStmt *n = (SelectStmt *) parseTree;
|
||||
|
||||
if (n->valuesLists)
|
||||
result = transformValuesClause(pstate, n);
|
||||
else if (n->op == SETOP_NONE)
|
||||
result = transformSelectStmt(pstate, n);
|
||||
else
|
||||
result = transformSetOperationStmt(pstate, n);
|
||||
}
|
||||
break;
|
||||
|
||||
case T_DeclareCursorStmt:
|
||||
@@ -510,19 +517,30 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt,
|
||||
List **extras_before, List **extras_after)
|
||||
{
|
||||
Query *qry = makeNode(Query);
|
||||
Query *selectQuery = NULL;
|
||||
SelectStmt *selectStmt = (SelectStmt *) stmt->selectStmt;
|
||||
List *exprList = NIL;
|
||||
bool isGeneralSelect;
|
||||
List *sub_rtable;
|
||||
List *sub_relnamespace;
|
||||
List *sub_varnamespace;
|
||||
List *icolumns;
|
||||
List *attrnos;
|
||||
RangeTblEntry *rte;
|
||||
RangeTblRef *rtr;
|
||||
ListCell *icols;
|
||||
ListCell *attnos;
|
||||
ListCell *tl;
|
||||
ListCell *lc;
|
||||
|
||||
qry->commandType = CMD_INSERT;
|
||||
pstate->p_is_insert = true;
|
||||
|
||||
/*
|
||||
* We have three cases to deal with: DEFAULT VALUES (selectStmt == NULL),
|
||||
* VALUES list, or general SELECT input. We special-case VALUES, both
|
||||
* for efficiency and so we can handle DEFAULT specifications.
|
||||
*/
|
||||
isGeneralSelect = (selectStmt && selectStmt->valuesLists == NIL);
|
||||
|
||||
/*
|
||||
* If a non-nil rangetable/namespace was passed in, and we are doing
|
||||
* INSERT/SELECT, arrange to pass the rangetable/namespace down to the
|
||||
@@ -532,7 +550,7 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt,
|
||||
* The SELECT's joinlist is not affected however. We must do this before
|
||||
* adding the target table to the INSERT's rtable.
|
||||
*/
|
||||
if (stmt->selectStmt)
|
||||
if (isGeneralSelect)
|
||||
{
|
||||
sub_rtable = pstate->p_rtable;
|
||||
pstate->p_rtable = NIL;
|
||||
@@ -557,10 +575,23 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt,
|
||||
qry->resultRelation = setTargetTable(pstate, stmt->relation,
|
||||
false, false, ACL_INSERT);
|
||||
|
||||
/* Validate stmt->cols list, or build default list if no list given */
|
||||
icolumns = checkInsertTargets(pstate, stmt->cols, &attrnos);
|
||||
Assert(list_length(icolumns) == list_length(attrnos));
|
||||
|
||||
/*
|
||||
* Is it INSERT ... SELECT or INSERT ... VALUES?
|
||||
* Determine which variant of INSERT we have.
|
||||
*/
|
||||
if (stmt->selectStmt)
|
||||
if (selectStmt == NULL)
|
||||
{
|
||||
/*
|
||||
* We have INSERT ... DEFAULT VALUES. We can handle this case by
|
||||
* emitting an empty targetlist --- all columns will be defaulted
|
||||
* when the planner expands the targetlist.
|
||||
*/
|
||||
exprList = NIL;
|
||||
}
|
||||
else if (isGeneralSelect)
|
||||
{
|
||||
/*
|
||||
* We make the sub-pstate a child of the outer pstate so that it can
|
||||
@@ -570,8 +601,7 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt,
|
||||
* see.
|
||||
*/
|
||||
ParseState *sub_pstate = make_parsestate(pstate);
|
||||
RangeTblEntry *rte;
|
||||
RangeTblRef *rtr;
|
||||
Query *selectQuery;
|
||||
|
||||
/*
|
||||
* Process the source SELECT.
|
||||
@@ -617,8 +647,8 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt,
|
||||
pstate->p_joinlist = lappend(pstate->p_joinlist, rtr);
|
||||
|
||||
/*----------
|
||||
* Generate a targetlist for the INSERT that selects all the
|
||||
* non-resjunk columns from the subquery. (We need this to be
|
||||
* Generate an expression list for the INSERT that selects all the
|
||||
* non-resjunk columns from the subquery. (INSERT's tlist must be
|
||||
* separate from the subquery's tlist because we may add columns,
|
||||
* insert datatype coercions, etc.)
|
||||
*
|
||||
@@ -630,10 +660,10 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt,
|
||||
* INSERT INTO foo SELECT 'bar', ... FROM baz
|
||||
*----------
|
||||
*/
|
||||
qry->targetList = NIL;
|
||||
foreach(tl, selectQuery->targetList)
|
||||
exprList = NIL;
|
||||
foreach(lc, selectQuery->targetList)
|
||||
{
|
||||
TargetEntry *tle = (TargetEntry *) lfirst(tl);
|
||||
TargetEntry *tle = (TargetEntry *) lfirst(lc);
|
||||
Expr *expr;
|
||||
|
||||
if (tle->resjunk)
|
||||
@@ -648,82 +678,220 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt,
|
||||
exprType((Node *) tle->expr),
|
||||
exprTypmod((Node *) tle->expr),
|
||||
0);
|
||||
tle = makeTargetEntry(expr,
|
||||
(AttrNumber) pstate->p_next_resno++,
|
||||
tle->resname,
|
||||
false);
|
||||
qry->targetList = lappend(qry->targetList, tle);
|
||||
exprList = lappend(exprList, expr);
|
||||
}
|
||||
|
||||
/* Prepare row for assignment to target table */
|
||||
exprList = transformInsertRow(pstate, exprList,
|
||||
stmt->cols,
|
||||
icolumns, attrnos);
|
||||
}
|
||||
else if (list_length(selectStmt->valuesLists) > 1)
|
||||
{
|
||||
/*
|
||||
* Process INSERT ... VALUES with multiple VALUES sublists.
|
||||
* We generate a VALUES RTE holding the transformed expression
|
||||
* lists, and build up a targetlist containing Vars that reference
|
||||
* the VALUES RTE.
|
||||
*/
|
||||
List *exprsLists = NIL;
|
||||
int sublist_length = -1;
|
||||
|
||||
foreach(lc, selectStmt->valuesLists)
|
||||
{
|
||||
List *sublist = (List *) lfirst(lc);
|
||||
|
||||
/* Do basic expression transformation (same as a ROW() expr) */
|
||||
sublist = transformExpressionList(pstate, sublist);
|
||||
|
||||
/*
|
||||
* All the sublists must be the same length, *after* transformation
|
||||
* (which might expand '*' into multiple items). The VALUES RTE
|
||||
* can't handle anything different.
|
||||
*/
|
||||
if (sublist_length < 0)
|
||||
{
|
||||
/* Remember post-transformation length of first sublist */
|
||||
sublist_length = list_length(sublist);
|
||||
}
|
||||
else if (sublist_length != list_length(sublist))
|
||||
{
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_SYNTAX_ERROR),
|
||||
errmsg("VALUES lists must all be the same length")));
|
||||
}
|
||||
|
||||
/* Prepare row for assignment to target table */
|
||||
sublist = transformInsertRow(pstate, sublist,
|
||||
stmt->cols,
|
||||
icolumns, attrnos);
|
||||
|
||||
exprsLists = lappend(exprsLists, sublist);
|
||||
}
|
||||
|
||||
/*
|
||||
* There mustn't have been any table references in the expressions,
|
||||
* else strange things would happen, like Cartesian products of
|
||||
* those tables with the VALUES list ...
|
||||
*/
|
||||
if (pstate->p_joinlist != NIL)
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
||||
errmsg("VALUES must not contain table references")));
|
||||
|
||||
/*
|
||||
* Another thing we can't currently support is NEW/OLD references
|
||||
* in rules --- seems we'd need something like SQL99's LATERAL
|
||||
* construct to ensure that the values would be available while
|
||||
* evaluating the VALUES RTE. This is a shame. FIXME
|
||||
*/
|
||||
if (contain_vars_of_level((Node *) exprsLists, 0))
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
||||
errmsg("VALUES must not contain OLD or NEW references")));
|
||||
|
||||
/*
|
||||
* Generate the VALUES RTE
|
||||
*/
|
||||
rte = addRangeTableEntryForValues(pstate, exprsLists, NULL, true);
|
||||
rtr = makeNode(RangeTblRef);
|
||||
/* assume new rte is at end */
|
||||
rtr->rtindex = list_length(pstate->p_rtable);
|
||||
Assert(rte == rt_fetch(rtr->rtindex, pstate->p_rtable));
|
||||
pstate->p_joinlist = lappend(pstate->p_joinlist, rtr);
|
||||
|
||||
/*
|
||||
* Generate list of Vars referencing the RTE
|
||||
*/
|
||||
expandRTE(rte, rtr->rtindex, 0, false, NULL, &exprList);
|
||||
}
|
||||
else
|
||||
{
|
||||
/*
|
||||
* For INSERT ... VALUES, transform the given list of values to form a
|
||||
* targetlist for the INSERT.
|
||||
/*----------
|
||||
* Process INSERT ... VALUES with a single VALUES sublist.
|
||||
* We treat this separately for efficiency and for historical
|
||||
* compatibility --- specifically, allowing table references,
|
||||
* such as
|
||||
* INSERT INTO foo VALUES(bar.*)
|
||||
*
|
||||
* The sublist is just computed directly as the Query's targetlist,
|
||||
* with no VALUES RTE. So it works just like SELECT without FROM.
|
||||
*----------
|
||||
*/
|
||||
qry->targetList = transformTargetList(pstate, stmt->targetList);
|
||||
List *valuesLists = selectStmt->valuesLists;
|
||||
|
||||
Assert(list_length(valuesLists) == 1);
|
||||
|
||||
/* Do basic expression transformation (same as a ROW() expr) */
|
||||
exprList = transformExpressionList(pstate,
|
||||
(List *) linitial(valuesLists));
|
||||
|
||||
/* Prepare row for assignment to target table */
|
||||
exprList = transformInsertRow(pstate, exprList,
|
||||
stmt->cols,
|
||||
icolumns, attrnos);
|
||||
}
|
||||
|
||||
/*
|
||||
* Now we are done with SELECT-like processing, and can get on with
|
||||
* transforming the target list to match the INSERT target columns.
|
||||
*/
|
||||
|
||||
/* Prepare to assign non-conflicting resnos to resjunk attributes */
|
||||
if (pstate->p_next_resno <= pstate->p_target_relation->rd_rel->relnatts)
|
||||
pstate->p_next_resno = pstate->p_target_relation->rd_rel->relnatts + 1;
|
||||
|
||||
/* Validate stmt->cols list, or build default list if no list given */
|
||||
icolumns = checkInsertTargets(pstate, stmt->cols, &attrnos);
|
||||
|
||||
/*
|
||||
* Prepare columns for assignment to target table.
|
||||
* Generate query's target list using the computed list of expressions.
|
||||
*/
|
||||
qry->targetList = NIL;
|
||||
icols = list_head(icolumns);
|
||||
attnos = list_head(attrnos);
|
||||
foreach(tl, qry->targetList)
|
||||
foreach(lc, exprList)
|
||||
{
|
||||
TargetEntry *tle = (TargetEntry *) lfirst(tl);
|
||||
Expr *expr = (Expr *) lfirst(lc);
|
||||
ResTarget *col;
|
||||
|
||||
if (icols == NULL || attnos == NULL)
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_SYNTAX_ERROR),
|
||||
errmsg("INSERT has more expressions than target columns")));
|
||||
TargetEntry *tle;
|
||||
|
||||
col = (ResTarget *) lfirst(icols);
|
||||
Assert(IsA(col, ResTarget));
|
||||
|
||||
Assert(!tle->resjunk);
|
||||
updateTargetListEntry(pstate, tle, col->name, lfirst_int(attnos),
|
||||
col->indirection, col->location);
|
||||
tle = makeTargetEntry(expr,
|
||||
(AttrNumber) lfirst_int(attnos),
|
||||
col->name,
|
||||
false);
|
||||
qry->targetList = lappend(qry->targetList, tle);
|
||||
|
||||
icols = lnext(icols);
|
||||
attnos = lnext(attnos);
|
||||
}
|
||||
|
||||
/*
|
||||
* Ensure that the targetlist has the same number of entries that were
|
||||
* present in the columns list. Don't do the check unless an explicit
|
||||
* columns list was given, though.
|
||||
*/
|
||||
if (stmt->cols != NIL && (icols != NULL || attnos != NULL))
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_SYNTAX_ERROR),
|
||||
errmsg("INSERT has more target columns than expressions")));
|
||||
|
||||
/* done building the range table and jointree */
|
||||
qry->rtable = pstate->p_rtable;
|
||||
qry->jointree = makeFromExpr(pstate->p_joinlist, NULL);
|
||||
|
||||
qry->hasSubLinks = pstate->p_hasSubLinks;
|
||||
qry->hasAggs = pstate->p_hasAggs;
|
||||
/* aggregates not allowed (but subselects are okay) */
|
||||
if (pstate->p_hasAggs)
|
||||
parseCheckAggregates(pstate, qry);
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_GROUPING_ERROR),
|
||||
errmsg("cannot use aggregate function in VALUES")));
|
||||
|
||||
return qry;
|
||||
}
|
||||
|
||||
/*
|
||||
* Prepare an INSERT row for assignment to the target table.
|
||||
*
|
||||
* The row might be either a VALUES row, or variables referencing a
|
||||
* sub-SELECT output.
|
||||
*/
|
||||
static List *
|
||||
transformInsertRow(ParseState *pstate, List *exprlist,
|
||||
List *stmtcols, List *icolumns, List *attrnos)
|
||||
{
|
||||
List *result;
|
||||
ListCell *lc;
|
||||
ListCell *icols;
|
||||
ListCell *attnos;
|
||||
|
||||
/*
|
||||
* Check length of expr list. It must not have more expressions than
|
||||
* there are target columns. We allow fewer, but only if no explicit
|
||||
* columns list was given (the remaining columns are implicitly
|
||||
* defaulted). Note we must check this *after* transformation because
|
||||
* that could expand '*' into multiple items.
|
||||
*/
|
||||
if (list_length(exprlist) > list_length(icolumns))
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_SYNTAX_ERROR),
|
||||
errmsg("INSERT has more expressions than target columns")));
|
||||
if (stmtcols != NIL &&
|
||||
list_length(exprlist) < list_length(icolumns))
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_SYNTAX_ERROR),
|
||||
errmsg("INSERT has more target columns than expressions")));
|
||||
|
||||
/*
|
||||
* Prepare columns for assignment to target table.
|
||||
*/
|
||||
result = NIL;
|
||||
icols = list_head(icolumns);
|
||||
attnos = list_head(attrnos);
|
||||
foreach(lc, exprlist)
|
||||
{
|
||||
Expr *expr = (Expr *) lfirst(lc);
|
||||
ResTarget *col;
|
||||
|
||||
col = (ResTarget *) lfirst(icols);
|
||||
Assert(IsA(col, ResTarget));
|
||||
|
||||
expr = transformAssignedExpr(pstate, expr,
|
||||
col->name,
|
||||
lfirst_int(attnos),
|
||||
col->indirection,
|
||||
col->location);
|
||||
|
||||
result = lappend(result, expr);
|
||||
|
||||
icols = lnext(icols);
|
||||
attnos = lnext(attnos);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
* transformCreateStmt -
|
||||
* transforms the "create table" statement
|
||||
@@ -1933,6 +2101,187 @@ transformSelectStmt(ParseState *pstate, SelectStmt *stmt)
|
||||
return qry;
|
||||
}
|
||||
|
||||
/*
|
||||
* transformValuesClause -
|
||||
* transforms a VALUES clause that's being used as a standalone SELECT
|
||||
*
|
||||
* We build a Query containing a VALUES RTE, rather as if one had written
|
||||
* SELECT * FROM (VALUES ...)
|
||||
*/
|
||||
static Query *
|
||||
transformValuesClause(ParseState *pstate, SelectStmt *stmt)
|
||||
{
|
||||
Query *qry = makeNode(Query);
|
||||
List *exprsLists = NIL;
|
||||
List **coltype_lists = NULL;
|
||||
Oid *coltypes = NULL;
|
||||
int sublist_length = -1;
|
||||
List *newExprsLists;
|
||||
RangeTblEntry *rte;
|
||||
RangeTblRef *rtr;
|
||||
ListCell *lc;
|
||||
ListCell *lc2;
|
||||
int i;
|
||||
|
||||
qry->commandType = CMD_SELECT;
|
||||
|
||||
/* Most SELECT stuff doesn't apply in a VALUES clause */
|
||||
Assert(stmt->distinctClause == NIL);
|
||||
Assert(stmt->into == NULL);
|
||||
Assert(stmt->intoColNames == NIL);
|
||||
Assert(stmt->targetList == NIL);
|
||||
Assert(stmt->fromClause == NIL);
|
||||
Assert(stmt->whereClause == NULL);
|
||||
Assert(stmt->groupClause == NIL);
|
||||
Assert(stmt->havingClause == NULL);
|
||||
Assert(stmt->op == SETOP_NONE);
|
||||
|
||||
/*
|
||||
* For each row of VALUES, transform the raw expressions and gather
|
||||
* type information. This is also a handy place to reject DEFAULT
|
||||
* nodes, which the grammar allows for simplicity.
|
||||
*/
|
||||
foreach(lc, stmt->valuesLists)
|
||||
{
|
||||
List *sublist = (List *) lfirst(lc);
|
||||
|
||||
/* Do basic expression transformation (same as a ROW() expr) */
|
||||
sublist = transformExpressionList(pstate, sublist);
|
||||
|
||||
/*
|
||||
* All the sublists must be the same length, *after* transformation
|
||||
* (which might expand '*' into multiple items). The VALUES RTE
|
||||
* can't handle anything different.
|
||||
*/
|
||||
if (sublist_length < 0)
|
||||
{
|
||||
/* Remember post-transformation length of first sublist */
|
||||
sublist_length = list_length(sublist);
|
||||
/* and allocate arrays for column-type info */
|
||||
coltype_lists = (List **) palloc0(sublist_length * sizeof(List *));
|
||||
coltypes = (Oid *) palloc0(sublist_length * sizeof(Oid));
|
||||
}
|
||||
else if (sublist_length != list_length(sublist))
|
||||
{
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_SYNTAX_ERROR),
|
||||
errmsg("VALUES lists must all be the same length")));
|
||||
}
|
||||
|
||||
exprsLists = lappend(exprsLists, sublist);
|
||||
|
||||
i = 0;
|
||||
foreach(lc2, sublist)
|
||||
{
|
||||
Node *col = (Node *) lfirst(lc2);
|
||||
|
||||
if (IsA(col, SetToDefault))
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_SYNTAX_ERROR),
|
||||
errmsg("DEFAULT can only appear in a VALUES list within INSERT")));
|
||||
coltype_lists[i] = lappend_oid(coltype_lists[i], exprType(col));
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Now resolve the common types of the columns, and coerce everything
|
||||
* to those types.
|
||||
*/
|
||||
for (i = 0; i < sublist_length; i++)
|
||||
{
|
||||
coltypes[i] = select_common_type(coltype_lists[i], "VALUES");
|
||||
}
|
||||
|
||||
newExprsLists = NIL;
|
||||
foreach(lc, exprsLists)
|
||||
{
|
||||
List *sublist = (List *) lfirst(lc);
|
||||
List *newsublist = NIL;
|
||||
|
||||
i = 0;
|
||||
foreach(lc2, sublist)
|
||||
{
|
||||
Node *col = (Node *) lfirst(lc2);
|
||||
|
||||
col = coerce_to_common_type(pstate, col, coltypes[i], "VALUES");
|
||||
newsublist = lappend(newsublist, col);
|
||||
i++;
|
||||
}
|
||||
|
||||
newExprsLists = lappend(newExprsLists, newsublist);
|
||||
}
|
||||
|
||||
/*
|
||||
* Generate the VALUES RTE
|
||||
*/
|
||||
rte = addRangeTableEntryForValues(pstate, newExprsLists, NULL, true);
|
||||
rtr = makeNode(RangeTblRef);
|
||||
/* assume new rte is at end */
|
||||
rtr->rtindex = list_length(pstate->p_rtable);
|
||||
Assert(rte == rt_fetch(rtr->rtindex, pstate->p_rtable));
|
||||
pstate->p_joinlist = lappend(pstate->p_joinlist, rtr);
|
||||
|
||||
/*
|
||||
* Generate a targetlist as though expanding "*"
|
||||
*/
|
||||
Assert(pstate->p_next_resno == 1);
|
||||
qry->targetList = expandRelAttrs(pstate, rte, rtr->rtindex, 0);
|
||||
|
||||
/*
|
||||
* The grammar does allow attaching ORDER BY, LIMIT, and FOR UPDATE
|
||||
* to a VALUES, so cope.
|
||||
*/
|
||||
qry->sortClause = transformSortClause(pstate,
|
||||
stmt->sortClause,
|
||||
&qry->targetList,
|
||||
true /* fix unknowns */ );
|
||||
|
||||
qry->limitOffset = transformLimitClause(pstate, stmt->limitOffset,
|
||||
"OFFSET");
|
||||
qry->limitCount = transformLimitClause(pstate, stmt->limitCount,
|
||||
"LIMIT");
|
||||
|
||||
if (stmt->lockingClause)
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
||||
errmsg("SELECT FOR UPDATE/SHARE cannot be applied to VALUES")));
|
||||
|
||||
/*
|
||||
* There mustn't have been any table references in the expressions,
|
||||
* else strange things would happen, like Cartesian products of
|
||||
* those tables with the VALUES list. We have to check this after
|
||||
* parsing ORDER BY et al since those could insert more junk.
|
||||
*/
|
||||
if (list_length(pstate->p_joinlist) != 1)
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
||||
errmsg("VALUES must not contain table references")));
|
||||
|
||||
/*
|
||||
* Another thing we can't currently support is NEW/OLD references
|
||||
* in rules --- seems we'd need something like SQL99's LATERAL
|
||||
* construct to ensure that the values would be available while
|
||||
* evaluating the VALUES RTE. This is a shame. FIXME
|
||||
*/
|
||||
if (contain_vars_of_level((Node *) newExprsLists, 0))
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
||||
errmsg("VALUES must not contain OLD or NEW references")));
|
||||
|
||||
qry->rtable = pstate->p_rtable;
|
||||
qry->jointree = makeFromExpr(pstate->p_joinlist, NULL);
|
||||
|
||||
qry->hasSubLinks = pstate->p_hasSubLinks;
|
||||
/* aggregates not allowed (but subselects are okay) */
|
||||
if (pstate->p_hasAggs)
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_GROUPING_ERROR),
|
||||
errmsg("cannot use aggregate function in VALUES")));
|
||||
|
||||
return qry;
|
||||
}
|
||||
|
||||
/*
|
||||
* transformSetOperationsStmt -
|
||||
* transforms a set-operations tree
|
||||
@@ -2931,6 +3280,11 @@ transformLockingClause(Query *qry, LockingClause *lc)
|
||||
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
||||
errmsg("SELECT FOR UPDATE/SHARE cannot be applied to a function")));
|
||||
break;
|
||||
case RTE_VALUES:
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
||||
errmsg("SELECT FOR UPDATE/SHARE cannot be applied to VALUES")));
|
||||
break;
|
||||
default:
|
||||
elog(ERROR, "unrecognized RTE type: %d",
|
||||
(int) rte->rtekind);
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $PostgreSQL: pgsql/src/backend/parser/gram.y,v 2.553 2006/07/31 01:16:37 tgl Exp $
|
||||
* $PostgreSQL: pgsql/src/backend/parser/gram.y,v 2.554 2006/08/02 01:59:46 joe Exp $
|
||||
*
|
||||
* HISTORY
|
||||
* AUTHOR DATE MAJOR EVENT
|
||||
@@ -173,7 +173,7 @@ static void doNegateFloat(Value *v);
|
||||
DropOwnedStmt ReassignOwnedStmt
|
||||
|
||||
%type <node> select_no_parens select_with_parens select_clause
|
||||
simple_select
|
||||
simple_select values_clause
|
||||
|
||||
%type <node> alter_column_default opclass_item alter_using
|
||||
%type <ival> add_drop
|
||||
@@ -238,7 +238,7 @@ static void doNegateFloat(Value *v);
|
||||
qualified_name_list any_name any_name_list
|
||||
any_operator expr_list attrs
|
||||
target_list update_target_list insert_column_list
|
||||
insert_target_list def_list indirection opt_indirection
|
||||
values_list def_list indirection opt_indirection
|
||||
group_clause TriggerFuncArgs select_limit
|
||||
opt_select_limit opclass_item_list
|
||||
transaction_mode_list_or_empty
|
||||
@@ -299,7 +299,7 @@ static void doNegateFloat(Value *v);
|
||||
%type <list> when_clause_list
|
||||
%type <ival> sub_type
|
||||
%type <list> OptCreateAs CreateAsList
|
||||
%type <node> CreateAsElement
|
||||
%type <node> CreateAsElement values_item
|
||||
%type <value> NumericOnly FloatOnly IntegerOnly
|
||||
%type <alias> alias_clause
|
||||
%type <sortby> sortby
|
||||
@@ -308,7 +308,7 @@ static void doNegateFloat(Value *v);
|
||||
%type <jexpr> joined_table
|
||||
%type <range> relation_expr
|
||||
%type <range> relation_expr_opt_alias
|
||||
%type <target> target_el insert_target_el update_target_el insert_column_item
|
||||
%type <target> target_el update_target_el insert_column_item
|
||||
|
||||
%type <typnam> Typename SimpleTypename ConstTypename
|
||||
GenericType Numeric opt_float
|
||||
@@ -5342,41 +5342,24 @@ InsertStmt:
|
||||
;
|
||||
|
||||
insert_rest:
|
||||
VALUES '(' insert_target_list ')'
|
||||
SelectStmt
|
||||
{
|
||||
$$ = makeNode(InsertStmt);
|
||||
$$->cols = NIL;
|
||||
$$->targetList = $3;
|
||||
$$->selectStmt = NULL;
|
||||
}
|
||||
| DEFAULT VALUES
|
||||
{
|
||||
$$ = makeNode(InsertStmt);
|
||||
$$->cols = NIL;
|
||||
$$->targetList = NIL;
|
||||
$$->selectStmt = NULL;
|
||||
}
|
||||
| SelectStmt
|
||||
{
|
||||
$$ = makeNode(InsertStmt);
|
||||
$$->cols = NIL;
|
||||
$$->targetList = NIL;
|
||||
$$->selectStmt = $1;
|
||||
}
|
||||
| '(' insert_column_list ')' VALUES '(' insert_target_list ')'
|
||||
{
|
||||
$$ = makeNode(InsertStmt);
|
||||
$$->cols = $2;
|
||||
$$->targetList = $6;
|
||||
$$->selectStmt = NULL;
|
||||
}
|
||||
| '(' insert_column_list ')' SelectStmt
|
||||
{
|
||||
$$ = makeNode(InsertStmt);
|
||||
$$->cols = $2;
|
||||
$$->targetList = NIL;
|
||||
$$->selectStmt = $4;
|
||||
}
|
||||
| DEFAULT VALUES
|
||||
{
|
||||
$$ = makeNode(InsertStmt);
|
||||
$$->cols = NIL;
|
||||
$$->selectStmt = NULL;
|
||||
}
|
||||
;
|
||||
|
||||
insert_column_list:
|
||||
@@ -5629,6 +5612,7 @@ simple_select:
|
||||
n->havingClause = $8;
|
||||
$$ = (Node *)n;
|
||||
}
|
||||
| values_clause { $$ = $1; }
|
||||
| select_clause UNION opt_all select_clause
|
||||
{
|
||||
$$ = makeSetOp(SETOP_UNION, $3, $1, $4);
|
||||
@@ -5848,6 +5832,32 @@ locked_rels_list:
|
||||
| /* EMPTY */ { $$ = NIL; }
|
||||
;
|
||||
|
||||
|
||||
values_clause:
|
||||
VALUES '(' values_list ')'
|
||||
{
|
||||
SelectStmt *n = makeNode(SelectStmt);
|
||||
n->valuesLists = list_make1($3);
|
||||
$$ = (Node *) n;
|
||||
}
|
||||
| values_clause ',' '(' values_list ')'
|
||||
{
|
||||
SelectStmt *n = (SelectStmt *) $1;
|
||||
n->valuesLists = lappend(n->valuesLists, $4);
|
||||
$$ = (Node *) n;
|
||||
}
|
||||
;
|
||||
|
||||
values_list: values_item { $$ = list_make1($1); }
|
||||
| values_list ',' values_item { $$ = lappend($1, $3); }
|
||||
;
|
||||
|
||||
values_item:
|
||||
a_expr { $$ = (Node *) $1; }
|
||||
| DEFAULT { $$ = (Node *) makeNode(SetToDefault); }
|
||||
;
|
||||
|
||||
|
||||
/*****************************************************************************
|
||||
*
|
||||
* clauses common to all Optimizable Stmts:
|
||||
@@ -5937,10 +5947,17 @@ table_ref: relation_expr
|
||||
* However, it does seem like a good idea to emit
|
||||
* an error message that's better than "syntax error".
|
||||
*/
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_SYNTAX_ERROR),
|
||||
errmsg("subquery in FROM must have an alias"),
|
||||
errhint("For example, FROM (SELECT ...) [AS] foo.")));
|
||||
if (IsA($1, SelectStmt) &&
|
||||
((SelectStmt *) $1)->valuesLists)
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_SYNTAX_ERROR),
|
||||
errmsg("VALUES in FROM must have an alias"),
|
||||
errhint("For example, FROM (VALUES ...) [AS] foo.")));
|
||||
else
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_SYNTAX_ERROR),
|
||||
errmsg("subquery in FROM must have an alias"),
|
||||
errhint("For example, FROM (SELECT ...) [AS] foo.")));
|
||||
$$ = NULL;
|
||||
}
|
||||
| select_with_parens alias_clause
|
||||
@@ -8176,30 +8193,6 @@ update_target_el:
|
||||
|
||||
;
|
||||
|
||||
insert_target_list:
|
||||
insert_target_el { $$ = list_make1($1); }
|
||||
| insert_target_list ',' insert_target_el { $$ = lappend($1, $3); }
|
||||
;
|
||||
|
||||
insert_target_el:
|
||||
a_expr
|
||||
{
|
||||
$$ = makeNode(ResTarget);
|
||||
$$->name = NULL;
|
||||
$$->indirection = NIL;
|
||||
$$->val = (Node *)$1;
|
||||
$$->location = @1;
|
||||
}
|
||||
| DEFAULT
|
||||
{
|
||||
$$ = makeNode(ResTarget);
|
||||
$$->name = NULL;
|
||||
$$->indirection = NIL;
|
||||
$$->val = (Node *) makeNode(SetToDefault);
|
||||
$$->location = @1;
|
||||
}
|
||||
;
|
||||
|
||||
|
||||
/*****************************************************************************
|
||||
*
|
||||
@@ -8656,7 +8649,6 @@ unreserved_keyword:
|
||||
| VACUUM
|
||||
| VALID
|
||||
| VALIDATOR
|
||||
| VALUES
|
||||
| VARYING
|
||||
| VIEW
|
||||
| VOLATILE
|
||||
@@ -8715,6 +8707,7 @@ col_name_keyword:
|
||||
| TIMESTAMP
|
||||
| TREAT
|
||||
| TRIM
|
||||
| VALUES
|
||||
| VARCHAR
|
||||
;
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $PostgreSQL: pgsql/src/backend/parser/parse_expr.c,v 1.195 2006/07/14 14:52:22 momjian Exp $
|
||||
* $PostgreSQL: pgsql/src/backend/parser/parse_expr.c,v 1.196 2006/08/02 01:59:46 joe Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -1281,56 +1281,9 @@ static Node *
|
||||
transformRowExpr(ParseState *pstate, RowExpr *r)
|
||||
{
|
||||
RowExpr *newr = makeNode(RowExpr);
|
||||
List *newargs = NIL;
|
||||
ListCell *arg;
|
||||
|
||||
/* Transform the field expressions */
|
||||
foreach(arg, r->args)
|
||||
{
|
||||
Node *e = (Node *) lfirst(arg);
|
||||
Node *newe;
|
||||
|
||||
/*
|
||||
* Check for "something.*". Depending on the complexity of the
|
||||
* "something", the star could appear as the last name in ColumnRef,
|
||||
* or as the last indirection item in A_Indirection.
|
||||
*/
|
||||
if (IsA(e, ColumnRef))
|
||||
{
|
||||
ColumnRef *cref = (ColumnRef *) e;
|
||||
|
||||
if (strcmp(strVal(llast(cref->fields)), "*") == 0)
|
||||
{
|
||||
/* It is something.*, expand into multiple items */
|
||||
newargs = list_concat(newargs,
|
||||
ExpandColumnRefStar(pstate, cref,
|
||||
false));
|
||||
continue;
|
||||
}
|
||||
}
|
||||
else if (IsA(e, A_Indirection))
|
||||
{
|
||||
A_Indirection *ind = (A_Indirection *) e;
|
||||
Node *lastitem = llast(ind->indirection);
|
||||
|
||||
if (IsA(lastitem, String) &&
|
||||
strcmp(strVal(lastitem), "*") == 0)
|
||||
{
|
||||
/* It is something.*, expand into multiple items */
|
||||
newargs = list_concat(newargs,
|
||||
ExpandIndirectionStar(pstate, ind,
|
||||
false));
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Not "something.*", so transform as a single expression
|
||||
*/
|
||||
newe = transformExpr(pstate, e);
|
||||
newargs = lappend(newargs, newe);
|
||||
}
|
||||
newr->args = newargs;
|
||||
newr->args = transformExpressionList(pstate, r->args);
|
||||
|
||||
/* Barring later casting, we consider the type RECORD */
|
||||
newr->row_typeid = RECORDOID;
|
||||
@@ -1526,6 +1479,15 @@ transformWholeRowRef(ParseState *pstate, char *schemaname, char *relname,
|
||||
sublevels_up);
|
||||
}
|
||||
break;
|
||||
case RTE_VALUES:
|
||||
toid = RECORDOID;
|
||||
/* returns composite; same as relation case */
|
||||
result = (Node *) makeVar(vnum,
|
||||
InvalidAttrNumber,
|
||||
toid,
|
||||
-1,
|
||||
sublevels_up);
|
||||
break;
|
||||
default:
|
||||
|
||||
/*
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $PostgreSQL: pgsql/src/backend/parser/parse_node.c,v 1.93 2006/07/14 14:52:22 momjian Exp $
|
||||
* $PostgreSQL: pgsql/src/backend/parser/parse_node.c,v 1.94 2006/08/02 01:59:47 joe Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -258,7 +258,7 @@ transformArraySubscripts(ParseState *pstate,
|
||||
|
||||
/*
|
||||
* If doing an array store, coerce the source value to the right type.
|
||||
* (This should agree with the coercion done by updateTargetListEntry.)
|
||||
* (This should agree with the coercion done by transformAssignedExpr.)
|
||||
*/
|
||||
if (assignFrom != NULL)
|
||||
{
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $PostgreSQL: pgsql/src/backend/parser/parse_relation.c,v 1.123 2006/04/30 18:30:39 tgl Exp $
|
||||
* $PostgreSQL: pgsql/src/backend/parser/parse_relation.c,v 1.124 2006/08/02 01:59:47 joe Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -940,6 +940,75 @@ addRangeTableEntryForFunction(ParseState *pstate,
|
||||
return rte;
|
||||
}
|
||||
|
||||
/*
|
||||
* Add an entry for a VALUES list to the pstate's range table (p_rtable).
|
||||
*
|
||||
* This is much like addRangeTableEntry() except that it makes a values RTE.
|
||||
*/
|
||||
RangeTblEntry *
|
||||
addRangeTableEntryForValues(ParseState *pstate,
|
||||
List *exprs,
|
||||
Alias *alias,
|
||||
bool inFromCl)
|
||||
{
|
||||
RangeTblEntry *rte = makeNode(RangeTblEntry);
|
||||
char *refname = alias ? alias->aliasname : pstrdup("*VALUES*");
|
||||
Alias *eref;
|
||||
int numaliases;
|
||||
int numcolumns;
|
||||
|
||||
rte->rtekind = RTE_VALUES;
|
||||
rte->relid = InvalidOid;
|
||||
rte->subquery = NULL;
|
||||
rte->values_lists = exprs;
|
||||
rte->alias = alias;
|
||||
|
||||
eref = alias ? copyObject(alias) : makeAlias(refname, NIL);
|
||||
|
||||
/* fill in any unspecified alias columns */
|
||||
numcolumns = list_length((List *) linitial(exprs));
|
||||
numaliases = list_length(eref->colnames);
|
||||
while (numaliases < numcolumns)
|
||||
{
|
||||
char attrname[64];
|
||||
|
||||
numaliases++;
|
||||
snprintf(attrname, sizeof(attrname), "column%d", numaliases);
|
||||
eref->colnames = lappend(eref->colnames,
|
||||
makeString(pstrdup(attrname)));
|
||||
}
|
||||
if (numcolumns < numaliases)
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_INVALID_COLUMN_REFERENCE),
|
||||
errmsg("VALUES lists \"%s\" have %d columns available but %d columns specified",
|
||||
refname, numcolumns, numaliases)));
|
||||
|
||||
rte->eref = eref;
|
||||
|
||||
/*----------
|
||||
* Flags:
|
||||
* - this RTE should be expanded to include descendant tables,
|
||||
* - this RTE is in the FROM clause,
|
||||
* - this RTE should be checked for appropriate access rights.
|
||||
*
|
||||
* Subqueries are never checked for access rights.
|
||||
*----------
|
||||
*/
|
||||
rte->inh = false; /* never true for values RTEs */
|
||||
rte->inFromCl = inFromCl;
|
||||
rte->requiredPerms = 0;
|
||||
rte->checkAsUser = InvalidOid;
|
||||
|
||||
/*
|
||||
* Add completed RTE to pstate's range table list, but not to join list
|
||||
* nor namespace --- caller must do that if appropriate.
|
||||
*/
|
||||
if (pstate != NULL)
|
||||
pstate->p_rtable = lappend(pstate->p_rtable, rte);
|
||||
|
||||
return rte;
|
||||
}
|
||||
|
||||
/*
|
||||
* Add an entry for a join to the pstate's range table (p_rtable).
|
||||
*
|
||||
@@ -1233,6 +1302,41 @@ expandRTE(RangeTblEntry *rte, int rtindex, int sublevels_up,
|
||||
}
|
||||
}
|
||||
break;
|
||||
case RTE_VALUES:
|
||||
{
|
||||
/* Values RTE */
|
||||
ListCell *aliasp_item = list_head(rte->eref->colnames);
|
||||
ListCell *lc;
|
||||
|
||||
varattno = 0;
|
||||
foreach(lc, (List *) linitial(rte->values_lists))
|
||||
{
|
||||
Node *col = (Node *) lfirst(lc);
|
||||
|
||||
varattno++;
|
||||
if (colnames)
|
||||
{
|
||||
/* Assume there is one alias per column */
|
||||
char *label = strVal(lfirst(aliasp_item));
|
||||
|
||||
*colnames = lappend(*colnames,
|
||||
makeString(pstrdup(label)));
|
||||
aliasp_item = lnext(aliasp_item);
|
||||
}
|
||||
|
||||
if (colvars)
|
||||
{
|
||||
Var *varnode;
|
||||
|
||||
varnode = makeVar(rtindex, varattno,
|
||||
exprType(col),
|
||||
exprTypmod(col),
|
||||
sublevels_up);
|
||||
*colvars = lappend(*colvars, varnode);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case RTE_JOIN:
|
||||
{
|
||||
/* Join RTE */
|
||||
@@ -1569,6 +1673,20 @@ get_rte_attribute_type(RangeTblEntry *rte, AttrNumber attnum,
|
||||
}
|
||||
}
|
||||
break;
|
||||
case RTE_VALUES:
|
||||
{
|
||||
/* Values RTE --- get type info from first sublist */
|
||||
List *collist = (List *) linitial(rte->values_lists);
|
||||
Node *col;
|
||||
|
||||
if (attnum < 1 || attnum > list_length(collist))
|
||||
elog(ERROR, "values list %s does not have attribute %d",
|
||||
rte->eref->aliasname, attnum);
|
||||
col = (Node *) list_nth(collist, attnum-1);
|
||||
*vartype = exprType(col);
|
||||
*vartypmod = exprTypmod(col);
|
||||
}
|
||||
break;
|
||||
case RTE_JOIN:
|
||||
{
|
||||
/*
|
||||
@@ -1619,7 +1737,8 @@ get_rte_attribute_is_dropped(RangeTblEntry *rte, AttrNumber attnum)
|
||||
}
|
||||
break;
|
||||
case RTE_SUBQUERY:
|
||||
/* Subselect RTEs never have dropped columns */
|
||||
case RTE_VALUES:
|
||||
/* Subselect and Values RTEs never have dropped columns */
|
||||
result = false;
|
||||
break;
|
||||
case RTE_JOIN:
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $PostgreSQL: pgsql/src/backend/parser/parse_target.c,v 1.146 2006/07/14 14:52:22 momjian Exp $
|
||||
* $PostgreSQL: pgsql/src/backend/parser/parse_target.c,v 1.147 2006/08/02 01:59:47 joe Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -42,7 +42,11 @@ static Node *transformAssignmentIndirection(ParseState *pstate,
|
||||
ListCell *indirection,
|
||||
Node *rhs,
|
||||
int location);
|
||||
static List *ExpandColumnRefStar(ParseState *pstate, ColumnRef *cref,
|
||||
bool targetlist);
|
||||
static List *ExpandAllTables(ParseState *pstate);
|
||||
static List *ExpandIndirectionStar(ParseState *pstate, A_Indirection *ind,
|
||||
bool targetlist);
|
||||
static int FigureColnameInternal(Node *node, char **name);
|
||||
|
||||
|
||||
@@ -151,6 +155,69 @@ transformTargetList(ParseState *pstate, List *targetlist)
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* transformExpressionList()
|
||||
*
|
||||
* This is the identical transformation to transformTargetList, except that
|
||||
* the input list elements are bare expressions without ResTarget decoration,
|
||||
* and the output elements are likewise just expressions without TargetEntry
|
||||
* decoration. We use this for ROW() and VALUES() constructs.
|
||||
*/
|
||||
List *
|
||||
transformExpressionList(ParseState *pstate, List *exprlist)
|
||||
{
|
||||
List *result = NIL;
|
||||
ListCell *lc;
|
||||
|
||||
foreach(lc, exprlist)
|
||||
{
|
||||
Node *e = (Node *) lfirst(lc);
|
||||
|
||||
/*
|
||||
* Check for "something.*". Depending on the complexity of the
|
||||
* "something", the star could appear as the last name in ColumnRef,
|
||||
* or as the last indirection item in A_Indirection.
|
||||
*/
|
||||
if (IsA(e, ColumnRef))
|
||||
{
|
||||
ColumnRef *cref = (ColumnRef *) e;
|
||||
|
||||
if (strcmp(strVal(llast(cref->fields)), "*") == 0)
|
||||
{
|
||||
/* It is something.*, expand into multiple items */
|
||||
result = list_concat(result,
|
||||
ExpandColumnRefStar(pstate, cref,
|
||||
false));
|
||||
continue;
|
||||
}
|
||||
}
|
||||
else if (IsA(e, A_Indirection))
|
||||
{
|
||||
A_Indirection *ind = (A_Indirection *) e;
|
||||
Node *lastitem = llast(ind->indirection);
|
||||
|
||||
if (IsA(lastitem, String) &&
|
||||
strcmp(strVal(lastitem), "*") == 0)
|
||||
{
|
||||
/* It is something.*, expand into multiple items */
|
||||
result = list_concat(result,
|
||||
ExpandIndirectionStar(pstate, ind,
|
||||
false));
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Not "something.*", so transform as a single expression
|
||||
*/
|
||||
result = lappend(result,
|
||||
transformExpr(pstate, e));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* markTargetListOrigins()
|
||||
* Mark targetlist columns that are simple Vars with the source
|
||||
@@ -229,6 +296,7 @@ markTargetListOrigin(ParseState *pstate, TargetEntry *tle,
|
||||
break;
|
||||
case RTE_SPECIAL:
|
||||
case RTE_FUNCTION:
|
||||
case RTE_VALUES:
|
||||
/* not a simple relation, leave it unmarked */
|
||||
break;
|
||||
}
|
||||
@@ -236,23 +304,26 @@ markTargetListOrigin(ParseState *pstate, TargetEntry *tle,
|
||||
|
||||
|
||||
/*
|
||||
* updateTargetListEntry()
|
||||
* This is used in INSERT and UPDATE statements only. It prepares a
|
||||
* TargetEntry for assignment to a column of the target table.
|
||||
* transformAssignedExpr()
|
||||
* This is used in INSERT and UPDATE statements only. It prepares an
|
||||
* expression for assignment to a column of the target table.
|
||||
* This includes coercing the given value to the target column's type
|
||||
* (if necessary), and dealing with any subfield names or subscripts
|
||||
* attached to the target column itself.
|
||||
* attached to the target column itself. The input expression has
|
||||
* already been through transformExpr().
|
||||
*
|
||||
* pstate parse state
|
||||
* tle target list entry to be modified
|
||||
* expr expression to be modified
|
||||
* colname target column name (ie, name of attribute to be assigned to)
|
||||
* attrno target attribute number
|
||||
* indirection subscripts/field names for target column, if any
|
||||
* location error cursor position (should point at column name), or -1
|
||||
* location error cursor position, or -1
|
||||
*
|
||||
* Returns the modified expression.
|
||||
*/
|
||||
void
|
||||
updateTargetListEntry(ParseState *pstate,
|
||||
TargetEntry *tle,
|
||||
Expr *
|
||||
transformAssignedExpr(ParseState *pstate,
|
||||
Expr *expr,
|
||||
char *colname,
|
||||
int attrno,
|
||||
List *indirection,
|
||||
@@ -281,9 +352,9 @@ updateTargetListEntry(ParseState *pstate,
|
||||
* or array element with DEFAULT, since there can't be any default for
|
||||
* portions of a column.
|
||||
*/
|
||||
if (tle->expr && IsA(tle->expr, SetToDefault))
|
||||
if (expr && IsA(expr, SetToDefault))
|
||||
{
|
||||
SetToDefault *def = (SetToDefault *) tle->expr;
|
||||
SetToDefault *def = (SetToDefault *) expr;
|
||||
|
||||
def->typeId = attrtype;
|
||||
def->typeMod = attrtypmod;
|
||||
@@ -303,7 +374,7 @@ updateTargetListEntry(ParseState *pstate,
|
||||
}
|
||||
|
||||
/* Now we can use exprType() safely. */
|
||||
type_id = exprType((Node *) tle->expr);
|
||||
type_id = exprType((Node *) expr);
|
||||
|
||||
/*
|
||||
* If there is indirection on the target column, prepare an array or
|
||||
@@ -334,7 +405,7 @@ updateTargetListEntry(ParseState *pstate,
|
||||
attrno);
|
||||
}
|
||||
|
||||
tle->expr = (Expr *)
|
||||
expr = (Expr *)
|
||||
transformAssignmentIndirection(pstate,
|
||||
colVar,
|
||||
colname,
|
||||
@@ -342,7 +413,7 @@ updateTargetListEntry(ParseState *pstate,
|
||||
attrtype,
|
||||
attrtypmod,
|
||||
list_head(indirection),
|
||||
(Node *) tle->expr,
|
||||
(Node *) expr,
|
||||
location);
|
||||
}
|
||||
else
|
||||
@@ -351,13 +422,13 @@ updateTargetListEntry(ParseState *pstate,
|
||||
* For normal non-qualified target column, do type checking and
|
||||
* coercion.
|
||||
*/
|
||||
tle->expr = (Expr *)
|
||||
expr = (Expr *)
|
||||
coerce_to_target_type(pstate,
|
||||
(Node *) tle->expr, type_id,
|
||||
(Node *) expr, type_id,
|
||||
attrtype, attrtypmod,
|
||||
COERCION_ASSIGNMENT,
|
||||
COERCE_IMPLICIT_CAST);
|
||||
if (tle->expr == NULL)
|
||||
if (expr == NULL)
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_DATATYPE_MISMATCH),
|
||||
errmsg("column \"%s\" is of type %s"
|
||||
@@ -369,6 +440,41 @@ updateTargetListEntry(ParseState *pstate,
|
||||
parser_errposition(pstate, location)));
|
||||
}
|
||||
|
||||
return expr;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* updateTargetListEntry()
|
||||
* This is used in UPDATE statements only. It prepares an UPDATE
|
||||
* TargetEntry for assignment to a column of the target table.
|
||||
* This includes coercing the given value to the target column's type
|
||||
* (if necessary), and dealing with any subfield names or subscripts
|
||||
* attached to the target column itself.
|
||||
*
|
||||
* pstate parse state
|
||||
* tle target list entry to be modified
|
||||
* colname target column name (ie, name of attribute to be assigned to)
|
||||
* attrno target attribute number
|
||||
* indirection subscripts/field names for target column, if any
|
||||
* location error cursor position (should point at column name), or -1
|
||||
*/
|
||||
void
|
||||
updateTargetListEntry(ParseState *pstate,
|
||||
TargetEntry *tle,
|
||||
char *colname,
|
||||
int attrno,
|
||||
List *indirection,
|
||||
int location)
|
||||
{
|
||||
/* Fix up expression as needed */
|
||||
tle->expr = transformAssignedExpr(pstate,
|
||||
tle->expr,
|
||||
colname,
|
||||
attrno,
|
||||
indirection,
|
||||
location);
|
||||
|
||||
/*
|
||||
* Set the resno to identify the target column --- the rewriter and
|
||||
* planner depend on this. We also set the resname to identify the target
|
||||
@@ -379,6 +485,7 @@ updateTargetListEntry(ParseState *pstate,
|
||||
tle->resname = colname;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Process indirection (field selection or subscripting) of the target
|
||||
* column in INSERT/UPDATE. This routine recurses for multiple levels
|
||||
@@ -701,9 +808,10 @@ checkInsertTargets(ParseState *pstate, List *cols, List **attrnos)
|
||||
* This handles the case where '*' appears as the last or only name in a
|
||||
* ColumnRef. The code is shared between the case of foo.* at the top level
|
||||
* in a SELECT target list (where we want TargetEntry nodes in the result)
|
||||
* and foo.* in a ROW() construct (where we want just bare expressions).
|
||||
* and foo.* in a ROW() or VALUES() construct (where we want just bare
|
||||
* expressions).
|
||||
*/
|
||||
List *
|
||||
static List *
|
||||
ExpandColumnRefStar(ParseState *pstate, ColumnRef *cref,
|
||||
bool targetlist)
|
||||
{
|
||||
@@ -836,9 +944,9 @@ ExpandAllTables(ParseState *pstate)
|
||||
* This handles the case where '*' appears as the last item in A_Indirection.
|
||||
* The code is shared between the case of foo.* at the top level in a SELECT
|
||||
* target list (where we want TargetEntry nodes in the result) and foo.* in
|
||||
* a ROW() construct (where we want just bare expressions).
|
||||
* a ROW() or VALUES() construct (where we want just bare expressions).
|
||||
*/
|
||||
List *
|
||||
static List *
|
||||
ExpandIndirectionStar(ParseState *pstate, A_Indirection *ind,
|
||||
bool targetlist)
|
||||
{
|
||||
@@ -996,11 +1104,12 @@ expandRecordVariable(ParseState *pstate, Var *var, int levelsup)
|
||||
{
|
||||
case RTE_RELATION:
|
||||
case RTE_SPECIAL:
|
||||
case RTE_VALUES:
|
||||
|
||||
/*
|
||||
* This case should not occur: a column of a table shouldn't have
|
||||
* type RECORD. Fall through and fail (most likely) at the
|
||||
* bottom.
|
||||
* This case should not occur: a column of a table or values list
|
||||
* shouldn't have type RECORD. Fall through and fail
|
||||
* (most likely) at the bottom.
|
||||
*/
|
||||
break;
|
||||
case RTE_SUBQUERY:
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $PostgreSQL: pgsql/src/backend/parser/parse_type.c,v 1.82 2006/07/14 14:52:22 momjian Exp $
|
||||
* $PostgreSQL: pgsql/src/backend/parser/parse_type.c,v 1.83 2006/08/02 01:59:47 joe Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -426,6 +426,7 @@ parseTypeString(const char *str, Oid *type_id, int32 *typmod)
|
||||
stmt->whereClause != NULL ||
|
||||
stmt->groupClause != NIL ||
|
||||
stmt->havingClause != NULL ||
|
||||
stmt->valuesLists != NIL ||
|
||||
stmt->sortClause != NIL ||
|
||||
stmt->limitOffset != NULL ||
|
||||
stmt->limitCount != NULL ||
|
||||
|
||||
Reference in New Issue
Block a user