Skip to content

Commit cb20f36

Browse files
jianhe-funCommitfest Bot
authored andcommitted
array_random
we can not use function signature as array_random(anyelement, anyelement, int[] [, int[]]) because currently, we cannot resolve the conflict for array_random(1, 2::bigint). In this case, the first argument should be promoted to bigint. For example: create or replace function polyf(x anyelement, y anyelement) returns anyelement as $$ select x + 1 $$ language sql; select polyf(1, 2::bigint); ERROR: function polyf(integer, bigint) does not exist select polyf(1::bigint, 2); ERROR: function polyf(bigint, integer) does not exist So, we define three separate functions for array_random, similar to the approach used for the random() function. now it looks like: \df array_random List of functions Schema | Name | Result data type | Argument data types | Type ------------+--------------+------------------+-------------------------------------------------------------------------------------+------ pg_catalog | array_random | bigint[] | min bigint, max bigint, dims integer[], lbounds integer[] DEFAULT NULL::integer[] | func pg_catalog | array_random | integer[] | min integer, max integer, dims integer[], lbounds integer[] DEFAULT NULL::integer[] | func pg_catalog | array_random | numeric[] | min numeric, max numeric, dims integer[], lbounds integer[] DEFAULT NULL::integer[] | func (3 rows) original discussion: https://postgr.es/m/87plssezpc.fsf@163.com discussion: https://postgr.es/m/CACJufxF8_VzCFRHRt4OHHF74QtB8tj5Z=djsy7Y31OHKG5s1-w@mail.gmail.com
1 parent 2106fe2 commit cb20f36

File tree

6 files changed

+437
-0
lines changed

6 files changed

+437
-0
lines changed

doc/src/sgml/func.sgml

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20657,6 +20657,40 @@ SELECT NULLIF(value, '(none)') ...
2065720657
</para></entry>
2065820658
</row>
2065920659

20660+
<row>
20661+
<entry role="func_table_entry"><para role="func_signature">
20662+
<indexterm>
20663+
<primary>array_random</primary>
20664+
</indexterm>
20665+
20666+
<function>array_random</function> ( <parameter>min</parameter> <type>integer</type>, <parameter>max</parameter> <type>integer</type>,
20667+
<parameter>dims</parameter> <type>integer[]</type> <optional>, <parameter>lbounds</parameter> <type>integer[]</type></optional>)
20668+
<returnvalue>integer[]</returnvalue>
20669+
</para>
20670+
<para role="func_signature">
20671+
<function>array_random</function> ( <parameter>min</parameter> <type>bigint</type>, <parameter>max</parameter> <type>bigint</type>,
20672+
<parameter>dims</parameter> <type>integer[]</type> <optional>, <parameter>lbounds</parameter> <type>integer[]</type></optional>)
20673+
<returnvalue>bigint[]</returnvalue>
20674+
</para>
20675+
<para role="func_signature">
20676+
<function>array_random</function> ( <parameter>min</parameter> <type>numeric</type>, <parameter>max</parameter> <type>numeric</type>,
20677+
<parameter>dims</parameter> <type>integer[]</type>, <optional>, <parameter>lbounds</parameter> <type>integer[]</type></optional>)
20678+
<returnvalue>numeric[]</returnvalue>
20679+
</para>
20680+
20681+
<para>
20682+
Returns an array populated with random values, each value is in the range
20683+
<parameter>min</parameter> &lt;= x &lt;= <parameter>max</parameter>.
20684+
The array has dimensions specified by <parameter>dims</parameter>
20685+
The optional fourth argument (<parameter>lbounds</parameter>) supplies lower-bound values for each dimension (which default to all 1).
20686+
See <xref linkend="functions-math-random-table"/> also.
20687+
</para>
20688+
<para>
20689+
<literal>array_random(1, 10::bigint, '{2}'::int[])</literal>
20690+
<returnvalue>{3,3}</returnvalue>
20691+
</para></entry>
20692+
</row>
20693+
2066020694
<row>
2066120695
<entry role="func_table_entry"><para role="func_signature">
2066220696
<indexterm>

src/backend/catalog/system_functions.sql

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,27 @@ CREATE OR REPLACE FUNCTION
7373
VOLATILE PARALLEL RESTRICTED STRICT COST 1
7474
AS 'drandom_normal';
7575

