Dean Rasheed <dean.a.rash...@gmail.com> writes:

Hello Dean,

>> I did have some issue to run 'make html', but the
>> error exists before my patch, so I change the document carefully without
>> testing it.  do you know how to fix the below error in 'make html'?
>>
>> $/usr/bin/xsltproc --nonet --path . --path . --stringparam pg.version 
>> '18devel'  stylesheet.xsl postgres-full.xml
>>
>> I/O error : Attempt to load network entity 
>> http://docbook.sourceforge.net/release/xsl/current/xhtml/chunk.xsl
>> warning: failed to load external entity
>> "http://docbook.sourceforge.net/release/xsl/current/xhtml/chunk.xsl";
..
>
> This looks like you're missing a required package. Try installing
> docbook-xsl or docbook-xsl-stylesheets or something similar (the
> package name varies depending on your distro).

This does work, thank you!

>> My suggestion would be to mirror the signatures of the core random()
>> functions more closely, and have this:
>>
>> 1). rand_array(numvals int, minlen int, maxlen int)
>>     returns setof float8[]
>>
..>
>> 4). rand_array(numvals int, minlen int, maxlen int,
>>                minval numeric, maxval numeric)
>>     returns setof numeric[]

> this is indeed a more clean and correct APIs, I will use the above ones
> in the next version. Thanks for the suggestion.

I followed your suggestion in the new attached version. They are not
only some cleaner APIs for user and but also some cleaner implementation
in core, Thank for this suggestion as well.

Sorry for the late response, just my new posistion is bit of busy that I
don't have enough time on community work.

-- 
Best Regards
Andy Fan

>From e6f30ef4fcccee36bc577c5612c3713ae64890f0 Mon Sep 17 00:00:00 2001
From: Andy Fan <zhihuifan1...@163.com>
Date: Mon, 26 Aug 2024 18:50:57 +0800
Subject: [PATCH v20240826 1/1] Add functions rand_array function to 
 contrib/tablefunc.

It produces an array of numeric_type with its length in the range of
[minlen,maxlen] and each value is in the range of [minval,maxval].
---
 contrib/tablefunc/Makefile                |   2 +-
 contrib/tablefunc/expected/tablefunc.out  | 105 ++++++++++++++
 contrib/tablefunc/sql/tablefunc.sql       |  12 ++
 contrib/tablefunc/tablefunc--1.0--1.1.sql |  22 +++
 contrib/tablefunc/tablefunc.c             | 162 ++++++++++++++++++++++
 contrib/tablefunc/tablefunc.control       |   2 +-
 doc/src/sgml/tablefunc.sgml               |  11 ++
 src/backend/utils/adt/arrayfuncs.c        |   7 +
 8 files changed, 321 insertions(+), 2 deletions(-)
 create mode 100644 contrib/tablefunc/tablefunc--1.0--1.1.sql

diff --git a/contrib/tablefunc/Makefile b/contrib/tablefunc/Makefile
index 191a3a1d38..f0c67308fd 100644
--- a/contrib/tablefunc/Makefile
+++ b/contrib/tablefunc/Makefile
@@ -3,7 +3,7 @@
 MODULES = tablefunc
 
 EXTENSION = tablefunc
-DATA = tablefunc--1.0.sql
+DATA = tablefunc--1.0.sql tablefunc--1.0--1.1.sql
 PGFILEDESC = "tablefunc - various functions that return tables"
 
 REGRESS = tablefunc
diff --git a/contrib/tablefunc/expected/tablefunc.out b/contrib/tablefunc/expected/tablefunc.out
index ddece79029..586988881e 100644
--- a/contrib/tablefunc/expected/tablefunc.out
+++ b/contrib/tablefunc/expected/tablefunc.out
@@ -12,6 +12,111 @@ SELECT avg(normal_rand)::int, count(*) FROM normal_rand(100, 250, 0.2);
 -- negative number of tuples
 SELECT avg(normal_rand)::int, count(*) FROM normal_rand(-1, 250, 0.2);
 ERROR:  number of rows cannot be negative
