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 34 1342216 : initialize_prng(void) 35 : { 36 1342216 : 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 140 : if (unlikely(!pg_prng_strong_seed(&prng_state))) 44 : { 45 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 : } 52 140 : prng_seed_set = true; 53 : } 54 1342216 : } 55 : 56 : /* 57 : * setseed() - 58 : * 59 : * Seed the PRNG from a specified value in the range [-1.0, 1.0]. 60 : */ 61 : Datum 62 8 : setseed(PG_FUNCTION_ARGS) 63 : { 64 8 : float8 seed = PG_GETARG_FLOAT8(0); 65 : 66 8 : if (seed < -1 || seed > 1 || isnan(seed)) 67 0 : ereport(ERROR, 68 : errcode(ERRCODE_INVALID_PARAMETER_VALUE), 69 : errmsg("setseed parameter %g is out of allowed range [-1,1]", 70 : seed)); 71 : 72 8 : pg_prng_fseed(&prng_state, seed); 73 8 : prng_seed_set = true; 74 : 75 8 : 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 1187242 : drandom(PG_FUNCTION_ARGS) 85 : { 86 : float8 result; 87 : 88 1187242 : initialize_prng(); 89 : 90 : /* pg_prng_double produces desired result range [0.0, 1.0) */ 91 1187242 : result = pg_prng_double(&prng_state); 92 : 93 1187242 : PG_RETURN_FLOAT8(result); 94 : } 95 : 96 : /* 97 : * drandom_normal() - 98 : * 99 : * Returns a random number from a normal distribution. 100 : */ 101 : Datum 102 25320 : drandom_normal(PG_FUNCTION_ARGS) 103 : { 104 25320 : float8 mean = PG_GETARG_FLOAT8(0); 105 25320 : float8 stddev = PG_GETARG_FLOAT8(1); 106 : float8 result, 107 : z; 108 : 109 25320 : initialize_prng(); 110 : 111 : /* Get random value from standard normal(mean = 0.0, stddev = 1.0) */ 112 25320 : z = pg_prng_double_normal(&prng_state); 113 : /* Transform the normal standard variable (z) */ 114 : /* using the target normal distribution parameters */ 115 25320 : result = (stddev * z) + mean; 116 : 117 25320 : 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 51132 : int4random(PG_FUNCTION_ARGS) 127 : { 128 51132 : int32 rmin = PG_GETARG_INT32(0); 129 51132 : int32 rmax = PG_GETARG_INT32(1); 130 : int32 result; 131 : 132 51132 : if (rmin > rmax) 133 6 : ereport(ERROR, 134 : errcode(ERRCODE_INVALID_PARAMETER_VALUE), 135 : errmsg("lower bound must be less than or equal to upper bound")); 136 : 137 51126 : initialize_prng(); 138 : 139 51126 : result = (int32) pg_prng_int64_range(&prng_state, rmin, rmax); 140 : 141 51126 : 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 45072 : int8random(PG_FUNCTION_ARGS) 151 : { 152 45072 : int64 rmin = PG_GETARG_INT64(0); 153 45072 : int64 rmax = PG_GETARG_INT64(1); 154 : int64 result; 155 : 156 45072 : if (rmin > rmax) 157 6 : ereport(ERROR, 158 : errcode(ERRCODE_INVALID_PARAMETER_VALUE), 159 : errmsg("lower bound must be less than or equal to upper bound")); 160 : 161 45066 : initialize_prng(); 162 : 163 45066 : result = pg_prng_int64_range(&prng_state, rmin, rmax); 164 : 165 45066 : 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 33462 : numeric_random(PG_FUNCTION_ARGS) 175 : { 176 33462 : Numeric rmin = PG_GETARG_NUMERIC(0); 177 33462 : Numeric rmax = PG_GETARG_NUMERIC(1); 178 : Numeric result; 179 : 180 33462 : initialize_prng(); 181 : 182 33462 : result = random_numeric(&prng_state, rmin, rmax); 183 : 184 33432 : PG_RETURN_NUMERIC(result); 185 : }