76+
CREATE OR REPLACE FUNCTION
77+
array_random(min integer, max integer, dims integer[], lbounds integer[] DEFAULT NULL)
78+
RETURNS integer[]
79+
LANGUAGE internal
80+
VOLATILE PARALLEL RESTRICTED COST 1
81+
AS 'int4array_random';
82+
83+
CREATE OR REPLACE FUNCTION
84+
array_random (min bigint, max bigint, dims integer[], lbounds integer[] DEFAULT NULL)
85+
RETURNS bigint[]
86+
LANGUAGE internal
87+
VOLATILE PARALLEL RESTRICTED COST 1
88+
AS 'int8array_random';
89+
90+
CREATE OR REPLACE FUNCTION
91+
array_random (min numeric, max numeric, dims integer[], lbounds integer[] DEFAULT NULL)
92+
RETURNS numeric[]
93+
LANGUAGE internal
94+
VOLATILE PARALLEL RESTRICTED COST 1
95+
AS 'numeric_array_random';
96+
7697
CREATE OR REPLACE FUNCTION log(numeric)
7798
RETURNS numeric
7899
LANGUAGE sql

src/backend/utils/adt/arrayfuncs.c

Lines changed: 258 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,7 @@ static int width_bucket_array_variable(Datum operand,
166166
Oid collation,
167167
TypeCacheEntry *typentry);
168168

169+
static Datum array_random_internal(FunctionCallInfo fcinfo, Oid elmtype);
169170