+select setseed(0.8);
+ setseed 
+---------
+ 
+(1 row)
+
+select rand_array(10, 0, 3, 50::int, 80::int);
+ rand_array 
+------------
+ {57,58,61}
+ {}
+ {50,50,78}
+ {}
+ {52,52}
+ {55,79,61}
+ {72,78}
+ {}
+ {64,70}
+ {59,78}
+(10 rows)
+
+select setseed(0.8);
+ setseed 
+---------
+ 
+(1 row)
+
+select rand_array(10, 0, 3, 50::bigint, 80::bigint);
+ rand_array 
+------------
+ {57,58,61}
+ {}
+ {50,50,78}
+ {}
+ {52,52}
+ {55,79,61}
+ {72,78}
+ {}
+ {64,70}
+ {59,78}
+(10 rows)
+
+select setseed(0.8);
+ setseed 
+---------
+ 
+(1 row)
+
+select rand_array(10, 0, 3, 50::float8, 80::float8);
+                           rand_array                           
+----------------------------------------------------------------
+ {0.2328377064354923,0.2557039868740123,0.3469304653544969}
+ {}
+ {0.000835197565928425,0.011969003780824217,0.8981013605381762}
+ {}
+ {0.08527426384290426,0.06616981677920686}
+ {0.1809935627858228,0.9105400064668479,0.34989620485343464}
+ {0.7139255282088908,0.8803672957879083}
+ {}
+ {0.45650965657162845,0.6253950409407578}
+ {0.30472421749069234,0.8833976124588141}
+(10 rows)
+
+select setseed(0.8);
+ setseed 
+---------
+ 
+(1 row)
+
+select rand_array(10, 0, 3, 50::float4, 80::float4);
+                           rand_array                           
+----------------------------------------------------------------
+ {0.2328377064354923,0.2557039868740123,0.3469304653544969}
+ {}
+ {0.000835197565928425,0.011969003780824217,0.8981013605381762}
+ {}
+ {0.08527426384290426,0.06616981677920686}
+ {0.1809935627858228,0.9105400064668479,0.34989620485343464}
+ {0.7139255282088908,0.8803672957879083}
+ {}
+ {0.45650965657162845,0.6253950409407578}
+ {0.30472421749069234,0.8833976124588141}
+(10 rows)
+
+select setseed(0.8);
+ setseed 
+---------
+ 
+(1 row)
+
+select rand_array(10, 0, 3, 50::numeric, 80::numeric);
+ rand_array 
+------------
+ {57,58,61}
+ {}
+ {50,50,78}
+ {}
+ {52,52}
+ {55,79,61}
+ {72,78}
+ {}
+ {64,70}
+ {59,78}
+(10 rows)
+
 --
 -- crosstab()
 --
diff --git a/contrib/tablefunc/sql/tablefunc.sql b/contrib/tablefunc/sql/tablefunc.sql
index 0fb8e40de2..39e51e8024 100644
--- a/contrib/tablefunc/sql/tablefunc.sql
+++ b/contrib/tablefunc/sql/tablefunc.sql
@@ -8,6 +8,18 @@ SELECT avg(normal_rand)::int, count(*) FROM normal_rand(100, 250, 0.2);
 -- negative number of tuples
 SELECT avg(normal_rand)::int, count(*) FROM normal_rand(-1, 250, 0.2);
 
+select setseed(0.8);
+select rand_array(10, 0, 3, 50::int, 80::int);
+select setseed(0.8);
+select rand_array(10, 0, 3, 50::bigint, 80::bigint);
+select setseed(0.8);
+select rand_array(10, 0, 3, 50::float8, 80::float8);
+select setseed(0.8);
+select rand_array(10, 0, 3, 50::float4, 80::float4);
+select setseed(0.8);
+select rand_array(10, 0, 3, 50::numeric, 80::numeric);
+
+
 --
 -- crosstab()
 --
diff --git a/contrib/tablefunc/tablefunc--1.0--1.1.sql b/contrib/tablefunc/tablefunc--1.0--1.1.sql
new file mode 100644
index 0000000000..973f6aa68c
--- /dev/null
+++ b/contrib/tablefunc/tablefunc--1.0--1.1.sql
@@ -0,0 +1,22 @@
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION tablefunc UPDATE TO '1.1'" to load this file. \quit
+
+CREATE FUNCTION rand_array(numvals int, minlen int, maxlen int, minval int, maxval int)
+RETURNS setof int[]
+AS 'MODULE_PATHNAME','rand_array_int'
+LANGUAGE C VOLATILE STRICT;
+
+CREATE FUNCTION rand_array(numvals int, minlen int, maxlen int, minval bigint, maxval bigint)
+RETURNS setof bigint[]
+AS 'MODULE_PATHNAME','rand_array_bigint'
+LANGUAGE C VOLATILE STRICT;
+
+CREATE FUNCTION rand_array(numvals int, minlen int, maxlen int, minval float8, maxval float8)
+RETURNS setof float8[]
+AS 'MODULE_PATHNAME','rand_array_float8'
+LANGUAGE C VOLATILE STRICT;
+
+CREATE FUNCTION rand_array(numvals int, minlen int, maxlen int, minval numeric, maxval numeric)
+RETURNS setof numeric[]
+AS 'MODULE_PATHNAME','rand_array_numeric'
+LANGUAGE C VOLATILE STRICT;
diff --git a/contrib/tablefunc/tablefunc.c b/contrib/tablefunc/tablefunc.c
index 7d1b5f5143..21d36bf101 100644
--- a/contrib/tablefunc/tablefunc.c
+++ b/contrib/tablefunc/tablefunc.c
@@ -42,7 +42,9 @@
 #include "lib/stringinfo.h"
 #include "miscadmin.h"
 #include "tablefunc.h"
