diff options
author | Tom Lane <tgl@sss.pgh.pa.us> | 2010-10-21 16:07:17 -0400 |
---|---|---|
committer | Tom Lane <tgl@sss.pgh.pa.us> | 2010-10-21 16:07:17 -0400 |
commit | 529cb267a6843a6a8190c86b75d091771d99d6a9 (patch) | |
tree | aea2869f7fb92b00c25e67d20d4326b77720b27c /src/backend/parser/parse_node.c | |
parent | 572ab1a542c170ddd2e4c30ef472e13f531b64a4 (diff) | |
download | postgresql-529cb267a6843a6a8190c86b75d091771d99d6a9.tar.gz postgresql-529cb267a6843a6a8190c86b75d091771d99d6a9.zip |
Improve handling of domains over arrays.
This patch eliminates various bizarre behaviors caused by sloppy thinking
about the difference between a domain type and its underlying array type.
In particular, the operation of updating one element of such an array
has to be considered as yielding a value of the underlying array type,
*not* a value of the domain, because there's no assurance that the
domain's CHECK constraints are still satisfied. If we're intending to
store the result back into a domain column, we have to re-cast to the
domain type so that constraints are re-checked.
For similar reasons, such a domain can't be blindly matched to an ANYARRAY
polymorphic parameter, because the polymorphic function is likely to apply
array-ish operations that could invalidate the domain constraints. For the
moment, we just forbid such matching. We might later wish to insert an
automatic downcast to the underlying array type, but such a change should
also change matching of domains to ANYELEMENT for consistency.
To ensure that all such logic is rechecked, this patch removes the original
hack of setting a domain's pg_type.typelem field to match its base type;
the typelem will always be zero instead. In those places where it's really
okay to look through the domain type with no other logic changes, use the
newly added get_base_element_type function in place of get_element_type.
catversion bumped due to change in pg_type contents.
Per bug #5717 from Richard Huxton and subsequent discussion.
Diffstat (limited to 'src/backend/parser/parse_node.c')
-rw-r--r-- | src/backend/parser/parse_node.c | 51 |
1 files changed, 38 insertions, 13 deletions
diff --git a/src/backend/parser/parse_node.c b/src/backend/parser/parse_node.c index 8f7b8dc8fb9..0f0a188eec4 100644 --- a/src/backend/parser/parse_node.c +++ b/src/backend/parser/parse_node.c @@ -25,6 +25,7 @@ #include "parser/parse_relation.h" #include "utils/builtins.h" #include "utils/int8.h" +#include "utils/lsyscache.h" #include "utils/syscache.h" #include "utils/varbit.h" @@ -198,19 +199,35 @@ make_var(ParseState *pstate, RangeTblEntry *rte, int attrno, int location) /* * transformArrayType() - * Get the element type of an array type in preparation for subscripting + * Identify the types involved in a subscripting operation + * + * On entry, arrayType/arrayTypmod identify the type of the input value + * to be subscripted (which could be a domain type). These are modified + * if necessary to identify the actual array type and typmod, and the + * array's element type is returned. An error is thrown if the input isn't + * an array type. */ Oid -transformArrayType(Oid arrayType) +transformArrayType(Oid *arrayType, int32 *arrayTypmod) { + Oid origArrayType = *arrayType; Oid elementType; HeapTuple type_tuple_array; Form_pg_type type_struct_array; + /* + * If the input is a domain, smash to base type, and extract the actual + * typmod to be applied to the base type. Subscripting a domain is an + * operation that necessarily works on the base array type, not the domain + * itself. (Note that we provide no method whereby the creator of a + * domain over an array type could hide its ability to be subscripted.) + */ + *arrayType = getBaseTypeAndTypmod(*arrayType, arrayTypmod); + /* Get the type tuple for the array */ - type_tuple_array = SearchSysCache1(TYPEOID, ObjectIdGetDatum(arrayType)); + type_tuple_array = SearchSysCache1(TYPEOID, ObjectIdGetDatum(*arrayType)); if (!HeapTupleIsValid(type_tuple_array)) - elog(ERROR, "cache lookup failed for type %u", arrayType); + 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 */ @@ -220,7 +237,7 @@ transformArrayType(Oid arrayType) ereport(ERROR, (errcode(ERRCODE_DATATYPE_MISMATCH), errmsg("cannot subscript type %s because it is not an array", - format_type_be(arrayType)))); + format_type_be(origArrayType)))); ReleaseSysCache(type_tuple_array); @@ -241,13 +258,17 @@ transformArrayType(Oid arrayType) * that array. We produce an expression that represents the new array value * with the source data inserted into the right part of the array. * + * For both cases, if the source array is of a domain-over-array type, + * the result is of the base array type or its element type; essentially, + * we must fold a domain to its base type before applying subscripting. + * * pstate Parse state * arrayBase Already-transformed expression for the array as a whole - * arrayType OID of array's datatype (should match type of arrayBase) + * arrayType OID of array's datatype (should match type of arrayBase, + * or be the base type of arrayBase's domain type) * 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) or of - * the source array (if fetching) + * arrayTypMod typmod for the array (which is also typmod for the elements) * indirection Untransformed list of subscripts (must not be NIL) * assignFrom NULL for array fetch, else transformed expression for source. */ @@ -256,7 +277,7 @@ transformArraySubscripts(ParseState *pstate, Node *arrayBase, Oid arrayType, Oid elementType, - int32 elementTypMod, + int32 arrayTypMod, List *indirection, Node *assignFrom) { @@ -266,9 +287,13 @@ transformArraySubscripts(ParseState *pstate, ListCell *idx; ArrayRef *aref; - /* Caller may or may not have bothered to determine elementType */ + /* + * Caller may or may not have bothered to determine elementType. Note + * that if the caller did do so, arrayType/arrayTypMod must be as + * modified by transformArrayType, ie, smash domain to base type. + */ if (!OidIsValid(elementType)) - elementType = transformArrayType(arrayType); + elementType = transformArrayType(&arrayType, &arrayTypMod); /* * A list containing only single subscripts refers to a single array @@ -356,7 +381,7 @@ transformArraySubscripts(ParseState *pstate, newFrom = coerce_to_target_type(pstate, assignFrom, typesource, - typeneeded, elementTypMod, + typeneeded, arrayTypMod, COERCION_ASSIGNMENT, COERCE_IMPLICIT_CAST, -1); @@ -378,7 +403,7 @@ transformArraySubscripts(ParseState *pstate, aref = makeNode(ArrayRef); aref->refarraytype = arrayType; aref->refelemtype = elementType; - aref->reftypmod = elementTypMod; + aref->reftypmod = arrayTypMod; aref->refupperindexpr = upperIndexpr; aref->reflowerindexpr = lowerIndexpr; aref->refexpr = (Expr *) arrayBase; |