Skip to content

Commit afa9aab

Browse files
deanrasheedCommitfest Bot
authored andcommitted
Add date and timestamp variants of random(min, max).
This adds 3 new variants of the random() function: random(min date, max date) returns date random(min timestamp, max timestamp) returns timestamp random(min timestamptz, max timestamptz) returns timestamptz Each returns a random value x in the range min <= x <= max. Author: Damien Clochard <[email protected]> Reviewed-by: Greg Sabino Mullane <[email protected]> Reviewed-by: Dean Rasheed <[email protected]> Reviewed-by: Vik Fearing <[email protected]> Reviewed-by: Chao Li <[email protected]> Discussion: https://postgr.es/m/[email protected]
1 parent 06473f5 commit afa9aab

File tree

6 files changed

+253
-9
lines changed

6 files changed

+253
-9
lines changed

doc/src/sgml/func/func-datetime.sgml

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -928,6 +928,36 @@
928928
</para></entry>
929929
</row>
930930

931+
<row>
932+
<entry role="func_table_entry"><para role="func_signature">
933+
<indexterm>
934+
<primary>random</primary>
935+
</indexterm>
936+
<function>random</function> ( <parameter>min</parameter> <type>date</type>, <parameter>max</parameter> <type>date</type> )
937+
<returnvalue>date</returnvalue>
938+
</para>
939+
<para role="func_signature">
940+
<function>random</function> ( <parameter>min</parameter> <type>timestamp</type>, <parameter>max</parameter> <type>timestamp</type> )
941+
<returnvalue>timestamp</returnvalue>
942+
</para>
943+
<para role="func_signature">
944+
<function>random</function> ( <parameter>min</parameter> <type>timestamptz</type>, <parameter>max</parameter> <type>timestamptz</type> )
945+
<returnvalue>timestamptz</returnvalue>
946+
</para>
947+
<para>
948+
Returns a random value in the range
949+
<parameter>min</parameter> &lt;= x &lt;= <parameter>max</parameter>.
950+
</para>
951+
<para>
952+
<literal>random('1979-02-08'::date,'2025-07-03'::date)</literal>
953+
<returnvalue>1983-04-21</returnvalue>
954+
</para>
955+
<para>
956+
<literal>random('2000-01-01'::timestamptz, now())</literal>
957+
<returnvalue>2015-09-27 09:11:33.732707+00</returnvalue>
958+
</para></entry>
959+
</row>
960+
931961
<row>
932962
<entry role="func_table_entry"><para role="func_signature">
933963
<indexterm>

doc/src/sgml/func/func-math.sgml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1151,7 +1151,8 @@
11511151

11521152
<para>
11531153
The <function>random()</function> and <function>random_normal()</function>
1154-
functions listed in <xref linkend="functions-math-random-table"/> use a
1154+
functions listed in <xref linkend="functions-math-random-table"/> and
1155+
<xref linkend="functions-datetime-table"/> use a
11551156
deterministic pseudo-random number generator.
11561157
It is fast but not suitable for cryptographic
11571158
applications; see the <xref linkend="pgcrypto"/> module for a more

src/backend/utils/adt/pseudorandomfuncs.c

Lines changed: 96 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717

1818
#include "common/pg_prng.h"
1919
#include "miscadmin.h"
20+
#include "utils/date.h"
2021
#include "utils/fmgrprotos.h"
2122
#include "utils/numeric.h"
2223
#include "utils/timestamp.h"
@@ -25,6 +26,18 @@
2526
static pg_prng_state prng_state;
2627
static bool prng_seed_set = false;
2728

29+
/*
30+
* Macro for checking the range bounds of random(min, max) functions. Throws
31+
* an error if they're the wrong way round.
32+
*/
33+
#define CHECK_RANGE_BOUNDS(rmin, rmax) \
34+
do { \
35+
if ((rmin) > (rmax)) \
36+
ereport(ERROR, \
37+
errcode(ERRCODE_INVALID_PARAMETER_VALUE), \
38+
errmsg("lower bound must be less than or equal to upper bound")); \
39+
} while (0)
40+
2841
/*
2942
* initialize_prng() -
3043
*
@@ -129,10 +142,7 @@ int4random(PG_FUNCTION_ARGS)
129142
int32 rmax = PG_GETARG_INT32(1);
130143
int32 result;
131144

132-
if (rmin > rmax)
133-
ereport(ERROR,
134-
errcode(ERRCODE_INVALID_PARAMETER_VALUE),
135-
errmsg("lower bound must be less than or equal to upper bound"));
145+
CHECK_RANGE_BOUNDS(rmin, rmax);
136146

137147
initialize_prng();
138148

@@ -153,10 +163,7 @@ int8random(PG_FUNCTION_ARGS)
153163
int64 rmax = PG_GETARG_INT64(1);
154164
int64 result;
155165

156-
if (rmin > rmax)
157-
ereport(ERROR,
158-
errcode(ERRCODE_INVALID_PARAMETER_VALUE),
159-
errmsg("lower bound must be less than or equal to upper bound"));
166+
CHECK_RANGE_BOUNDS(rmin, rmax);
160167

161168
initialize_prng();
162169

@@ -177,9 +184,90 @@ numeric_random(PG_FUNCTION_ARGS)
177184
Numeric rmax = PG_GETARG_NUMERIC(1);
178185
Numeric result;
179186

187+
/* Leave range bound checking to random_numeric() */
188+
180189
initialize_prng();
181190

182191
result = random_numeric(&prng_state, rmin, rmax);
183192

184193
PG_RETURN_NUMERIC(result);
185194
}
195+
196+
197+
/*
198+
* date_random() -
199+
*
200+
* Returns a random date chosen uniformly in the specified range.
201+
*/
202+
Datum
203+
date_random(PG_FUNCTION_ARGS)
204+
{
205+
int32 rmin = (int32) PG_GETARG_DATEADT(0);
206+
int32 rmax = (int32) PG_GETARG_DATEADT(1);
207+
DateADT result;
208+
209+
CHECK_RANGE_BOUNDS(rmin, rmax);
210+
211+
if (DATE_IS_NOBEGIN(rmin) || DATE_IS_NOEND(rmax))
212+
ereport(ERROR,
213+
errcode(ERRCODE_INVALID_PARAMETER_VALUE),
214+
errmsg("lower and upper bounds must be finite"));
215+
216+
initialize_prng();
217+
218+
result = (DateADT) pg_prng_int64_range(&prng_state, rmin, rmax);
219+
220+
PG_RETURN_DATEADT(result);
221+
}
222+
223+
/*
224+
* timestamp_random() -
225+
*
226+
* Returns a random timestamp chosen uniformly in the specified range.
227+
*/
228+
Datum
229+
timestamp_random(PG_FUNCTION_ARGS)
230+
{
231+
int64 rmin = (int64) PG_GETARG_TIMESTAMP(0);
232+
int64 rmax = (int64) PG_GETARG_TIMESTAMP(1);
233+
Timestamp result;
234+
235+
CHECK_RANGE_BOUNDS(rmin, rmax);
236+
237+
if (TIMESTAMP_IS_NOBEGIN(rmin) || TIMESTAMP_IS_NOEND(rmax))
238+
ereport(ERROR,
239+
errcode(ERRCODE_INVALID_PARAMETER_VALUE),
240+
errmsg("lower and upper bounds must be finite"));
241+
242+
initialize_prng();
243+
244+
result = (Timestamp) pg_prng_int64_range(&prng_state, rmin, rmax);
245+
246+
PG_RETURN_TIMESTAMP(result);
247+
}
248+
249+
/*
250+
* timestamptz_random() -
251+
*
252+
* Returns a random timestamptz chosen uniformly in the specified range.
253+
*/
254+
Datum
255+
timestamptz_random(PG_FUNCTION_ARGS)
256+
{
257+
int64 rmin = (int64) PG_GETARG_TIMESTAMPTZ(0);
258+
int64 rmax = (int64) PG_GETARG_TIMESTAMPTZ(1);
259+
TimestampTz result;
260+
261+
CHECK_RANGE_BOUNDS(rmin, rmax);
262+
263+
if (TIMESTAMP_IS_NOBEGIN(rmin) || TIMESTAMP_IS_NOEND(rmax))
264+
ereport(ERROR,
265+
errcode(ERRCODE_INVALID_PARAMETER_VALUE),
266+
errmsg("lower and upper bounds must be finite"));
267+
268+
initialize_prng();
269+
270+
result = (TimestampTz) pg_prng_int64_range(&prng_state, rmin, rmax);
271+
272+
PG_RETURN_TIMESTAMPTZ(result);
273+
}

