/src/postgres/src/backend/utils/adt/pseudorandomfuncs.c
Line | Count | Source (jump to first uncovered line) |
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/fmgrprotos.h" |
21 | | #include "utils/numeric.h" |
22 | | #include "utils/timestamp.h" |
23 | | |
24 | | /* Shared PRNG state used by all the random functions */ |
25 | | static pg_prng_state prng_state; |
26 | | static bool prng_seed_set = false; |
27 | | |
28 | | /* |
29 | | * initialize_prng() - |
30 | | * |
31 | | * Initialize (seed) the PRNG, if not done yet in this process. |
32 | | */ |
33 | | static void |
34 | | initialize_prng(void) |
35 | 0 | { |
36 | 0 | if (unlikely(!prng_seed_set)) |
37 | 0 | { |
38 | | /* |
39 | | * If possible, seed the PRNG using high-quality random bits. Should |
40 | | * that fail for some reason, we fall back on a lower-quality seed |
41 | | * based on current time and PID. |
42 | | */ |
43 | 0 | if (unlikely(!pg_prng_strong_seed(&prng_state))) |
44 | 0 | { |
45 | 0 | TimestampTz now = GetCurrentTimestamp(); |
46 | 0 | uint64 iseed; |
47 | | |
48 | | /* Mix the PID with the most predictable bits of the timestamp */ |
49 | 0 | iseed = (uint64) now ^ ((uint64) MyProcPid << 32); |
50 | 0 | pg_prng_seed(&prng_state, iseed); |
51 | 0 | } |
52 | 0 | prng_seed_set = true; |
53 | 0 | } |
54 | 0 | } |
55 | | |
56 | | /* |
57 | | * setseed() - |
58 | | * |
59 | | * Seed the PRNG from a specified value in the range [-1.0, 1.0]. |
60 | | */ |
61 | | Datum |
62 | | setseed(PG_FUNCTION_ARGS) |
63 | 0 | { |
64 | 0 | float8 seed = PG_GETARG_FLOAT8(0); |
65 | |
|
66 | 0 | if (seed < -1 || seed > 1 || isnan(seed)) |
67 | 0 | ereport(ERROR, |
68 | 0 | errcode(ERRCODE_INVALID_PARAMETER_VALUE), |
69 | 0 | errmsg("setseed parameter %g is out of allowed range [-1,1]", |
70 | 0 | seed)); |
71 | | |
72 | 0 | pg_prng_fseed(&prng_state, seed); |
73 | 0 | prng_seed_set = true; |
74 | |
|
75 | 0 | PG_RETURN_VOID(); |
76 | 0 | } |
77 | | |
78 | | /* |
79 | | * drandom() - |
80 | | * |
81 | | * Returns a random number chosen uniformly in the range [0.0, 1.0). |
82 | | */ |
83 | | Datum |
84 | | drandom(PG_FUNCTION_ARGS) |
85 | 0 | { |
86 | 0 | float8 result; |
87 | |
|
88 | 0 | initialize_prng(); |
89 | | |
90 | | /* pg_prng_double produces desired result range [0.0, 1.0) */ |
91 | 0 | result = pg_prng_double(&prng_state); |
92 | |
|
93 | 0 | PG_RETURN_FLOAT8(result); |
94 | 0 | } |
95 | | |
96 | | /* |
97 | | * drandom_normal() - |
98 | | * |
99 | | * Returns a random number from a normal distribution. |
100 | | */ |
101 | | Datum |
102 | | drandom_normal(PG_FUNCTION_ARGS) |
103 | 0 | { |
104 | 0 | float8 mean = PG_GETARG_FLOAT8(0); |
105 | 0 | float8 stddev = PG_GETARG_FLOAT8(1); |
106 | 0 | float8 result, |
107 | 0 | z; |
108 | |
|
109 | 0 | initialize_prng(); |
110 | | |
111 | | /* Get random value from standard normal(mean = 0.0, stddev = 1.0) */ |
112 | 0 | z = pg_prng_double_normal(&prng_state); |
113 | | /* Transform the normal standard variable (z) */ |
114 | | /* using the target normal distribution parameters */ |
115 | 0 | result = (stddev * z) + mean; |
116 | |
|
117 | 0 | PG_RETURN_FLOAT8(result); |
118 | 0 | } |
119 | | |
120 | | /* |
121 | | * int4random() - |
122 | | * |
123 | | * Returns a random 32-bit integer chosen uniformly in the specified range. |
124 | | */ |
125 | | Datum |
126 | | int4random(PG_FUNCTION_ARGS) |
127 | 0 | { |
128 | 0 | int32 rmin = PG_GETARG_INT32(0); |
129 | 0 | int32 rmax = PG_GETARG_INT32(1); |
130 | 0 | int32 result; |
131 | |
|
132 | 0 | if (rmin > rmax) |
133 | 0 | ereport(ERROR, |
134 | 0 | errcode(ERRCODE_INVALID_PARAMETER_VALUE), |
135 | 0 | errmsg("lower bound must be less than or equal to upper bound")); |
136 | | |
137 | 0 | initialize_prng(); |
138 | |
|
139 | 0 | result = (int32) pg_prng_int64_range(&prng_state, rmin, rmax); |
140 | |
|
141 | 0 | PG_RETURN_INT32(result); |
142 | 0 | } |
143 | | |
144 | | /* |
145 | | * int8random() - |
146 | | * |
147 | | * Returns a random 64-bit integer chosen uniformly in the specified range. |
148 | | */ |
149 | | Datum |
150 | | int8random(PG_FUNCTION_ARGS) |
151 | 0 | { |
152 | 0 | int64 rmin = PG_GETARG_INT64(0); |
153 | 0 | int64 rmax = PG_GETARG_INT64(1); |
154 | 0 | int64 result; |
155 | |
|
156 | 0 | if (rmin > rmax) |
157 | 0 | ereport(ERROR, |
158 | 0 | errcode(ERRCODE_INVALID_PARAMETER_VALUE), |
159 | 0 | errmsg("lower bound must be less than or equal to upper bound")); |
160 | | |
161 | 0 | initialize_prng(); |
162 | |
|
163 | 0 | result = pg_prng_int64_range(&prng_state, rmin, rmax); |
164 | |
|
165 | 0 | PG_RETURN_INT64(result); |
166 | 0 | } |
167 | | |
168 | | /* |
169 | | * numeric_random() - |
170 | | * |
171 | | * Returns a random numeric value chosen uniformly in the specified range. |
172 | | */ |
173 | | Datum |
174 | | numeric_random(PG_FUNCTION_ARGS) |
175 | 0 | { |
176 | 0 | Numeric rmin = PG_GETARG_NUMERIC(0); |
177 | 0 | Numeric rmax = PG_GETARG_NUMERIC(1); |
178 | 0 | Numeric result; |
179 | |
|
180 | 0 | initialize_prng(); |
181 | |
|
182 | 0 | result = random_numeric(&prng_state, rmin, rmax); |
183 | |
|
184 | 0 | PG_RETURN_NUMERIC(result); |
185 | 0 | } |