170171
/*
171172
* array_in :
@@ -6064,6 +6065,263 @@ array_fill(PG_FUNCTION_ARGS)
60646065
PG_RETURN_ARRAYTYPE_P(result);
60656066
}
60666067

6068+
Datum
6069+
int4array_random(PG_FUNCTION_ARGS)
6070+
{
6071+
return array_random_internal(fcinfo, INT4OID);
6072+
}
6073+
6074+
Datum
6075+
int8array_random(PG_FUNCTION_ARGS)
6076+
{
6077+
return array_random_internal(fcinfo, INT8OID);
6078+
}
6079+
6080+
Datum
6081+
numeric_array_random(PG_FUNCTION_ARGS)
6082+
{
6083+
return array_random_internal(fcinfo, NUMERICOID);
6084+
}
6085+
6086+
/*
6087+
* array_random_internal:
6088+
*
6089+
* Create an array with the specified dimensions and lower bounds, and populate
6090+
* it with random values.
6091+
*
6092+
* helper function for:
6093+
* array_random(min int4, max int4, dims int[] [, lbounds int[]]) -> int[]
6094+
* array_random(min int8, max int8, dims int[] [, lbounds int[]]) -> int8[]
6095+
* array_random(min numeric, max numeric, dims int[] [, lbounds int[]]) -> numeric[]
6096+
*/
6097+
static Datum
6098+
array_random_internal(FunctionCallInfo fcinfo, Oid elmtype)
6099+
{
6100+
ArrayType *result;
6101+
ArrayType *dims;
6102+
ArrayType *lbs;
6103+
int *dimv;
6104+
int *lbsv;
6105+
int i;
6106+
int ndims;
6107+
int nitems;
6108+
int deflbs[MAXDIM];
6109+
int16 elmlen;
6110+
bool elmbyval;
6111+
char elmalign;
6112+
Datum *values;
6113+
bool *nulls;
6114+
Oid random_fn_oid = InvalidOid;
6115+
6116+
values = (Datum *) palloc(2 * sizeof(Datum));
6117+
nulls = (bool *) palloc(2 * sizeof(bool));
6118+
if (!PG_ARGISNULL(0))
6119+
{
6120+
values[0] = PG_GETARG_DATUM(0);
6121+
nulls[0] = false;
6122+
}
6123+
else
6124+
{
6125+
values[0] = 0;
6126+
nulls[0] = true;
6127+
}
6128+
6129+
if (!PG_ARGISNULL(1))
6130+
{
6131+
values[1] = PG_GETARG_DATUM(1);
6132+
nulls[1] = false;
6133+
}
6134+
else
6135+
{
6136+
values[1] = 0;
6137+
nulls[1] = true;
6138+
}
6139+
6140+
if (PG_ARGISNULL(2))
6141+
ereport(ERROR,
6142+
errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
6143+
errmsg("dimension array cannot be null"));
6144+
6145+
dims = PG_GETARG_ARRAYTYPE_P(2);
6146+
if (ARR_NDIM(dims) > 1)
6147+
ereport(ERROR,
6148+
errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
6149+
errmsg("wrong number of array subscripts"),
6150+
errdetail("Dimension array must be one dimensional."));
6151+
6152+
if (array_contains_nulls(dims))
6153+
ereport(ERROR,
6154+
errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
6155+
errmsg("dimension values cannot be null"));
6156+
6157+
dimv = (int *) ARR_DATA_PTR(dims);
6158+
ndims = (ARR_NDIM(dims) > 0) ? ARR_DIMS(dims)[0] : 0;
6159+
6160+
if (ndims < 0) /* we do allow zero-dimension arrays */
6161+
ereport(ERROR,
6162+
errcode(ERRCODE_INVALID_PARAMETER_VALUE),
6163+
errmsg("invalid number of dimensions: %d", ndims));
6164+
if (ndims > MAXDIM)
6165+
ereport(ERROR,
6166+
errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
6167+
errmsg("number of array dimensions (%d) exceeds the maximum allowed (%d)",
6168+
ndims, MAXDIM));
6169+
6170+
if (!PG_ARGISNULL(3))
6171+
{
6172+
lbs = PG_GETARG_ARRAYTYPE_P(3);
6173+
6174+
if (ARR_NDIM(lbs) > 1)
6175+
ereport(ERROR,
6176+
errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
6177+
errmsg("wrong number of array subscripts"),
6178+
errdetail("Low bound array must be one dimensional."));
6179+
6180+
if (array_contains_nulls(lbs))
6181+
ereport(ERROR,
6182+
errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
6183+
errmsg("Low bound array can not contain null values"));
6184+
6185+
if (ndims != ((ARR_NDIM(lbs) > 0) ? ARR_DIMS(lbs)[0] : 0))
6186+
ereport(ERROR,
6187+
errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
6188+
errmsg("wrong number of array subscripts"),
6189+
errdetail("Low bound array has different size than dimensions array."));
6190+
6191+
lbsv = (int *) ARR_DATA_PTR(lbs);
6192+
}
6193+
else
6194+
{
6195+
for (i = 0; i < MAXDIM; i++)
6196+
deflbs[i] = 1;
6197+
6198+
lbsv = deflbs;
6199+
}
6200+
6201+
/* random function for generating each array element for array_random */
6202+
switch (elmtype)
6203+
{
6204+
case INT4OID:
6205+
random_fn_oid = F_RANDOM_INT4_INT4;
6206+
break;
6207+
case INT8OID:
6208+
random_fn_oid = F_RANDOM_INT8_INT8;
6209+
break;
6210+
case NUMERICOID:
6211+
random_fn_oid = F_RANDOM_NUMERIC_NUMERIC;
6212+
break;
6213+
default:
6214+
elog(ERROR, "unsupported type %u for array_random function", elmtype);
6215+
break;
6216+
}
6217+
6218+
if (get_fn_expr_argtype(fcinfo->flinfo, 0) != elmtype)
6219+
elog(ERROR, "expected input data type as %u", elmtype);
6220+
6221+
/* This checks for overflow of the array dimensions */
6222+
nitems = ArrayGetNItems(ndims, dimv);
6223+
ArrayCheckBounds(ndims, dimv, lbsv);
6224+
6225+
/* fast track for empty array */
6226+
if (nitems <= 0)
6227+
{
6228+
/*
6229+
* If nitems is zero, we just return an empty array, in that case, the
6230+
* array_random min value maybe larger than max. XXX is this OK?
6231+
*/
6232+
result = construct_empty_array(elmtype);
6233+
PG_RETURN_ARRAYTYPE_P(result);
6234+
}
6235+
6236+
get_typlenbyvalalign(elmtype, &elmlen, &elmbyval, &elmalign);
6237+
6238+
/*
6239+
* For each array element call random(minval, maxval). minval is a type of
6240+
* elmtype.
6241+
*/
6242+
if (!nulls[0] && !nulls[1])
6243+
{
6244+
int nbytes = 0;
6245+
int totbytes = 0;
6246+
Datum *out_datums;
6247+
bool *out_nulls;
6248+
FmgrInfo *random_val_flinfo;
6249+
FunctionCallInfo random_val_fcinfo;
6250+
6251+
random_val_flinfo = (FmgrInfo *) palloc0(sizeof(FmgrInfo));
6252+
fmgr_info(random_fn_oid, random_val_flinfo);
6253+
6254+
random_val_fcinfo = (FunctionCallInfo) palloc0(SizeForFunctionCallInfo(2));
6255+
InitFunctionCallInfoData(*random_val_fcinfo, random_val_flinfo, 2,
6256+
InvalidOid, NULL, NULL);
6257+
6258+
random_val_fcinfo->args[0].value = values[0];
6259+
random_val_fcinfo->args[0].isnull = false;
6260+
random_val_fcinfo->args[1].value = values[1];
6261+
random_val_fcinfo->args[1].isnull = false;
6262+
6263+
out_datums = palloc(sizeof(Datum) * nitems);
6264+
out_nulls = palloc0(sizeof(bool) * nitems);
6265+
6266+
for (i = 0; i < nitems; i++)
6267+
{
6268+
out_datums[i] = FunctionCallInvoke(random_val_fcinfo);
6269+
out_nulls[i] = false;
6270+
6271+
/* make sure data is not toasted */
6272+
if (elmlen == -1)
6273+
out_datums[i] = PointerGetDatum(PG_DETOAST_DATUM(out_datums[i]));
6274+
6275+
/* XXX TODO is this correct? */
6276+
if (i == 0)
6277+
{
6278+
nbytes = att_addlength_datum(0, elmlen, out_datums[i]);
6279+
nbytes = att_align_nominal(nbytes, elmalign);
6280+
Assert(nbytes > 0);
6281+
6282+
totbytes = nbytes * nitems;
6283+
6284+
/* check for overflow of multiplication or total request */
6285+
if (totbytes / nbytes != nitems ||
6286+
!AllocSizeIsValid(totbytes))
6287+
ereport(ERROR,
6288+
errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
6289+
errmsg("array size exceeds the maximum allowed (%d)",
6290+
(int) MaxAllocSize));
6291+
}
6292+
}
6293+
6294+
result = construct_md_array(out_datums,
6295+
out_nulls,
6296+
ndims,
6297+
dimv,
6298+
lbsv,
6299+
elmtype,
6300+
elmlen,
6301+
elmbyval,
6302+
elmalign);
6303+
}
6304+
else
6305+
{
6306+
/*
6307+
* if array_random specified lower bound or upper bound is null, then
6308+
* return null. Here we can use create_array_envelope do the job
6309+
*/
6310+
int nbytes;
6311+
int dataoffset;
6312+
6313+
dataoffset = ARR_OVERHEAD_WITHNULLS(ndims, nitems);
6314+
nbytes = dataoffset;
6315+
6316+
result = create_array_envelope(ndims, dimv, lbsv, nbytes,
6317+
elmtype, dataoffset);
6318+
6319+
/* create_array_envelope already zeroed the bitmap, so we're done */
6320+
}
6321+
6322+
PG_RETURN_ARRAYTYPE_P(result);
6323+
}
6324+
60676325
static ArrayType *
60686326
create_array_envelope(int ndims, int *dimv, int *lbsv, int nbytes,
60696327
Oid elmtype, int dataoffset)