src/include/catalog/pg_proc.dat

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3503,6 +3503,18 @@
35033503
proname => 'random', provolatile => 'v', proparallel => 'r',
35043504
prorettype => 'numeric', proargtypes => 'numeric numeric',
35053505
proargnames => '{min,max}', prosrc => 'numeric_random' },
3506+
{ oid => '6431', descr => 'random date in range',
3507+
proname => 'random', provolatile => 'v', proparallel => 'r',
3508+
prorettype => 'date', proargtypes => 'date date',
3509+
proargnames => '{min,max}', prosrc => 'date_random' },
3510+
{ oid => '6432', descr => 'random timestamp in range',
3511+
proname => 'random', provolatile => 'v', proparallel => 'r',
3512+
prorettype => 'timestamp', proargtypes => 'timestamp timestamp',
3513+
proargnames => '{min,max}', prosrc => 'timestamp_random' },
3514+
{ oid => '6433', descr => 'random timestamptz in range',
3515+
proname => 'random', provolatile => 'v', proparallel => 'r',
3516+
prorettype => 'timestamptz', proargtypes => 'timestamptz timestamptz',
3517+
proargnames => '{min,max}', prosrc => 'timestamptz_random' },
35063518
{ oid => '1599', descr => 'set random seed',
35073519
proname => 'setseed', provolatile => 'v', proparallel => 'r',
35083520
prorettype => 'void', proargtypes => 'float8', prosrc => 'setseed' },

