Commit 56a79a86 authored by Tom Lane's avatar Tom Lane

Split array_push into separate array_append and array_prepend functions.

There wasn't any good reason for a single C function to implement both
these SQL functions: it saved very little code overall, and it required
significant pushups to re-determine at runtime which case applied.  Redoing
it as two functions ends up with just slightly more lines of code, but it's
simpler to understand, and faster too because we need not repeat syscache
lookups on every call.

An important side benefit is that this eliminates the only case in which
different aliases of the same C function had both anyarray and anyelement
arguments at the same position, which would almost always be a mistake.
The opr_sanity regression test will now notice such mistakes since there's
no longer a valid case where it happens.
parent d30292b8
...@@ -17,102 +17,152 @@ ...@@ -17,102 +17,152 @@
#include "utils/lsyscache.h" #include "utils/lsyscache.h"
/*
* fetch_array_arg_replace_nulls
*
* Fetch an array-valued argument; if it's null, construct an empty array
* value of the proper data type. Also cache basic element type information
* in fn_extra.
*/
static ArrayType *
fetch_array_arg_replace_nulls(FunctionCallInfo fcinfo, int argno)
{
ArrayType *v;
ArrayMetaState *my_extra;
my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
if (my_extra == NULL)
{
/* First time through, so look up the array type and element type */
Oid arr_typeid = get_fn_expr_argtype(fcinfo->flinfo, argno);
Oid element_type;
if (!OidIsValid(arr_typeid))
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("could not determine input data type")));
element_type = get_element_type(arr_typeid);
if (!OidIsValid(element_type))
ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH),
errmsg("input data type is not an array")));
my_extra = (ArrayMetaState *)
MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
sizeof(ArrayMetaState));
my_extra->element_type = element_type;
/* Cache info about element type */
get_typlenbyvalalign(element_type,
&my_extra->typlen,
&my_extra->typbyval,
&my_extra->typalign);
fcinfo->flinfo->fn_extra = my_extra;
}
/* Now we can collect the array value */
if (PG_ARGISNULL(argno))
v = construct_empty_array(my_extra->element_type);
else
v = PG_GETARG_ARRAYTYPE_P(argno);
return v;
}
/*----------------------------------------------------------------------------- /*-----------------------------------------------------------------------------
* array_push : * array_append :
* push an element onto either end of a one-dimensional array * push an element onto the end of a one-dimensional array
*---------------------------------------------------------------------------- *----------------------------------------------------------------------------
*/ */
Datum Datum
array_push(PG_FUNCTION_ARGS) array_append(PG_FUNCTION_ARGS)
{ {
ArrayType *v; ArrayType *v;
Datum newelem; Datum newelem;
bool isNull; bool isNull;
ArrayType *result;
int *dimv, int *dimv,
*lb; *lb;
ArrayType *result;
int indx; int indx;
Oid element_type;
int16 typlen;
bool typbyval;
char typalign;
Oid arg0_typeid = get_fn_expr_argtype(fcinfo->flinfo, 0);
Oid arg1_typeid = get_fn_expr_argtype(fcinfo->flinfo, 1);
Oid arg0_elemid;
Oid arg1_elemid;
ArrayMetaState *my_extra; ArrayMetaState *my_extra;
if (arg0_typeid == InvalidOid || arg1_typeid == InvalidOid) v = fetch_array_arg_replace_nulls(fcinfo, 0);
ereport(ERROR,
(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
v = PG_GETARG_ARRAYTYPE_P(0);
isNull = PG_ARGISNULL(1); isNull = PG_ARGISNULL(1);
if (isNull) if (isNull)
newelem = (Datum) 0; newelem = (Datum) 0;
else else
newelem = PG_GETARG_DATUM(1); newelem = PG_GETARG_DATUM(1);
}
else if (arg1_elemid != InvalidOid)
{
if (PG_ARGISNULL(1))
v = construct_empty_array(arg1_elemid);
else
v = PG_GETARG_ARRAYTYPE_P(1);
isNull = PG_ARGISNULL(0);
if (isNull)
newelem = (Datum) 0;
else
newelem = PG_GETARG_DATUM(0);
}
else
{
/* Shouldn't get here given proper type checking in parser */
ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH),
errmsg("neither input type is an array")));
PG_RETURN_NULL(); /* keep compiler quiet */
}
element_type = ARR_ELEMTYPE(v);
if (ARR_NDIM(v) == 1) if (ARR_NDIM(v) == 1)
{
lb = ARR_LBOUND(v);
dimv = ARR_DIMS(v);
if (arg0_elemid != InvalidOid)
{ {
/* append newelem */ /* append newelem */
int ub = dimv[0] + lb[0] - 1; int ub;
lb = ARR_LBOUND(v);
dimv = ARR_DIMS(v);
ub = dimv[0] + lb[0] - 1;
indx = ub + 1; indx = ub + 1;
/* overflow? */ /* overflow? */
if (indx < ub) if (indx < ub)
ereport(ERROR, ereport(ERROR,
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
errmsg("integer out of range"))); errmsg("integer out of range")));
} }
else if (ARR_NDIM(v) == 0)
indx = 1;
else
ereport(ERROR,
(errcode(ERRCODE_DATA_EXCEPTION),
errmsg("argument must be empty or one-dimensional array")));
/* Perform element insertion */
my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
result = array_set(v, 1, &indx, newelem, isNull,
-1, my_extra->typlen, my_extra->typbyval, my_extra->typalign);
PG_RETURN_ARRAYTYPE_P(result);
}
/*-----------------------------------------------------------------------------
* array_prepend :
* push an element onto the front of a one-dimensional array
*----------------------------------------------------------------------------
*/
Datum
array_prepend(PG_FUNCTION_ARGS)
{
ArrayType *v;
Datum newelem;
bool isNull;
ArrayType *result;
int *dimv,
*lb;
int indx;
ArrayMetaState *my_extra;
isNull = PG_ARGISNULL(0);
if (isNull)
newelem = (Datum) 0;
else else
newelem = PG_GETARG_DATUM(0);
v = fetch_array_arg_replace_nulls(fcinfo, 1);
if (ARR_NDIM(v) == 1)
{ {
/* prepend newelem */ /* prepend newelem */
lb = ARR_LBOUND(v);
dimv = ARR_DIMS(v);
indx = lb[0] - 1; indx = lb[0] - 1;
/* overflow? */ /* overflow? */
if (indx > lb[0]) if (indx > lb[0])
ereport(ERROR, ereport(ERROR,
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
errmsg("integer out of range"))); errmsg("integer out of range")));
} }
}
else if (ARR_NDIM(v) == 0) else if (ARR_NDIM(v) == 0)
indx = 1; indx = 1;
else else
...@@ -120,39 +170,13 @@ array_push(PG_FUNCTION_ARGS) ...@@ -120,39 +170,13 @@ array_push(PG_FUNCTION_ARGS)
(errcode(ERRCODE_DATA_EXCEPTION), (errcode(ERRCODE_DATA_EXCEPTION),
errmsg("argument must be empty or one-dimensional array"))); errmsg("argument must be empty or one-dimensional array")));
/* /* Perform element insertion */
* We arrange to look up info about element type only once per series of
* calls, assuming the element type doesn't change underneath us.
*/
my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
if (my_extra == NULL)
{
fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
sizeof(ArrayMetaState));
my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra; my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
my_extra->element_type = ~element_type;
}
if (my_extra->element_type != element_type)
{
/* Get info about element type */
get_typlenbyvalalign(element_type,
&my_extra->typlen,
&my_extra->typbyval,
&my_extra->typalign);
my_extra->element_type = element_type;
}
typlen = my_extra->typlen;
typbyval = my_extra->typbyval;
typalign = my_extra->typalign;
result = array_set(v, 1, &indx, newelem, isNull, result = array_set(v, 1, &indx, newelem, isNull,
-1, typlen, typbyval, typalign); -1, my_extra->typlen, my_extra->typbyval, my_extra->typalign);
/* /* Readjust result's LB to match the input's, as expected for prepend */
* Readjust result's LB to match the input's. This does nothing in the
* append case, but it's the simplest way to implement the prepend case.
*/
if (ARR_NDIM(v) == 1) if (ARR_NDIM(v) == 1)
ARR_LBOUND(result)[0] = ARR_LBOUND(v)[0]; ARR_LBOUND(result)[0] = ARR_LBOUND(v)[0];
......
...@@ -53,6 +53,6 @@ ...@@ -53,6 +53,6 @@
*/ */
/* yyyymmddN */ /* yyyymmddN */
#define CATALOG_VERSION_NO 201501281 #define CATALOG_VERSION_NO 201502181
#endif #endif
...@@ -878,9 +878,9 @@ DATA(insert OID = 2176 ( array_length PGNSP PGUID 12 1 0 0 0 f f f f t f i 2 ...@@ -878,9 +878,9 @@ DATA(insert OID = 2176 ( array_length PGNSP PGUID 12 1 0 0 0 f f f f t f i 2
DESCR("array length"); DESCR("array length");
DATA(insert OID = 3179 ( cardinality PGNSP PGUID 12 1 0 0 0 f f f f t f i 1 0 23 "2277" _null_ _null_ _null_ _null_ array_cardinality _null_ _null_ _null_ )); DATA(insert OID = 3179 ( cardinality PGNSP PGUID 12 1 0 0 0 f f f f t f i 1 0 23 "2277" _null_ _null_ _null_ _null_ array_cardinality _null_ _null_ _null_ ));
DESCR("array cardinality"); DESCR("array cardinality");
DATA(insert OID = 378 ( array_append PGNSP PGUID 12 1 0 0 0 f f f f f f i 2 0 2277 "2277 2283" _null_ _null_ _null_ _null_ array_push _null_ _null_ _null_ )); DATA(insert OID = 378 ( array_append PGNSP PGUID 12 1 0 0 0 f f f f f f i 2 0 2277 "2277 2283" _null_ _null_ _null_ _null_ array_append _null_ _null_ _null_ ));
DESCR("append element onto end of array"); DESCR("append element onto end of array");
DATA(insert OID = 379 ( array_prepend PGNSP PGUID 12 1 0 0 0 f f f f f f i 2 0 2277 "2283 2277" _null_ _null_ _null_ _null_ array_push _null_ _null_ _null_ )); DATA(insert OID = 379 ( array_prepend PGNSP PGUID 12 1 0 0 0 f f f f f f i 2 0 2277 "2283 2277" _null_ _null_ _null_ _null_ array_prepend _null_ _null_ _null_ ));
DESCR("prepend element onto front of array"); DESCR("prepend element onto front of array");
DATA(insert OID = 383 ( array_cat PGNSP PGUID 12 1 0 0 0 f f f f f f i 2 0 2277 "2277 2277" _null_ _null_ _null_ _null_ array_cat _null_ _null_ _null_ )); DATA(insert OID = 383 ( array_cat PGNSP PGUID 12 1 0 0 0 f f f f f f i 2 0 2277 "2277 2277" _null_ _null_ _null_ _null_ array_cat _null_ _null_ _null_ ));
DATA(insert OID = 394 ( string_to_array PGNSP PGUID 12 1 0 0 0 f f f f f f i 2 0 1009 "25 25" _null_ _null_ _null_ _null_ text_to_array _null_ _null_ _null_ )); DATA(insert OID = 394 ( string_to_array PGNSP PGUID 12 1 0 0 0 f f f f f f i 2 0 1009 "25 25" _null_ _null_ _null_ _null_ text_to_array _null_ _null_ _null_ ));
......
...@@ -341,7 +341,8 @@ extern int32 *ArrayGetIntegerTypmods(ArrayType *arr, int *n); ...@@ -341,7 +341,8 @@ extern int32 *ArrayGetIntegerTypmods(ArrayType *arr, int *n);
/* /*
* prototypes for functions defined in array_userfuncs.c * prototypes for functions defined in array_userfuncs.c
*/ */
extern Datum array_push(PG_FUNCTION_ARGS); extern Datum array_append(PG_FUNCTION_ARGS);
extern Datum array_prepend(PG_FUNCTION_ARGS);
extern Datum array_cat(PG_FUNCTION_ARGS); extern Datum array_cat(PG_FUNCTION_ARGS);
extern ArrayType *create_singleton_array(FunctionCallInfo fcinfo, extern ArrayType *create_singleton_array(FunctionCallInfo fcinfo,
......
...@@ -176,8 +176,7 @@ ORDER BY 1, 2; ...@@ -176,8 +176,7 @@ ORDER BY 1, 2;
25 | 1043 25 | 1043
1114 | 1184 1114 | 1184
1560 | 1562 1560 | 1562
2277 | 2283 (4 rows)
(5 rows)
SELECT DISTINCT p1.proargtypes[1], p2.proargtypes[1] SELECT DISTINCT p1.proargtypes[1], p2.proargtypes[1]
FROM pg_proc AS p1, pg_proc AS p2 FROM pg_proc AS p1, pg_proc AS p2
...@@ -194,8 +193,7 @@ ORDER BY 1, 2; ...@@ -194,8 +193,7 @@ ORDER BY 1, 2;
23 | 28 23 | 28
1114 | 1184 1114 | 1184
1560 | 1562 1560 | 1562
2277 | 2283 (3 rows)
(4 rows)
SELECT DISTINCT p1.proargtypes[2], p2.proargtypes[2] SELECT DISTINCT p1.proargtypes[2], p2.proargtypes[2]
FROM pg_proc AS p1, pg_proc AS p2 FROM pg_proc AS p1, pg_proc AS p2
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment