Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

Commit 529cb26

Browse files
committed
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.
1 parent 572ab1a commit 529cb26

File tree

21 files changed

+439
-101
lines changed

21 files changed

+439
-101
lines changed

doc/src/sgml/catalogs.sgml

+2-3
Original file line numberDiff line numberDiff line change
@@ -5698,9 +5698,8 @@
56985698
<entry></entry>
56995699
<entry><para>
57005700
<structfield>typndims</structfield> is the number of array dimensions
5701-
for a domain that is an array (that is, <structfield>typbasetype</> is
5702-
an array type; the domain's <structfield>typelem</> will match the base
5703-
type's <structfield>typelem</structfield>).
5701+
for a domain over an array (that is, <structfield>typbasetype</> is
5702+
an array type).
57045703
Zero for types other than domains over array types.
57055704
</para></entry>
57065705
</row>

src/backend/commands/functioncmds.c

+17
Original file line numberDiff line numberDiff line change
@@ -1657,6 +1657,23 @@ CreateCast(CreateCastStmt *stmt)
16571657
ereport(ERROR,
16581658
(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
16591659
errmsg("array data types are not binary-compatible")));
1660+
1661+
/*
1662+
* We also disallow creating binary-compatibility casts involving
1663+
* domains. Casting from a domain to its base type is already
1664+
* allowed, and casting the other way ought to go through domain
1665+
* coercion to permit constraint checking. Again, if you're intent on
1666+
* having your own semantics for that, create a no-op cast function.
1667+
*
1668+
* NOTE: if we were to relax this, the above checks for composites
1669+
* etc. would have to be modified to look through domains to their
1670+
* base types.
1671+
*/
1672+
if (sourcetyptype == TYPTYPE_DOMAIN ||
1673+
targettyptype == TYPTYPE_DOMAIN)
1674+
ereport(ERROR,
1675+
(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
1676+
errmsg("domain data types must not be marked binary-compatible")));
16601677
}
16611678

16621679
/*

src/backend/commands/typecmds.c

+6-12
Original file line numberDiff line numberDiff line change
@@ -525,14 +525,12 @@ DefineType(List *names, List *parameters)
525525

526526
/*
527527
* now have TypeCreate do all the real work.
528+
*
529+
* Note: the pg_type.oid is stored in user tables as array elements (base
530+
* types) in ArrayType and in composite types in DatumTupleFields. This
531+
* oid must be preserved by binary upgrades.
528532
*/
529533
typoid =
530-
531-
/*
532-
* The pg_type.oid is stored in user tables as array elements (base types)
533-
* in ArrayType and in composite types in DatumTupleFields. This oid must
534-
* be preserved by binary upgrades.
535-
*/
536534
TypeCreate(InvalidOid, /* no predetermined type OID */
537535
typeName, /* type name */
538536
typeNamespace, /* namespace */
@@ -746,7 +744,6 @@ DefineDomain(CreateDomainStmt *stmt)
746744
Oid sendProcedure;
747745
Oid analyzeProcedure;
748746
bool byValue;
749-
Oid typelem;
750747
char category;
751748
char delimiter;
752749
char alignment;
@@ -831,9 +828,6 @@ DefineDomain(CreateDomainStmt *stmt)
831828
/* Type Category */
832829
category = baseType->typcategory;
833830

834-
/* Array element type (in case base type is an array) */
835-
typelem = baseType->typelem;
836-
837831
/* Array element Delimiter */
838832
delimiter = baseType->typdelim;
839833

@@ -1033,7 +1027,7 @@ DefineDomain(CreateDomainStmt *stmt)
10331027
InvalidOid, /* typmodin procedure - none */
10341028
InvalidOid, /* typmodout procedure - none */
10351029
analyzeProcedure, /* analyze procedure */
1036-
typelem, /* element type ID */
1030+
InvalidOid, /* no array element type */
10371031
false, /* this isn't an array */
10381032
InvalidOid, /* no arrays for domains (yet) */
10391033
basetypeoid, /* base type ID */
@@ -1670,7 +1664,7 @@ AlterDomainDefault(List *names, Node *defaultRaw)
16701664
typTup->typmodin,
16711665
typTup->typmodout,
16721666
typTup->typanalyze,
1673-
typTup->typelem,
1667+
InvalidOid,
16741668
false, /* a domain isn't an implicit array */
16751669
typTup->typbasetype,
16761670
defaultExpr,

src/backend/parser/parse_coerce.c

+6-5
Original file line numberDiff line numberDiff line change
@@ -1908,6 +1908,9 @@ find_coercion_pathway(Oid targetTypeId, Oid sourceTypeId,
19081908
* array types. If so, and if the element types have a suitable cast,
19091909
* report that we can coerce with an ArrayCoerceExpr.
19101910
*
1911+
* Note that the source type can be a domain over array, but not the
1912+
* target, because ArrayCoerceExpr won't check domain constraints.
1913+
*
19111914
* Hack: disallow coercions to oidvector and int2vector, which
19121915
* otherwise tend to capture coercions that should go to "real" array
19131916
* types. We want those types to be considered "real" arrays for many
@@ -1921,7 +1924,7 @@ find_coercion_pathway(Oid targetTypeId, Oid sourceTypeId,
19211924
Oid sourceElem;
19221925

19231926
if ((targetElem = get_element_type(targetTypeId)) != InvalidOid &&
1924-
(sourceElem = get_element_type(sourceTypeId)) != InvalidOid)
1927+
(sourceElem = get_base_element_type(sourceTypeId)) != InvalidOid)
19251928
{
19261929
CoercionPathType elempathtype;
19271930
Oid elemfuncid;
@@ -2001,10 +2004,8 @@ find_typmod_coercion_function(Oid typeId,
20012004
targetType = typeidType(typeId);
20022005
typeForm = (Form_pg_type) GETSTRUCT(targetType);
20032006

2004-
/* Check for a varlena array type (and not a domain) */
2005-
if (typeForm->typelem != InvalidOid &&
2006-
typeForm->typlen == -1 &&
2007-
typeForm->typtype != TYPTYPE_DOMAIN)
2007+
/* Check for a varlena array type */
2008+
if (typeForm->typelem != InvalidOid && typeForm->typlen == -1)
20082009
{
20092010
/* Yes, switch our attention to the element type */
20102011
typeId = typeForm->typelem;

src/backend/parser/parse_expr.c

+8-10
Original file line numberDiff line numberDiff line change
@@ -161,19 +161,17 @@ transformExpr(ParseState *pstate, Node *expr)
161161

162162
targetType = typenameTypeId(pstate, tc->typeName,
163163
&targetTypmod);
164+
/*
165+
* If target is a domain over array, work with the base
166+
* array type here. transformTypeCast below will cast the
167+
* array type to the domain. In the usual case that the
168+
* target is not a domain, transformTypeCast is a no-op.
169+
*/
170+
targetType = getBaseTypeAndTypmod(targetType,
171+
&targetTypmod);
164172
elementType = get_element_type(targetType);
165173
if (OidIsValid(elementType))
166174
{
167-
/*
168-
* tranformArrayExpr doesn't know how to check domain
169-
* constraints, so ask it to return the base type
170-
* instead. transformTypeCast below will cast it to
171-
* the domain. In the usual case that the target is
172-
* not a domain, transformTypeCast is a no-op.
173-
*/
174-
targetType = getBaseTypeAndTypmod(targetType,
175-
&targetTypmod);
176-
177175
tc = copyObject(tc);
178176
tc->arg = transformArrayExpr(pstate,
179177
(A_ArrayExpr *) tc->arg,

src/backend/parser/parse_node.c

+38-13
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
#include "parser/parse_relation.h"
2626
#include "utils/builtins.h"
2727
#include "utils/int8.h"
28+
#include "utils/lsyscache.h"
2829
#include "utils/syscache.h"
2930
#include "utils/varbit.h"
3031

@@ -198,19 +199,35 @@ make_var(ParseState *pstate, RangeTblEntry *rte, int attrno, int location)
198199

199200
/*
200201
* transformArrayType()
201-
* Get the element type of an array type in preparation for subscripting
202+
* Identify the types involved in a subscripting operation
203+
*
204+
* On entry, arrayType/arrayTypmod identify the type of the input value
205+
* to be subscripted (which could be a domain type). These are modified
206+
* if necessary to identify the actual array type and typmod, and the
207+
* array's element type is returned. An error is thrown if the input isn't
208+
* an array type.
202209
*/
203210
Oid
204-
transformArrayType(Oid arrayType)
211+
transformArrayType(Oid *arrayType, int32 *arrayTypmod)
205212
{
213+
Oid origArrayType = *arrayType;
206214
Oid elementType;
207215
HeapTuple type_tuple_array;
208216
Form_pg_type type_struct_array;
209217

218+
/*
219+
* If the input is a domain, smash to base type, and extract the actual
220+
* typmod to be applied to the base type. Subscripting a domain is an
221+
* operation that necessarily works on the base array type, not the domain
222+
* itself. (Note that we provide no method whereby the creator of a
223+
* domain over an array type could hide its ability to be subscripted.)
224+
*/
225+
*arrayType = getBaseTypeAndTypmod(*arrayType, arrayTypmod);
226+
210227
/* Get the type tuple for the array */
211-
type_tuple_array = SearchSysCache1(TYPEOID, ObjectIdGetDatum(arrayType));
228+
type_tuple_array = SearchSysCache1(TYPEOID, ObjectIdGetDatum(*arrayType));
212229
if (!HeapTupleIsValid(type_tuple_array))
213-
elog(ERROR, "cache lookup failed for type %u", arrayType);
230+
elog(ERROR, "cache lookup failed for type %u", *arrayType);
214231
type_struct_array = (Form_pg_type) GETSTRUCT(type_tuple_array);
215232

216233
/* needn't check typisdefined since this will fail anyway */
@@ -220,7 +237,7 @@ transformArrayType(Oid arrayType)
220237
ereport(ERROR,
221238
(errcode(ERRCODE_DATATYPE_MISMATCH),
222239
errmsg("cannot subscript type %s because it is not an array",
223-
format_type_be(arrayType))));
240+
format_type_be(origArrayType))));
224241

225242
ReleaseSysCache(type_tuple_array);
226243

@@ -241,13 +258,17 @@ transformArrayType(Oid arrayType)
241258
* that array. We produce an expression that represents the new array value
242259
* with the source data inserted into the right part of the array.
243260
*
261+
* For both cases, if the source array is of a domain-over-array type,
262+
* the result is of the base array type or its element type; essentially,
263+
* we must fold a domain to its base type before applying subscripting.
264+
*
244265
* pstate Parse state
245266
* arrayBase Already-transformed expression for the array as a whole
246-
* arrayType OID of array's datatype (should match type of arrayBase)
267+
* arrayType OID of array's datatype (should match type of arrayBase,
268+
* or be the base type of arrayBase's domain type)
247269
* elementType OID of array's element type (fetch with transformArrayType,
248270
* or pass InvalidOid to do it here)
249-
* elementTypMod typmod to be applied to array elements (if storing) or of
250-
* the source array (if fetching)
271+
* arrayTypMod typmod for the array (which is also typmod for the elements)
251272
* indirection Untransformed list of subscripts (must not be NIL)
252273
* assignFrom NULL for array fetch, else transformed expression for source.
253274
*/
@@ -256,7 +277,7 @@ transformArraySubscripts(ParseState *pstate,
256277
Node *arrayBase,
257278
Oid arrayType,
258279
Oid elementType,
259-
int32 elementTypMod,
280+
int32 arrayTypMod,
260281
List *indirection,
261282
Node *assignFrom)
262283
{
@@ -266,9 +287,13 @@ transformArraySubscripts(ParseState *pstate,
266287
ListCell *idx;
267288
ArrayRef *aref;
268289

269-
/* Caller may or may not have bothered to determine elementType */
290+
/*
291+
* Caller may or may not have bothered to determine elementType. Note
292+
* that if the caller did do so, arrayType/arrayTypMod must be as
293+
* modified by transformArrayType, ie, smash domain to base type.
294+
*/
270295
if (!OidIsValid(elementType))
271-
elementType = transformArrayType(arrayType);
296+
elementType = transformArrayType(&arrayType, &arrayTypMod);
272297

273298
/*
274299
* A list containing only single subscripts refers to a single array
@@ -356,7 +381,7 @@ transformArraySubscripts(ParseState *pstate,
356381

357382
newFrom = coerce_to_target_type(pstate,
358383
assignFrom, typesource,
359-
typeneeded, elementTypMod,
384+
typeneeded, arrayTypMod,
360385
COERCION_ASSIGNMENT,
361386
COERCE_IMPLICIT_CAST,
362387
-1);
@@ -378,7 +403,7 @@ transformArraySubscripts(ParseState *pstate,
378403
aref = makeNode(ArrayRef);
379404
aref->refarraytype = arrayType;
380405
aref->refelemtype = elementType;
381-
aref->reftypmod = elementTypMod;
406+
aref->reftypmod = arrayTypMod;
382407
aref->refupperindexpr = upperIndexpr;
383408
aref->reflowerindexpr = lowerIndexpr;
384409
aref->refexpr = (Expr *) arrayBase;

src/backend/parser/parse_oper.c

+2-2
Original file line numberDiff line numberDiff line change
@@ -209,7 +209,7 @@ get_sort_group_operators(Oid argtype,
209209
eq_opr == ARRAY_EQ_OP ||
210210
gt_opr == ARRAY_GT_OP)
211211
{
212-
Oid elem_type = get_element_type(argtype);
212+
Oid elem_type = get_base_element_type(argtype);
213213

214214
if (OidIsValid(elem_type))
215215
{
@@ -906,7 +906,7 @@ make_scalar_array_op(ParseState *pstate, List *opname,
906906
rtypeId = UNKNOWNOID;
907907
else
908908
{
909-
rtypeId = get_element_type(atypeId);
909+
rtypeId = get_base_element_type(atypeId);
910910
if (!OidIsValid(rtypeId))
911911
ereport(ERROR,
912912
(errcode(ERRCODE_WRONG_OBJECT_TYPE),

0 commit comments

Comments
 (0)