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 <[email protected]> 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/[email protected]
> >
> > > 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/[email protected]
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 ([email protected])
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers