array_random
hi.
context: [1]/messages/by-id/CACJufxGRCP19Rm66=TSBwmEuVr92FwL_e6YFjmCpJrgu6Km9hQ@mail.gmail.com.
the attached patch already posted in [1]/messages/by-id/CACJufxGRCP19Rm66=TSBwmEuVr92FwL_e6YFjmCpJrgu6Km9hQ@mail.gmail.com.
I don't want to hijack another thread. so I post it in a separate thread.
The attached patch introduces a new function: array_random.
array_random description:
Returns an array filled with random values in the range min <= x <= max,
having dimensions of the lengths specified by dims. The optional lbounds
argument supplies lower-bound values for each dimension (which default
to all 1).
array_random function signature:
array_random(min int4, max int4, dims int[] [, lbounds int[]]) -> int[]
array_random(min int8, max int8, dims int[] [, lbounds int[]]) -> int8[]
array_random(min numeric, max numeric, dims int[] [, lbounds int[]])
-> numeric[]
demo:
SELECT array_random(1, 6, array[2,5], array[2,4]);
array_random
--------------------------------------
[2:3][4:8]={{6,2,2,5,4},{4,5,6,4,6}}
reasons for adding array_random is:
1. This is better than array_fill. This can fill random and constant
values (random, min and max the same).
2. Building a multi-dimensional PL/pgSQL function equivalent to
array_random is not efficient and is also not easier.
[1]: /messages/by-id/CACJufxGRCP19Rm66=TSBwmEuVr92FwL_e6YFjmCpJrgu6Km9hQ@mail.gmail.com
Attachments:
v1-0001-array_random.patchtext/x-patch; charset=US-ASCII; name=v1-0001-array_random.patchDownload
From 0fb93c0edf7c100178794396bc5f09a9696e03ce Mon Sep 17 00:00:00 2001
From: jian he <jian.universality@gmail.com>
Date: Mon, 30 Jun 2025 23:02:57 +0800
Subject: [PATCH v1 1/1] 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/XXX
---
doc/src/sgml/func.sgml | 36 ++++
src/backend/catalog/system_functions.sql | 21 ++
src/backend/utils/adt/arrayfuncs.c | 258 +++++++++++++++++++++++
src/include/catalog/pg_proc.dat | 12 ++
src/test/regress/expected/arrays.out | 85 ++++++++
src/test/regress/sql/arrays.sql | 27 +++
6 files changed, 439 insertions(+)
diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml
index 298791858be..a653454b424 100644
--- a/doc/src/sgml/func.sgml
+++ b/doc/src/sgml/func.sgml
@@ -20654,6 +20654,42 @@ SELECT NULLIF(value, '(none)') ...
</para></entry>
</row>
+ <row>
+ <entry role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>array_random</primary>
+ </indexterm>
+
+ <function>array_random</function> ( <parameter>min</parameter> <type>integer</type>, <parameter>max</parameter> <type>integer</type>,
+ <parameter>dims</parameter> <type>integer[]</type> <optional>, <parameter>lbounds</parameter> <type>integer[]</type></optional>)
+ <returnvalue>integer[]</returnvalue>
+ </para>
+
+ <para role="func_signature">
+ <function>array_random</function> ( <parameter>min</parameter> <type>bigint</type>, <parameter>max</parameter> <type>bigint</type>,
+ <parameter>dims</parameter> <type>integer[]</type> <optional>, <parameter>lbounds</parameter> <type>integer[]</type></optional>)
+ <returnvalue>bigint[]</returnvalue>
+ </para>
+
+ <para role="func_signature">
+ <function>array_random</function> ( <parameter>min</parameter> <type>numeric</type>, <parameter>max</parameter> <type>numeric</type>,
+ <parameter>dims</parameter> <type>integer[]</type>, <optional>, <parameter>lbounds</parameter> <type>integer[]</type></optional>)
+ <returnvalue>numeric[]</returnvalue>
+ </para>
+
+ <para>
+ Returns an array populated with random values, each value is in the range
+ <parameter>min</parameter> <= x <= <parameter>max</parameter>.
+ The array has dimensions specified by <parameter>dims</parameter>
+ The optional fourth argument (<parameter>lbounds</parameter>) supplies lower-bound values for each dimension (which default to all 1).
+ See <xref linkend="functions-math-random-table"/> also.
+ </para>
+ <para>
+ <literal>array_random(1, 10::bigint, '{2}'::int[])</literal>
+ <returnvalue>{3,3}</returnvalue>
+ </para></entry>
+ </row>
+
<row>
<entry role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/catalog/system_functions.sql b/src/backend/catalog/system_functions.sql
index 566f308e443..b3819a0cbdd 100644
--- a/src/backend/catalog/system_functions.sql
+++ b/src/backend/catalog/system_functions.sql
@@ -73,6 +73,27 @@ CREATE OR REPLACE FUNCTION
VOLATILE PARALLEL RESTRICTED STRICT COST 1
AS 'drandom_normal';
+CREATE OR REPLACE FUNCTION
+ array_random(min integer, max integer, dims integer[], lbounds integer[] DEFAULT NULL)
+ RETURNS integer[]
+ LANGUAGE internal
+ VOLATILE PARALLEL RESTRICTED COST 1
+AS 'int4array_random';
+
+CREATE OR REPLACE FUNCTION
+ array_random (min bigint, max bigint, dims integer[], lbounds integer[] DEFAULT NULL)
+ RETURNS bigint[]
+ LANGUAGE internal
+ VOLATILE PARALLEL RESTRICTED COST 1
+AS 'int8array_random';
+
+CREATE OR REPLACE FUNCTION
+ array_random (min numeric, max numeric, dims integer[], lbounds integer[] DEFAULT NULL)
+ RETURNS numeric[]
+ LANGUAGE internal
+ VOLATILE PARALLEL RESTRICTED COST 1
+AS 'numeric_array_random';
+
CREATE OR REPLACE FUNCTION log(numeric)
RETURNS numeric
LANGUAGE sql
diff --git a/src/backend/utils/adt/arrayfuncs.c b/src/backend/utils/adt/arrayfuncs.c
index c8f53c6fbe7..60e3752c75b 100644
--- a/src/backend/utils/adt/arrayfuncs.c
+++ b/src/backend/utils/adt/arrayfuncs.c
@@ -166,6 +166,7 @@ static int width_bucket_array_variable(Datum operand,
Oid collation,
TypeCacheEntry *typentry);
+static Datum array_random_internal(FunctionCallInfo fcinfo, Oid elmtype);
/*
* array_in :
@@ -6064,6 +6065,263 @@ array_fill(PG_FUNCTION_ARGS)
PG_RETURN_ARRAYTYPE_P(result);
}
+Datum
+int4array_random(PG_FUNCTION_ARGS)
+{
+ return array_random_internal(fcinfo, INT4OID);
+}
+
+Datum
+int8array_random(PG_FUNCTION_ARGS)
+{
+ return array_random_internal(fcinfo, INT8OID);
+}
+
+Datum
+numeric_array_random(PG_FUNCTION_ARGS)
+{
+ return array_random_internal(fcinfo, NUMERICOID);
+}
+
+/*
+ * array_random_internal:
+ *
+ * Create an array with the specified dimensions and lower bounds, and populate
+ * it with random values.
+ *
+ * helper function for:
+ * array_random(min int4, max int4, dims int[] [, lbounds int[]]) -> int[]
+ * array_random(min int8, max int8, dims int[] [, lbounds int[]]) -> int8[]
+ * array_random(min numeric, max numeric, dims int[] [, lbounds int[]]) -> numeric[]
+ */
+static Datum
+array_random_internal(FunctionCallInfo fcinfo, Oid elmtype)
+{
+ ArrayType *result;
+ ArrayType *dims;
+ ArrayType *lbs;
+ int *dimv;
+ int *lbsv;
+ int i;
+ int ndims;
+ int nitems;
+ int deflbs[MAXDIM];
+ int16 elmlen;
+ bool elmbyval;
+ char elmalign;
+ Datum *values;
+ bool *nulls;
+ Oid random_fn_oid = InvalidOid;
+
+ values = (Datum *) palloc(2 * sizeof(Datum));
+ nulls = (bool *) palloc(2 * sizeof(bool));
+ if (!PG_ARGISNULL(0))
+ {
+ values[0] = PG_GETARG_DATUM(0);
+ nulls[0] = false;
+ }
+ else
+ {
+ values[0] = 0;
+ nulls[0] = true;
+ }
+
+ if (!PG_ARGISNULL(1))
+ {
+ values[1] = PG_GETARG_DATUM(1);
+ nulls[1] = false;
+ }
+ else
+ {
+ values[1] = 0;
+ nulls[1] = true;
+ }
+
+ if (PG_ARGISNULL(2))
+ ereport(ERROR,
+ errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
+ errmsg("dimension array cannot be null"));
+
+ dims = PG_GETARG_ARRAYTYPE_P(2);
+ if (ARR_NDIM(dims) > 1)
+ ereport(ERROR,
+ errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
+ errmsg("wrong number of array subscripts"),
+ errdetail("Dimension array must be one dimensional."));
+
+ if (array_contains_nulls(dims))
+ ereport(ERROR,
+ errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
+ errmsg("dimension values cannot be null"));
+
+ dimv = (int *) ARR_DATA_PTR(dims);
+ ndims = (ARR_NDIM(dims) > 0) ? ARR_DIMS(dims)[0] : 0;
+
+ if (ndims < 0) /* we do allow zero-dimension arrays */
+ ereport(ERROR,
+ errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("invalid number of dimensions: %d", ndims));
+ if (ndims > MAXDIM)
+ ereport(ERROR,
+ errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("number of array dimensions (%d) exceeds the maximum allowed (%d)",
+ ndims, MAXDIM));
+
+ if (!PG_ARGISNULL(3))
+ {
+ lbs = PG_GETARG_ARRAYTYPE_P(3);
+
+ if (ARR_NDIM(lbs) > 1)
+ ereport(ERROR,
+ errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
+ errmsg("wrong number of array subscripts"),
+ errdetail("Low bound array must be one dimensional."));
+
+ if (array_contains_nulls(lbs))
+ ereport(ERROR,
+ errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
+ errmsg("Low bound array can not contain null values"));
+
+ if (ndims != ((ARR_NDIM(lbs) > 0) ? ARR_DIMS(lbs)[0] : 0))
+ ereport(ERROR,
+ errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
+ errmsg("wrong number of array subscripts"),
+ errdetail("Low bound array has different size than dimensions array."));
+
+ lbsv = (int *) ARR_DATA_PTR(lbs);
+ }
+ else
+ {
+ for (i = 0; i < MAXDIM; i++)
+ deflbs[i] = 1;
+
+ lbsv = deflbs;
+ }
+
+ /* random function for generating each array element for array_random */
+ switch (elmtype)
+ {
+ case INT4OID:
+ random_fn_oid = F_RANDOM_INT4_INT4;
+ break;
+ case INT8OID:
+ random_fn_oid = F_RANDOM_INT8_INT8;
+ break;
+ case NUMERICOID:
+ random_fn_oid = F_RANDOM_NUMERIC_NUMERIC;
+ break;
+ default:
+ elog(ERROR, "unsupported type %u for array_random function", elmtype);
+ break;
+ }
+
+ if (get_fn_expr_argtype(fcinfo->flinfo, 0) != elmtype)
+ elog(ERROR, "expected input data type as %u", elmtype);
+
+ /* This checks for overflow of the array dimensions */
+ nitems = ArrayGetNItems(ndims, dimv);
+ ArrayCheckBounds(ndims, dimv, lbsv);
+
+ /* fast track for empty array */
+ if (nitems <= 0)
+ {
+ /*
+ * If nitems is zero, we just return an empty array, in that case, the
+ * array_random min value maybe larger than max. XXX is this OK?
+ */
+ result = construct_empty_array(elmtype);
+ PG_RETURN_ARRAYTYPE_P(result);
+ }
+
+ get_typlenbyvalalign(elmtype, &elmlen, &elmbyval, &elmalign);
+
+ /*
+ * For each array element call random(minval, maxval). minval is a type of
+ * elmtype.
+ */
+ if (!nulls[0] && !nulls[1])
+ {
+ int nbytes = 0;
+ int totbytes = 0;
+ Datum *out_datums;
+ bool *out_nulls;
+ FmgrInfo *random_val_flinfo;
+ FunctionCallInfo random_val_fcinfo;
+
+ random_val_flinfo = (FmgrInfo *) palloc0(sizeof(FmgrInfo));
+ fmgr_info(random_fn_oid, random_val_flinfo);
+
+ random_val_fcinfo = (FunctionCallInfo) palloc0(SizeForFunctionCallInfo(2));
+ InitFunctionCallInfoData(*random_val_fcinfo, random_val_flinfo, 2,
+ InvalidOid, NULL, NULL);
+
+ random_val_fcinfo->args[0].value = values[0];
+ random_val_fcinfo->args[0].isnull = false;
+ random_val_fcinfo->args[1].value = values[1];
+ random_val_fcinfo->args[1].isnull = false;
+
+ out_datums = palloc(sizeof(Datum) * nitems);
+ out_nulls = palloc0(sizeof(bool) * nitems);
+
+ for (i = 0; i < nitems; i++)
+ {
+ out_datums[i] = FunctionCallInvoke(random_val_fcinfo);
+ out_nulls[i] = false;
+
+ /* make sure data is not toasted */
+ if (elmlen == -1)
+ out_datums[i] = PointerGetDatum(PG_DETOAST_DATUM(out_datums[i]));
+
+ /* XXX TODO is this correct? */
+ if (i == 0)
+ {
+ nbytes = att_addlength_datum(0, elmlen, out_datums[i]);
+ nbytes = att_align_nominal(nbytes, elmalign);
+ Assert(nbytes > 0);
+
+ totbytes = nbytes * nitems;
+
+ /* check for overflow of multiplication or total request */
+ if (totbytes / nbytes != nitems ||
+ !AllocSizeIsValid(totbytes))
+ ereport(ERROR,
+ errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("array size exceeds the maximum allowed (%d)",
+ (int) MaxAllocSize));
+ }
+ }
+
+ result = construct_md_array(out_datums,
+ out_nulls,
+ ndims,
+ dimv,
+ lbsv,
+ elmtype,
+ elmlen,
+ elmbyval,
+ elmalign);
+ }
+ else
+ {
+ /*
+ * if array_random specified lower bound or upper bound is null, then
+ * return null. Here we can use create_array_envelope do the job
+ */
+ int nbytes;
+ int dataoffset;
+
+ dataoffset = ARR_OVERHEAD_WITHNULLS(ndims, nitems);
+ nbytes = dataoffset;
+
+ result = create_array_envelope(ndims, dimv, lbsv, nbytes,
+ elmtype, dataoffset);
+
+ /* create_array_envelope already zeroed the bitmap, so we're done */
+ }
+
+ PG_RETURN_ARRAYTYPE_P(result);
+}
+
static ArrayType *
create_array_envelope(int ndims, int *dimv, int *lbsv, int nbytes,
Oid elmtype, int dataoffset)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index fb4f7f50350..8c08706f293 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -1712,6 +1712,18 @@
proname => 'array_fill', proisstrict => 'f', prorettype => 'anyarray',
proargtypes => 'anyelement _int4 _int4',
prosrc => 'array_fill_with_lower_bounds' },
+{ oid => '6347', descr => 'array constructor with random integer element value',
+ proname => 'array_random', provolatile => 'v', proisstrict => 'f',
+ prorettype => '_int4', proargtypes => 'int4 int4 _int4 _int4',
+ proargnames => '{min,max,dims,lbounds}', prosrc => 'int4array_random' },
+{ oid => '6348', descr => 'array constructor with random bigint element value',
+ proname => 'array_random', provolatile => 'v', proisstrict => 'f',
+ prorettype => '_int8', proargtypes => 'int8 int8 _int4 _int4',
+ proargnames => '{min,max,dims,lbounds}', prosrc => 'int8array_random' },
+{ oid => '6349', descr => 'array constructor with random numeric element value',
+ proname => 'array_random', provolatile => 'v', proisstrict => 'f',
+ prorettype => '_numeric', proargtypes => 'numeric numeric _int4 _int4',
+ proargnames => '{min,max,dims,lbounds}', prosrc => 'numeric_array_random' },
{ oid => '2331', descr => 'expand array to set of rows',
proname => 'unnest', prorows => '100', prosupport => 'array_unnest_support',
proretset => 't', prorettype => 'anyelement', proargtypes => 'anyarray',
diff --git a/src/test/regress/expected/arrays.out b/src/test/regress/expected/arrays.out
index b815473f414..172e04fe1fe 100644
--- a/src/test/regress/expected/arrays.out
+++ b/src/test/regress/expected/arrays.out
@@ -3002,3 +3002,88 @@ SELECT array_sort(a [-1:0][20:20]) FROM (VALUES ('[-2:0][20:21]={{1,2},{10,20},{
{{1},{10}}
(1 row)
+--array_random error case
+SELECT array_random(1, 10, '{134217728}');
+ERROR: array size exceeds the maximum allowed (134217727)
+SELECT array_random(2, 1, '{2}'::int[]);
+ERROR: lower bound must be less than or equal to upper bound
+SELECT array_random(2, 1, '{-1}'::int[]);
+ERROR: array size exceeds the maximum allowed (134217727)
+SELECT array_random(NULL, 1, NULL);
+ERROR: dimension array cannot be null
+SELECT array_random(1, 2, '{NULL}'::int[]);
+ERROR: dimension values cannot be null
+SELECT array_random(1, 2, '{{1},{1}}'::int[]);
+ERROR: wrong number of array subscripts
+DETAIL: Dimension array must be one dimensional.
+SELECT array_random('-infinity', 2.1, '{2,1}'::int[]);
+ERROR: lower bound cannot be infinity
+SELECT array_random('NaN', 2.1, '{2,1}'::int[]);
+ERROR: lower bound cannot be NaN
+SELECT array_random(1::numeric, 'infinity', '{2,1}'::int[]);
+ERROR: upper bound cannot be infinity
+SELECT array_random(1::numeric, 'NaN', '{2,1}'::int[]);
+ERROR: upper bound cannot be NaN
+SELECT array_random(1, 10, '{2}'::int[],'{NULL}');
+ERROR: Low bound array can not contain null values
+SELECT array_random(1, 10, '{2}'::int[],'{1,2}');
+ERROR: wrong number of array subscripts
+DETAIL: Low bound array has different size than dimensions array.
+SELECT array_random(1, 10, '{2}'::int[],'{{1},{2}}');
+ERROR: wrong number of array subscripts
+DETAIL: Low bound array must be one dimensional.
+--array_random ok case, roughly copied from random.sql
+SELECT setseed(0.5);
+ setseed
+---------
+
+(1 row)
+
+SELECT array_random(1, 10::int8, '{2}'::int[]);
+ array_random
+--------------
+ {3,3}
+(1 row)
+
+SELECT array_random(1, 6, array[2,5], array[2,4]);
+ array_random
+--------------------------------------
+ [2:3][4:8]={{6,2,2,5,4},{4,5,6,4,6}}
+(1 row)
+
+SELECT array_random(-2147483648, 2147483647, array[5,2], '{5,2}');
+ array_random
+---------------------------------------------------------------------------------------------------------------------------------------
+ [5:9][2:3]={{358987948,1720494942},{1522989794,-1952561092},{-562516644,-1082313839},{1917063748,1470913398},{-1663900214,383076569}}
+(1 row)
+
+SELECT array_random(-9223372036854775808, 9223372036854775807, '{5,2}', '{5,2}');
+ array_random
+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+ [5:9][2:3]={{8946456079150312946,-5017870099771078560},{2119550555182341436,4426024215363564415},{108422942355035228,8010643357635886140},{7966634679525141473,-7256609373765379790},{1904816578321642612,2378596988253719677}}
+(1 row)
+
+\gdesc
+ Column | Type
+--------------+----------
+ array_random | bigint[]
+(1 row)
+
+SELECT array_random(-1e30, -1e30, '{3,2}');
+ array_random
+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+ {{-1000000000000000000000000000000,-1000000000000000000000000000000},{-1000000000000000000000000000000,-1000000000000000000000000000000},{-1000000000000000000000000000000,-1000000000000000000000000000000}}
+(1 row)
+
+SELECT array_random(-0.45, 0.45, '{2,2,3}', '{5,2,3}');
+ array_random
+--------------------------------------------------------------------------------------------------
+ [5:6][2:3][3:5]={{{-0.31,0.17,-0.16},{-0.25,-0.33,-0.02}},{{0.26,-0.31,-0.16},{0.08,0.23,0.13}}}
+(1 row)
+
+SELECT array_random(0, 1 - 1e-30, '{2, 5}');
+ array_random
+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+ {{0.068990271742366814671825526682,0.865328382850933408408243093209,0.535285120767345535159068774920,0.550766856332365486462263124933,0.327167727807358961934200155904},{0.985749287984797224624803826994,0.475761851397322538460984536852,0.793479002749253814546091362589,0.164860958495312801172978594373,0.519707802923826482493035607188}}
+(1 row)
+
diff --git a/src/test/regress/sql/arrays.sql b/src/test/regress/sql/arrays.sql
index 47d62c1d38d..0acb810f599 100644
--- a/src/test/regress/sql/arrays.sql
+++ b/src/test/regress/sql/arrays.sql
@@ -892,3 +892,30 @@ SELECT array_sort(a) FROM (VALUES ('[-1:0]={7,1}'::int[])) v(a);
SELECT array_sort(a) FROM (VALUES ('[-2:0][20:21]={{1,2},{10,20},{1,-4}}'::int[])) v(a);
SELECT array_sort(a [-1:0]) FROM (VALUES ('[-2:0][20:21]={{1,2},{10,20},{1,-4}}'::int[])) v(a);
SELECT array_sort(a [-1:0][20:20]) FROM (VALUES ('[-2:0][20:21]={{1,2},{10,20},{1,-4}}'::int[])) v(a);
+
+--array_random error case
+SELECT array_random(1, 10, '{134217728}');
+SELECT array_random(2, 1, '{2}'::int[]);
+SELECT array_random(2, 1, '{-1}'::int[]);
+SELECT array_random(NULL, 1, NULL);
+SELECT array_random(1, 2, '{NULL}'::int[]);
+SELECT array_random(1, 2, '{{1},{1}}'::int[]);
+SELECT array_random('-infinity', 2.1, '{2,1}'::int[]);
+SELECT array_random('NaN', 2.1, '{2,1}'::int[]);
+SELECT array_random(1::numeric, 'infinity', '{2,1}'::int[]);
+SELECT array_random(1::numeric, 'NaN', '{2,1}'::int[]);
+SELECT array_random(1, 10, '{2}'::int[],'{NULL}');
+SELECT array_random(1, 10, '{2}'::int[],'{1,2}');
+SELECT array_random(1, 10, '{2}'::int[],'{{1},{2}}');
+
+--array_random ok case
+SELECT setseed(0.5);
+SELECT array_random(1, 10::int8, '{2}'::int[]);
+SELECT array_random(1, 6, array[2,5], array[2,4]);
+SELECT array_random(-2147483648, 2147483647, array[5,2], '{5,2}');
+SELECT array_random(-9223372036854775808, 9223372036854775807, '{5,2}', '{5,2}');
+\gdesc
+
+SELECT array_random(-1e30, -1e30, '{3,2}');
+SELECT array_random(-0.45, 0.45, '{2,2,3}', '{5,2,3}');
+SELECT array_random(0, 1 - 1e-30, '{2, 5}');
--
2.34.1
On Mon, Jun 30, 2025 at 11:04 PM jian he <jian.universality@gmail.com> wrote:
demo:
SELECT array_random(1, 6, array[2,5], array[2,4]);
array_random
--------------------------------------
[2:3][4:8]={{6,2,2,5,4},{4,5,6,4,6}}reasons for adding array_random is:
1. This is better than array_fill. This can fill random and constant
values (random, min and max the same).
2. Building a multi-dimensional PL/pgSQL function equivalent to
array_random is not efficient and is also not easier.[1] /messages/by-id/CACJufxGRCP19Rm66=TSBwmEuVr92FwL_e6YFjmCpJrgu6Km9hQ@mail.gmail.com
i didn't use ./src/include/catalog/duplicate_oids
then there is some duplicated oid issue.
the attached patch fixes this issue.
Attachments:
v2-0001-array_random.patchtext/x-patch; charset=US-ASCII; name=v2-0001-array_random.patchDownload
From e170c7110604ec7f3aee69e83dcef42da447bbcc Mon Sep 17 00:00:00 2001
From: jian he <jian.universality@gmail.com>
Date: Sat, 5 Jul 2025 11:31:55 +0800
Subject: [PATCH v2 1/1] 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
---
doc/src/sgml/func.sgml | 34 +++
src/backend/catalog/system_functions.sql | 21 ++
src/backend/utils/adt/arrayfuncs.c | 258 +++++++++++++++++++++++
src/include/catalog/pg_proc.dat | 12 ++
src/test/regress/expected/arrays.out | 85 ++++++++
src/test/regress/sql/arrays.sql | 27 +++
6 files changed, 437 insertions(+)
diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml
index 810b2b50f0d..038a6653f17 100644
--- a/doc/src/sgml/func.sgml
+++ b/doc/src/sgml/func.sgml
@@ -20654,6 +20654,40 @@ SELECT NULLIF(value, '(none)') ...
</para></entry>
</row>
+ <row>
+ <entry role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>array_random</primary>
+ </indexterm>
+
+ <function>array_random</function> ( <parameter>min</parameter> <type>integer</type>, <parameter>max</parameter> <type>integer</type>,
+ <parameter>dims</parameter> <type>integer[]</type> <optional>, <parameter>lbounds</parameter> <type>integer[]</type></optional>)
+ <returnvalue>integer[]</returnvalue>
+ </para>
+ <para role="func_signature">
+ <function>array_random</function> ( <parameter>min</parameter> <type>bigint</type>, <parameter>max</parameter> <type>bigint</type>,
+ <parameter>dims</parameter> <type>integer[]</type> <optional>, <parameter>lbounds</parameter> <type>integer[]</type></optional>)
+ <returnvalue>bigint[]</returnvalue>
+ </para>
+ <para role="func_signature">
+ <function>array_random</function> ( <parameter>min</parameter> <type>numeric</type>, <parameter>max</parameter> <type>numeric</type>,
+ <parameter>dims</parameter> <type>integer[]</type>, <optional>, <parameter>lbounds</parameter> <type>integer[]</type></optional>)
+ <returnvalue>numeric[]</returnvalue>
+ </para>
+
+ <para>
+ Returns an array populated with random values, each value is in the range
+ <parameter>min</parameter> <= x <= <parameter>max</parameter>.
+ The array has dimensions specified by <parameter>dims</parameter>
+ The optional fourth argument (<parameter>lbounds</parameter>) supplies lower-bound values for each dimension (which default to all 1).
+ See <xref linkend="functions-math-random-table"/> also.
+ </para>
+ <para>
+ <literal>array_random(1, 10::bigint, '{2}'::int[])</literal>
+ <returnvalue>{3,3}</returnvalue>
+ </para></entry>
+ </row>
+
<row>
<entry role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/src/backend/catalog/system_functions.sql b/src/backend/catalog/system_functions.sql
index 566f308e443..b3819a0cbdd 100644
--- a/src/backend/catalog/system_functions.sql
+++ b/src/backend/catalog/system_functions.sql
@@ -73,6 +73,27 @@ CREATE OR REPLACE FUNCTION
VOLATILE PARALLEL RESTRICTED STRICT COST 1
AS 'drandom_normal';
+CREATE OR REPLACE FUNCTION
+ array_random(min integer, max integer, dims integer[], lbounds integer[] DEFAULT NULL)
+ RETURNS integer[]
+ LANGUAGE internal
+ VOLATILE PARALLEL RESTRICTED COST 1
+AS 'int4array_random';
+
+CREATE OR REPLACE FUNCTION
+ array_random (min bigint, max bigint, dims integer[], lbounds integer[] DEFAULT NULL)
+ RETURNS bigint[]
+ LANGUAGE internal
+ VOLATILE PARALLEL RESTRICTED COST 1
+AS 'int8array_random';
+
+CREATE OR REPLACE FUNCTION
+ array_random (min numeric, max numeric, dims integer[], lbounds integer[] DEFAULT NULL)
+ RETURNS numeric[]
+ LANGUAGE internal
+ VOLATILE PARALLEL RESTRICTED COST 1
+AS 'numeric_array_random';
+
CREATE OR REPLACE FUNCTION log(numeric)
RETURNS numeric
LANGUAGE sql
diff --git a/src/backend/utils/adt/arrayfuncs.c b/src/backend/utils/adt/arrayfuncs.c
index c8f53c6fbe7..60e3752c75b 100644
--- a/src/backend/utils/adt/arrayfuncs.c
+++ b/src/backend/utils/adt/arrayfuncs.c
@@ -166,6 +166,7 @@ static int width_bucket_array_variable(Datum operand,
Oid collation,
TypeCacheEntry *typentry);
+static Datum array_random_internal(FunctionCallInfo fcinfo, Oid elmtype);
/*
* array_in :
@@ -6064,6 +6065,263 @@ array_fill(PG_FUNCTION_ARGS)
PG_RETURN_ARRAYTYPE_P(result);
}
+Datum
+int4array_random(PG_FUNCTION_ARGS)
+{
+ return array_random_internal(fcinfo, INT4OID);
+}
+
+Datum
+int8array_random(PG_FUNCTION_ARGS)
+{
+ return array_random_internal(fcinfo, INT8OID);
+}
+
+Datum
+numeric_array_random(PG_FUNCTION_ARGS)
+{
+ return array_random_internal(fcinfo, NUMERICOID);
+}
+
+/*
+ * array_random_internal:
+ *
+ * Create an array with the specified dimensions and lower bounds, and populate
+ * it with random values.
+ *
+ * helper function for:
+ * array_random(min int4, max int4, dims int[] [, lbounds int[]]) -> int[]
+ * array_random(min int8, max int8, dims int[] [, lbounds int[]]) -> int8[]
+ * array_random(min numeric, max numeric, dims int[] [, lbounds int[]]) -> numeric[]
+ */
+static Datum
+array_random_internal(FunctionCallInfo fcinfo, Oid elmtype)
+{
+ ArrayType *result;
+ ArrayType *dims;
+ ArrayType *lbs;
+ int *dimv;
+ int *lbsv;
+ int i;
+ int ndims;
+ int nitems;
+ int deflbs[MAXDIM];
+ int16 elmlen;
+ bool elmbyval;
+ char elmalign;
+ Datum *values;
+ bool *nulls;
+ Oid random_fn_oid = InvalidOid;
+
+ values = (Datum *) palloc(2 * sizeof(Datum));
+ nulls = (bool *) palloc(2 * sizeof(bool));
+ if (!PG_ARGISNULL(0))
+ {
+ values[0] = PG_GETARG_DATUM(0);
+ nulls[0] = false;
+ }
+ else
+ {
+ values[0] = 0;
+ nulls[0] = true;
+ }
+
+ if (!PG_ARGISNULL(1))
+ {
+ values[1] = PG_GETARG_DATUM(1);
+ nulls[1] = false;
+ }
+ else
+ {
+ values[1] = 0;
+ nulls[1] = true;
+ }
+
+ if (PG_ARGISNULL(2))
+ ereport(ERROR,
+ errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
+ errmsg("dimension array cannot be null"));
+
+ dims = PG_GETARG_ARRAYTYPE_P(2);
+ if (ARR_NDIM(dims) > 1)
+ ereport(ERROR,
+ errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
+ errmsg("wrong number of array subscripts"),
+ errdetail("Dimension array must be one dimensional."));
+
+ if (array_contains_nulls(dims))
+ ereport(ERROR,
+ errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
+ errmsg("dimension values cannot be null"));
+
+ dimv = (int *) ARR_DATA_PTR(dims);
+ ndims = (ARR_NDIM(dims) > 0) ? ARR_DIMS(dims)[0] : 0;
+
+ if (ndims < 0) /* we do allow zero-dimension arrays */
+ ereport(ERROR,
+ errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("invalid number of dimensions: %d", ndims));
+ if (ndims > MAXDIM)
+ ereport(ERROR,
+ errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("number of array dimensions (%d) exceeds the maximum allowed (%d)",
+ ndims, MAXDIM));
+
+ if (!PG_ARGISNULL(3))
+ {
+ lbs = PG_GETARG_ARRAYTYPE_P(3);
+
+ if (ARR_NDIM(lbs) > 1)
+ ereport(ERROR,
+ errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
+ errmsg("wrong number of array subscripts"),
+ errdetail("Low bound array must be one dimensional."));
+
+ if (array_contains_nulls(lbs))
+ ereport(ERROR,
+ errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
+ errmsg("Low bound array can not contain null values"));
+
+ if (ndims != ((ARR_NDIM(lbs) > 0) ? ARR_DIMS(lbs)[0] : 0))
+ ereport(ERROR,
+ errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
+ errmsg("wrong number of array subscripts"),
+ errdetail("Low bound array has different size than dimensions array."));
+
+ lbsv = (int *) ARR_DATA_PTR(lbs);
+ }
+ else
+ {
+ for (i = 0; i < MAXDIM; i++)
+ deflbs[i] = 1;
+
+ lbsv = deflbs;
+ }
+
+ /* random function for generating each array element for array_random */
+ switch (elmtype)
+ {
+ case INT4OID:
+ random_fn_oid = F_RANDOM_INT4_INT4;
+ break;
+ case INT8OID:
+ random_fn_oid = F_RANDOM_INT8_INT8;
+ break;
+ case NUMERICOID:
+ random_fn_oid = F_RANDOM_NUMERIC_NUMERIC;
+ break;
+ default:
+ elog(ERROR, "unsupported type %u for array_random function", elmtype);
+ break;
+ }
+
+ if (get_fn_expr_argtype(fcinfo->flinfo, 0) != elmtype)
+ elog(ERROR, "expected input data type as %u", elmtype);
+
+ /* This checks for overflow of the array dimensions */
+ nitems = ArrayGetNItems(ndims, dimv);
+ ArrayCheckBounds(ndims, dimv, lbsv);
+
+ /* fast track for empty array */
+ if (nitems <= 0)
+ {
+ /*
+ * If nitems is zero, we just return an empty array, in that case, the
+ * array_random min value maybe larger than max. XXX is this OK?
+ */
+ result = construct_empty_array(elmtype);
+ PG_RETURN_ARRAYTYPE_P(result);
+ }
+
+ get_typlenbyvalalign(elmtype, &elmlen, &elmbyval, &elmalign);
+
+ /*
+ * For each array element call random(minval, maxval). minval is a type of
+ * elmtype.
+ */
+ if (!nulls[0] && !nulls[1])
+ {
+ int nbytes = 0;
+ int totbytes = 0;
+ Datum *out_datums;
+ bool *out_nulls;
+ FmgrInfo *random_val_flinfo;
+ FunctionCallInfo random_val_fcinfo;
+
+ random_val_flinfo = (FmgrInfo *) palloc0(sizeof(FmgrInfo));
+ fmgr_info(random_fn_oid, random_val_flinfo);
+
+ random_val_fcinfo = (FunctionCallInfo) palloc0(SizeForFunctionCallInfo(2));
+ InitFunctionCallInfoData(*random_val_fcinfo, random_val_flinfo, 2,
+ InvalidOid, NULL, NULL);
+
+ random_val_fcinfo->args[0].value = values[0];
+ random_val_fcinfo->args[0].isnull = false;
+ random_val_fcinfo->args[1].value = values[1];
+ random_val_fcinfo->args[1].isnull = false;
+
+ out_datums = palloc(sizeof(Datum) * nitems);
+ out_nulls = palloc0(sizeof(bool) * nitems);
+
+ for (i = 0; i < nitems; i++)
+ {
+ out_datums[i] = FunctionCallInvoke(random_val_fcinfo);
+ out_nulls[i] = false;
+
+ /* make sure data is not toasted */
+ if (elmlen == -1)
+ out_datums[i] = PointerGetDatum(PG_DETOAST_DATUM(out_datums[i]));
+
+ /* XXX TODO is this correct? */
+ if (i == 0)
+ {
+ nbytes = att_addlength_datum(0, elmlen, out_datums[i]);
+ nbytes = att_align_nominal(nbytes, elmalign);
+ Assert(nbytes > 0);
+
+ totbytes = nbytes * nitems;
+
+ /* check for overflow of multiplication or total request */
+ if (totbytes / nbytes != nitems ||
+ !AllocSizeIsValid(totbytes))
+ ereport(ERROR,
+ errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("array size exceeds the maximum allowed (%d)",
+ (int) MaxAllocSize));
+ }
+ }
+
+ result = construct_md_array(out_datums,
+ out_nulls,
+ ndims,
+ dimv,
+ lbsv,
+ elmtype,
+ elmlen,
+ elmbyval,
+ elmalign);
+ }
+ else
+ {
+ /*
+ * if array_random specified lower bound or upper bound is null, then
+ * return null. Here we can use create_array_envelope do the job
+ */
+ int nbytes;
+ int dataoffset;
+
+ dataoffset = ARR_OVERHEAD_WITHNULLS(ndims, nitems);
+ nbytes = dataoffset;
+
+ result = create_array_envelope(ndims, dimv, lbsv, nbytes,
+ elmtype, dataoffset);
+
+ /* create_array_envelope already zeroed the bitmap, so we're done */
+ }
+
+ PG_RETURN_ARRAYTYPE_P(result);
+}
+
static ArrayType *
create_array_envelope(int ndims, int *dimv, int *lbsv, int nbytes,
Oid elmtype, int dataoffset)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index d4650947c63..3b8d8468dcf 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -1712,6 +1712,18 @@
proname => 'array_fill', proisstrict => 'f', prorettype => 'anyarray',
proargtypes => 'anyelement _int4 _int4',
prosrc => 'array_fill_with_lower_bounds' },
+{ oid => '4551', descr => 'array constructor with random integer element value',
+ proname => 'array_random', provolatile => 'v', proisstrict => 'f',
+ prorettype => '_int4', proargtypes => 'int4 int4 _int4 _int4',
+ proargnames => '{min,max,dims,lbounds}', prosrc => 'int4array_random' },
+{ oid => '4552', descr => 'array constructor with random bigint element value',
+ proname => 'array_random', provolatile => 'v', proisstrict => 'f',
+ prorettype => '_int8', proargtypes => 'int8 int8 _int4 _int4',
+ proargnames => '{min,max,dims,lbounds}', prosrc => 'int8array_random' },
+{ oid => '4553', descr => 'array constructor with random numeric element value',
+ proname => 'array_random', provolatile => 'v', proisstrict => 'f',
+ prorettype => '_numeric', proargtypes => 'numeric numeric _int4 _int4',
+ proargnames => '{min,max,dims,lbounds}', prosrc => 'numeric_array_random' },
{ oid => '2331', descr => 'expand array to set of rows',
proname => 'unnest', prorows => '100', prosupport => 'array_unnest_support',
proretset => 't', prorettype => 'anyelement', proargtypes => 'anyarray',
diff --git a/src/test/regress/expected/arrays.out b/src/test/regress/expected/arrays.out
index b815473f414..d210b84c193 100644
--- a/src/test/regress/expected/arrays.out
+++ b/src/test/regress/expected/arrays.out
@@ -3002,3 +3002,88 @@ SELECT array_sort(a [-1:0][20:20]) FROM (VALUES ('[-2:0][20:21]={{1,2},{10,20},{
{{1},{10}}
(1 row)
+--array_random error case
+SELECT array_random(1, 10, '{134217728}');
+ERROR: array size exceeds the maximum allowed (134217727)
+SELECT array_random(2, 1, '{2}'::int[]);
+ERROR: lower bound must be less than or equal to upper bound
+SELECT array_random(2, 1, '{-1}'::int[]);
+ERROR: array size exceeds the maximum allowed (134217727)
+SELECT array_random(NULL, 1, NULL);
+ERROR: dimension array cannot be null
+SELECT array_random(1, 2, '{NULL}'::int[]);
+ERROR: dimension values cannot be null
+SELECT array_random(1, 2, '{{1},{1}}'::int[]);
+ERROR: wrong number of array subscripts
+DETAIL: Dimension array must be one dimensional.
+SELECT array_random('-infinity', 2.1, '{2,1}'::int[]);
+ERROR: lower bound cannot be infinity
+SELECT array_random('NaN', 2.1, '{2,1}'::int[]);
+ERROR: lower bound cannot be NaN
+SELECT array_random(1::numeric, 'infinity', '{2,1}'::int[]);
+ERROR: upper bound cannot be infinity
+SELECT array_random(1::numeric, 'NaN', '{2,1}'::int[]);
+ERROR: upper bound cannot be NaN
+SELECT array_random(1, 10, '{2}'::int[],'{NULL}');
+ERROR: Low bound array can not contain null values
+SELECT array_random(1, 10, '{2}'::int[],'{1,2}');
+ERROR: wrong number of array subscripts
+DETAIL: Low bound array has different size than dimensions array.
+SELECT array_random(1, 10, '{2}'::int[],'{{1},{2}}');
+ERROR: wrong number of array subscripts
+DETAIL: Low bound array must be one dimensional.
+--array_random ok case
+SELECT setseed(0.5);
+ setseed
+---------
+
+(1 row)
+
+SELECT array_random(1, 10::int8, '{2}'::int[]);
+ array_random
+--------------
+ {3,3}
+(1 row)
+
+SELECT array_random(1, 6, array[2,5], array[2,4]);
+ array_random
+--------------------------------------
+ [2:3][4:8]={{6,2,2,5,4},{4,5,6,4,6}}
+(1 row)
+
+SELECT array_random(-2147483648, 2147483647, array[5,2], '{5,2}');
+ array_random
+---------------------------------------------------------------------------------------------------------------------------------------
+ [5:9][2:3]={{358987948,1720494942},{1522989794,-1952561092},{-562516644,-1082313839},{1917063748,1470913398},{-1663900214,383076569}}
+(1 row)
+
+SELECT array_random(-9223372036854775808, 9223372036854775807, '{5,2}', '{5,2}');
+ array_random
+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+ [5:9][2:3]={{8946456079150312946,-5017870099771078560},{2119550555182341436,4426024215363564415},{108422942355035228,8010643357635886140},{7966634679525141473,-7256609373765379790},{1904816578321642612,2378596988253719677}}
+(1 row)
+
+\gdesc
+ Column | Type
+--------------+----------
+ array_random | bigint[]
+(1 row)
+
+SELECT array_random(-1e30, -1e30, '{3,2}');
+ array_random
+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+ {{-1000000000000000000000000000000,-1000000000000000000000000000000},{-1000000000000000000000000000000,-1000000000000000000000000000000},{-1000000000000000000000000000000,-1000000000000000000000000000000}}
+(1 row)
+
+SELECT array_random(-0.45, 0.45, '{2,2,3}', '{5,2,3}');
+ array_random
+--------------------------------------------------------------------------------------------------
+ [5:6][2:3][3:5]={{{-0.31,0.17,-0.16},{-0.25,-0.33,-0.02}},{{0.26,-0.31,-0.16},{0.08,0.23,0.13}}}
+(1 row)
+
+SELECT array_random(0, 1 - 1e-30, '{2, 5}');
+ array_random
+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+ {{0.068990271742366814671825526682,0.865328382850933408408243093209,0.535285120767345535159068774920,0.550766856332365486462263124933,0.327167727807358961934200155904},{0.985749287984797224624803826994,0.475761851397322538460984536852,0.793479002749253814546091362589,0.164860958495312801172978594373,0.519707802923826482493035607188}}
+(1 row)
+
diff --git a/src/test/regress/sql/arrays.sql b/src/test/regress/sql/arrays.sql
index 47d62c1d38d..0acb810f599 100644
--- a/src/test/regress/sql/arrays.sql
+++ b/src/test/regress/sql/arrays.sql
@@ -892,3 +892,30 @@ SELECT array_sort(a) FROM (VALUES ('[-1:0]={7,1}'::int[])) v(a);
SELECT array_sort(a) FROM (VALUES ('[-2:0][20:21]={{1,2},{10,20},{1,-4}}'::int[])) v(a);
SELECT array_sort(a [-1:0]) FROM (VALUES ('[-2:0][20:21]={{1,2},{10,20},{1,-4}}'::int[])) v(a);
SELECT array_sort(a [-1:0][20:20]) FROM (VALUES ('[-2:0][20:21]={{1,2},{10,20},{1,-4}}'::int[])) v(a);
+
+--array_random error case
+SELECT array_random(1, 10, '{134217728}');
+SELECT array_random(2, 1, '{2}'::int[]);
+SELECT array_random(2, 1, '{-1}'::int[]);
+SELECT array_random(NULL, 1, NULL);
+SELECT array_random(1, 2, '{NULL}'::int[]);
+SELECT array_random(1, 2, '{{1},{1}}'::int[]);
+SELECT array_random('-infinity', 2.1, '{2,1}'::int[]);
+SELECT array_random('NaN', 2.1, '{2,1}'::int[]);
+SELECT array_random(1::numeric, 'infinity', '{2,1}'::int[]);
+SELECT array_random(1::numeric, 'NaN', '{2,1}'::int[]);
+SELECT array_random(1, 10, '{2}'::int[],'{NULL}');
+SELECT array_random(1, 10, '{2}'::int[],'{1,2}');
+SELECT array_random(1, 10, '{2}'::int[],'{{1},{2}}');
+
+--array_random ok case
+SELECT setseed(0.5);
+SELECT array_random(1, 10::int8, '{2}'::int[]);
+SELECT array_random(1, 6, array[2,5], array[2,4]);
+SELECT array_random(-2147483648, 2147483647, array[5,2], '{5,2}');
+SELECT array_random(-9223372036854775808, 9223372036854775807, '{5,2}', '{5,2}');
+\gdesc
+
+SELECT array_random(-1e30, -1e30, '{3,2}');
+SELECT array_random(-0.45, 0.45, '{2,2,3}', '{5,2,3}');
+SELECT array_random(0, 1 - 1e-30, '{2, 5}');
--
2.34.1
On 30/06/2025 17:04, jian he wrote:
reasons for adding array_random is:
1. This is better than array_fill. This can fill random and constant
values (random, min and max the same).
2. Building a multi-dimensional PL/pgSQL function equivalent to
array_random is not efficient and is also not easier.
I am not against this at all, but what is the actual use case?
--
Vik Fearing
On Sat, 5 Jul 2025 at 08:32, Vik Fearing <vik@postgresfriends.org> wrote:
On 30/06/2025 17:04, jian he wrote:
reasons for adding array_random is:
1. This is better than array_fill. This can fill random and constant
values (random, min and max the same).
2. Building a multi-dimensional PL/pgSQL function equivalent to
array_random is not efficient and is also not easier.
I took a quick look at this and there are a number of things that I
don't like about the implementation. However, ...
I am not against this at all, but what is the actual use case?
I think that's a reasonable question to ask.
The original use case [1]/messages/by-id/87plssezpc.fsf@163.com was to produce arrays of random numbers for
testing gin indexes.
[1]: /messages/by-id/87plssezpc.fsf@163.com
However, this is adding quite a lot of C code for something that's
actually pretty easy to do as a user-defined function in SQL. Maybe
that's OK, if it's something that there's a lot of demand for, but
it's worth asking that question.
Regards,
Dean
On Sat, Jul 5, 2025 at 3:32 PM Vik Fearing <vik@postgresfriends.org> wrote:
On 30/06/2025 17:04, jian he wrote:
reasons for adding array_random is:
1. This is better than array_fill. This can fill random and constant
values (random, min and max the same).
2. Building a multi-dimensional PL/pgSQL function equivalent to
array_random is not efficient and is also not easier.I am not against this at all, but what is the actual use case?
--
it seems not trivial to wrap up all the generated random values into a specific
multi-dimensional array (more than 2 dimensions).
for example, say we generated 24 random values and wanted to arrange them into a
3-dimensional array with shape [4, 3, 2].
we can easily use:
SELECT array_random(1, 6, array[4,3, 2]);
of course, we can use plpgsql to do it, but the c function would be
more convenient.
does this make sense?
Hi,
it seems not trivial to wrap up all the generated random values into a specific
multi-dimensional array (more than 2 dimensions).
for example, say we generated 24 random values and wanted to arrange them into a
3-dimensional array with shape [4, 3, 2].
we can easily use:
SELECT array_random(1, 6, array[4,3, 2]);of course, we can use plpgsql to do it, but the c function would be
more convenient.
does this make sense?
The proposed function seems to do two things at a time - generating
random values and transforming them into an array of desired
dimensions. Generally we try to avoid such interfaces. Can you think
of something like array_transform() / array_reshape() that takes an
arbitrary single-dimension array and modifies it?
On Tue, 8 Jul 2025 at 15:26, Aleksander Alekseev
<aleksander@tigerdata.com> wrote:
The proposed function seems to do two things at a time - generating
random values and transforming them into an array of desired
dimensions. Generally we try to avoid such interfaces. Can you think
of something like array_transform() / array_reshape() that takes an
arbitrary single-dimension array and modifies it?
That's a good point. Arguably, creating a simple 1-D array of random
values is trivial enough to leave to users, and there isn't sufficient
demand to justify creating core functions for it.
OTOH, creating multi-dimensional arrays (of any values, not just
random) is harder. Perhaps there is an argument for in-core functions
to make that easier, though I'm not sure how many people actually use
multi-dimensional arrays. In any case, that seems like a separate
discussion for a new thread, if there's demand.
Regards,
Dean
On Fri, Jul 11, 2025 at 3:49 PM Dean Rasheed <dean.a.rasheed@gmail.com> wrote:
On Tue, 8 Jul 2025 at 15:26, Aleksander Alekseev
<aleksander@tigerdata.com> wrote:The proposed function seems to do two things at a time - generating
random values and transforming them into an array of desired
dimensions. Generally we try to avoid such interfaces. Can you think
of something like array_transform() / array_reshape() that takes an
arbitrary single-dimension array and modifies it?That's a good point. Arguably, creating a simple 1-D array of random
values is trivial enough to leave to users, and there isn't sufficient
demand to justify creating core functions for it.
use sql, we generally do something like:
select array_agg(random(1, 10)) from generate_series(1, 2) g;
but its performance is worse than array_random.
Does performance and other factors justify adding array_random?
jian he <jian.universality@gmail.com> writes:
Does performance and other factors justify adding array_random?
There hasn't really been field demand for such a function, AFAIR.
There's an infinite amount of stuff that would be faster if coded
in C than if written in SQL or plpgsql. We can't support it all,
so we need some fairly high bar to accepting what functionality
we'll support.
In the case at hand, "fill a 1-D array with random values" is
quite trivial to do in either SQL or plpgsql. However, "reshape
an array to have such-and-such dimensions" isn't. So in the abstract
I'd be more sympathetic to inventing a function that can do reshaping.
But there is still the question of whether anybody really needs that
functionality.
regards, tom lane