aboutsummaryrefslogtreecommitdiff
path: root/src/backend/parser
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/parser')
-rw-r--r--src/backend/parser/analyze.c474
-rw-r--r--src/backend/parser/gram.y107
-rw-r--r--src/backend/parser/parse_expr.c60
-rw-r--r--src/backend/parser/parse_node.c4
-rw-r--r--src/backend/parser/parse_relation.c123
-rw-r--r--src/backend/parser/parse_target.c159
-rw-r--r--src/backend/parser/parse_type.c3
7 files changed, 734 insertions, 196 deletions
diff --git a/src/backend/parser/analyze.c b/src/backend/parser/analyze.c
index b086afe8ca2..4f7001b6f1a 100644
--- a/src/backend/parser/analyze.c
+++ b/src/backend/parser/analyze.c
@@ -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,20 +517,31 @@ 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
* SELECT. This can only happen if we are inside a CREATE RULE, and in
@@ -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,83 +678,221 @@ 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
+ else if (list_length(selectStmt->valuesLists) > 1)
{
/*
- * For INSERT ... VALUES, transform the given list of values to form a
- * targetlist for the INSERT.
+ * 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.
*/
- qry->targetList = transformTargetList(pstate, stmt->targetList);
+ 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
+ {
+ /*----------
+ * 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.
+ *----------
+ */
+ List *valuesLists = selectStmt->valuesLists;
- /*
- * Now we are done with SELECT-like processing, and can get on with
- * transforming the target list to match the INSERT target columns.
- */
+ Assert(list_length(valuesLists) == 1);
- /* 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;
+ /* Do basic expression transformation (same as a ROW() expr) */
+ exprList = transformExpressionList(pstate,
+ (List *) linitial(valuesLists));
- /* Validate stmt->cols list, or build default list if no list given */
- icolumns = checkInsertTargets(pstate, stmt->cols, &attrnos);
+ /* Prepare row for assignment to target table */
+ exprList = transformInsertRow(pstate, exprList,
+ stmt->cols,
+ icolumns, 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
* SQL92 allows constraints to be scattered all over, so thumb through
@@ -1934,6 +2102,187 @@ transformSelectStmt(ParseState *pstate, SelectStmt *stmt)
}
/*
+ * 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);
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 434f05bc4d8..4586c77f8a1 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -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,40 +5342,23 @@ InsertStmt:
;
insert_rest:
- VALUES '(' insert_target_list ')'
- {
- $$ = makeNode(InsertStmt);
- $$->cols = NIL;
- $$->targetList = $3;
- $$->selectStmt = NULL;
- }
- | DEFAULT VALUES
- {
- $$ = makeNode(InsertStmt);
- $$->cols = NIL;
- $$->targetList = NIL;
- $$->selectStmt = NULL;
- }
- | SelectStmt
+ SelectStmt
{
$$ = makeNode(InsertStmt);
$$->cols = NIL;
- $$->targetList = NIL;
$$->selectStmt = $1;
}
- | '(' insert_column_list ')' VALUES '(' insert_target_list ')'
+ | '(' insert_column_list ')' SelectStmt
{
$$ = makeNode(InsertStmt);
$$->cols = $2;
- $$->targetList = $6;
- $$->selectStmt = NULL;
+ $$->selectStmt = $4;
}
- | '(' insert_column_list ')' SelectStmt
+ | DEFAULT VALUES
{
$$ = makeNode(InsertStmt);
- $$->cols = $2;
- $$->targetList = NIL;
- $$->selectStmt = $4;
+ $$->cols = NIL;
+ $$->selectStmt = NULL;
}
;
@@ -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
;
diff --git a/src/backend/parser/parse_expr.c b/src/backend/parser/parse_expr.c
index 11ef7e4f1e9..6a7117e98bf 100644
--- a/src/backend/parser/parse_expr.c
+++ b/src/backend/parser/parse_expr.c
@@ -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:
/*
diff --git a/src/backend/parser/parse_node.c b/src/backend/parser/parse_node.c
index f1b6a45e7fc..9c1b58704e9 100644
--- a/src/backend/parser/parse_node.c
+++ b/src/backend/parser/parse_node.c
@@ -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)
{
diff --git a/src/backend/parser/parse_relation.c b/src/backend/parser/parse_relation.c
index 10f71712ff6..e9896be6349 100644
--- a/src/backend/parser/parse_relation.c
+++ b/src/backend/parser/parse_relation.c
@@ -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 $
*
*-------------------------------------------------------------------------
*/
@@ -941,6 +941,75 @@ addRangeTableEntryForFunction(ParseState *pstate,
}
/*
+ * 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).
*
* This is much like addRangeTableEntry() except that it makes a join RTE.
@@ -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:
diff --git a/src/backend/parser/parse_target.c b/src/backend/parser/parse_target.c
index 6f2e44f6e12..9258acccfbc 100644
--- a/src/backend/parser/parse_target.c
+++ b/src/backend/parser/parse_target.c
@@ -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);
@@ -152,6 +156,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
* table's OID and column number.
@@ -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:
diff --git a/src/backend/parser/parse_type.c b/src/backend/parser/parse_type.c
index b6eac7417f8..a12aea6c38a 100644
--- a/src/backend/parser/parse_type.c
+++ b/src/backend/parser/parse_type.c
@@ -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 ||