Line data Source code
1 : /*-------------------------------------------------------------------------
2 : *
3 : * pseudorandomfuncs.c
4 : * Functions giving SQL access to a pseudorandom number generator.
5 : *
6 : * Portions Copyright (c) 1996-2025, PostgreSQL Global Development Group
7 : * Portions Copyright (c) 1994, Regents of the University of California
8 : *
9 : * IDENTIFICATION
10 : * src/backend/utils/adt/pseudorandomfuncs.c
11 : *
12 : *-------------------------------------------------------------------------
13 : */
14 : #include "postgres.h"
15 :
16 : #include <math.h>
17 :
18 : #include "common/pg_prng.h"
19 : #include "miscadmin.h"
20 : #include "utils/date.h"
21 : #include "utils/fmgrprotos.h"
22 : #include "utils/numeric.h"
23 : #include "utils/timestamp.h"
24 :
25 : /* Shared PRNG state used by all the random functions */
26 : static pg_prng_state prng_state;
27 : static bool prng_seed_set = false;
28 :
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 :
41 : /*
42 : * initialize_prng() -
43 : *
44 : * Initialize (seed) the PRNG, if not done yet in this process.
45 : */
46 : static void
47 2571830 : initialize_prng(void)
48 : {
49 2571830 : if (unlikely(!prng_seed_set))
50 : {
51 : /*
52 : * If possible, seed the PRNG using high-quality random bits. Should
53 : * that fail for some reason, we fall back on a lower-quality seed
54 : * based on current time and PID.
55 : */
56 178 : if (unlikely(!pg_prng_strong_seed(&prng_state)))
57 : {
58 0 : TimestampTz now = GetCurrentTimestamp();
59 : uint64 iseed;
60 :
61 : /* Mix the PID with the most predictable bits of the timestamp */
62 0 : iseed = (uint64) now ^ ((uint64) MyProcPid << 32);
63 0 : pg_prng_seed(&prng_state, iseed);
64 : }
65 178 : prng_seed_set = true;
66 : }
67 2571830 : }
68 :
69 : /*
70 : * setseed() -
71 : *
72 : * Seed the PRNG from a specified value in the range [-1.0, 1.0].
73 : */
74 : Datum
75 14 : setseed(PG_FUNCTION_ARGS)
76 : {
77 14 : float8 seed = PG_GETARG_FLOAT8(0);
78 :
79 14 : if (seed < -1 || seed > 1 || isnan(seed))
80 0 : ereport(ERROR,
81 : errcode(ERRCODE_INVALID_PARAMETER_VALUE),
82 : errmsg("setseed parameter %g is out of allowed range [-1,1]",
83 : seed));
84 :
85 14 : pg_prng_fseed(&prng_state, seed);
86 14 : prng_seed_set = true;
87 :
88 14 : PG_RETURN_VOID();
89 : }
90 :
91 : /*
92 : * drandom() -
93 : *
94 : * Returns a random number chosen uniformly in the range [0.0, 1.0).
95 : */
96 : Datum
97 2416778 : drandom(PG_FUNCTION_ARGS)
98 : {
99 : float8 result;
100 :
101 2416778 : initialize_prng();
102 :
103 : /* pg_prng_double produces desired result range [0.0, 1.0) */
104 2416778 : result = pg_prng_double(&prng_state);
105 :
106 2416778 : PG_RETURN_FLOAT8(result);
107 : }
108 :
109 : /*
110 : * drandom_normal() -
111 : *
112 : * Returns a random number from a normal distribution.
113 : */
114 : Datum
115 25320 : drandom_normal(PG_FUNCTION_ARGS)
116 : {
117 25320 : float8 mean = PG_GETARG_FLOAT8(0);
118 25320 : float8 stddev = PG_GETARG_FLOAT8(1);
119 : float8 result,
120 : z;
121 :
122 25320 : initialize_prng();
123 :
124 : /* Get random value from standard normal(mean = 0.0, stddev = 1.0) */
125 25320 : z = pg_prng_double_normal(&prng_state);
126 : /* Transform the normal standard variable (z) */
127 : /* using the target normal distribution parameters */
128 25320 : result = (stddev * z) + mean;
129 :
130 25320 : PG_RETURN_FLOAT8(result);
131 : }
132 :
133 : /*
134 : * int4random() -
135 : *
136 : * Returns a random 32-bit integer chosen uniformly in the specified range.
137 : */
138 : Datum
139 51144 : int4random(PG_FUNCTION_ARGS)
140 : {
141 51144 : int32 rmin = PG_GETARG_INT32(0);
142 51144 : int32 rmax = PG_GETARG_INT32(1);
143 : int32 result;
144 :
145 51144 : CHECK_RANGE_BOUNDS(rmin, rmax);
146 :
147 51138 : initialize_prng();
148 :
149 51138 : result = (int32) pg_prng_int64_range(&prng_state, rmin, rmax);
150 :
151 51138 : PG_RETURN_INT32(result);
152 : }
153 :
154 : /*
155 : * int8random() -
156 : *
157 : * Returns a random 64-bit integer chosen uniformly in the specified range.
158 : */
159 : Datum
160 45072 : int8random(PG_FUNCTION_ARGS)
161 : {
162 45072 : int64 rmin = PG_GETARG_INT64(0);
163 45072 : int64 rmax = PG_GETARG_INT64(1);
164 : int64 result;
165 :
166 45072 : CHECK_RANGE_BOUNDS(rmin, rmax);
167 :
168 45066 : initialize_prng();
169 :
170 45066 : result = pg_prng_int64_range(&prng_state, rmin, rmax);
171 :
172 45066 : PG_RETURN_INT64(result);
173 : }
174 :
175 : /*
176 : * numeric_random() -
177 : *
178 : * Returns a random numeric value chosen uniformly in the specified range.
179 : */
180 : Datum
181 33462 : numeric_random(PG_FUNCTION_ARGS)
182 : {
183 33462 : Numeric rmin = PG_GETARG_NUMERIC(0);
184 33462 : Numeric rmax = PG_GETARG_NUMERIC(1);
185 : Numeric result;
186 :
187 : /* Leave range bound checking to random_numeric() */
188 :
189 33462 : initialize_prng();
190 :
191 33462 : result = random_numeric(&prng_state, rmin, rmax);
192 :
193 33432 : PG_RETURN_NUMERIC(result);
194 : }
195 :
196 :
197 : /*
198 : * date_random() -
199 : *
200 : * Returns a random date chosen uniformly in the specified range.
201 : */
202 : Datum
203 36 : date_random(PG_FUNCTION_ARGS)
204 : {
205 36 : int32 rmin = (int32) PG_GETARG_DATEADT(0);
206 36 : int32 rmax = (int32) PG_GETARG_DATEADT(1);
207 : DateADT result;
208 :
209 36 : CHECK_RANGE_BOUNDS(rmin, rmax);
210 :
211 30 : if (DATE_IS_NOBEGIN(rmin) || DATE_IS_NOEND(rmax))
212 12 : ereport(ERROR,
213 : errcode(ERRCODE_INVALID_PARAMETER_VALUE),
214 : errmsg("lower and upper bounds must be finite"));
215 :
216 18 : initialize_prng();
217 :
218 18 : result = (DateADT) pg_prng_int64_range(&prng_state, rmin, rmax);
219 :
220 18 : 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 42 : timestamp_random(PG_FUNCTION_ARGS)
230 : {
231 42 : int64 rmin = (int64) PG_GETARG_TIMESTAMP(0);
232 42 : int64 rmax = (int64) PG_GETARG_TIMESTAMP(1);
233 : Timestamp result;
234 :
235 42 : CHECK_RANGE_BOUNDS(rmin, rmax);
236 :
237 36 : if (TIMESTAMP_IS_NOBEGIN(rmin) || TIMESTAMP_IS_NOEND(rmax))
238 12 : ereport(ERROR,
239 : errcode(ERRCODE_INVALID_PARAMETER_VALUE),
240 : errmsg("lower and upper bounds must be finite"));
241 :
242 24 : initialize_prng();
243 :
244 24 : result = (Timestamp) pg_prng_int64_range(&prng_state, rmin, rmax);
245 :
246 24 : 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 42 : timestamptz_random(PG_FUNCTION_ARGS)
256 : {
257 42 : int64 rmin = (int64) PG_GETARG_TIMESTAMPTZ(0);
258 42 : int64 rmax = (int64) PG_GETARG_TIMESTAMPTZ(1);
259 : TimestampTz result;
260 :
261 42 : CHECK_RANGE_BOUNDS(rmin, rmax);
262 :
263 36 : if (TIMESTAMP_IS_NOBEGIN(rmin) || TIMESTAMP_IS_NOEND(rmax))
264 12 : ereport(ERROR,
265 : errcode(ERRCODE_INVALID_PARAMETER_VALUE),
266 : errmsg("lower and upper bounds must be finite"));
267 :
268 24 : initialize_prng();
269 :
270 24 : result = (TimestampTz) pg_prng_int64_range(&prng_state, rmin, rmax);
271 :
272 24 : PG_RETURN_TIMESTAMPTZ(result);
273 : }
|