arrdom1v1-polymorphism.patch

text/plain

Filename: arrdom1v1-polymorphism.patch
Type: text/plain
Part: 0
Message: Re: Domains versus polymorphic functions, redux

Patch

Same data as JSON: GET /api/v1/attachments/:id/patch the parsed metadata as JSON — format, series position, per-file stats; never the diff bytes. API reference →
Format: unified
File+
src/backend/catalog/pg_aggregate.c 0 0
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