On Tue, May 24, 2011 at 02:00:54PM -0400, Noah Misch wrote: > On Tue, May 24, 2011 at 01:28:38PM -0400, Tom Lane wrote: > > Noah Misch <n...@leadboat.com> writes: > > > On Tue, May 24, 2011 at 12:12:55PM -0400, Tom Lane wrote: > > >> This is a consequence of the changes I made to fix bug #5717, > > >> particularly the issues around ANYARRAY matching discussed here: > > >> http://archives.postgresql.org/pgsql-hackers/2010-10/msg01545.php > > > > > We discussed this a few weeks ago: > > > http://archives.postgresql.org/message-id/20110511093217.gb26...@tornado.gateway.2wire.net > > > > > What's to recommend #1 over what I proposed then? Seems like a discard of > > > functionality for little benefit. > > > > I am unwilling to commit to making #2 work, especially not under time > > constraints; and you apparently aren't either, since you haven't > > produced the patch you alluded to at the end of that thread. > > I took your lack of any response as non-acceptance of the plan I outlined. > Alas, the wrong conclusion. I'll send a patch this week.
See attached arrdom1v1-polymorphism.patch. This currently adds one syscache lookup per array_append, array_prepend or array_cat call when the anyarray type is not a domain. When the type is a domain, it adds a few more. We could add caching without too much trouble. I suppose someone out there uses these functions in bulk operations, though I've yet to see it. Is it worth optimizing this straightway? For a function like CREATE FUNCTION f(anyarray, VARIADIC anyarray) RETURNS int LANGUAGE sql AS 'SELECT array_length($1, 1) + array_length($2, 1)' we must coerce the variadic argument array to a domain type when the other anyarray argument(s) compel it. Having implemented that, it was nearly free to re-support a VARIADIC parameter specifically declared with a domain over an array. Consequently, I've done that as well. See here for previously-disclosed rationale: http://archives.postgresql.org/message-id/20110511191249.ga29...@tornado.gateway.2wire.net I audited remaining get_element_type() callers. CheckAttributeType() needs to recurse into domains over array types just like any other array type. Fixed trivially in arrdom2v1-checkattr.patch; see its test case for an example hole. nm
diff --git a/src/backend/catalog/pg_aggregate.c b/src/backend/catalog/pg_aggregate.c index 86e8c6b..8194519 100644 *** a/src/backend/catalog/pg_aggregate.c --- b/src/backend/catalog/pg_aggregate.c *************** *** 305,310 **** lookup_agg_function(List *fnName, --- 305,311 ---- Oid *rettype) { Oid fnOid; + Oid vartype; bool retset; int nvargs; Oid *true_oid_array; *************** *** 321,327 **** lookup_agg_function(List *fnName, */ fdresult = func_get_detail(fnName, NIL, NIL, nargs, input_types, false, false, ! &fnOid, rettype, &retset, &nvargs, &true_oid_array, NULL); /* only valid case is a normal function not returning a set */ --- 322,328 ---- */ fdresult = func_get_detail(fnName, NIL, NIL, nargs, input_types, false, false, ! &fnOid, rettype, &vartype, &retset, &nvargs, &true_oid_array, NULL); /* only valid case is a normal function not returning a set */ diff --git a/src/backend/catalog/pg_proc.index 6250b07..8fdd88e 100644 *** a/src/backend/catalog/pg_proc.c --- b/src/backend/catalog/pg_proc.c *************** *** 272,278 **** ProcedureCreate(const char *procedureName, variadicType = ANYELEMENTOID; break; default: ! variadicType = get_element_type(allParams[i]); if (!OidIsValid(variadicType)) elog(ERROR, "variadic parameter is not an array"); break; --- 272,278 ---- variadicType = ANYELEMENTOID; break; default: ! variadicType = get_base_element_type(allParams[i]); if (!OidIsValid(variadicType)) elog(ERROR, "variadic parameter is not an array"); break; diff --git a/src/backend/commands/fuindex 03da168..717c632 100644 *** a/src/backend/commands/functioncmds.c --- b/src/backend/commands/functioncmds.c *************** *** 273,279 **** examine_parameter_list(List *parameters, Oid languageOid, /* okay */ break; default: ! if (!OidIsValid(get_element_type(toid))) ereport(ERROR, (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION), errmsg("VARIADIC parameter must be an array"))); --- 273,279 ---- /* okay */ break; default: ! if (!OidIsValid(get_base_element_type(toid))) ereport(ERROR, (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION), errmsg("VARIADIC parameter must be an array"))); diff --git a/src/backend/parser/parse_coerindex 0418972..990ee29 100644 *** a/src/backend/parser/parse_coerce.c --- b/src/backend/parser/parse_coerce.c *************** *** 1326,1332 **** check_generic_type_consistency(Oid *actual_arg_types, return true; } ! array_typelem = get_element_type(array_typeid); if (!OidIsValid(array_typelem)) return false; /* should be an array, but isn't */ --- 1326,1332 ---- return true; } ! array_typelem = get_base_element_type(array_typeid); if (!OidIsValid(array_typelem)) return false; /* should be an array, but isn't */ *************** *** 1513,1519 **** enforce_generic_type_consistency(Oid *actual_arg_types, } else { ! array_typelem = get_element_type(array_typeid); if (!OidIsValid(array_typelem)) ereport(ERROR, (errcode(ERRCODE_DATATYPE_MISMATCH), --- 1513,1519 ---- } else { ! array_typelem = get_base_element_type(array_typeid); if (!OidIsValid(array_typelem)) ereport(ERROR, (errcode(ERRCODE_DATATYPE_MISMATCH), *************** *** 1656,1662 **** resolve_generic_type(Oid declared_type, if (context_declared_type == ANYARRAYOID) { /* Use actual type, but it must be an array */ ! Oid array_typelem = get_element_type(context_actual_type); if (!OidIsValid(array_typelem)) ereport(ERROR, --- 1656,1662 ---- if (context_declared_type == ANYARRAYOID) { /* Use actual type, but it must be an array */ ! Oid array_typelem = get_base_element_type(context_actual_type); if (!OidIsValid(array_typelem)) ereport(ERROR, *************** *** 1687,1693 **** resolve_generic_type(Oid declared_type, if (context_declared_type == ANYARRAYOID) { /* Use the element type corresponding to actual type */ ! Oid array_typelem = get_element_type(context_actual_type); if (!OidIsValid(array_typelem)) ereport(ERROR, --- 1687,1693 ---- if (context_declared_type == ANYARRAYOID) { /* Use the element type corresponding to actual type */ ! Oid array_typelem = get_base_element_type(context_actual_type); if (!OidIsValid(array_typelem)) ereport(ERROR, diff --git a/src/backend/parser/parse_fuindex 75f1e20..acf9317 100644 *** a/src/backend/parser/parse_func.c --- b/src/backend/parser/parse_func.c *************** *** 64,69 **** ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs, --- 64,70 ---- WindowDef *over, bool is_column, int location) { Oid rettype; + Oid vartype; Oid funcid; ListCell *l; ListCell *nextl; *************** *** 212,218 **** ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs, fdresult = func_get_detail(funcname, fargs, argnames, nargs, actual_arg_types, !func_variadic, true, ! &funcid, &rettype, &retset, &nvargs, &declared_arg_types, &argdefaults); if (fdresult == FUNCDETAIL_COERCION) { --- 213,219 ---- fdresult = func_get_detail(funcname, fargs, argnames, nargs, actual_arg_types, !func_variadic, true, ! &funcid, &rettype, &vartype, &retset, &nvargs, &declared_arg_types, &argdefaults); if (fdresult == FUNCDETAIL_COERCION) { *************** *** 371,377 **** ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs, newa->multidims = false; newa->location = exprLocation((Node *) vargs); ! fargs = lappend(fargs, newa); } /* build the appropriate output structure */ --- 372,403 ---- newa->multidims = false; newa->location = exprLocation((Node *) vargs); ! /* ! * If the VARIADIC parameter is anyarray, look for other anyarray ! * arguments that constrain its true type. If we don't find one, the ! * standard array type of the variadic element type fills in. ! */ ! if (vartype == ANYARRAYOID) ! { ! int i; ! ! vartype = newa->array_typeid; ! for (i = 0; i < nargs; i++) ! if (declared_arg_types[i] == ANYARRAYOID) ! { ! vartype = actual_arg_types[i]; ! break; ! } ! } ! ! /* VARIADIC parameter type may be a domain - coerce it */ ! fargs = lappend(fargs, coerce_type(pstate, ! (Node *) newa, ! newa->array_typeid, ! vartype, -1, ! COERCION_IMPLICIT, ! COERCE_IMPLICIT_CAST, ! -1)); } /* build the appropriate output structure */ *************** *** 929,934 **** func_get_detail(List *funcname, --- 955,961 ---- bool expand_defaults, Oid *funcid, /* return value */ Oid *rettype, /* return value */ + Oid *vartype, /* return value */ bool *retset, /* return value */ int *nvargs, /* return value */ Oid **true_typeids, /* return value */ *************** *** 940,945 **** func_get_detail(List *funcname, --- 967,973 ---- /* initialize output arguments to silence compiler warnings */ *funcid = InvalidOid; *rettype = InvalidOid; + *vartype = InvalidOid; *retset = false; *nvargs = 0; *true_typeids = NULL; *************** *** 1050,1055 **** func_get_detail(List *funcname, --- 1078,1084 ---- /* Treat it as a type coercion */ *funcid = InvalidOid; *rettype = targetType; + *vartype = InvalidOid; *retset = false; *nvargs = 0; *true_typeids = argtypes; *************** *** 1148,1153 **** func_get_detail(List *funcname, --- 1177,1186 ---- best_candidate->oid); pform = (Form_pg_proc) GETSTRUCT(ftup); *rettype = pform->prorettype; + if (*nvargs > 0) + *vartype = pform->proargtypes.values[pform->pronargs - 1]; + else + *vartype = InvalidOid; *retset = pform->proretset; /* fetch default args if caller wants 'em */ if (argdefaults && best_candidate->ndargs > 0) diff --git a/src/backend/utils/adt/arrindex 274e867..6f9824d 100644 *** a/src/backend/utils/adt/array_userfuncs.c --- b/src/backend/utils/adt/array_userfuncs.c *************** *** 32,37 **** array_push(PG_FUNCTION_ARGS) --- 32,38 ---- *lb; ArrayType *result; int indx; + Oid array_type; Oid element_type; int16 typlen; bool typbyval; *************** *** 47,57 **** array_push(PG_FUNCTION_ARGS) (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("could not determine input data types"))); ! arg0_elemid = get_element_type(arg0_typeid); ! arg1_elemid = get_element_type(arg1_typeid); if (arg0_elemid != InvalidOid) { if (PG_ARGISNULL(0)) v = construct_empty_array(arg0_elemid); else --- 48,59 ---- (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("could not determine input data types"))); ! arg0_elemid = get_base_element_type(arg0_typeid); ! arg1_elemid = get_base_element_type(arg1_typeid); if (arg0_elemid != InvalidOid) { + array_type = arg0_typeid; if (PG_ARGISNULL(0)) v = construct_empty_array(arg0_elemid); else *************** *** 64,69 **** array_push(PG_FUNCTION_ARGS) --- 66,72 ---- } else if (arg1_elemid != InvalidOid) { + array_type = arg1_typeid; if (PG_ARGISNULL(1)) v = construct_empty_array(arg1_elemid); else *************** *** 156,161 **** array_push(PG_FUNCTION_ARGS) --- 159,171 ---- if (ARR_NDIM(v) == 1) ARR_LBOUND(result)[0] = ARR_LBOUND(v)[0]; + /* + * If the input array type is a domain, our returned value must also + * conform to the constraints of that domain. + */ + if (getBaseType(array_type) != array_type) + domain_check(PointerGetDatum(result), false, array_type, NULL, NULL); + PG_RETURN_ARRAYTYPE_P(result); } *************** *** 195,200 **** array_cat(PG_FUNCTION_ARGS) --- 205,211 ---- Oid element_type; Oid element_type1; Oid element_type2; + Oid array_type; int32 dataoffset; /* Concatenating a null array is a no-op, just return the other input */ *************** *** 396,401 **** array_cat(PG_FUNCTION_ARGS) --- 407,421 ---- nitems2); } + /* + * If the input array type is a domain, our returned value must also + * conform to the constraints of that domain. Earlier exits did not need + * this check, because they merely returned input data unchanged. + */ + array_type = get_fn_expr_argtype(fcinfo->flinfo, 0); + if (getBaseType(array_type) != array_type) + domain_check(PointerGetDatum(result), false, array_type, NULL, NULL); + PG_RETURN_ARRAYTYPE_P(result); } diff --git a/src/backend/utils/adt/ruleutils.cindex 3ab90cb..b7bdd0b 100644 *** a/src/backend/utils/adt/ruleutils.c --- b/src/backend/utils/adt/ruleutils.c *************** *** 7001,7006 **** generate_function_name(Oid funcid, int nargs, List *argnames, --- 7001,7007 ---- FuncDetailCode p_result; Oid p_funcid; Oid p_rettype; + Oid p_vartype; bool p_retset; int p_nvargs; Oid *p_true_typeids; *************** *** 7020,7026 **** generate_function_name(Oid funcid, int nargs, List *argnames, p_result = func_get_detail(list_make1(makeString(proname)), NIL, argnames, nargs, argtypes, !OidIsValid(procform->provariadic), true, ! &p_funcid, &p_rettype, &p_retset, &p_nvargs, &p_true_typeids, NULL); if ((p_result == FUNCDETAIL_NORMAL || p_result == FUNCDETAIL_AGGREGATE || --- 7021,7027 ---- p_result = func_get_detail(list_make1(makeString(proname)), NIL, argnames, nargs, argtypes, !OidIsValid(procform->provariadic), true, ! &p_funcid, &p_rettype, &p_vartype, &p_retset, &p_nvargs, &p_true_typeids, NULL); if ((p_result == FUNCDETAIL_NORMAL || p_result == FUNCDETAIL_AGGREGATE || diff --git a/src/include/parser/parse_fuindex 2fe6f90..01f4954 100644 *** a/src/include/parser/parse_func.h --- b/src/include/parser/parse_func.h *************** *** 52,58 **** extern FuncDetailCode func_get_detail(List *funcname, List *fargs, List *fargnames, int nargs, Oid *argtypes, bool expand_variadic, bool expand_defaults, ! Oid *funcid, Oid *rettype, bool *retset, int *nvargs, Oid **true_typeids, List **argdefaults); --- 52,58 ---- List *fargs, List *fargnames, int nargs, Oid *argtypes, bool expand_variadic, bool expand_defaults, ! Oid *funcid, Oid *rettype, Oid *vartype, bool *retset, int *nvargs, Oid **true_typeids, List **argdefaults); diff --git a/src/test/regress/expectedindex 7d72791..6c1d2ee 100644 *** a/src/test/regress/expected/domain.out --- b/src/test/regress/expected/domain.out *************** *** 595,597 **** select array_elem_check(-1); --- 595,637 ---- ERROR: value for domain orderedpair violates check constraint "orderedpair_check" CONTEXT: PL/pgSQL function "array_elem_check" line 5 at assignment drop function array_elem_check(int); + select array_append('{1}'::orderedpair, 2); + array_append + -------------- + {1,2} + (1 row) + + select array_prepend(2, '{1}'::orderedpair); -- fail + ERROR: value for domain orderedpair violates check constraint "orderedpair_check" + select array_cat('{2}'::orderedpair, '{1}'); -- fail + ERROR: value for domain orderedpair violates check constraint "orderedpair_check" + create or replace function domain_variadic(variadic orderedpair) + returns int + as 'select $1[2] - $1[1]' language sql; + select domain_variadic(1, 2); + domain_variadic + ----------------- + 1 + (1 row) + + select domain_variadic(2, 1); -- fail + ERROR: value for domain orderedpair violates check constraint "orderedpair_check" + select domain_variadic(variadic array[1,2]); + domain_variadic + ----------------- + 1 + (1 row) + + select domain_variadic(variadic array[2,1]); -- fail + ERROR: value for domain orderedpair violates check constraint "orderedpair_check" + create or replace function domain_poly_variadic(anyarray, variadic anyarray) + returns int + as 'select array_length($1, 1) * array_length($2, 1)' language sql; + select domain_poly_variadic('{2}'::orderedpair, 3, 4); + domain_poly_variadic + ---------------------- + 2 + (1 row) + + select domain_poly_variadic('{2}'::orderedpair, 3, 1); -- fail + ERROR: value for domain orderedpair violates check constraint "orderedpair_check" diff --git a/src/test/regress/sql/domain.sqindex 545af62..180fdcf 100644 *** a/src/test/regress/sql/domain.sql --- b/src/test/regress/sql/domain.sql *************** *** 466,468 **** select array_elem_check(3); --- 466,486 ---- select array_elem_check(-1); drop function array_elem_check(int); + + select array_append('{1}'::orderedpair, 2); + select array_prepend(2, '{1}'::orderedpair); -- fail + select array_cat('{2}'::orderedpair, '{1}'); -- fail + + create or replace function domain_variadic(variadic orderedpair) + returns int + as 'select $1[2] - $1[1]' language sql; + select domain_variadic(1, 2); + select domain_variadic(2, 1); -- fail + select domain_variadic(variadic array[1,2]); + select domain_variadic(variadic array[2,1]); -- fail + + create or replace function domain_poly_variadic(anyarray, variadic anyarray) + returns int + as 'select array_length($1, 1) * array_length($2, 1)' language sql; + select domain_poly_variadic('{2}'::orderedpair, 3, 4); + select domain_poly_variadic('{2}'::orderedpair, 3, 1); -- fail
diff --git a/src/backend/catalog/heap.c b/src/backend/catalog/heap.c index 71c9931..41fd13e 100644 *** a/src/backend/catalog/heap.c --- b/src/backend/catalog/heap.c *************** *** 530,536 **** CheckAttributeType(const char *attname, containing_rowtypes = list_delete_first(containing_rowtypes); } ! else if (OidIsValid((att_typelem = get_element_type(atttypid)))) { /* * Must recurse into array types, too, in case they are composite. --- 530,536 ---- containing_rowtypes = list_delete_first(containing_rowtypes); } ! else if (OidIsValid((att_typelem = get_base_element_type(atttypid)))) { /* * Must recurse into array types, too, in case they are composite. diff --git a/src/test/regress/expindex 26e7bfd..f84da45 100644 *** a/src/test/regress/expected/alter_table.out --- b/src/test/regress/expected/alter_table.out *************** *** 1516,1521 **** alter table recur1 add column f2 recur1; -- fails --- 1516,1524 ---- ERROR: composite type recur1 cannot be made a member of itself alter table recur1 add column f2 recur1[]; -- fails ERROR: composite type recur1 cannot be made a member of itself + create domain array_of_recur1 as recur1[]; + alter table recur1 add column f2 array_of_recur1; -- fails + ERROR: composite type recur1 cannot be made a member of itself create temp table recur2 (f1 int, f2 recur1); alter table recur1 add column f2 recur2; -- fails ERROR: composite type recur1 cannot be made a member of itself diff --git a/src/test/regress/sql/alter_table.sqindex 0ed16fb..b5d76ea 100644 *** a/src/test/regress/sql/alter_table.sql --- b/src/test/regress/sql/alter_table.sql *************** *** 1128,1133 **** alter table tab1 alter column b type varchar; -- fails --- 1128,1135 ---- create temp table recur1 (f1 int); alter table recur1 add column f2 recur1; -- fails alter table recur1 add column f2 recur1[]; -- fails + create domain array_of_recur1 as recur1[]; + alter table recur1 add column f2 array_of_recur1; -- fails create temp table recur2 (f1 int, f2 recur1); alter table recur1 add column f2 recur2; -- fails alter table recur1 add column f2 int;
-- Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org) To make changes to your subscription: http://www.postgresql.org/mailpref/pgsql-hackers