src/test/regress/expected/random.out

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -536,3 +536,90 @@ SELECT n, random(0, trim_scale(abs(1 - 10.0^(-n)))) FROM generate_series(-20, 20
536536
20 | 0.60795101234744211935
537537
(41 rows)
538538

539+
-- random dates
540+
SELECT random('1979-02-08'::date,'2025-07-03'::date) AS random_date_multiple_years;
541+
random_date_multiple_years
542+
----------------------------
543+
04-09-1986
544+
(1 row)
545+
546+
SELECT random('4714-11-24 BC'::date,'5874897-12-31 AD'::date) AS random_date_maximum_range;
547+
random_date_maximum_range
548+
---------------------------
549+
10-02-2898131
550+
(1 row)
551+
552+
SELECT random('1979-02-08'::date,'1979-02-08'::date) AS random_date_empty_range;
553+
random_date_empty_range
554+
-------------------------
555+
02-08-1979
556+
(1 row)
557+
558+
SELECT random('2024-12-31'::date, '2024-01-01'::date); -- fail
559+
ERROR: lower bound must be less than or equal to upper bound
560+
SELECT random('-infinity'::date, '2024-01-01'::date); -- fail
561+
ERROR: lower and upper bounds must be finite
562+
SELECT random('2024-12-31'::date, 'infinity'::date); -- fail
563+
ERROR: lower and upper bounds must be finite
564+
-- random timestamps
565+
SELECT random('1979-02-08'::timestamp,'2025-07-03'::timestamp) AS random_timestamp_multiple_years;
566+
random_timestamp_multiple_years
567+
---------------------------------
568+
Fri Jan 27 18:52:05.366009 2017
569+
(1 row)
570+
571+
SELECT random('4714-11-24 BC'::timestamp,'294276-12-31 23:59:59.999999'::timestamp) AS random_timestamp_maximum_range;
572+
random_timestamp_maximum_range
573+
-----------------------------------
574+
Wed Mar 28 00:45:36.180395 226694
575+
(1 row)
576+
577+
SELECT random('2024-07-01 12:00:00.000001'::timestamp, '2024-07-01 12:00:00.999999'::timestamp) AS random_narrow_range;
578+
random_narrow_range
579+
---------------------------------
580+
Mon Jul 01 12:00:00.999286 2024
581+
(1 row)
582+
583+
SELECT random('1979-02-08'::timestamp,'1979-02-08'::timestamp) AS random_timestamp_empty_range;
584+
random_timestamp_empty_range
585+
------------------------------
586+
Thu Feb 08 00:00:00 1979
587+
(1 row)
588+
589+
SELECT random('2024-12-31'::timestamp, '2024-01-01'::timestamp); -- fail
590+
ERROR: lower bound must be less than or equal to upper bound
591+
SELECT random('-infinity'::timestamp, '2024-01-01'::timestamp); -- fail
592+
ERROR: lower and upper bounds must be finite
593+
SELECT random('2024-12-31'::timestamp, 'infinity'::timestamp); -- fail
594+
ERROR: lower and upper bounds must be finite
595+
-- random timestamps with timezone
596+
SELECT random('1979-02-08 +01'::timestamptz,'2025-07-03 +02'::timestamptz) AS random_timestamptz_multiple_years;
597+
random_timestamptz_multiple_years
598+
-------------------------------------
599+
Tue Jun 14 04:41:16.652896 2016 PDT
600+
(1 row)
601+
602+
SELECT random('4714-11-24 BC +00'::timestamptz,'294276-12-31 23:59:59.999999 +00'::timestamptz) AS random_timestamptz_maximum_range;
603+
random_timestamptz_maximum_range
604+
--------------------------------------
605+
Wed Mar 26 14:07:16.980265 31603 PDT
606+
(1 row)
607+
608+
SELECT random('2024-07-01 12:00:00.000001 +04'::timestamptz, '2024-07-01 12:00:00.999999 +04'::timestamptz) AS random_timestamptz_narrow_range;
609+
random_timestamptz_narrow_range
610+
-------------------------------------
611+
Mon Jul 01 01:00:00.835808 2024 PDT
612+
(1 row)
613+
614+
SELECT random('1979-02-08 +05'::timestamptz,'1979-02-08 +05'::timestamptz) AS random_timestamptz_empty_range;
615+
random_timestamptz_empty_range
616+
--------------------------------
617+
Wed Feb 07 11:00:00 1979 PST
618+
(1 row)
619+
620+
SELECT random('2024-01-01 +06'::timestamptz, '2024-01-01 +07'::timestamptz); -- fail
621+
ERROR: lower bound must be less than or equal to upper bound
622+
SELECT random('-infinity'::timestamptz, '2024-01-01 +07'::timestamptz); -- fail
623+
ERROR: lower and upper bounds must be finite
624+
SELECT random('2024-01-01 +06'::timestamptz, 'infinity'::timestamptz); -- fail
625+
ERROR: lower and upper bounds must be finite

src/test/regress/sql/random.sql

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -277,3 +277,29 @@ SELECT random(-1e30, 1e30) FROM generate_series(1, 10);
277277
SELECT random(-0.4, 0.4) FROM generate_series(1, 10);
278278
SELECT random(0, 1 - 1e-30) FROM generate_series(1, 10);
279279
SELECT n, random(0, trim_scale(abs(1 - 10.0^(-n)))) FROM generate_series(-20, 20) n;
280+
281+
-- random dates
282+
SELECT random('1979-02-08'::date,'2025-07-03'::date) AS random_date_multiple_years;
283+
SELECT random('4714-11-24 BC'::date,'5874897-12-31 AD'::date) AS random_date_maximum_range;
284+
SELECT random('1979-02-08'::date,'1979-02-08'::date) AS random_date_empty_range;
285+
SELECT random('2024-12-31'::date, '2024-01-01'::date); -- fail
286+
SELECT random('-infinity'::date, '2024-01-01'::date); -- fail
287+
SELECT random('2024-12-31'::date, 'infinity'::date); -- fail
288+
289+
-- random timestamps
290+
SELECT random('1979-02-08'::timestamp,'2025-07-03'::timestamp) AS random_timestamp_multiple_years;
291+
SELECT random('4714-11-24 BC'::timestamp,'294276-12-31 23:59:59.999999'::timestamp) AS random_timestamp_maximum_range;
292+
SELECT random('2024-07-01 12:00:00.000001'::timestamp, '2024-07-01 12:00:00.999999'::timestamp) AS random_narrow_range;
293+
SELECT random('1979-02-08'::timestamp,'1979-02-08'::timestamp) AS random_timestamp_empty_range;
294+
SELECT random('2024-12-31'::timestamp, '2024-01-01'::timestamp); -- fail
295+
SELECT random('-infinity'::timestamp, '2024-01-01'::timestamp); -- fail
296+
SELECT random('2024-12-31'::timestamp, 'infinity'::timestamp); -- fail
297+
298+
-- random timestamps with timezone
299+
SELECT random('1979-02-08 +01'::timestamptz,'2025-07-03 +02'::timestamptz) AS random_timestamptz_multiple_years;
300+
SELECT random('4714-11-24 BC +00'::timestamptz,'294276-12-31 23:59:59.999999 +00'::timestamptz) AS random_timestamptz_maximum_range;
301+
SELECT random('2024-07-01 12:00:00.000001 +04'::timestamptz, '2024-07-01 12:00:00.999999 +04'::timestamptz) AS random_timestamptz_narrow_range;
302+
SELECT random('1979-02-08 +05'::timestamptz,'1979-02-08 +05'::timestamptz) AS random_timestamptz_empty_range;
303+
SELECT random('2024-01-01 +06'::timestamptz, '2024-01-01 +07'::timestamptz); -- fail
304+
SELECT random('-infinity'::timestamptz, '2024-01-01 +07'::timestamptz); -- fail
305+
SELECT random('2024-01-01 +06'::timestamptz, 'infinity'::timestamptz); -- fail

0 commit comments

Comments
 (0)