/src/postgres/src/backend/utils/adt/pseudorandomfuncs.c
Line | Count | Source |
1 | | /*------------------------------------------------------------------------- |
2 | | * |
3 | | * pseudorandomfuncs.c |
4 | | * Functions giving SQL access to a pseudorandom number generator. |
5 | | * |
6 | | * Portions Copyright (c) 1996-2026, 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 | 0 | do { \ |
35 | 0 | if ((rmin) > (rmax)) \ |
36 | 0 | ereport(ERROR, \ |
37 | 0 | errcode(ERRCODE_INVALID_PARAMETER_VALUE), \ |
38 | 0 | errmsg("lower bound must be less than or equal to upper bound")); \ |
39 | 0 | } 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 | | initialize_prng(void) |
48 | 0 | { |
49 | 0 | if (unlikely(!prng_seed_set)) |
50 | 0 | { |
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 | 0 | if (unlikely(!pg_prng_strong_seed(&prng_state))) |
57 | 0 | { |
58 | 0 | TimestampTz now = GetCurrentTimestamp(); |
59 | 0 | 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 | 0 | } |
65 | 0 | prng_seed_set = true; |
66 | 0 | } |
67 | 0 | } |
68 | | |
69 | | /* |
70 | | * setseed() - |
71 | | * |
72 | | * Seed the PRNG from a specified value in the range [-1.0, 1.0]. |
73 | | */ |
74 | | Datum |
75 | | setseed(PG_FUNCTION_ARGS) |
76 | 0 | { |
77 | 0 | float8 seed = PG_GETARG_FLOAT8(0); |
78 | |
|
79 | 0 | if (seed < -1 || seed > 1 || isnan(seed)) |
80 | 0 | ereport(ERROR, |
81 | 0 | errcode(ERRCODE_INVALID_PARAMETER_VALUE), |
82 | 0 | errmsg("setseed parameter %g is out of allowed range [-1,1]", |
83 | 0 | seed)); |
84 | | |
85 | 0 | pg_prng_fseed(&prng_state, seed); |
86 | 0 | prng_seed_set = true; |
87 | |
|
88 | 0 | PG_RETURN_VOID(); |
89 | 0 | } |
90 | | |
91 | | /* |
92 | | * drandom() - |
93 | | * |
94 | | * Returns a random number chosen uniformly in the range [0.0, 1.0). |
95 | | */ |
96 | | Datum |
97 | | drandom(PG_FUNCTION_ARGS) |
98 | 0 | { |
99 | 0 | float8 result; |
100 | |
|
101 | 0 | initialize_prng(); |
102 | | |
103 | | /* pg_prng_double produces desired result range [0.0, 1.0) */ |
104 | 0 | result = pg_prng_double(&prng_state); |
105 | |
|
106 | 0 | PG_RETURN_FLOAT8(result); |
107 | 0 | } |
108 | | |
109 | | /* |
110 | | * drandom_normal() - |
111 | | * |
112 | | * Returns a random number from a normal distribution. |
113 | | */ |
114 | | Datum |
115 | | drandom_normal(PG_FUNCTION_ARGS) |
116 | 0 | { |
117 | 0 | float8 mean = PG_GETARG_FLOAT8(0); |
118 | 0 | float8 stddev = PG_GETARG_FLOAT8(1); |
119 | 0 | float8 result, |
120 | 0 | z; |
121 | |
|
122 | 0 | initialize_prng(); |
123 | | |
124 | | /* Get random value from standard normal(mean = 0.0, stddev = 1.0) */ |
125 | 0 | z = pg_prng_double_normal(&prng_state); |
126 | | /* Transform the normal standard variable (z) */ |
127 | | /* using the target normal distribution parameters */ |
128 | 0 | result = (stddev * z) + mean; |
129 | |
|
130 | 0 | PG_RETURN_FLOAT8(result); |
131 | 0 | } |
132 | | |
133 | | /* |
134 | | * int4random() - |
135 | | * |
136 | | * Returns a random 32-bit integer chosen uniformly in the specified range. |
137 | | */ |
138 | | Datum |
139 | | int4random(PG_FUNCTION_ARGS) |
140 | 0 | { |
141 | 0 | int32 rmin = PG_GETARG_INT32(0); |
142 | 0 | int32 rmax = PG_GETARG_INT32(1); |
143 | 0 | int32 result; |
144 | |
|
145 | 0 | CHECK_RANGE_BOUNDS(rmin, rmax); |
146 | | |
147 | 0 | initialize_prng(); |
148 | |
|
149 | 0 | result = (int32) pg_prng_int64_range(&prng_state, rmin, rmax); |
150 | |
|
151 | 0 | PG_RETURN_INT32(result); |
152 | 0 | } |
153 | | |
154 | | /* |
155 | | * int8random() - |
156 | | * |
157 | | * Returns a random 64-bit integer chosen uniformly in the specified range. |
158 | | */ |
159 | | Datum |
160 | | int8random(PG_FUNCTION_ARGS) |
161 | 0 | { |
162 | 0 | int64 rmin = PG_GETARG_INT64(0); |
163 | 0 | int64 rmax = PG_GETARG_INT64(1); |
164 | 0 | int64 result; |
165 | |
|
166 | 0 | CHECK_RANGE_BOUNDS(rmin, rmax); |
167 | | |
168 | 0 | initialize_prng(); |
169 | |
|
170 | 0 | result = pg_prng_int64_range(&prng_state, rmin, rmax); |
171 | |
|
172 | 0 | PG_RETURN_INT64(result); |
173 | 0 | } |
174 | | |
175 | | /* |
176 | | * numeric_random() - |
177 | | * |
178 | | * Returns a random numeric value chosen uniformly in the specified range. |
179 | | */ |
180 | | Datum |
181 | | numeric_random(PG_FUNCTION_ARGS) |
182 | 0 | { |
183 | 0 | Numeric rmin = PG_GETARG_NUMERIC(0); |
184 | 0 | Numeric rmax = PG_GETARG_NUMERIC(1); |
185 | 0 | Numeric result; |
186 | | |
187 | | /* Leave range bound checking to random_numeric() */ |
188 | |
|
189 | 0 | initialize_prng(); |
190 | |
|
191 | 0 | result = random_numeric(&prng_state, rmin, rmax); |
192 | |
|
193 | 0 | PG_RETURN_NUMERIC(result); |
194 | 0 | } |
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 | 0 | { |
205 | 0 | int32 rmin = (int32) PG_GETARG_DATEADT(0); |
206 | 0 | int32 rmax = (int32) PG_GETARG_DATEADT(1); |
207 | 0 | DateADT result; |
208 | |
|
209 | 0 | CHECK_RANGE_BOUNDS(rmin, rmax); |
210 | | |
211 | 0 | if (DATE_IS_NOBEGIN(rmin) || DATE_IS_NOEND(rmax)) |
212 | 0 | ereport(ERROR, |
213 | 0 | errcode(ERRCODE_INVALID_PARAMETER_VALUE), |
214 | 0 | errmsg("lower and upper bounds must be finite")); |
215 | | |
216 | 0 | initialize_prng(); |
217 | |
|
218 | 0 | result = (DateADT) pg_prng_int64_range(&prng_state, rmin, rmax); |
219 | |
|
220 | 0 | PG_RETURN_DATEADT(result); |
221 | 0 | } |
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 | 0 | { |
231 | 0 | int64 rmin = (int64) PG_GETARG_TIMESTAMP(0); |
232 | 0 | int64 rmax = (int64) PG_GETARG_TIMESTAMP(1); |
233 | 0 | Timestamp result; |
234 | |
|
235 | 0 | CHECK_RANGE_BOUNDS(rmin, rmax); |
236 | | |
237 | 0 | if (TIMESTAMP_IS_NOBEGIN(rmin) || TIMESTAMP_IS_NOEND(rmax)) |
238 | 0 | ereport(ERROR, |
239 | 0 | errcode(ERRCODE_INVALID_PARAMETER_VALUE), |
240 | 0 | errmsg("lower and upper bounds must be finite")); |
241 | | |
242 | 0 | initialize_prng(); |
243 | |
|
244 | 0 | result = (Timestamp) pg_prng_int64_range(&prng_state, rmin, rmax); |
245 | |
|
246 | 0 | PG_RETURN_TIMESTAMP(result); |
247 | 0 | } |
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 | 0 | { |
257 | 0 | int64 rmin = (int64) PG_GETARG_TIMESTAMPTZ(0); |
258 | 0 | int64 rmax = (int64) PG_GETARG_TIMESTAMPTZ(1); |
259 | 0 | TimestampTz result; |
260 | |
|
261 | 0 | CHECK_RANGE_BOUNDS(rmin, rmax); |
262 | | |
263 | 0 | if (TIMESTAMP_IS_NOBEGIN(rmin) || TIMESTAMP_IS_NOEND(rmax)) |
264 | 0 | ereport(ERROR, |
265 | 0 | errcode(ERRCODE_INVALID_PARAMETER_VALUE), |
266 | 0 | errmsg("lower and upper bounds must be finite")); |
267 | | |
268 | 0 | initialize_prng(); |
269 | |
|
270 | 0 | result = (TimestampTz) pg_prng_int64_range(&prng_state, rmin, rmax); |
271 | |
|
272 | 0 | PG_RETURN_TIMESTAMPTZ(result); |
273 | 0 | } |