mirror of
https://github.com/postgres/postgres.git
synced 2025-11-09 06:21:09 +03:00
Support assignment to subfields of composite columns in UPDATE and INSERT.
As a side effect, cause subscripts in INSERT targetlists to do something more or less sensible; previously we evaluated such subscripts and then effectively ignored them. Another side effect is that UPDATE-ing an element or slice of an array value that is NULL now produces a non-null result, namely an array containing just the assigned-to positions.
This commit is contained in:
@@ -6,7 +6,7 @@
|
||||
* Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
|
||||
* Portions Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* $PostgreSQL: pgsql/src/backend/parser/analyze.c,v 1.303 2004/06/04 03:24:04 tgl Exp $
|
||||
* $PostgreSQL: pgsql/src/backend/parser/analyze.c,v 1.304 2004/06/09 19:08:16 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -2461,6 +2461,7 @@ transformUpdateStmt(ParseState *pstate, UpdateStmt *stmt)
|
||||
if (origTargetList == NULL)
|
||||
elog(ERROR, "UPDATE target count mismatch --- internal error");
|
||||
origTarget = (ResTarget *) lfirst(origTargetList);
|
||||
Assert(IsA(origTarget, ResTarget));
|
||||
|
||||
updateTargetListEntry(pstate, tle, origTarget->name,
|
||||
attnameAttNum(pstate->p_target_relation,
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $PostgreSQL: pgsql/src/backend/parser/gram.y,v 2.460 2004/06/02 21:01:09 momjian Exp $
|
||||
* $PostgreSQL: pgsql/src/backend/parser/gram.y,v 2.461 2004/06/09 19:08:17 tgl Exp $
|
||||
*
|
||||
* HISTORY
|
||||
* AUTHOR DATE MAJOR EVENT
|
||||
@@ -75,6 +75,7 @@ static bool QueryIsRule = FALSE;
|
||||
*/
|
||||
/*#define __YYSCLASS*/
|
||||
|
||||
static Node *makeColumnRef(char *relname, List *indirection);
|
||||
static Node *makeTypeCast(Node *arg, TypeName *typename);
|
||||
static Node *makeStringConst(char *str, TypeName *typename);
|
||||
static Node *makeIntConst(int val);
|
||||
@@ -84,6 +85,7 @@ static Node *makeRowNullTest(NullTestType test, RowExpr *row);
|
||||
static DefElem *makeDefElem(char *name, Node *arg);
|
||||
static A_Const *makeBoolAConst(bool state);
|
||||
static FuncCall *makeOverlaps(List *largs, List *rargs);
|
||||
static List *check_func_name(List *names);
|
||||
static List *extractArgTypes(List *parameters);
|
||||
static SelectStmt *findLeftmostSelect(SelectStmt *node);
|
||||
static void insertSelectOptions(SelectStmt *stmt,
|
||||
@@ -110,7 +112,6 @@ static void doNegateFloat(Value *v);
|
||||
List *list;
|
||||
Node *node;
|
||||
Value *value;
|
||||
ColumnRef *columnref;
|
||||
ObjectType objtype;
|
||||
|
||||
TypeName *typnam;
|
||||
@@ -221,9 +222,9 @@ static void doNegateFloat(Value *v);
|
||||
sort_clause opt_sort_clause sortby_list index_params
|
||||
name_list from_clause from_list opt_array_bounds
|
||||
qualified_name_list any_name any_name_list
|
||||
any_operator expr_list dotted_name attrs
|
||||
any_operator expr_list attrs
|
||||
target_list update_target_list insert_column_list
|
||||
insert_target_list def_list opt_indirection
|
||||
insert_target_list def_list indirection opt_indirection
|
||||
group_clause TriggerFuncArgs select_limit
|
||||
opt_select_limit opclass_item_list transaction_mode_list
|
||||
transaction_mode_list_or_empty
|
||||
@@ -274,8 +275,8 @@ static void doNegateFloat(Value *v);
|
||||
%type <node> TableElement ConstraintElem TableFuncElement
|
||||
%type <node> columnDef
|
||||
%type <defelt> def_elem
|
||||
%type <node> def_arg columnElem where_clause insert_column_item
|
||||
a_expr b_expr c_expr AexprConst
|
||||
%type <node> def_arg columnElem where_clause
|
||||
a_expr b_expr c_expr AexprConst indirection_el columnref
|
||||
in_expr having_clause func_table array_expr
|
||||
%type <list> row type_list array_expr_list
|
||||
%type <node> case_expr case_arg when_clause case_default
|
||||
@@ -284,14 +285,13 @@ static void doNegateFloat(Value *v);
|
||||
%type <list> OptCreateAs CreateAsList
|
||||
%type <node> CreateAsElement
|
||||
%type <value> NumericOnly FloatOnly IntegerOnly
|
||||
%type <columnref> columnref
|
||||
%type <alias> alias_clause
|
||||
%type <sortby> sortby
|
||||
%type <ielem> index_elem
|
||||
%type <node> table_ref
|
||||
%type <jexpr> joined_table
|
||||
%type <range> relation_expr
|
||||
%type <target> target_el insert_target_el update_target_el
|
||||
%type <target> target_el insert_target_el update_target_el insert_column_item
|
||||
|
||||
%type <typnam> Typename SimpleTypename ConstTypename
|
||||
GenericType Numeric opt_float
|
||||
@@ -2102,12 +2102,11 @@ opt_trusted:
|
||||
|
||||
/* This ought to be just func_name, but that causes reduce/reduce conflicts
|
||||
* (CREATE LANGUAGE is the only place where func_name isn't followed by '(').
|
||||
* Work around by using name and dotted_name separately.
|
||||
* Work around by using simple names, instead.
|
||||
*/
|
||||
handler_name:
|
||||
name
|
||||
{ $$ = list_make1(makeString($1)); }
|
||||
| dotted_name { $$ = $1; }
|
||||
name { $$ = list_make1(makeString($1)); }
|
||||
| name attrs { $$ = lcons(makeString($1), $2); }
|
||||
;
|
||||
|
||||
opt_lancompiler:
|
||||
@@ -2578,9 +2577,20 @@ any_name_list:
|
||||
;
|
||||
|
||||
any_name: ColId { $$ = list_make1(makeString($1)); }
|
||||
| dotted_name { $$ = $1; }
|
||||
| ColId attrs { $$ = lcons(makeString($1), $2); }
|
||||
;
|
||||
|
||||
/*
|
||||
* The slightly convoluted way of writing this production avoids reduce/reduce
|
||||
* errors against indirection_el.
|
||||
*/
|
||||
attrs: '.' attr_name
|
||||
{ $$ = list_make1(makeString($2)); }
|
||||
| '.' attr_name attrs
|
||||
{ $$ = lcons(makeString($2), $3); }
|
||||
;
|
||||
|
||||
|
||||
/*****************************************************************************
|
||||
*
|
||||
* QUERY:
|
||||
@@ -4387,7 +4397,8 @@ insert_rest:
|
||||
;
|
||||
|
||||
insert_column_list:
|
||||
insert_column_item { $$ = list_make1($1); }
|
||||
insert_column_item
|
||||
{ $$ = list_make1($1); }
|
||||
| insert_column_list ',' insert_column_item
|
||||
{ $$ = lappend($1, $3); }
|
||||
;
|
||||
@@ -4395,11 +4406,10 @@ insert_column_list:
|
||||
insert_column_item:
|
||||
ColId opt_indirection
|
||||
{
|
||||
ResTarget *n = makeNode(ResTarget);
|
||||
n->name = $1;
|
||||
n->indirection = $2;
|
||||
n->val = NULL;
|
||||
$$ = (Node *)n;
|
||||
$$ = makeNode(ResTarget);
|
||||
$$->name = $1;
|
||||
$$->indirection = $2;
|
||||
$$->val = NULL;
|
||||
}
|
||||
;
|
||||
|
||||
@@ -6203,35 +6213,28 @@ b_expr: c_expr
|
||||
* inside parentheses, such as function arguments; that cannot introduce
|
||||
* ambiguity to the b_expr syntax.
|
||||
*/
|
||||
c_expr: columnref { $$ = (Node *) $1; }
|
||||
c_expr: columnref { $$ = $1; }
|
||||
| AexprConst { $$ = $1; }
|
||||
| PARAM attrs opt_indirection
|
||||
| PARAM opt_indirection
|
||||
{
|
||||
/*
|
||||
* PARAM without field names is considered a constant,
|
||||
* but with 'em, it is not. Not very consistent ...
|
||||
*/
|
||||
ParamRef *n = makeNode(ParamRef);
|
||||
n->number = $1;
|
||||
n->fields = $2;
|
||||
n->indirection = $3;
|
||||
$$ = (Node *)n;
|
||||
}
|
||||
| '(' a_expr ')' attrs opt_indirection
|
||||
{
|
||||
ExprFieldSelect *n = makeNode(ExprFieldSelect);
|
||||
n->arg = $2;
|
||||
n->fields = $4;
|
||||
n->indirection = $5;
|
||||
$$ = (Node *)n;
|
||||
ParamRef *p = makeNode(ParamRef);
|
||||
p->number = $1;
|
||||
if ($2)
|
||||
{
|
||||
A_Indirection *n = makeNode(A_Indirection);
|
||||
n->arg = (Node *) p;
|
||||
n->indirection = $2;
|
||||
$$ = (Node *) n;
|
||||
}
|
||||
else
|
||||
$$ = (Node *) p;
|
||||
}
|
||||
| '(' a_expr ')' opt_indirection
|
||||
{
|
||||
if ($4)
|
||||
{
|
||||
ExprFieldSelect *n = makeNode(ExprFieldSelect);
|
||||
A_Indirection *n = makeNode(A_Indirection);
|
||||
n->arg = $2;
|
||||
n->fields = NIL;
|
||||
n->indirection = $4;
|
||||
$$ = (Node *)n;
|
||||
}
|
||||
@@ -6806,25 +6809,6 @@ subquery_Op:
|
||||
*/
|
||||
;
|
||||
|
||||
opt_indirection:
|
||||
opt_indirection '[' a_expr ']'
|
||||
{
|
||||
A_Indices *ai = makeNode(A_Indices);
|
||||
ai->lidx = NULL;
|
||||
ai->uidx = $3;
|
||||
$$ = lappend($1, ai);
|
||||
}
|
||||
| opt_indirection '[' a_expr ':' a_expr ']'
|
||||
{
|
||||
A_Indices *ai = makeNode(A_Indices);
|
||||
ai->lidx = $3;
|
||||
ai->uidx = $5;
|
||||
$$ = lappend($1, ai);
|
||||
}
|
||||
| /*EMPTY*/
|
||||
{ $$ = NIL; }
|
||||
;
|
||||
|
||||
expr_list: a_expr
|
||||
{
|
||||
$$ = list_make1($1);
|
||||
@@ -7050,42 +7034,58 @@ case_arg: a_expr { $$ = $1; }
|
||||
* references can be accepted. Note that when there are more than two
|
||||
* dotted names, the first name is not actually a relation name...
|
||||
*/
|
||||
columnref: relation_name opt_indirection
|
||||
columnref: relation_name
|
||||
{
|
||||
$$ = makeNode(ColumnRef);
|
||||
$$->fields = list_make1(makeString($1));
|
||||
$$->indirection = $2;
|
||||
$$ = makeColumnRef($1, NIL);
|
||||
}
|
||||
| dotted_name opt_indirection
|
||||
| relation_name indirection
|
||||
{
|
||||
$$ = makeNode(ColumnRef);
|
||||
$$->fields = $1;
|
||||
$$->indirection = $2;
|
||||
$$ = makeColumnRef($1, $2);
|
||||
}
|
||||
;
|
||||
|
||||
dotted_name:
|
||||
relation_name attrs
|
||||
{ $$ = lcons(makeString($1), $2); }
|
||||
;
|
||||
|
||||
attrs: '.' attr_name
|
||||
{ $$ = list_make1(makeString($2)); }
|
||||
indirection_el:
|
||||
'.' attr_name
|
||||
{
|
||||
$$ = (Node *) makeString($2);
|
||||
}
|
||||
| '.' '*'
|
||||
{ $$ = list_make1(makeString("*")); }
|
||||
| '.' attr_name attrs
|
||||
{ $$ = lcons(makeString($2), $3); }
|
||||
{
|
||||
$$ = (Node *) makeString("*");
|
||||
}
|
||||
| '[' a_expr ']'
|
||||
{
|
||||
A_Indices *ai = makeNode(A_Indices);
|
||||
ai->lidx = NULL;
|
||||
ai->uidx = $2;
|
||||
$$ = (Node *) ai;
|
||||
}
|
||||
| '[' a_expr ':' a_expr ']'
|
||||
{
|
||||
A_Indices *ai = makeNode(A_Indices);
|
||||
ai->lidx = $2;
|
||||
ai->uidx = $4;
|
||||
$$ = (Node *) ai;
|
||||
}
|
||||
;
|
||||
|
||||
indirection:
|
||||
indirection_el { $$ = list_make1($1); }
|
||||
| indirection indirection_el { $$ = lappend($1, $2); }
|
||||
;
|
||||
|
||||
opt_indirection:
|
||||
/*EMPTY*/ { $$ = NIL; }
|
||||
| opt_indirection indirection_el { $$ = lappend($1, $2); }
|
||||
;
|
||||
|
||||
|
||||
/*****************************************************************************
|
||||
*
|
||||
* target lists
|
||||
* target lists for SELECT, UPDATE, INSERT
|
||||
*
|
||||
*****************************************************************************/
|
||||
|
||||
/* Target lists as found in SELECT ... and INSERT VALUES ( ... ) */
|
||||
|
||||
target_list:
|
||||
target_el { $$ = list_make1($1); }
|
||||
| target_list ',' target_el { $$ = lappend($1, $3); }
|
||||
@@ -7110,7 +7110,7 @@ target_el: a_expr AS ColLabel
|
||||
{
|
||||
ColumnRef *n = makeNode(ColumnRef);
|
||||
n->fields = list_make1(makeString("*"));
|
||||
n->indirection = NIL;
|
||||
|
||||
$$ = makeNode(ResTarget);
|
||||
$$->name = NULL;
|
||||
$$->indirection = NIL;
|
||||
@@ -7118,12 +7118,6 @@ target_el: a_expr AS ColLabel
|
||||
}
|
||||
;
|
||||
|
||||
/* Target list as found in UPDATE table SET ...
|
||||
| '(' row_ ')' = '(' row_ ')'
|
||||
{
|
||||
$$ = NULL;
|
||||
}
|
||||
*/
|
||||
update_target_list:
|
||||
update_target_el { $$ = list_make1($1); }
|
||||
| update_target_list ',' update_target_el { $$ = lappend($1,$3); }
|
||||
@@ -7153,7 +7147,13 @@ insert_target_list:
|
||||
;
|
||||
|
||||
insert_target_el:
|
||||
target_el { $$ = $1; }
|
||||
a_expr
|
||||
{
|
||||
$$ = makeNode(ResTarget);
|
||||
$$->name = NULL;
|
||||
$$->indirection = NIL;
|
||||
$$->val = (Node *)$1;
|
||||
}
|
||||
| DEFAULT
|
||||
{
|
||||
$$ = makeNode(ResTarget);
|
||||
@@ -7188,26 +7188,26 @@ qualified_name:
|
||||
$$->schemaname = NULL;
|
||||
$$->relname = $1;
|
||||
}
|
||||
| dotted_name
|
||||
| relation_name attrs
|
||||
{
|
||||
$$ = makeNode(RangeVar);
|
||||
switch (list_length($1))
|
||||
switch (list_length($2))
|
||||
{
|
||||
case 2:
|
||||
case 1:
|
||||
$$->catalogname = NULL;
|
||||
$$->schemaname = strVal(linitial($1));
|
||||
$$->relname = strVal(lsecond($1));
|
||||
$$->schemaname = $1;
|
||||
$$->relname = strVal(linitial($2));
|
||||
break;
|
||||
case 3:
|
||||
$$->catalogname = strVal(linitial($1));
|
||||
$$->schemaname = strVal(lsecond($1));
|
||||
$$->relname = strVal(lthird($1));
|
||||
case 2:
|
||||
$$->catalogname = $1;
|
||||
$$->schemaname = strVal(linitial($2));
|
||||
$$->relname = strVal(lsecond($2));
|
||||
break;
|
||||
default:
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_SYNTAX_ERROR),
|
||||
errmsg("improper qualified name (too many dotted names): %s",
|
||||
NameListToString($1))));
|
||||
NameListToString(lcons(makeString($1), $2)))));
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -7234,9 +7234,18 @@ index_name: ColId { $$ = $1; };
|
||||
|
||||
file_name: Sconst { $$ = $1; };
|
||||
|
||||
/*
|
||||
* The production for a qualified func_name has to exactly match the
|
||||
* production for a qualified columnref, because we cannot tell which we
|
||||
* are parsing until we see what comes after it ('(' for a func_name,
|
||||
* anything else for a columnref). Therefore we allow 'indirection' which
|
||||
* may contain subscripts, and reject that case in the C code. (If we
|
||||
* ever implement SQL99-like methods, such syntax may actually become legal!)
|
||||
*/
|
||||
func_name: function_name
|
||||
{ $$ = list_make1(makeString($1)); }
|
||||
| dotted_name { $$ = $1; }
|
||||
| relation_name indirection
|
||||
{ $$ = check_func_name(lcons(makeString($1), $2)); }
|
||||
;
|
||||
|
||||
|
||||
@@ -7325,14 +7334,6 @@ AexprConst: Iconst
|
||||
n->typename->typmod = INTERVAL_TYPMOD($3, $6);
|
||||
$$ = (Node *)n;
|
||||
}
|
||||
| PARAM opt_indirection
|
||||
{
|
||||
ParamRef *n = makeNode(ParamRef);
|
||||
n->number = $1;
|
||||
n->fields = NIL;
|
||||
n->indirection = $2;
|
||||
$$ = (Node *)n;
|
||||
}
|
||||
| TRUE_P
|
||||
{
|
||||
$$ = (Node *)makeBoolAConst(TRUE);
|
||||
@@ -7781,6 +7782,48 @@ SpecialRuleRelation:
|
||||
|
||||
%%
|
||||
|
||||
static Node *
|
||||
makeColumnRef(char *relname, List *indirection)
|
||||
{
|
||||
/*
|
||||
* Generate a ColumnRef node, with an A_Indirection node added if there
|
||||
* is any subscripting in the specified indirection list. However,
|
||||
* any field selection at the start of the indirection list must be
|
||||
* transposed into the "fields" part of the ColumnRef node.
|
||||
*/
|
||||
ColumnRef *c = makeNode(ColumnRef);
|
||||
int nfields = 0;
|
||||
ListCell *l;
|
||||
|
||||
foreach(l, indirection)
|
||||
{
|
||||
if (IsA(lfirst(l), A_Indices))
|
||||
{
|
||||
A_Indirection *i = makeNode(A_Indirection);
|
||||
|
||||
if (nfields == 0)
|
||||
{
|
||||
/* easy case - all indirection goes to A_Indirection */
|
||||
c->fields = list_make1(makeString(relname));
|
||||
i->indirection = indirection;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* got to split the list in two */
|
||||
i->indirection = list_copy_tail(indirection, nfields);
|
||||
indirection = list_truncate(indirection, nfields);
|
||||
c->fields = lcons(makeString(relname), indirection);
|
||||
}
|
||||
i->arg = (Node *) c;
|
||||
return (Node *) i;
|
||||
}
|
||||
nfields++;
|
||||
}
|
||||
/* No subscripting, so all indirection gets added to field list */
|
||||
c->fields = lcons(makeString(relname), indirection);
|
||||
return (Node *) c;
|
||||
}
|
||||
|
||||
static Node *
|
||||
makeTypeCast(Node *arg, TypeName *typename)
|
||||
{
|
||||
@@ -7945,6 +7988,26 @@ makeOverlaps(List *largs, List *rargs)
|
||||
return n;
|
||||
}
|
||||
|
||||
/* check_func_name --- check the result of func_name production
|
||||
*
|
||||
* It's easiest to let the grammar production for func_name allow subscripts
|
||||
* and '*', which we then must reject here.
|
||||
*/
|
||||
static List *
|
||||
check_func_name(List *names)
|
||||
{
|
||||
ListCell *i;
|
||||
|
||||
foreach(i, names)
|
||||
{
|
||||
if (!IsA(lfirst(i), String))
|
||||
yyerror("syntax error");
|
||||
else if (strcmp(strVal(lfirst(i)), "*") == 0)
|
||||
yyerror("syntax error");
|
||||
}
|
||||
return names;
|
||||
}
|
||||
|
||||
/* extractArgTypes()
|
||||
* Given a list of FunctionParameter nodes, extract a list of just the
|
||||
* argument types (TypeNames). Most of the productions using func_args
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $PostgreSQL: pgsql/src/backend/parser/parse_clause.c,v 1.131 2004/05/30 23:40:34 neilc Exp $
|
||||
* $PostgreSQL: pgsql/src/backend/parser/parse_clause.c,v 1.132 2004/06/09 19:08:17 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -1122,8 +1122,7 @@ findTargetlistEntry(ParseState *pstate, Node *node, List **tlist, int clause)
|
||||
*----------
|
||||
*/
|
||||
if (IsA(node, ColumnRef) &&
|
||||
list_length(((ColumnRef *) node)->fields) == 1 &&
|
||||
((ColumnRef *) node)->indirection == NIL)
|
||||
list_length(((ColumnRef *) node)->fields) == 1)
|
||||
{
|
||||
char *name = strVal(linitial(((ColumnRef *) node)->fields));
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $PostgreSQL: pgsql/src/backend/parser/parse_expr.c,v 1.172 2004/05/30 23:40:35 neilc Exp $
|
||||
* $PostgreSQL: pgsql/src/backend/parser/parse_expr.c,v 1.173 2004/06/09 19:08:17 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -100,7 +100,6 @@ transformExpr(ParseState *pstate, Node *expr)
|
||||
int paramno = pref->number;
|
||||
ParseState *toppstate;
|
||||
Param *param;
|
||||
ListCell *fields;
|
||||
|
||||
/*
|
||||
* Find topmost ParseState, which is where paramtype info
|
||||
@@ -148,18 +147,6 @@ transformExpr(ParseState *pstate, Node *expr)
|
||||
param->paramid = (AttrNumber) paramno;
|
||||
param->paramtype = toppstate->p_paramtypes[paramno - 1];
|
||||
result = (Node *) param;
|
||||
|
||||
/* handle qualification, if any */
|
||||
foreach(fields, pref->fields)
|
||||
{
|
||||
result = ParseFuncOrColumn(pstate,
|
||||
list_make1(lfirst(fields)),
|
||||
list_make1(result),
|
||||
false, false, true);
|
||||
}
|
||||
/* handle subscripts, if any */
|
||||
result = transformIndirection(pstate, result,
|
||||
pref->indirection);
|
||||
break;
|
||||
}
|
||||
case T_A_Const:
|
||||
@@ -173,23 +160,13 @@ transformExpr(ParseState *pstate, Node *expr)
|
||||
con->typename);
|
||||
break;
|
||||
}
|
||||
case T_ExprFieldSelect:
|
||||
case T_A_Indirection:
|
||||
{
|
||||
ExprFieldSelect *efs = (ExprFieldSelect *) expr;
|
||||
ListCell *fields;
|
||||
A_Indirection *ind = (A_Indirection *) expr;
|
||||
|
||||
result = transformExpr(pstate, efs->arg);
|
||||
/* handle qualification, if any */
|
||||
foreach(fields, efs->fields)
|
||||
{
|
||||
result = ParseFuncOrColumn(pstate,
|
||||
list_make1(lfirst(fields)),
|
||||
list_make1(result),
|
||||
false, false, true);
|
||||
}
|
||||
/* handle subscripts, if any */
|
||||
result = transformExpr(pstate, ind->arg);
|
||||
result = transformIndirection(pstate, result,
|
||||
efs->indirection);
|
||||
ind->indirection);
|
||||
break;
|
||||
}
|
||||
case T_TypeCast:
|
||||
@@ -961,6 +938,7 @@ transformExpr(ParseState *pstate, Node *expr)
|
||||
case T_NullIfExpr:
|
||||
case T_BoolExpr:
|
||||
case T_FieldSelect:
|
||||
case T_FieldStore:
|
||||
case T_RelabelType:
|
||||
case T_CaseTestExpr:
|
||||
case T_CoerceToDomain:
|
||||
@@ -983,15 +961,55 @@ transformExpr(ParseState *pstate, Node *expr)
|
||||
static Node *
|
||||
transformIndirection(ParseState *pstate, Node *basenode, List *indirection)
|
||||
{
|
||||
if (indirection == NIL)
|
||||
return basenode;
|
||||
return (Node *) transformArraySubscripts(pstate,
|
||||
basenode,
|
||||
exprType(basenode),
|
||||
exprTypmod(basenode),
|
||||
indirection,
|
||||
false,
|
||||
NULL);
|
||||
Node *result = basenode;
|
||||
List *subscripts = NIL;
|
||||
ListCell *i;
|
||||
|
||||
/*
|
||||
* We have to split any field-selection operations apart from
|
||||
* subscripting. Adjacent A_Indices nodes have to be treated
|
||||
* as a single multidimensional subscript operation.
|
||||
*/
|
||||
foreach(i, indirection)
|
||||
{
|
||||
Node *n = lfirst(i);
|
||||
|
||||
if (IsA(n, A_Indices))
|
||||
{
|
||||
subscripts = lappend(subscripts, n);
|
||||
}
|
||||
else
|
||||
{
|
||||
Assert(IsA(n, String));
|
||||
|
||||
/* process subscripts before this field selection */
|
||||
if (subscripts)
|
||||
result = (Node *) transformArraySubscripts(pstate,
|
||||
result,
|
||||
exprType(result),
|
||||
InvalidOid,
|
||||
-1,
|
||||
subscripts,
|
||||
NULL);
|
||||
subscripts = NIL;
|
||||
|
||||
result = ParseFuncOrColumn(pstate,
|
||||
list_make1(n),
|
||||
list_make1(result),
|
||||
false, false, true);
|
||||
}
|
||||
}
|
||||
/* process trailing subscripts, if any */
|
||||
if (subscripts)
|
||||
result = (Node *) transformArraySubscripts(pstate,
|
||||
result,
|
||||
exprType(result),
|
||||
InvalidOid,
|
||||
-1,
|
||||
subscripts,
|
||||
NULL);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static Node *
|
||||
@@ -1051,17 +1069,15 @@ transformColumnRef(ParseState *pstate, ColumnRef *cref)
|
||||
}
|
||||
|
||||
/*
|
||||
* Try to find the name as a relation ... but not if
|
||||
* subscripts appear. Note also that only relations
|
||||
* already entered into the rangetable will be
|
||||
* Try to find the name as a relation. Note that only
|
||||
* relations already entered into the rangetable will be
|
||||
* recognized.
|
||||
*
|
||||
* This is a hack for backwards compatibility with
|
||||
* PostQUEL-inspired syntax. The preferred form now
|
||||
* is "rel.*".
|
||||
*/
|
||||
if (cref->indirection == NIL &&
|
||||
refnameRangeTblEntry(pstate, NULL, name,
|
||||
if (refnameRangeTblEntry(pstate, NULL, name,
|
||||
&levels_up) != NULL)
|
||||
node = transformWholeRowRef(pstate, NULL, name);
|
||||
else
|
||||
@@ -1172,7 +1188,7 @@ transformColumnRef(ParseState *pstate, ColumnRef *cref)
|
||||
break;
|
||||
}
|
||||
|
||||
return transformIndirection(pstate, node, cref->indirection);
|
||||
return node;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -1385,6 +1401,9 @@ exprType(Node *expr)
|
||||
case T_FieldSelect:
|
||||
type = ((FieldSelect *) expr)->resulttype;
|
||||
break;
|
||||
case T_FieldStore:
|
||||
type = ((FieldStore *) expr)->resulttype;
|
||||
break;
|
||||
case T_RelabelType:
|
||||
type = ((RelabelType *) expr)->resulttype;
|
||||
break;
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $PostgreSQL: pgsql/src/backend/parser/parse_node.c,v 1.83 2004/05/26 04:41:30 neilc Exp $
|
||||
* $PostgreSQL: pgsql/src/backend/parser/parse_node.c,v 1.84 2004/06/09 19:08:17 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -67,6 +67,39 @@ make_var(ParseState *pstate, RangeTblEntry *rte, int attrno)
|
||||
return makeVar(vnum, attrno, vartypeid, type_mod, sublevels_up);
|
||||
}
|
||||
|
||||
/*
|
||||
* transformArrayType()
|
||||
* Get the element type of an array type in preparation for subscripting
|
||||
*/
|
||||
Oid
|
||||
transformArrayType(Oid arrayType)
|
||||
{
|
||||
Oid elementType;
|
||||
HeapTuple type_tuple_array;
|
||||
Form_pg_type type_struct_array;
|
||||
|
||||
/* Get the type tuple for the array */
|
||||
type_tuple_array = SearchSysCache(TYPEOID,
|
||||
ObjectIdGetDatum(arrayType),
|
||||
0, 0, 0);
|
||||
if (!HeapTupleIsValid(type_tuple_array))
|
||||
elog(ERROR, "cache lookup failed for type %u", arrayType);
|
||||
type_struct_array = (Form_pg_type) GETSTRUCT(type_tuple_array);
|
||||
|
||||
/* needn't check typisdefined since this will fail anyway */
|
||||
|
||||
elementType = type_struct_array->typelem;
|
||||
if (elementType == InvalidOid)
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_DATATYPE_MISMATCH),
|
||||
errmsg("cannot subscript type %s because it is not an array",
|
||||
format_type_be(arrayType))));
|
||||
|
||||
ReleaseSysCache(type_tuple_array);
|
||||
|
||||
return elementType;
|
||||
}
|
||||
|
||||
/*
|
||||
* transformArraySubscripts()
|
||||
* Transform array subscripting. This is used for both
|
||||
@@ -83,68 +116,49 @@ make_var(ParseState *pstate, RangeTblEntry *rte, int attrno)
|
||||
*
|
||||
* pstate Parse state
|
||||
* arrayBase Already-transformed expression for the array as a whole
|
||||
* (may be NULL if we are handling an INSERT)
|
||||
* arrayType OID of array's datatype
|
||||
* arrayTypMod typmod to be applied to array elements
|
||||
* arrayType OID of array's datatype (should match type of arrayBase)
|
||||
* elementType OID of array's element type (fetch with transformArrayType,
|
||||
* or pass InvalidOid to do it here)
|
||||
* elementTypMod typmod to be applied to array elements (if storing)
|
||||
* 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 *
|
||||
transformArraySubscripts(ParseState *pstate,
|
||||
Node *arrayBase,
|
||||
Oid arrayType,
|
||||
int32 arrayTypMod,
|
||||
Oid elementType,
|
||||
int32 elementTypMod,
|
||||
List *indirection,
|
||||
bool forceSlice,
|
||||
Node *assignFrom)
|
||||
{
|
||||
Oid elementType,
|
||||
resultType;
|
||||
HeapTuple type_tuple_array;
|
||||
Form_pg_type type_struct_array;
|
||||
bool isSlice = forceSlice;
|
||||
Oid resultType;
|
||||
bool isSlice = false;
|
||||
List *upperIndexpr = NIL;
|
||||
List *lowerIndexpr = NIL;
|
||||
ListCell *idx;
|
||||
ArrayRef *aref;
|
||||
|
||||
/* Get the type tuple for the array */
|
||||
type_tuple_array = SearchSysCache(TYPEOID,
|
||||
ObjectIdGetDatum(arrayType),
|
||||
0, 0, 0);
|
||||
if (!HeapTupleIsValid(type_tuple_array))
|
||||
elog(ERROR, "cache lookup failed for type %u", arrayType);
|
||||
type_struct_array = (Form_pg_type) GETSTRUCT(type_tuple_array);
|
||||
|
||||
elementType = type_struct_array->typelem;
|
||||
if (elementType == InvalidOid)
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_DATATYPE_MISMATCH),
|
||||
errmsg("cannot subscript type %s because it is not an array",
|
||||
format_type_be(arrayType))));
|
||||
/* Caller may or may not have bothered to determine elementType */
|
||||
if (!OidIsValid(elementType))
|
||||
elementType = transformArrayType(arrayType);
|
||||
|
||||
/*
|
||||
* 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.
|
||||
* contain only a single subscript. We have to prescan the indirection
|
||||
* list to see if there are any double subscripts.
|
||||
*/
|
||||
if (!isSlice)
|
||||
foreach(idx, indirection)
|
||||
{
|
||||
foreach(idx, indirection)
|
||||
{
|
||||
A_Indices *ai = (A_Indices *) lfirst(idx);
|
||||
A_Indices *ai = (A_Indices *) lfirst(idx);
|
||||
|
||||
if (ai->lidx != NULL)
|
||||
{
|
||||
isSlice = true;
|
||||
break;
|
||||
}
|
||||
if (ai->lidx != NULL)
|
||||
{
|
||||
isSlice = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -166,6 +180,7 @@ transformArraySubscripts(ParseState *pstate,
|
||||
A_Indices *ai = (A_Indices *) lfirst(idx);
|
||||
Node *subexpr;
|
||||
|
||||
Assert(IsA(ai, A_Indices));
|
||||
if (isSlice)
|
||||
{
|
||||
if (ai->lidx)
|
||||
@@ -209,28 +224,26 @@ 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.)
|
||||
*/
|
||||
if (assignFrom != NULL)
|
||||
{
|
||||
Oid typesource = exprType(assignFrom);
|
||||
Oid typeneeded = isSlice ? arrayType : elementType;
|
||||
|
||||
if (typesource != InvalidOid)
|
||||
{
|
||||
assignFrom = coerce_to_target_type(pstate,
|
||||
assignFrom, typesource,
|
||||
typeneeded, arrayTypMod,
|
||||
COERCION_ASSIGNMENT,
|
||||
COERCE_IMPLICIT_CAST);
|
||||
if (assignFrom == NULL)
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_DATATYPE_MISMATCH),
|
||||
errmsg("array assignment requires type %s"
|
||||
" but expression is of type %s",
|
||||
format_type_be(typeneeded),
|
||||
format_type_be(typesource)),
|
||||
errhint("You will need to rewrite or cast the expression.")));
|
||||
}
|
||||
assignFrom = coerce_to_target_type(pstate,
|
||||
assignFrom, typesource,
|
||||
typeneeded, elementTypMod,
|
||||
COERCION_ASSIGNMENT,
|
||||
COERCE_IMPLICIT_CAST);
|
||||
if (assignFrom == NULL)
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_DATATYPE_MISMATCH),
|
||||
errmsg("array assignment requires type %s"
|
||||
" but expression is of type %s",
|
||||
format_type_be(typeneeded),
|
||||
format_type_be(typesource)),
|
||||
errhint("You will need to rewrite or cast the expression.")));
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -245,8 +258,6 @@ transformArraySubscripts(ParseState *pstate,
|
||||
aref->refexpr = (Expr *) arrayBase;
|
||||
aref->refassgnexpr = (Expr *) assignFrom;
|
||||
|
||||
ReleaseSysCache(type_tuple_array);
|
||||
|
||||
return aref;
|
||||
}
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $PostgreSQL: pgsql/src/backend/parser/parse_target.c,v 1.120 2004/06/01 03:28:48 tgl Exp $
|
||||
* $PostgreSQL: pgsql/src/backend/parser/parse_target.c,v 1.121 2004/06/09 19:08:17 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -25,9 +25,18 @@
|
||||
#include "parser/parse_target.h"
|
||||
#include "parser/parse_type.h"
|
||||
#include "utils/builtins.h"
|
||||
#include "utils/lsyscache.h"
|
||||
|
||||
|
||||
static void markTargetListOrigin(ParseState *pstate, Resdom *res, Var *var);
|
||||
static Node *transformAssignmentIndirection(ParseState *pstate,
|
||||
Node *basenode,
|
||||
const char *targetName,
|
||||
bool targetIsArray,
|
||||
Oid targetTypeId,
|
||||
int32 targetTypMod,
|
||||
ListCell *indirection,
|
||||
Node *rhs);
|
||||
static List *ExpandAllTables(ParseState *pstate);
|
||||
static char *FigureColname(Node *node);
|
||||
static int FigureColnameInternal(Node *node, char **name);
|
||||
@@ -87,7 +96,7 @@ transformTargetEntry(ParseState *pstate,
|
||||
* Turns a list of ResTarget's into a list of TargetEntry's.
|
||||
*
|
||||
* At this point, we don't care whether we are doing SELECT, INSERT,
|
||||
* or UPDATE; we just transform the given expressions.
|
||||
* or UPDATE; we just transform the given expressions (the "val" fields).
|
||||
*/
|
||||
List *
|
||||
transformTargetList(ParseState *pstate, List *targetlist)
|
||||
@@ -284,14 +293,14 @@ markTargetListOrigin(ParseState *pstate, Resdom *res, Var *var)
|
||||
* This is used in INSERT and UPDATE statements only. It prepares a
|
||||
* 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 subscripts attached to the target
|
||||
* column itself.
|
||||
* (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 for target column, if any
|
||||
* indirection subscripts/field names for target column, if any
|
||||
*/
|
||||
void
|
||||
updateTargetListEntry(ParseState *pstate,
|
||||
@@ -320,8 +329,8 @@ updateTargetListEntry(ParseState *pstate,
|
||||
* type/typmod into it so that exprType will report the right things.
|
||||
* (We expect that the eventually substituted default expression will
|
||||
* in fact have this type and typmod.) Also, reject trying to update
|
||||
* an array element with DEFAULT, since there can't be any default for
|
||||
* individual elements of a column.
|
||||
* a subfield or array element with DEFAULT, since there can't be any
|
||||
* default for portions of a column.
|
||||
*/
|
||||
if (tle->expr && IsA(tle->expr, SetToDefault))
|
||||
{
|
||||
@@ -330,82 +339,81 @@ updateTargetListEntry(ParseState *pstate,
|
||||
def->typeId = attrtype;
|
||||
def->typeMod = attrtypmod;
|
||||
if (indirection)
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
||||
errmsg("cannot set an array element to DEFAULT")));
|
||||
{
|
||||
if (IsA(linitial(indirection), A_Indices))
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
||||
errmsg("cannot set an array element to DEFAULT")));
|
||||
else
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
||||
errmsg("cannot set a subfield to DEFAULT")));
|
||||
}
|
||||
}
|
||||
|
||||
/* Now we can use exprType() safely. */
|
||||
type_id = exprType((Node *) tle->expr);
|
||||
|
||||
/*
|
||||
* If there are subscripts on the target column, prepare an array
|
||||
* assignment expression. This will generate an array value that the
|
||||
* source value has been inserted into, which can then be placed in
|
||||
* the new tuple constructed by INSERT or UPDATE. Note that
|
||||
* transformArraySubscripts takes care of type coercion.
|
||||
* If there is indirection on the target column, prepare an array or
|
||||
* subfield assignment expression. This will generate a new column value
|
||||
* that the source value has been inserted into, which can then be placed
|
||||
* in the new tuple constructed by INSERT or UPDATE.
|
||||
*/
|
||||
if (indirection)
|
||||
{
|
||||
Node *arrayBase;
|
||||
ArrayRef *aref;
|
||||
Node *colVar;
|
||||
|
||||
if (pstate->p_is_insert)
|
||||
{
|
||||
/*
|
||||
* The command is INSERT INTO table (arraycol[subscripts]) ...
|
||||
* so there is not really a source array value to work with.
|
||||
* Let the executor do something reasonable, if it can. Notice
|
||||
* that we force transformArraySubscripts to treat the
|
||||
* subscripting op as an array-slice op below, so the source
|
||||
* data will have been coerced to the array type.
|
||||
* The command is INSERT INTO table (col.something) ...
|
||||
* so there is not really a source value to work with.
|
||||
* Insert a NULL constant as the source value.
|
||||
*/
|
||||
arrayBase = NULL; /* signal there is no source array */
|
||||
colVar = (Node *) makeNullConst(attrtype);
|
||||
}
|
||||
else
|
||||
{
|
||||
/*
|
||||
* Build a Var for the array to be updated.
|
||||
* Build a Var for the column to be updated.
|
||||
*/
|
||||
arrayBase = (Node *) make_var(pstate,
|
||||
pstate->p_target_rangetblentry,
|
||||
attrno);
|
||||
colVar = (Node *) make_var(pstate,
|
||||
pstate->p_target_rangetblentry,
|
||||
attrno);
|
||||
}
|
||||
|
||||
aref = transformArraySubscripts(pstate,
|
||||
arrayBase,
|
||||
attrtype,
|
||||
attrtypmod,
|
||||
indirection,
|
||||
pstate->p_is_insert,
|
||||
(Node *) tle->expr);
|
||||
tle->expr = (Expr *) aref;
|
||||
tle->expr = (Expr *)
|
||||
transformAssignmentIndirection(pstate,
|
||||
colVar,
|
||||
colname,
|
||||
false,
|
||||
attrtype,
|
||||
attrtypmod,
|
||||
list_head(indirection),
|
||||
(Node *) tle->expr);
|
||||
}
|
||||
else
|
||||
{
|
||||
/*
|
||||
* For normal non-subscripted target column, do type checking and
|
||||
* coercion. But accept InvalidOid, which indicates the source is
|
||||
* a NULL constant. (XXX is that still true?)
|
||||
* For normal non-qualified target column, do type checking and
|
||||
* coercion.
|
||||
*/
|
||||
if (type_id != InvalidOid)
|
||||
{
|
||||
tle->expr = (Expr *)
|
||||
coerce_to_target_type(pstate,
|
||||
(Node *) tle->expr, type_id,
|
||||
attrtype, attrtypmod,
|
||||
COERCION_ASSIGNMENT,
|
||||
COERCE_IMPLICIT_CAST);
|
||||
if (tle->expr == NULL)
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_DATATYPE_MISMATCH),
|
||||
errmsg("column \"%s\" is of type %s"
|
||||
" but expression is of type %s",
|
||||
colname,
|
||||
format_type_be(attrtype),
|
||||
format_type_be(type_id)),
|
||||
errhint("You will need to rewrite or cast the expression.")));
|
||||
}
|
||||
tle->expr = (Expr *)
|
||||
coerce_to_target_type(pstate,
|
||||
(Node *) tle->expr, type_id,
|
||||
attrtype, attrtypmod,
|
||||
COERCION_ASSIGNMENT,
|
||||
COERCE_IMPLICIT_CAST);
|
||||
if (tle->expr == NULL)
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_DATATYPE_MISMATCH),
|
||||
errmsg("column \"%s\" is of type %s"
|
||||
" but expression is of type %s",
|
||||
colname,
|
||||
format_type_be(attrtype),
|
||||
format_type_be(type_id)),
|
||||
errhint("You will need to rewrite or cast the expression.")));
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -425,6 +433,208 @@ updateTargetListEntry(ParseState *pstate,
|
||||
resnode->resname = colname;
|
||||
}
|
||||
|
||||
/*
|
||||
* Process indirection (field selection or subscripting) of the target
|
||||
* column in INSERT/UPDATE. This routine recurses for multiple levels
|
||||
* of indirection --- but note that several adjacent A_Indices nodes in
|
||||
* the indirection list are treated as a single multidimensional subscript
|
||||
* operation.
|
||||
*
|
||||
* In the initial call, basenode is a Var for the target column in UPDATE,
|
||||
* or a null Const of the target's type in INSERT. In recursive calls,
|
||||
* basenode is NULL, indicating that a substitute node should be consed up if
|
||||
* needed.
|
||||
*
|
||||
* targetName is the name of the field or subfield we're assigning to, and
|
||||
* targetIsArray is true if we're subscripting it. These are just for
|
||||
* error reporting.
|
||||
*
|
||||
* targetTypeId and targetTypMod indicate the datatype of the object to
|
||||
* be assigned to (initially the target column, later some subobject).
|
||||
*
|
||||
* indirection is the sublist remaining to process. When it's NULL, we're
|
||||
* done recursing and can just coerce and return the RHS.
|
||||
*
|
||||
* rhs is the already-transformed value to be assigned; note it has not been
|
||||
* coerced to any particular type.
|
||||
*/
|
||||
static Node *
|
||||
transformAssignmentIndirection(ParseState *pstate,
|
||||
Node *basenode,
|
||||
const char *targetName,
|
||||
bool targetIsArray,
|
||||
Oid targetTypeId,
|
||||
int32 targetTypMod,
|
||||
ListCell *indirection,
|
||||
Node *rhs)
|
||||
{
|
||||
Node *result;
|
||||
List *subscripts = NIL;
|
||||
bool isSlice = false;
|
||||
ListCell *i;
|
||||
|
||||
if (indirection && !basenode)
|
||||
{
|
||||
/* Set up a substitution. We reuse CaseTestExpr for this. */
|
||||
CaseTestExpr *ctest = makeNode(CaseTestExpr);
|
||||
|
||||
ctest->typeId = targetTypeId;
|
||||
ctest->typeMod = targetTypMod;
|
||||
basenode = (Node *) ctest;
|
||||
}
|
||||
|
||||
/*
|
||||
* We have to split any field-selection operations apart from
|
||||
* subscripting. Adjacent A_Indices nodes have to be treated
|
||||
* as a single multidimensional subscript operation.
|
||||
*/
|
||||
for_each_cell(i, indirection)
|
||||
{
|
||||
Node *n = lfirst(i);
|
||||
|
||||
if (IsA(n, A_Indices))
|
||||
{
|
||||
subscripts = lappend(subscripts, n);
|
||||
if (((A_Indices *) n)->lidx != NULL)
|
||||
isSlice = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
FieldStore *fstore;
|
||||
Oid typrelid;
|
||||
AttrNumber attnum;
|
||||
Oid fieldTypeId;
|
||||
int32 fieldTypMod;
|
||||
|
||||
Assert(IsA(n, String));
|
||||
|
||||
/* process subscripts before this field selection */
|
||||
if (subscripts)
|
||||
{
|
||||
Oid elementTypeId = transformArrayType(targetTypeId);
|
||||
Oid typeNeeded = isSlice ? targetTypeId : elementTypeId;
|
||||
|
||||
/* recurse to create appropriate RHS for array assign */
|
||||
rhs = transformAssignmentIndirection(pstate,
|
||||
NULL,
|
||||
targetName,
|
||||
true,
|
||||
typeNeeded,
|
||||
targetTypMod,
|
||||
i,
|
||||
rhs);
|
||||
/* process subscripts */
|
||||
return (Node *) transformArraySubscripts(pstate,
|
||||
basenode,
|
||||
targetTypeId,
|
||||
elementTypeId,
|
||||
targetTypMod,
|
||||
subscripts,
|
||||
rhs);
|
||||
}
|
||||
|
||||
/* No subscripts, so can process field selection here */
|
||||
|
||||
typrelid = typeidTypeRelid(targetTypeId);
|
||||
if (!typrelid)
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_DATATYPE_MISMATCH),
|
||||
errmsg("cannot assign to a column of type %s because it is not a composite type",
|
||||
format_type_be(targetTypeId))));
|
||||
|
||||
attnum = get_attnum(typrelid, strVal(n));
|
||||
if (attnum == InvalidAttrNumber)
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_UNDEFINED_COLUMN),
|
||||
errmsg("column \"%s\" not found in data type %s",
|
||||
strVal(n), format_type_be(targetTypeId))));
|
||||
if (attnum < 0)
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_UNDEFINED_COLUMN),
|
||||
errmsg("cannot assign to system column \"%s\"",
|
||||
strVal(n))));
|
||||
|
||||
get_atttypetypmod(typrelid, attnum,
|
||||
&fieldTypeId, &fieldTypMod);
|
||||
|
||||
/* recurse to create appropriate RHS for field assign */
|
||||
rhs = transformAssignmentIndirection(pstate,
|
||||
NULL,
|
||||
strVal(n),
|
||||
false,
|
||||
fieldTypeId,
|
||||
fieldTypMod,
|
||||
lnext(i),
|
||||
rhs);
|
||||
|
||||
/* and build a FieldStore node */
|
||||
fstore = makeNode(FieldStore);
|
||||
fstore->arg = (Expr *) basenode;
|
||||
fstore->newvals = list_make1(rhs);
|
||||
fstore->fieldnums = list_make1_int(attnum);
|
||||
fstore->resulttype = targetTypeId;
|
||||
|
||||
return (Node *) fstore;
|
||||
}
|
||||
}
|
||||
|
||||
/* process trailing subscripts, if any */
|
||||
if (subscripts)
|
||||
{
|
||||
Oid elementTypeId = transformArrayType(targetTypeId);
|
||||
Oid typeNeeded = isSlice ? targetTypeId : elementTypeId;
|
||||
|
||||
/* recurse to create appropriate RHS for array assign */
|
||||
rhs = transformAssignmentIndirection(pstate,
|
||||
NULL,
|
||||
targetName,
|
||||
true,
|
||||
typeNeeded,
|
||||
targetTypMod,
|
||||
NULL,
|
||||
rhs);
|
||||
/* process subscripts */
|
||||
return (Node *) transformArraySubscripts(pstate,
|
||||
basenode,
|
||||
targetTypeId,
|
||||
elementTypeId,
|
||||
targetTypMod,
|
||||
subscripts,
|
||||
rhs);
|
||||
}
|
||||
|
||||
/* base case: just coerce RHS to match target type ID */
|
||||
|
||||
result = coerce_to_target_type(pstate,
|
||||
rhs, exprType(rhs),
|
||||
targetTypeId, targetTypMod,
|
||||
COERCION_ASSIGNMENT,
|
||||
COERCE_IMPLICIT_CAST);
|
||||
if (result == NULL)
|
||||
{
|
||||
if (targetIsArray)
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_DATATYPE_MISMATCH),
|
||||
errmsg("array assignment to \"%s\" requires type %s"
|
||||
" but expression is of type %s",
|
||||
targetName,
|
||||
format_type_be(targetTypeId),
|
||||
format_type_be(exprType(rhs))),
|
||||
errhint("You will need to rewrite or cast the expression.")));
|
||||
else
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_DATATYPE_MISMATCH),
|
||||
errmsg("subfield \"%s\" is of type %s"
|
||||
" but expression is of type %s",
|
||||
targetName,
|
||||
format_type_be(targetTypeId),
|
||||
format_type_be(exprType(rhs))),
|
||||
errhint("You will need to rewrite or cast the expression.")));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* checkInsertTargets -
|
||||
@@ -466,21 +676,42 @@ checkInsertTargets(ParseState *pstate, List *cols, List **attrnos)
|
||||
/*
|
||||
* Do initial validation of user-supplied INSERT column list.
|
||||
*/
|
||||
List *wholecols = NIL;
|
||||
ListCell *tl;
|
||||
|
||||
foreach(tl, cols)
|
||||
{
|
||||
char *name = ((ResTarget *) lfirst(tl))->name;
|
||||
ResTarget *col = (ResTarget *) lfirst(tl);
|
||||
char *name = col->name;
|
||||
int attrno;
|
||||
|
||||
/* Lookup column name, ereport on failure */
|
||||
attrno = attnameAttNum(pstate->p_target_relation, name, false);
|
||||
/* Check for duplicates */
|
||||
if (list_member_int(*attrnos, attrno))
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_DUPLICATE_COLUMN),
|
||||
errmsg("column \"%s\" specified more than once",
|
||||
name)));
|
||||
|
||||
/*
|
||||
* Check for duplicates, but only of whole columns --- we
|
||||
* allow INSERT INTO foo (col.subcol1, col.subcol2)
|
||||
*/
|
||||
if (col->indirection == NIL)
|
||||
{
|
||||
/* whole column; must not have any other assignment */
|
||||
if (list_member_int(*attrnos, attrno))
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_DUPLICATE_COLUMN),
|
||||
errmsg("column \"%s\" specified more than once",
|
||||
name)));
|
||||
wholecols = lappend_int(wholecols, attrno);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* partial column; must not have any whole assignment */
|
||||
if (list_member_int(wholecols, attrno))
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_DUPLICATE_COLUMN),
|
||||
errmsg("column \"%s\" specified more than once",
|
||||
name)));
|
||||
}
|
||||
|
||||
*attrnos = lappend_int(*attrnos, attrno);
|
||||
}
|
||||
}
|
||||
@@ -572,30 +803,45 @@ FigureColnameInternal(Node *node, char **name)
|
||||
{
|
||||
case T_ColumnRef:
|
||||
{
|
||||
char *cname = strVal(llast(((ColumnRef *) node)->fields));
|
||||
char *fname = NULL;
|
||||
ListCell *l;
|
||||
|
||||
if (strcmp(cname, "*") != 0)
|
||||
/* find last field name, if any, ignoring "*" */
|
||||
foreach(l, ((ColumnRef *) node)->fields)
|
||||
{
|
||||
*name = cname;
|
||||
Node *i = lfirst(l);
|
||||
|
||||
if (strcmp(strVal(i), "*") != 0)
|
||||
fname = strVal(i);
|
||||
}
|
||||
if (fname)
|
||||
{
|
||||
*name = fname;
|
||||
return 2;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case T_ExprFieldSelect:
|
||||
case T_A_Indirection:
|
||||
{
|
||||
ExprFieldSelect *efs = (ExprFieldSelect *) node;
|
||||
A_Indirection *ind = (A_Indirection *) node;
|
||||
char *fname = NULL;
|
||||
ListCell *l;
|
||||
|
||||
if (efs->fields)
|
||||
/* find last field name, if any, ignoring "*" */
|
||||
foreach(l, ind->indirection)
|
||||
{
|
||||
char *fname = strVal(llast(efs->fields));
|
||||
Node *i = lfirst(l);
|
||||
|
||||
if (strcmp(fname, "*") != 0)
|
||||
{
|
||||
*name = fname;
|
||||
return 2;
|
||||
}
|
||||
if (IsA(i, String) &&
|
||||
strcmp(strVal(i), "*") != 0)
|
||||
fname = strVal(i);
|
||||
}
|
||||
return FigureColnameInternal(efs->arg, name);
|
||||
if (fname)
|
||||
{
|
||||
*name = fname;
|
||||
return 2;
|
||||
}
|
||||
return FigureColnameInternal(ind->arg, name);
|
||||
}
|
||||
break;
|
||||
case T_FuncCall:
|
||||
|
||||
Reference in New Issue
Block a user