+#include "utils/array.h"
 #include "utils/builtins.h"
+#include "utils/fmgroids.h"
 
 PG_MODULE_MAGIC;
 
@@ -91,6 +93,12 @@ typedef struct
 	bool		use_carry;		/* use second generated value */
 } normal_rand_fctx;
 
+typedef struct
+{
+	FunctionCallInfo random_val_fcinfo;
+	FunctionCallInfo random_len_fcinfo;
+} rand_array_fctx;
+
 #define xpfree(var_) \
 	do { \
 		if (var_ != NULL) \
@@ -269,6 +277,160 @@ normal_rand(PG_FUNCTION_ARGS)
 		SRF_RETURN_DONE(funcctx);
 }
 
+static Datum
+rand_array_internal(FunctionCallInfo fcinfo, Oid datatype)
+{
+	FuncCallContext *funcctx;
+	Oid random_fn_oid;
+	int num_tuples = PG_GETARG_INT16(0);
+	int minlen = PG_GETARG_INT16(1);
+	int maxlen = PG_GETARG_INT16(2);
+	Datum minval = PG_GETARG_DATUM(3),
+		maxval = PG_GETARG_DATUM(4);
+	rand_array_fctx *fctx;
+
+	if (datatype == INT4OID)
+		random_fn_oid = F_RANDOM_INT4_INT4;
+	else if (datatype == INT8OID)
+		random_fn_oid = F_RANDOM_INT8_INT8;
+	else if	(datatype == FLOAT8OID)
+		random_fn_oid = F_RANDOM_;
+	else if (datatype == NUMERICOID)
+		random_fn_oid = F_RANDOM_NUMERIC_NUMERIC;
+	else
+		elog(ERROR, "unsupported type %d for rand_array function.",
+			 datatype);
+
+	if (num_tuples < 0)
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+				 errmsg("number of rows cannot be negative")));
+
+	if (minlen >= maxlen)
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+				 errmsg("minlen must be greater than maxlen.")));
+
+	if (minlen < 0)
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+				 errmsg("minlen and maxlen must be greater than zero.")));
+
+
+	/* stuff done only on the first call of the function */
+	if (SRF_IS_FIRSTCALL())
+	{
+		MemoryContext oldcontext;
+
+		FmgrInfo	*random_len_flinfo, *random_val_flinfo;
+		FunctionCallInfo random_len_fcinfo, random_val_fcinfo;
+
+		/* create a function context for cross-call persistence */
+		funcctx = SRF_FIRSTCALL_INIT();
+
+		/*
+		 * switch to memory context appropriate for multiple function calls
+		 */
+		oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
+
+		funcctx->max_calls = num_tuples;
+
+		/* allocate memory for user context */
+		fctx = (rand_array_fctx *) palloc(sizeof(rand_array_fctx));
+
+		/* build the random_len_fcinfo */
+		random_len_fcinfo = (FunctionCallInfo) palloc0(SizeForFunctionCallInfo(2));
+		random_len_flinfo = (FmgrInfo *) palloc0(sizeof(FmgrInfo));
+		fmgr_info(F_RANDOM_INT4_INT4, random_len_flinfo);
+		InitFunctionCallInfoData(*random_len_fcinfo, random_len_flinfo, 2,
+								 InvalidOid, NULL, NULL);
+
+		random_len_fcinfo->args[0].isnull = false;
+		random_len_fcinfo->args[1].isnull = false;
+		random_len_fcinfo->args[0].value = minlen;
+		random_len_fcinfo->args[1].value = maxlen;
+
+		/* build the random_val_fcinfo */
+		random_val_fcinfo = (FunctionCallInfo) palloc0(SizeForFunctionCallInfo(2));
+		random_val_flinfo = (FmgrInfo *) palloc0(sizeof(FmgrInfo));
+		fmgr_info(random_fn_oid, random_val_flinfo);
+		InitFunctionCallInfoData(*random_val_fcinfo, random_val_flinfo, 2,
+								 InvalidOid, NULL, NULL);
+
+		random_val_fcinfo->args[0].isnull = false;
+		random_val_fcinfo->args[1].isnull = false;
+		random_val_fcinfo->args[0].value = minval;
+		random_val_fcinfo->args[1].value = maxval;
+
+		fctx->random_val_fcinfo = random_val_fcinfo;
+		fctx->random_len_fcinfo = random_len_fcinfo;
+
+		funcctx->user_fctx = fctx;
+
+		MemoryContextSwitchTo(oldcontext);
+	}
+
+	/* stuff done on every call of the function */
+	funcctx = SRF_PERCALL_SETUP();
+
+	fctx = funcctx->user_fctx;
+
+	if (funcctx->call_cntr < funcctx->max_calls)
+	{
+		int		array_len;
+		int		i;
+		Datum	*results;
+
+		array_len = Int32GetDatum(FunctionCallInvoke(fctx->random_len_fcinfo));
+
+		results = palloc(array_len * sizeof(Datum));
+
+		for(i = 0; i < array_len; i++)
+			results[i] = FunctionCallInvoke(fctx->random_val_fcinfo);
+
+
+		SRF_RETURN_NEXT(funcctx, PointerGetDatum(
+							construct_array_builtin(results, array_len, datatype)));
+	}
+	else
+		/* do when there is no more left */
+		SRF_RETURN_DONE(funcctx);
+}
+
+
+PG_FUNCTION_INFO_V1(rand_array_int);
+Datum
+rand_array_int(PG_FUNCTION_ARGS)
+{
+	return rand_array_internal(fcinfo, INT4OID);
+}
+
+
+PG_FUNCTION_INFO_V1(rand_array_bigint);
+Datum
+rand_array_bigint(PG_FUNCTION_ARGS)
+{
+
+	return rand_array_internal(fcinfo, INT8OID);
+}
+
+
+PG_FUNCTION_INFO_V1(rand_array_float8);
+Datum
+rand_array_float8(PG_FUNCTION_ARGS)
+{
+	return rand_array_internal(fcinfo, FLOAT8OID);
+}
+
+
+PG_FUNCTION_INFO_V1(rand_array_numeric);
+Datum
+rand_array_numeric(PG_FUNCTION_ARGS)
+{
+	return rand_array_internal(fcinfo, NUMERICOID);
+}
+
+
 /*
  * get_normal_pair()
  * Assigns normally distributed (Gaussian) values to a pair of provided
diff --git a/contrib/tablefunc/tablefunc.control b/contrib/tablefunc/tablefunc.control
index 7b25d16170..9cc6222a4f 100644
--- a/contrib/tablefunc/tablefunc.control
+++ b/contrib/tablefunc/tablefunc.control
@@ -1,6 +1,6 @@
 # tablefunc extension
 comment = 'functions that manipulate whole tables, including crosstab'
-default_version = '1.0'
+default_version = '1.1'
 module_pathname = '$libdir/tablefunc'
 relocatable = true
 trusted = true
diff --git a/doc/src/sgml/tablefunc.sgml b/doc/src/sgml/tablefunc.sgml
index e10fe7009d..ae71374e91 100644
--- a/doc/src/sgml/tablefunc.sgml
+++ b/doc/src/sgml/tablefunc.sgml
@@ -53,6 +53,17 @@
        </para></entry>
       </row>
 
+      <row>
+       <entry role="func_table_entry"><para role="func_signature">
+        <function>rand_array</function> (<parameter>numvals</parameter> <type>integer</type>,<parameter>minlen</parameter> <type>int4</type>, <parameter>maxlen</parameter> <type>int4</type>,<parameter>minval</parameter> <type><replaceable>numeric_type</replaceable></type>, <parameter>maxval</parameter> <type><replaceable>numeric_type</replaceable></type> )
+        <returnvalue>setof <replaceable>numeric_type</replaceable>[]</returnvalue>
+       </para>
+       <para>
+        Produces a set of random numeric_type[], uses the same deterministic pseudo-random number generator as random().
+       </para>
+       </entry>
+      </row>
+
       <row>
        <entry role="func_table_entry"><para role="func_signature">
         <function>crosstab</function> ( <parameter>sql</parameter> <type>text</type> )
diff --git a/src/backend/utils/adt/arrayfuncs.c b/src/backend/utils/adt/arrayfuncs.c
index e5c7e57a5d..0165d97d45 100644
--- a/src/backend/utils/adt/arrayfuncs.c
+++ b/src/backend/utils/adt/arrayfuncs.c
@@ -3404,6 +3404,12 @@ construct_array_builtin(Datum *elems, int nelems, Oid elmtype)
 			elmalign = TYPALIGN_INT;
 			break;
 
+		case FLOAT8OID:
+			elmlen = sizeof(float8);
+			elmbyval = FLOAT8PASSBYVAL;
+			elmalign = TYPALIGN_DOUBLE;
+			break;
+
 		case INT2OID:
 			elmlen = sizeof(int16);
 			elmbyval = true;
@@ -3436,6 +3442,7 @@ construct_array_builtin(Datum *elems, int nelems, Oid elmtype)
 			break;
 
 		case TEXTOID:
+		case NUMERICOID:
 			elmlen = -1;
 			elmbyval = false;
 			elmalign = TYPALIGN_INT;
-- 
2.45.1

Reply via email to