1
0
mirror of https://github.com/postgres/postgres.git synced 2025-11-24 00:23:06 +03:00

Arrange for an explicit cast applied to an ARRAY[] constructor to be applied

directly to all the member expressions, instead of the previous implementation
where the ARRAY[] constructor would infer a common element type and then we'd
coerce the finished array after the fact.  This has a number of benefits,
one being that we can allow an empty ARRAY[] construct so long as its
element type is specified by such a cast.

Brendan Jurd, minor fixes by me.
This commit is contained in:
Tom Lane
2008-03-20 21:42:48 +00:00
parent 8759b79d0f
commit 6b0706ac33
11 changed files with 312 additions and 100 deletions

View File

@@ -11,7 +11,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/parser/gram.y,v 2.608 2008/03/19 18:38:30 tgl Exp $
* $PostgreSQL: pgsql/src/backend/parser/gram.y,v 2.609 2008/03/20 21:42:48 tgl Exp $
*
* HISTORY
* AUTHOR DATE MAJOR EVENT
@@ -107,6 +107,7 @@ static void insertSelectOptions(SelectStmt *stmt,
static Node *makeSetOp(SetOperation op, bool all, Node *larg, Node *rarg);
static Node *doNegate(Node *n, int location);
static void doNegateFloat(Value *v);
static Node *makeAArrayExpr(List *elements);
static Node *makeXmlExpr(XmlExprOp op, char *name, List *named_args, List *args);
%}
@@ -8429,6 +8430,29 @@ expr_list: a_expr
}
;
type_list: Typename { $$ = list_make1($1); }
| type_list ',' Typename { $$ = lappend($1, $3); }
;
array_expr: '[' expr_list ']'
{
$$ = makeAArrayExpr($2);
}
| '[' array_expr_list ']'
{
$$ = makeAArrayExpr($2);
}
| '[' ']'
{
$$ = makeAArrayExpr(NIL);
}
;
array_expr_list: array_expr { $$ = list_make1($1); }
| array_expr_list ',' array_expr { $$ = lappend($1, $3); }
;
extract_list:
extract_arg FROM a_expr
{
@@ -8440,34 +8464,9 @@ extract_list:
| /*EMPTY*/ { $$ = NIL; }
;
type_list: Typename { $$ = list_make1($1); }
| type_list ',' Typename { $$ = lappend($1, $3); }
;
array_expr_list: array_expr
{ $$ = list_make1($1); }
| array_expr_list ',' array_expr
{ $$ = lappend($1, $3); }
;
array_expr: '[' expr_list ']'
{
ArrayExpr *n = makeNode(ArrayExpr);
n->elements = $2;
$$ = (Node *)n;
}
| '[' array_expr_list ']'
{
ArrayExpr *n = makeNode(ArrayExpr);
n->elements = $2;
$$ = (Node *)n;
}
;
/* Allow delimited string SCONST in extract_arg as an SQL extension.
* - thomas 2001-04-12
*/
extract_arg:
IDENT { $$ = $1; }
| YEAR_P { $$ = "year"; }
@@ -9502,13 +9501,6 @@ makeColumnRef(char *relname, List *indirection, int location)
static Node *
makeTypeCast(Node *arg, TypeName *typename)
{
/*
* Simply generate a TypeCast node.
*
* Earlier we would determine whether an A_Const would
* be acceptable, however Domains require coerce_type()
* to process them -- applying constraints as required.
*/
TypeCast *n = makeNode(TypeCast);
n->arg = arg;
n->typename = typename;
@@ -9582,7 +9574,7 @@ makeBoolAConst(bool state)
{
A_Const *n = makeNode(A_Const);
n->val.type = T_String;
n->val.val.str = (state? "t": "f");
n->val.val.str = (state ? "t" : "f");
n->typename = SystemTypeName("bool");
return n;
}
@@ -9763,15 +9755,6 @@ SystemTypeName(char *name)
makeString(name)));
}
/* parser_init()
* Initialize to parse one query string
*/
void
parser_init(void)
{
QueryIsRule = FALSE;
}
/* doNegate()
* Handle negation of a numeric constant.
*
@@ -9827,6 +9810,15 @@ doNegateFloat(Value *v)
}
}
static Node *
makeAArrayExpr(List *elements)
{
A_ArrayExpr *n = makeNode(A_ArrayExpr);
n->elements = elements;
return (Node *) n;
}
static Node *
makeXmlExpr(XmlExprOp op, char *name, List *named_args, List *args)
{
@@ -9844,6 +9836,15 @@ makeXmlExpr(XmlExprOp op, char *name, List *named_args, List *args)
return (Node *) x;
}
/* parser_init()
* Initialize to parse one query string
*/
void
parser_init(void)
{
QueryIsRule = FALSE;
}
/*
* Must undefine base_yylex before including scan.c, since we want it
* to create the function base_yylex not filtered_base_yylex.

View File

@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/parser/parse_expr.c,v 1.226 2008/01/01 19:45:50 momjian Exp $
* $PostgreSQL: pgsql/src/backend/parser/parse_expr.c,v 1.227 2008/03/20 21:42:48 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -52,7 +52,8 @@ static Node *transformAExprIn(ParseState *pstate, A_Expr *a);
static Node *transformFuncCall(ParseState *pstate, FuncCall *fn);
static Node *transformCaseExpr(ParseState *pstate, CaseExpr *c);
static Node *transformSubLink(ParseState *pstate, SubLink *sublink);
static Node *transformArrayExpr(ParseState *pstate, ArrayExpr *a);
static Node *transformArrayExpr(ParseState *pstate, A_ArrayExpr *a,
Oid array_type, Oid element_type, int32 typmod);
static Node *transformRowExpr(ParseState *pstate, RowExpr *r);
static Node *transformCoalesceExpr(ParseState *pstate, CoalesceExpr *c);
static Node *transformMinMaxExpr(ParseState *pstate, MinMaxExpr *m);
@@ -142,11 +143,49 @@ transformExpr(ParseState *pstate, Node *expr)
break;
}
case T_A_ArrayExpr:
result = transformArrayExpr(pstate, (A_ArrayExpr *) expr,
InvalidOid, InvalidOid, -1);
break;
case T_TypeCast:
{
TypeCast *tc = (TypeCast *) expr;
Node *arg = transformExpr(pstate, tc->arg);
Node *arg;
/*
* If the subject of the typecast is an ARRAY[] construct
* and the target type is an array type, we invoke
* transformArrayExpr() directly so that we can pass down
* the type information. This avoids some cases where
* transformArrayExpr() might not infer the correct type.
*/
if (IsA(tc->arg, A_ArrayExpr))
{
Oid targetType;
Oid elementType;
int32 targetTypmod;
targetType = typenameTypeId(pstate, tc->typename,
&targetTypmod);
elementType = get_element_type(targetType);
if (OidIsValid(elementType))
{
result = transformArrayExpr(pstate,
(A_ArrayExpr *) tc->arg,
targetType,
elementType,
targetTypmod);
break;
}
/*
* Corner case: ARRAY[] cast to a non-array type.
* Fall through to do it the standard way.
*/
}
arg = transformExpr(pstate, tc->arg);
result = typecast_expression(pstate, arg, tc->typename);
break;
}
@@ -205,10 +244,6 @@ transformExpr(ParseState *pstate, Node *expr)
result = transformCaseExpr(pstate, (CaseExpr *) expr);
break;
case T_ArrayExpr:
result = transformArrayExpr(pstate, (ArrayExpr *) expr);
break;
case T_RowExpr:
result = transformRowExpr(pstate, (RowExpr *) expr);
break;
@@ -1255,64 +1290,156 @@ transformSubLink(ParseState *pstate, SubLink *sublink)
return result;
}
/*
* transformArrayExpr
*
* If the caller specifies the target type, the resulting array will
* be of exactly that type. Otherwise we try to infer a common type
* for the elements using select_common_type().
*/
static Node *
transformArrayExpr(ParseState *pstate, ArrayExpr *a)
transformArrayExpr(ParseState *pstate, A_ArrayExpr *a,
Oid array_type, Oid element_type, int32 typmod)
{
ArrayExpr *newa = makeNode(ArrayExpr);
List *newelems = NIL;
List *newcoercedelems = NIL;
List *typeids = NIL;
ListCell *element;
Oid array_type;
Oid element_type;
Oid coerce_type;
bool coerce_hard;
/* Transform the element expressions */
/*
* Transform the element expressions
*
* Assume that the array is one-dimensional unless we find an
* array-type element expression.
*/
newa->multidims = false;
foreach(element, a->elements)
{
Node *e = (Node *) lfirst(element);
Node *newe;
Oid newe_type;
/*
* If an element is itself an A_ArrayExpr, recurse directly so that
* we can pass down any target type we were given.
*/
if (IsA(e, A_ArrayExpr))
{
newe = transformArrayExpr(pstate,
(A_ArrayExpr *) e,
array_type,
element_type,
typmod);
newe_type = exprType(newe);
/* we certainly have an array here */
Assert(array_type == InvalidOid || array_type == newe_type);
newa->multidims = true;
}
else
{
newe = transformExpr(pstate, e);
newe_type = exprType(newe);
/*
* Check for sub-array expressions, if we haven't already
* found one.
*/
if (!newa->multidims && type_is_array(newe_type))
newa->multidims = true;
}
newe = transformExpr(pstate, e);
newelems = lappend(newelems, newe);
typeids = lappend_oid(typeids, exprType(newe));
typeids = lappend_oid(typeids, newe_type);
}
/* Select a common type for the elements */
element_type = select_common_type(typeids, "ARRAY");
/*
* Select a target type for the elements.
*
* If we haven't been given a target array type, we must try to deduce a
* common type based on the types of the individual elements present.
*/
if (OidIsValid(array_type))
{
/* Caller must ensure array_type matches element_type */
Assert(OidIsValid(element_type));
coerce_type = (newa->multidims ? array_type : element_type);
coerce_hard = true;
}
else
{
/* Can't handle an empty array without a target type */
if (typeids == NIL)
ereport(ERROR,
(errcode(ERRCODE_INDETERMINATE_DATATYPE),
errmsg("cannot determine type of empty array"),
errhint("Explicitly cast to the desired type, "
"for example ARRAY[]::integer[].")));
/* Coerce arguments to common type if necessary */
/* Select a common type for the elements */
coerce_type = select_common_type(typeids, "ARRAY");
if (newa->multidims)
{
array_type = coerce_type;
element_type = get_element_type(array_type);
if (!OidIsValid(element_type))
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_OBJECT),
errmsg("could not find element type for data type %s",
format_type_be(array_type))));
}
else
{
element_type = coerce_type;
array_type = get_array_type(element_type);
if (!OidIsValid(array_type))
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_OBJECT),
errmsg("could not find array type for data type %s",
format_type_be(element_type))));
}
coerce_hard = false;
}
/*
* Coerce elements to target type
*
* If the array has been explicitly cast, then the elements are in turn
* explicitly coerced.
*
* If the array's type was merely derived from the common type of its
* elements, then the elements are implicitly coerced to the common type.
* This is consistent with other uses of select_common_type().
*/
foreach(element, newelems)
{
Node *e = (Node *) lfirst(element);
Node *newe;
newe = coerce_to_common_type(pstate, e,
element_type,
"ARRAY");
if (coerce_hard)
{
newe = coerce_to_target_type(pstate, e,
exprType(e),
coerce_type,
typmod,
COERCION_EXPLICIT,
COERCE_EXPLICIT_CAST);
if (newe == NULL)
ereport(ERROR,
(errcode(ERRCODE_CANNOT_COERCE),
errmsg("cannot cast type %s to %s",
format_type_be(exprType(e)),
format_type_be(coerce_type))));
}
else
newe = coerce_to_common_type(pstate, e,
coerce_type,
"ARRAY");
newcoercedelems = lappend(newcoercedelems, newe);
}
/* Do we have an array type to use? */
array_type = get_array_type(element_type);
if (array_type != InvalidOid)
{
/* Elements are presumably of scalar type */
newa->multidims = false;
}
else
{
/* Must be nested array expressions */
newa->multidims = true;
array_type = element_type;
element_type = get_element_type(array_type);
if (!OidIsValid(element_type))
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_OBJECT),
errmsg("could not find array type for data type %s",
format_type_be(array_type))));
}
newa->array_typeid = array_type;
newa->element_typeid = element_type;
newa->elements = newcoercedelems;

View File

@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/parser/parse_target.c,v 1.158 2008/01/01 19:45:51 momjian Exp $
* $PostgreSQL: pgsql/src/backend/parser/parse_target.c,v 1.159 2008/03/20 21:42:48 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -1294,7 +1294,7 @@ FigureColnameInternal(Node *node, char **name)
return 1;
}
break;
case T_ArrayExpr:
case T_A_ArrayExpr:
/* make ARRAY[] act like a function */
*name = "array";
return 2;