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-2024, 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
18 dean.a.rasheed@gmail 34 :GNC 667336 : initialize_prng(void)
35 : : {
36 [ + + ]: 667336 : if (unlikely(!prng_seed_set))
37 : : {
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 [ + - - + ]: 69 : if (unlikely(!pg_prng_strong_seed(&prng_state)))
44 : : {
18 dean.a.rasheed@gmail 45 :UNC 0 : TimestampTz now = GetCurrentTimestamp();
46 : : 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 : : }
18 dean.a.rasheed@gmail 52 :GNC 69 : prng_seed_set = true;
53 : : }
54 : 667336 : }
55 : :
56 : : /*
57 : : * setseed() -
58 : : *
59 : : * Seed the PRNG from a specified value in the range [-1.0, 1.0].
60 : : */
61 : : Datum
62 : 4 : setseed(PG_FUNCTION_ARGS)
63 : : {
64 : 4 : float8 seed = PG_GETARG_FLOAT8(0);
65 : :
66 [ + - + - : 4 : if (seed < -1 || seed > 1 || isnan(seed))
- + ]
18 dean.a.rasheed@gmail 67 [ # # ]:UNC 0 : ereport(ERROR,
68 : : errcode(ERRCODE_INVALID_PARAMETER_VALUE),
69 : : errmsg("setseed parameter %g is out of allowed range [-1,1]",
70 : : seed));
71 : :
18 dean.a.rasheed@gmail 72 :GNC 4 : pg_prng_fseed(&prng_state, seed);
73 : 4 : prng_seed_set = true;
74 : :
75 : 4 : PG_RETURN_VOID();
76 : : }
77 : :
78 : : /*
79 : : * drandom() -
80 : : *
81 : : * Returns a random number chosen uniformly in the range [0.0, 1.0).
82 : : */
83 : : Datum
84 : 589849 : drandom(PG_FUNCTION_ARGS)
85 : : {
86 : : float8 result;
87 : :
88 : 589849 : initialize_prng();
89 : :
90 : : /* pg_prng_double produces desired result range [0.0, 1.0) */
91 : 589849 : result = pg_prng_double(&prng_state);
92 : :
93 : 589849 : PG_RETURN_FLOAT8(result);
94 : : }
95 : :
96 : : /*
97 : : * drandom_normal() -
98 : : *
99 : : * Returns a random number from a normal distribution.
100 : : */
101 : : Datum
102 : 12660 : drandom_normal(PG_FUNCTION_ARGS)
103 : : {
104 : 12660 : float8 mean = PG_GETARG_FLOAT8(0);
105 : 12660 : float8 stddev = PG_GETARG_FLOAT8(1);
106 : : float8 result,
107 : : z;
108 : :
109 : 12660 : initialize_prng();
110 : :
111 : : /* Get random value from standard normal(mean = 0.0, stddev = 1.0) */
112 : 12660 : z = pg_prng_double_normal(&prng_state);
113 : : /* Transform the normal standard variable (z) */
114 : : /* using the target normal distribution parameters */
115 : 12660 : result = (stddev * z) + mean;
116 : :
117 : 12660 : PG_RETURN_FLOAT8(result);
118 : : }
119 : :
120 : : /*
121 : : * int4random() -
122 : : *
123 : : * Returns a random 32-bit integer chosen uniformly in the specified range.
124 : : */
125 : : Datum
126 : 25566 : int4random(PG_FUNCTION_ARGS)
127 : : {
128 : 25566 : int32 rmin = PG_GETARG_INT32(0);
129 : 25566 : int32 rmax = PG_GETARG_INT32(1);
130 : : int32 result;
131 : :
132 [ + + ]: 25566 : if (rmin > rmax)
133 [ + - ]: 3 : ereport(ERROR,
134 : : errcode(ERRCODE_INVALID_PARAMETER_VALUE),
135 : : errmsg("lower bound must be less than or equal to upper bound"));
136 : :
137 : 25563 : initialize_prng();
138 : :
139 : 25563 : result = (int32) pg_prng_int64_range(&prng_state, rmin, rmax);
140 : :
141 : 25563 : PG_RETURN_INT32(result);
142 : : }
143 : :
144 : : /*
145 : : * int8random() -
146 : : *
147 : : * Returns a random 64-bit integer chosen uniformly in the specified range.
148 : : */
149 : : Datum
150 : 22536 : int8random(PG_FUNCTION_ARGS)
151 : : {
152 : 22536 : int64 rmin = PG_GETARG_INT64(0);
153 : 22536 : int64 rmax = PG_GETARG_INT64(1);
154 : : int64 result;
155 : :
156 [ + + ]: 22536 : if (rmin > rmax)
157 [ + - ]: 3 : ereport(ERROR,
158 : : errcode(ERRCODE_INVALID_PARAMETER_VALUE),
159 : : errmsg("lower bound must be less than or equal to upper bound"));
160 : :
161 : 22533 : initialize_prng();
162 : :
163 : 22533 : result = pg_prng_int64_range(&prng_state, rmin, rmax);
164 : :
165 : 22533 : PG_RETURN_INT64(result);
166 : : }
167 : :
168 : : /*
169 : : * numeric_random() -
170 : : *
171 : : * Returns a random numeric value chosen uniformly in the specified range.
172 : : */
173 : : Datum
174 : 16731 : numeric_random(PG_FUNCTION_ARGS)
175 : : {
176 : 16731 : Numeric rmin = PG_GETARG_NUMERIC(0);
177 : 16731 : Numeric rmax = PG_GETARG_NUMERIC(1);
178 : : Numeric result;
179 : :
180 : 16731 : initialize_prng();
181 : :
182 : 16731 : result = random_numeric(&prng_state, rmin, rmax);
183 : :
184 : 16716 : PG_RETURN_NUMERIC(result);
185 : : }
|