src/include/catalog/pg_proc.dat

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1712,6 +1712,18 @@
17121712
proname => 'array_fill', proisstrict => 'f', prorettype => 'anyarray',
17131713
proargtypes => 'anyelement _int4 _int4',
17141714
prosrc => 'array_fill_with_lower_bounds' },
1715+
{ oid => '4551', descr => 'array constructor with random integer element value',
1716+
proname => 'array_random', provolatile => 'v', proisstrict => 'f',
1717+
prorettype => '_int4', proargtypes => 'int4 int4 _int4 _int4',
1718+
proargnames => '{min,max,dims,lbounds}', prosrc => 'int4array_random' },
1719+
{ oid => '4552', descr => 'array constructor with random bigint element value',
1720+
proname => 'array_random', provolatile => 'v', proisstrict => 'f',
1721+
prorettype => '_int8', proargtypes => 'int8 int8 _int4 _int4',
1722+
proargnames => '{min,max,dims,lbounds}', prosrc => 'int8array_random' },
1723+
{ oid => '4553', descr => 'array constructor with random numeric element value',
1724+
proname => 'array_random', provolatile => 'v', proisstrict => 'f',
1725+
prorettype => '_numeric', proargtypes => 'numeric numeric _int4 _int4',
1726+
proargnames => '{min,max,dims,lbounds}', prosrc => 'numeric_array_random' },
17151727
{ oid => '2331', descr => 'expand array to set of rows',
17161728
proname => 'unnest', prorows => '100', prosupport => 'array_unnest_support',
17171729
proretset => 't', prorettype => 'anyelement', proargtypes => 'anyarray',

0 commit comments

Comments
 (0)