aboutsummaryrefslogtreecommitdiff
path: root/src/backend/parser/parse_expr.c
diff options
context:
space:
mode:
authorTom Lane <tgl@sss.pgh.pa.us>2008-03-20 21:42:48 +0000
committerTom Lane <tgl@sss.pgh.pa.us>2008-03-20 21:42:48 +0000
commit6b0706ac33ab5da815980c642a9cde9a4cd86b6b (patch)
tree511ad4743ad55a095cbacea0713b437af36ba9ce /src/backend/parser/parse_expr.c
parent8759b79d0fe8b9937b7cbebfed78480b3e6a94b2 (diff)
downloadpostgresql-6b0706ac33ab5da815980c642a9cde9a4cd86b6b.tar.gz
postgresql-6b0706ac33ab5da815980c642a9cde9a4cd86b6b.zip
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.
Diffstat (limited to 'src/backend/parser/parse_expr.c')
-rw-r--r--src/backend/parser/parse_expr.c207
1 files changed, 167 insertions, 40 deletions
diff --git a/src/backend/parser/parse_expr.c b/src/backend/parser/parse_expr.c
index d43d38c494d..29c058aa40b 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.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[].")));
+
+ /* 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 arguments to common type if necessary */
+ /*
+ * 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;