Age Owner Branch data TLA 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
580 dean.a.rasheed@gmail 47 :CBC 1194715 : initialize_prng(void)
48 : : {
49 [ + + ]: 1194715 : 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 [ + - - + ]: 87 : if (unlikely(!pg_prng_strong_seed(&prng_state)))
57 : : {
580 dean.a.rasheed@gmail 58 :UBC 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 : : }
580 dean.a.rasheed@gmail 65 :CBC 87 : prng_seed_set = true;
66 : : }
67 : 1194715 : }
68 : :
69 : : /*
70 : : * setseed() -
71 : : *
72 : : * Seed the PRNG from a specified value in the range [-1.0, 1.0].
73 : : */
74 : : Datum
75 : 7 : setseed(PG_FUNCTION_ARGS)
76 : : {
77 : 7 : float8 seed = PG_GETARG_FLOAT8(0);
78 : :
79 [ + - + - : 7 : if (seed < -1 || seed > 1 || isnan(seed))
- + ]
580 dean.a.rasheed@gmail 80 [ # # ]:UBC 0 : ereport(ERROR,
81 : : errcode(ERRCODE_INVALID_PARAMETER_VALUE),
82 : : errmsg("setseed parameter %g is out of allowed range [-1,1]",
83 : : seed));
84 : :
580 dean.a.rasheed@gmail 85 :CBC 7 : pg_prng_fseed(&prng_state, seed);
86 : 7 : prng_seed_set = true;
87 : :
88 : 7 : 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 : 1117189 : drandom(PG_FUNCTION_ARGS)
98 : : {
99 : : float8 result;
100 : :
101 : 1117189 : initialize_prng();
102 : :
103 : : /* pg_prng_double produces desired result range [0.0, 1.0) */
104 : 1117189 : result = pg_prng_double(&prng_state);
105 : :
106 : 1117189 : PG_RETURN_FLOAT8(result);
107 : : }
108 : :
109 : : /*
110 : : * drandom_normal() -
111 : : *
112 : : * Returns a random number from a normal distribution.
113 : : */
114 : : Datum
115 : 12660 : drandom_normal(PG_FUNCTION_ARGS)
116 : : {
117 : 12660 : float8 mean = PG_GETARG_FLOAT8(0);
118 : 12660 : float8 stddev = PG_GETARG_FLOAT8(1);
119 : : float8 result,
120 : : z;
121 : :
122 : 12660 : initialize_prng();
123 : :
124 : : /* Get random value from standard normal(mean = 0.0, stddev = 1.0) */
125 : 12660 : z = pg_prng_double_normal(&prng_state);
126 : : /* Transform the normal standard variable (z) */
127 : : /* using the target normal distribution parameters */
128 : 12660 : result = (stddev * z) + mean;
129 : :
130 : 12660 : 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 : 25572 : int4random(PG_FUNCTION_ARGS)
140 : : {
141 : 25572 : int32 rmin = PG_GETARG_INT32(0);
142 : 25572 : int32 rmax = PG_GETARG_INT32(1);
143 : : int32 result;
144 : :
49 dean.a.rasheed@gmail 145 [ + + + - ]:GNC 25572 : CHECK_RANGE_BOUNDS(rmin, rmax);
146 : :
580 dean.a.rasheed@gmail 147 :CBC 25569 : initialize_prng();
148 : :
149 : 25569 : result = (int32) pg_prng_int64_range(&prng_state, rmin, rmax);
150 : :
151 : 25569 : 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 : 22536 : int8random(PG_FUNCTION_ARGS)
161 : : {
162 : 22536 : int64 rmin = PG_GETARG_INT64(0);
163 : 22536 : int64 rmax = PG_GETARG_INT64(1);
164 : : int64 result;
165 : :
49 dean.a.rasheed@gmail 166 [ + + + - ]:GNC 22536 : CHECK_RANGE_BOUNDS(rmin, rmax);
167 : :
580 dean.a.rasheed@gmail 168 :CBC 22533 : initialize_prng();
169 : :
170 : 22533 : result = pg_prng_int64_range(&prng_state, rmin, rmax);
171 : :
172 : 22533 : 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 : 16731 : numeric_random(PG_FUNCTION_ARGS)
182 : : {
183 : 16731 : Numeric rmin = PG_GETARG_NUMERIC(0);
184 : 16731 : Numeric rmax = PG_GETARG_NUMERIC(1);
185 : : Numeric result;
186 : :
187 : : /* Leave range bound checking to random_numeric() */
188 : :
189 : 16731 : initialize_prng();
190 : :
191 : 16731 : result = random_numeric(&prng_state, rmin, rmax);
192 : :
193 : 16716 : 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
49 dean.a.rasheed@gmail 203 :GNC 18 : date_random(PG_FUNCTION_ARGS)
204 : : {
205 : 18 : int32 rmin = (int32) PG_GETARG_DATEADT(0);
206 : 18 : int32 rmax = (int32) PG_GETARG_DATEADT(1);
207 : : DateADT result;
208 : :
209 [ + + + - ]: 18 : CHECK_RANGE_BOUNDS(rmin, rmax);
210 : :
211 [ + + + + ]: 15 : if (DATE_IS_NOBEGIN(rmin) || DATE_IS_NOEND(rmax))
212 [ + - ]: 6 : ereport(ERROR,
213 : : errcode(ERRCODE_INVALID_PARAMETER_VALUE),
214 : : errmsg("lower and upper bounds must be finite"));
215 : :
216 : 9 : initialize_prng();
217 : :
218 : 9 : result = (DateADT) pg_prng_int64_range(&prng_state, rmin, rmax);
219 : :
220 : 9 : 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 : 21 : timestamp_random(PG_FUNCTION_ARGS)
230 : : {
231 : 21 : int64 rmin = (int64) PG_GETARG_TIMESTAMP(0);
232 : 21 : int64 rmax = (int64) PG_GETARG_TIMESTAMP(1);
233 : : Timestamp result;
234 : :
235 [ + + + - ]: 21 : CHECK_RANGE_BOUNDS(rmin, rmax);
236 : :
237 [ + + + + ]: 18 : if (TIMESTAMP_IS_NOBEGIN(rmin) || TIMESTAMP_IS_NOEND(rmax))
238 [ + - ]: 6 : ereport(ERROR,
239 : : errcode(ERRCODE_INVALID_PARAMETER_VALUE),
240 : : errmsg("lower and upper bounds must be finite"));
241 : :
242 : 12 : initialize_prng();
243 : :
244 : 12 : result = (Timestamp) pg_prng_int64_range(&prng_state, rmin, rmax);
245 : :
246 : 12 : 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 : 21 : timestamptz_random(PG_FUNCTION_ARGS)
256 : : {
257 : 21 : int64 rmin = (int64) PG_GETARG_TIMESTAMPTZ(0);
258 : 21 : int64 rmax = (int64) PG_GETARG_TIMESTAMPTZ(1);
259 : : TimestampTz result;
260 : :
261 [ + + + - ]: 21 : CHECK_RANGE_BOUNDS(rmin, rmax);
262 : :
263 [ + + + + ]: 18 : if (TIMESTAMP_IS_NOBEGIN(rmin) || TIMESTAMP_IS_NOEND(rmax))
264 [ + - ]: 6 : ereport(ERROR,
265 : : errcode(ERRCODE_INVALID_PARAMETER_VALUE),
266 : : errmsg("lower and upper bounds must be finite"));
267 : :
268 : 12 : initialize_prng();
269 : :
270 : 12 : result = (TimestampTz) pg_prng_int64_range(&prng_state, rmin, rmax);
271 : :
272 : 12 : PG_RETURN_TIMESTAMPTZ(result);
273 : : }
|