Here is a v4, which:- moves the stuff to common and fully removes random/srandom (Tom) - includes a range generation function based on the bitmask method (Dean) but iterates with splitmix so that the state always advances once (Me)
And a v5 where an unused test file does also compile if we insist. -- Fabien.
diff --git a/contrib/file_fdw/file_fdw.c b/contrib/file_fdw/file_fdw.c index 2c2f149fb0..146b524076 100644 --- a/contrib/file_fdw/file_fdw.c +++ b/contrib/file_fdw/file_fdw.c @@ -1188,7 +1188,7 @@ file_acquire_sample_rows(Relation onerel, int elevel, * Found a suitable tuple, so save it, replacing one old tuple * at random */ - int k = (int) (targrows * sampler_random_fract(rstate.randstate)); + int k = (int) (targrows * sampler_random_fract(&rstate.randstate)); Assert(k >= 0 && k < targrows); heap_freetuple(rows[k]); diff --git a/contrib/postgres_fdw/postgres_fdw.c b/contrib/postgres_fdw/postgres_fdw.c index fafbab6b02..3009861e45 100644 --- a/contrib/postgres_fdw/postgres_fdw.c +++ b/contrib/postgres_fdw/postgres_fdw.c @@ -5152,7 +5152,7 @@ analyze_row_processor(PGresult *res, int row, PgFdwAnalyzeState *astate) if (astate->rowstoskip <= 0) { /* Choose a random reservoir element to replace. */ - pos = (int) (targrows * sampler_random_fract(astate->rstate.randstate)); + pos = (int) (targrows * sampler_random_fract(&astate->rstate.randstate)); Assert(pos >= 0 && pos < targrows); heap_freetuple(astate->rows[pos]); } diff --git a/contrib/tsm_system_rows/tsm_system_rows.c b/contrib/tsm_system_rows/tsm_system_rows.c index 4996612902..1a46d4b143 100644 --- a/contrib/tsm_system_rows/tsm_system_rows.c +++ b/contrib/tsm_system_rows/tsm_system_rows.c @@ -69,7 +69,7 @@ static BlockNumber system_rows_nextsampleblock(SampleScanState *node, BlockNumbe static OffsetNumber system_rows_nextsampletuple(SampleScanState *node, BlockNumber blockno, OffsetNumber maxoffset); -static uint32 random_relative_prime(uint32 n, SamplerRandomState randstate); +static uint32 random_relative_prime(uint32 n, pg_prng_state *randstate); /* @@ -213,25 +213,25 @@ system_rows_nextsampleblock(SampleScanState *node, BlockNumber nblocks) if (sampler->step == 0) { /* Initialize now that we have scan descriptor */ - SamplerRandomState randstate; + pg_prng_state randstate; /* If relation is empty, there's nothing to scan */ if (nblocks == 0) return InvalidBlockNumber; /* We only need an RNG during this setup step */ - sampler_random_init_state(sampler->seed, randstate); + sampler_random_init_state(sampler->seed, &randstate); /* Compute nblocks/firstblock/step only once per query */ sampler->nblocks = nblocks; /* Choose random starting block within the relation */ /* (Actually this is the predecessor of the first block visited) */ - sampler->firstblock = sampler_random_fract(randstate) * + sampler->firstblock = sampler_random_fract(&randstate) * sampler->nblocks; /* Find relative prime as step size for linear probing */ - sampler->step = random_relative_prime(sampler->nblocks, randstate); + sampler->step = random_relative_prime(sampler->nblocks, &randstate); } /* Reinitialize lb */ @@ -317,7 +317,7 @@ gcd(uint32 a, uint32 b) * (else return 1). */ static uint32 -random_relative_prime(uint32 n, SamplerRandomState randstate) +random_relative_prime(uint32 n, pg_prng_state *randstate) { uint32 r; diff --git a/contrib/tsm_system_time/tsm_system_time.c b/contrib/tsm_system_time/tsm_system_time.c index 788d8f9a68..36acc6c106 100644 --- a/contrib/tsm_system_time/tsm_system_time.c +++ b/contrib/tsm_system_time/tsm_system_time.c @@ -69,7 +69,7 @@ static BlockNumber system_time_nextsampleblock(SampleScanState *node, BlockNumbe static OffsetNumber system_time_nextsampletuple(SampleScanState *node, BlockNumber blockno, OffsetNumber maxoffset); -static uint32 random_relative_prime(uint32 n, SamplerRandomState randstate); +static uint32 random_relative_prime(uint32 n, pg_prng_state *randstate); /* @@ -224,25 +224,25 @@ system_time_nextsampleblock(SampleScanState *node, BlockNumber nblocks) if (sampler->step == 0) { /* Initialize now that we have scan descriptor */ - SamplerRandomState randstate; + pg_prng_state randstate; /* If relation is empty, there's nothing to scan */ if (nblocks == 0) return InvalidBlockNumber; /* We only need an RNG during this setup step */ - sampler_random_init_state(sampler->seed, randstate); + sampler_random_init_state(sampler->seed, &randstate); /* Compute nblocks/firstblock/step only once per query */ sampler->nblocks = nblocks; /* Choose random starting block within the relation */ /* (Actually this is the predecessor of the first block visited) */ - sampler->firstblock = sampler_random_fract(randstate) * + sampler->firstblock = sampler_random_fract(&randstate) * sampler->nblocks; /* Find relative prime as step size for linear probing */ - sampler->step = random_relative_prime(sampler->nblocks, randstate); + sampler->step = random_relative_prime(sampler->nblocks, &randstate); } /* Reinitialize lb and start_time */ @@ -330,7 +330,7 @@ gcd(uint32 a, uint32 b) * (else return 1). */ static uint32 -random_relative_prime(uint32 n, SamplerRandomState randstate) +random_relative_prime(uint32 n, pg_prng_state *randstate) { uint32 r; diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index 0c9591415e..1ddbeed2dc 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -1308,7 +1308,7 @@ acquire_sample_rows(Relation onerel, int elevel, * Found a suitable tuple, so save it, replacing one old * tuple at random */ - int k = (int) (targrows * sampler_random_fract(rstate.randstate)); + int k = (int) (targrows * sampler_random_fract(&rstate.randstate)); Assert(k >= 0 && k < targrows); heap_freetuple(rows[k]); diff --git a/src/backend/optimizer/geqo/geqo_random.c b/src/backend/optimizer/geqo/geqo_random.c index f21bc047e6..8b42a9ffba 100644 --- a/src/backend/optimizer/geqo/geqo_random.c +++ b/src/backend/optimizer/geqo/geqo_random.c @@ -21,14 +21,7 @@ geqo_set_seed(PlannerInfo *root, double seed) { GeqoPrivateData *private = (GeqoPrivateData *) root->join_search_private; - /* - * XXX. This seeding algorithm could certainly be improved - but it is not - * critical to do so. - */ - memset(private->random_state, 0, sizeof(private->random_state)); - memcpy(private->random_state, - &seed, - Min(sizeof(private->random_state), sizeof(seed))); + pg_prng_fseed(&private->random_state, seed); } double @@ -36,5 +29,5 @@ geqo_rand(PlannerInfo *root) { GeqoPrivateData *private = (GeqoPrivateData *) root->join_search_private; - return pg_erand48(private->random_state); + return pg_prng_f64(&private->random_state); } diff --git a/src/backend/utils/adt/float.c b/src/backend/utils/adt/float.c index 098bbb372b..9fc0b108dd 100644 --- a/src/backend/utils/adt/float.c +++ b/src/backend/utils/adt/float.c @@ -21,6 +21,7 @@ #include "catalog/pg_type.h" #include "common/int.h" +#include "common/pg_prng.h" #include "common/shortest_dec.h" #include "libpq/pqformat.h" #include "miscadmin.h" @@ -65,7 +66,7 @@ float8 degree_c_one = 1.0; /* State for drandom() and setseed() */ static bool drandom_seed_set = false; -static unsigned short drandom_seed[3] = {0, 0, 0}; +static pg_prng_state drandom_seed = PG_PRNG_DEFAULT_STATE; /* Local function prototypes */ static double sind_q1(double x); @@ -2762,22 +2763,20 @@ drandom(PG_FUNCTION_ARGS) * Should that fail for some reason, we fall back on a lower-quality * seed based on current time and PID. */ - if (!pg_strong_random(drandom_seed, sizeof(drandom_seed))) + if (!pg_strong_random(&drandom_seed, sizeof(drandom_seed))) { TimestampTz now = GetCurrentTimestamp(); uint64 iseed; /* Mix the PID with the most predictable bits of the timestamp */ iseed = (uint64) now ^ ((uint64) MyProcPid << 32); - drandom_seed[0] = (unsigned short) iseed; - drandom_seed[1] = (unsigned short) (iseed >> 16); - drandom_seed[2] = (unsigned short) (iseed >> 32); + pg_prng_seed(&drandom_seed, iseed); } drandom_seed_set = true; } - /* pg_erand48 produces desired result range [0.0 - 1.0) */ - result = pg_erand48(drandom_seed); + /* produces desired result range [0.0 - 1.0) */ + result = pg_prng_f64(&drandom_seed); PG_RETURN_FLOAT8(result); } @@ -2790,7 +2789,6 @@ Datum setseed(PG_FUNCTION_ARGS) { float8 seed = PG_GETARG_FLOAT8(0); - uint64 iseed; if (seed < -1 || seed > 1 || isnan(seed)) ereport(ERROR, @@ -2798,11 +2796,7 @@ setseed(PG_FUNCTION_ARGS) errmsg("setseed parameter %g is out of allowed range [-1,1]", seed))); - /* Use sign bit + 47 fractional bits to fill drandom_seed[] */ - iseed = (int64) (seed * (float8) UINT64CONST(0x7FFFFFFFFFFF)); - drandom_seed[0] = (unsigned short) iseed; - drandom_seed[1] = (unsigned short) (iseed >> 16); - drandom_seed[2] = (unsigned short) (iseed >> 32); + pg_prng_fseed(&drandom_seed, seed); drandom_seed_set = true; PG_RETURN_VOID(); diff --git a/src/backend/utils/misc/sampling.c b/src/backend/utils/misc/sampling.c index 0c327e823f..7b59128878 100644 --- a/src/backend/utils/misc/sampling.c +++ b/src/backend/utils/misc/sampling.c @@ -49,7 +49,7 @@ BlockSampler_Init(BlockSampler bs, BlockNumber nblocks, int samplesize, bs->t = 0; /* blocks scanned so far */ bs->m = 0; /* blocks selected so far */ - sampler_random_init_state(randseed, bs->randstate); + sampler_random_init_state(randseed, &bs->randstate); return Min(bs->n, bs->N); } @@ -98,7 +98,7 @@ BlockSampler_Next(BlockSampler bs) * less than k, which means that we cannot fail to select enough blocks. *---------- */ - V = sampler_random_fract(bs->randstate); + V = sampler_random_fract(&bs->randstate); p = 1.0 - (double) k / (double) K; while (V < p) { @@ -136,10 +136,10 @@ reservoir_init_selection_state(ReservoirState rs, int n) * Reservoir sampling is not used anywhere where it would need to return * repeatable results so we can initialize it randomly. */ - sampler_random_init_state(random(), rs->randstate); + sampler_random_init_state(random(), &rs->randstate); /* Initial value of W (for use when Algorithm Z is first applied) */ - rs->W = exp(-log(sampler_random_fract(rs->randstate)) / n); + rs->W = exp(-log(sampler_random_fract(&rs->randstate)) / n); } double @@ -154,7 +154,7 @@ reservoir_get_next_S(ReservoirState rs, double t, int n) double V, quot; - V = sampler_random_fract(rs->randstate); /* Generate V */ + V = sampler_random_fract(&rs->randstate); /* Generate V */ S = 0; t += 1; /* Note: "num" in Vitter's code is always equal to t - n */ @@ -186,7 +186,7 @@ reservoir_get_next_S(ReservoirState rs, double t, int n) tmp; /* Generate U and X */ - U = sampler_random_fract(rs->randstate); + U = sampler_random_fract(&rs->randstate); X = t * (W - 1.0); S = floor(X); /* S is tentatively set to floor(X) */ /* Test if U <= h(S)/cg(X) in the manner of (6.3) */ @@ -215,7 +215,7 @@ reservoir_get_next_S(ReservoirState rs, double t, int n) y *= numer / denom; denom -= 1; } - W = exp(-log(sampler_random_fract(rs->randstate)) / n); /* Generate W in advance */ + W = exp(-log(sampler_random_fract(&rs->randstate)) / n); /* Generate W in advance */ if (exp(log(y) / n) <= (t + X) / t) break; } @@ -230,24 +230,22 @@ reservoir_get_next_S(ReservoirState rs, double t, int n) *---------- */ void -sampler_random_init_state(long seed, SamplerRandomState randstate) +sampler_random_init_state(long seed, pg_prng_state *randstate) { - randstate[0] = 0x330e; /* same as pg_erand48, but could be anything */ - randstate[1] = (unsigned short) seed; - randstate[2] = (unsigned short) (seed >> 16); + pg_prng_seed(randstate, (uint64_t) seed); } /* Select a random value R uniformly distributed in (0 - 1) */ double -sampler_random_fract(SamplerRandomState randstate) +sampler_random_fract(pg_prng_state *randstate) { double res; - /* pg_erand48 returns a value in [0.0 - 1.0), so we must reject 0 */ + /* pg_prng_f64 returned value in [0.0 - 1.0), so we must reject 0.0 */ do { - res = pg_erand48(randstate); - } while (res == 0.0); + res = pg_prng_f64(randstate); + } while (unlikely(res == 0.0)); return res; } @@ -266,22 +264,28 @@ double anl_random_fract(void) { /* initialize if first time through */ - if (oldrs.randstate[0] == 0) - sampler_random_init_state(random(), oldrs.randstate); + if (!oldrs.randstate_initialized) + { + sampler_random_init_state(random(), &oldrs.randstate); + oldrs.randstate_initialized = true; + } /* and compute a random fraction */ - return sampler_random_fract(oldrs.randstate); + return sampler_random_fract(&oldrs.randstate); } double anl_init_selection_state(int n) { /* initialize if first time through */ - if (oldrs.randstate[0] == 0) - sampler_random_init_state(random(), oldrs.randstate); + if (!oldrs.randstate_initialized) + { + sampler_random_init_state(random(), &oldrs.randstate); + oldrs.randstate_initialized = true; + } /* Initial value of W (for use when Algorithm Z is first applied) */ - return exp(-log(sampler_random_fract(oldrs.randstate)) / n); + return exp(-log(sampler_random_fract(&oldrs.randstate)) / n); } double diff --git a/src/bin/pgbench/pgbench.c b/src/bin/pgbench/pgbench.c index 4aeccd93af..6d749bbbb6 100644 --- a/src/bin/pgbench/pgbench.c +++ b/src/bin/pgbench/pgbench.c @@ -58,6 +58,7 @@ #endif #include "common/int.h" +#include "common/pg_prng.h" #include "common/logging.h" #include "common/string.h" #include "common/username.h" @@ -343,16 +344,8 @@ typedef struct StatsData SimpleStats lag; } StatsData; -/* - * Struct to keep random state. - */ -typedef struct RandomState -{ - unsigned short xseed[3]; -} RandomState; - /* Various random sequences are initialized from this one. */ -static RandomState base_random_sequence; +static pg_prng_state base_random_sequence; /* Synchronization barrier for start and connection */ static THREAD_BARRIER_T barrier; @@ -454,7 +447,7 @@ typedef struct * Separate randomness for each client. This is used for random functions * PGBENCH_RANDOM_* during the execution of the script. */ - RandomState cs_func_rs; + pg_prng_state cs_func_rs; int use_file; /* index in sql_script for this client */ int command; /* command number in script */ @@ -491,9 +484,9 @@ typedef struct * random state to make all of them independent of each other and * therefore deterministic at the thread level. */ - RandomState ts_choose_rs; /* random state for selecting a script */ - RandomState ts_throttle_rs; /* random state for transaction throttling */ - RandomState ts_sample_rs; /* random state for log sampling */ + pg_prng_state ts_choose_rs; /* random state for selecting a script */ + pg_prng_state ts_throttle_rs; /* random state for transaction throttling */ + pg_prng_state ts_sample_rs; /* random state for log sampling */ int64 throttle_trigger; /* previous/next throttling (us) */ FILE *logfile; /* where to log, or NULL */ @@ -891,42 +884,28 @@ strtodouble(const char *str, bool errorOK, double *dv) } /* - * Initialize a random state struct. + * Initialize a prng state struct. * * We derive the seed from base_random_sequence, which must be set up already. */ static void -initRandomState(RandomState *random_state) +initRandomState(pg_prng_state *state) { - random_state->xseed[0] = (unsigned short) - (pg_jrand48(base_random_sequence.xseed) & 0xFFFF); - random_state->xseed[1] = (unsigned short) - (pg_jrand48(base_random_sequence.xseed) & 0xFFFF); - random_state->xseed[2] = (unsigned short) - (pg_jrand48(base_random_sequence.xseed) & 0xFFFF); + pg_prng_seed(state, pg_prng_u64(&base_random_sequence)); } + /* - * Random number generator: uniform distribution from min to max inclusive. + * PRNG: uniform distribution from min to max inclusive. * * Although the limits are expressed as int64, you can't generate the full * int64 range in one call, because the difference of the limits mustn't - * overflow int64. In practice it's unwise to ask for more than an int32 - * range, because of the limited precision of pg_erand48(). + * overflow int64. */ static int64 -getrand(RandomState *random_state, int64 min, int64 max) +getrand(pg_prng_state *state, int64 min, int64 max) { - /* - * Odd coding is so that min and max have approximately the same chance of - * being selected as do numbers between them. - * - * pg_erand48() is thread-safe and concurrent, which is why we use it - * rather than random(), which in glibc is non-reentrant, and therefore - * protected by a mutex, and therefore a bottleneck on machines with many - * CPUs. - */ - return min + (int64) ((max - min + 1) * pg_erand48(random_state->xseed)); + return min + (int64) pg_prng_u64_range(state, max - min + 1); } /* @@ -935,8 +914,7 @@ getrand(RandomState *random_state, int64 min, int64 max) * value is exp(-parameter). */ static int64 -getExponentialRand(RandomState *random_state, int64 min, int64 max, - double parameter) +getExponentialRand(pg_prng_state *state, int64 min, int64 max, double parameter) { double cut, uniform, @@ -946,7 +924,7 @@ getExponentialRand(RandomState *random_state, int64 min, int64 max, Assert(parameter > 0.0); cut = exp(-parameter); /* erand in [0, 1), uniform in (0, 1] */ - uniform = 1.0 - pg_erand48(random_state->xseed); + uniform = 1.0 - pg_prng_f64(state); /* * inner expression in (cut, 1] (if parameter > 0), rand in [0, 1) @@ -959,8 +937,7 @@ getExponentialRand(RandomState *random_state, int64 min, int64 max, /* random number generator: gaussian distribution from min to max inclusive */ static int64 -getGaussianRand(RandomState *random_state, int64 min, int64 max, - double parameter) +getGaussianRand(pg_prng_state *state, int64 min, int64 max, double parameter) { double stdev; double rand; @@ -983,13 +960,13 @@ getGaussianRand(RandomState *random_state, int64 min, int64 max, do { /* - * pg_erand48 generates [0,1), but for the basic version of the + * pg_prng_f64 generates [0,1), but for the basic version of the * Box-Muller transform the two uniformly distributed random numbers * are expected in (0, 1] (see * https://en.wikipedia.org/wiki/Box-Muller_transform) */ - double rand1 = 1.0 - pg_erand48(random_state->xseed); - double rand2 = 1.0 - pg_erand48(random_state->xseed); + double rand1 = 1.0 - pg_prng_f64(state); + double rand2 = 1.0 - pg_prng_f64(state); /* Box-Muller basic form transform */ double var_sqrt = sqrt(-2.0 * log(rand1)); @@ -1019,7 +996,7 @@ getGaussianRand(RandomState *random_state, int64 min, int64 max, * not be one. */ static int64 -getPoissonRand(RandomState *random_state, double center) +getPoissonRand(pg_prng_state *state, double center) { /* * Use inverse transform sampling to generate a value > 0, such that the @@ -1027,8 +1004,8 @@ getPoissonRand(RandomState *random_state, double center) */ double uniform; - /* erand in [0, 1), uniform in (0, 1] */ - uniform = 1.0 - pg_erand48(random_state->xseed); + /* pseudo-random value in [0, 1), uniform in (0, 1] */ + uniform = 1.0 - pg_prng_f64(state); return (int64) (-log(uniform) * center + 0.5); } @@ -1041,7 +1018,7 @@ getPoissonRand(RandomState *random_state, double center) * This works for s > 1.0, but may perform badly for s very close to 1.0. */ static int64 -computeIterativeZipfian(RandomState *random_state, int64 n, double s) +computeIterativeZipfian(pg_prng_state *state, int64 n, double s) { double b = pow(2.0, s - 1.0); double x, @@ -1056,8 +1033,8 @@ computeIterativeZipfian(RandomState *random_state, int64 n, double s) while (true) { /* random variates */ - u = pg_erand48(random_state->xseed); - v = pg_erand48(random_state->xseed); + u = pg_prng_f64(state); + v = pg_prng_f64(state); x = floor(pow(u, -1.0 / (s - 1.0))); @@ -1071,14 +1048,14 @@ computeIterativeZipfian(RandomState *random_state, int64 n, double s) /* random number generator: zipfian distribution from min to max inclusive */ static int64 -getZipfianRand(RandomState *random_state, int64 min, int64 max, double s) +getZipfianRand(pg_prng_state *state, int64 min, int64 max, double s) { int64 n = max - min + 1; /* abort if parameter is invalid */ Assert(MIN_ZIPFIAN_PARAM <= s && s <= MAX_ZIPFIAN_PARAM); - return min - 1 + computeIterativeZipfian(random_state, n, s); + return min - 1 + computeIterativeZipfian(state, n, s); } /* @@ -1135,7 +1112,7 @@ getHashMurmur2(int64 val, uint64 seed) * For small sizes, this generates each of the (size!) possible permutations * of integers in the range [0, size) with roughly equal probability. Once * the size is larger than 20, the number of possible permutations exceeds the - * number of distinct states of the internal pseudorandom number generators, + * number of distinct states of the internal pseudorandom number generator, * and so not all possible permutations can be generated, but the permutations * chosen should continue to give the appearance of being random. * @@ -1145,25 +1122,19 @@ getHashMurmur2(int64 val, uint64 seed) static int64 permute(const int64 val, const int64 isize, const int64 seed) { - RandomState random_state1; - RandomState random_state2; - uint64 size; - uint64 v; - int masklen; - uint64 mask; - int i; + /* using a high-end PRNG is probably overkill */ + pg_prng_state state; + uint64 size; + uint64 v; + int masklen; + uint64 mask; + int i; if (isize < 2) return 0; /* nothing to permute */ - /* Initialize a pair of random states using the seed */ - random_state1.xseed[0] = seed & 0xFFFF; - random_state1.xseed[1] = (seed >> 16) & 0xFFFF; - random_state1.xseed[2] = (seed >> 32) & 0xFFFF; - - random_state2.xseed[0] = (((uint64) seed) >> 48) & 0xFFFF; - random_state2.xseed[1] = seed & 0xFFFF; - random_state2.xseed[2] = (seed >> 16) & 0xFFFF; + /* Initialize prng state using the seed */ + pg_prng_seed(&state, (uint64) seed); /* Computations are performed on unsigned values */ size = (uint64) isize; @@ -1209,8 +1180,8 @@ permute(const int64 val, const int64 isize, const int64 seed) t; /* Random multiply (by an odd number), XOR and rotate of lower half */ - m = (uint64) getrand(&random_state1, 0, mask) | 1; - r = (uint64) getrand(&random_state2, 0, mask); + m = (pg_prng_u64(&state) & mask) | 1; + r = pg_prng_u64(&state) & mask; if (v <= mask) { v = ((v * m) ^ r) & mask; @@ -1218,8 +1189,8 @@ permute(const int64 val, const int64 isize, const int64 seed) } /* Random multiply (by an odd number), XOR and rotate of upper half */ - m = (uint64) getrand(&random_state1, 0, mask) | 1; - r = (uint64) getrand(&random_state2, 0, mask); + m = (pg_prng_u64(&state) & mask) | 1; + r = pg_prng_u64(&state) & mask; t = size - 1 - v; if (t <= mask) { @@ -1229,7 +1200,7 @@ permute(const int64 val, const int64 isize, const int64 seed) } /* Random offset */ - r = (uint64) getrand(&random_state2, 0, size - 1); + r = pg_prng_u64(&state) % (size - 1); v = (v + r) % size; } @@ -3790,7 +3761,7 @@ doLog(TState *thread, CState *st, * to the random sample. */ if (sample_rate != 0.0 && - pg_erand48(thread->ts_sample_rs.xseed) > sample_rate) + pg_prng_f64(&thread->ts_sample_rs) > sample_rate) return; /* should we aggregate the results or not? */ @@ -5714,12 +5685,11 @@ set_random_seed(const char *seed) if (seed != NULL) pg_log_info("setting random seed to %llu", (unsigned long long) iseed); + random_seed = iseed; /* Fill base_random_sequence with low-order bits of seed */ - base_random_sequence.xseed[0] = iseed & 0xFFFF; - base_random_sequence.xseed[1] = (iseed >> 16) & 0xFFFF; - base_random_sequence.xseed[2] = (iseed >> 32) & 0xFFFF; + pg_prng_seed(&base_random_sequence, (uint64_t) iseed); return true; } @@ -6405,9 +6375,7 @@ main(int argc, char **argv) /* set default seed for hash functions */ if (lookupVariable(&state[0], "default_seed") == NULL) { - uint64 seed = - ((uint64) pg_jrand48(base_random_sequence.xseed) & 0xFFFFFFFF) | - (((uint64) pg_jrand48(base_random_sequence.xseed) & 0xFFFFFFFF) << 32); + uint64 seed = pg_prng_u64(&base_random_sequence); for (i = 0; i < nclients; i++) if (!putVariableInt(&state[i], "startup", "default_seed", (int64) seed)) diff --git a/src/bin/pgbench/t/001_pgbench_with_server.pl b/src/bin/pgbench/t/001_pgbench_with_server.pl index 3aa9d5d753..a5a9bcbd56 100644 --- a/src/bin/pgbench/t/001_pgbench_with_server.pl +++ b/src/bin/pgbench/t/001_pgbench_with_server.pl @@ -427,9 +427,9 @@ pgbench( # After explicit seeding, the four random checks (1-3,20) are # deterministic - qr{command=1.: int 13\b}, # uniform random - qr{command=2.: int 116\b}, # exponential random - qr{command=3.: int 1498\b}, # gaussian random + qr{command=1.: int 18\b}, # uniform random + qr{command=2.: int 100\b}, # exponential random + qr{command=3.: int 1526\b}, # gaussian random qr{command=4.: int 4\b}, qr{command=5.: int 5\b}, qr{command=6.: int 6\b}, @@ -496,6 +496,7 @@ pgbench( qr{command=109.: boolean true\b}, qr{command=110.: boolean true\b}, qr{command=111.: boolean true\b}, + qr{command=113.: boolean true\b}, ], 'pgbench expressions', { @@ -639,8 +640,17 @@ SELECT :v0, :v1, :v2, :v3; \set t debug(0 <= :p and :p < :size and :p = permute(:v + :size, :size) and :p <> permute(:v + 1, :size)) -- actual values \set t debug(permute(:v, 1) = 0) -\set t debug(permute(0, 2, 5432) = 0 and permute(1, 2, 5432) = 1 and \ - permute(0, 2, 5435) = 1 and permute(1, 2, 5435) = 0) +\set t debug(permute(0, 2, 5431) = 1 and permute(1, 2, 5431) = 0 and \ + permute(0, 2, 5432) = 0 and permute(1, 2, 5432) = 1) +-- 63 bits tests for checking permute portability across architectures +\set size debug(:max - 10) +\set t debug(permute(:size-1, :size, 5432) = 4352241160009873020 and \ + permute(:size-2, :size, 5432) = 4990004119691638710 and \ + permute(:size-3, :size, 5432) = 5868029867256127549 and \ + permute(:size-4, :size, 5432) = 1238830324886341378 and \ + permute(:size-5, :size, 5432) = 7415914367102906897 and \ + permute(:size-6, :size, 5432) = 3214501037984818995 and \ + permute(:size-7, :size, 5432) = 600660351261124695) } }); diff --git a/src/common/Makefile b/src/common/Makefile index 38a8599337..9aee26f694 100644 --- a/src/common/Makefile +++ b/src/common/Makefile @@ -67,6 +67,7 @@ OBJS_COMMON = \ md5_common.o \ pg_get_line.o \ pg_lzcompress.o \ + pg_prng.o \ pgfnames.o \ psprintf.o \ relpath.o \ diff --git a/src/common/pg_prng.c b/src/common/pg_prng.c new file mode 100644 index 0000000000..fac7a04a49 --- /dev/null +++ b/src/common/pg_prng.c @@ -0,0 +1,222 @@ +/* + * Pseudo-Random Number Generator + * + * Copyright (c) 2021-2021, PostgreSQL Global Development Group + * + * Previous version was the 1988 (?) rand48 standard with 48 bits state LCG, + * designed for running on 32 bits or even 16 bits architectures, and to produce + * 32 bit ints and floats (i.e. *not* doubles). + * + * The implied 16-bits unpacking/packing may have a detrimental performance impact + * on modern 64 bits architectures. + * + * We (probably) want: + * - one reasonable default PRNG for all pg internal uses. + * - NOT to invent a new design! + * - something fast, close to rand48 (which basically does 2 arithmetic ops) + * no need for something cryptographic though, which would imply slow + * - to produce 64 bits integers & doubles with a 52 bits mantissa + * so state size > 64 bits. + * - a small state though, because we might generate quite a few of them + * for different purposes so state size <= 256 or even <= 128 bits + * - the state to be aligned to whatever, so state size = 128 bits + * - 64 bits operations for efficiency on modern architectures, + * but NOT 128 bits operations. + * - not to depend on special hardware for speed (eg MMX/SSE/AES). + * - not something with obvious known and relevant defects. + * - not something with "rights" attached. + * + * These constraints reduce a lot the available options from + * https://en.wikipedia.org/wiki/List_of_random_number_generators + * + * LCG + * - POSIX rand48: m = 2^48, a = 25214903917, c = 11, extract 47...16 + * - MMIX by Donald Knuth: m = 2^64, a = 6364136223846793005, c = 1442695040888963407 + * - very bad on low bits, which MUST NOT BE USED + * a 128 bits would do, but no clear standards, and it would imply 128-bit ops, + * see PCG below. + * + * PCG Family (https://www.pcg-random.org/) + * - this is basically an LCG with an improved outpout function + * so as to avoid typical LCG issues and pass stats tests. + * - recommands state size to be twice the output size, + * which seems a reasonable requirement. + * - this implies 128 bit ops for our target parameters, + * otherwise it is a fairly good candidate for simplicity, + * efficiency and quality. + * + * Xor-Shift-Rotate PRNGs: + * - a generic annoying feature of this generator class is that there is one + * special all-zero state which must not be used, inducing a constraint on + * initialization, and when the generator get close to it (i.e. many zeros + * in the state) it takes some iterations to recover, with small lumps + * of close/related/bad numbers generated in the process. + * Xoroshiro128+ + * - fails statistical linearity tests in the low bits. + * Xoroshiro128** + * https://prng.di.unimi.it/xoroshiro128starstar.c + * - mild and irrelevant issues: close to zero state, stats issues after some transforms + * Xoshiro256** + * - it looks good, but some issues (no big deal for pg usage IMO) + * https://www.pcg-random.org/posts/a-quick-look-at-xoshiro256.html + * mild problems getting out of closes-to-zero state + * + * MT19937-64 + * + default in many places, standard + * - huge 2.5 KiB state (but TinyMT, 127 bits state space) + * - slow (but SFMT, twice as fast) + * - vulnerabilities on tinyMT? + * + * WELL + * - large state, 32-bit oriented + * + * ACORN Family (https://en.wikipedia.org/wiki/ACORN_(PRNG)) + * - Fortran / double oriented, no C implementation found?! + * - medium large state "In practice we recommend using k > 10, M = 2^60 (for general application) + * or M = 2^120 (for demanding applications requiring high-quality pseudo-random numbers + * that will consistently pass all the tests in standard test packages such as TestU01) + * and choose any odd value less than M for the seed." + * k = 11 => 88 bytes/704-bits state, too large:-( + * + * Splitmix? + * Others? + * + * The conclusion is that we use xor-shift-rotate generator below. + */ + +#include "c.h" +#include "common/pg_prng.h" +#include "port/pg_bitutils.h" +#include <math.h> + +#define FIRST_BIT_MASK UINT64CONST(0x8000000000000000) +#define RIGHT_HALF_MASK UINT64CONST(0x00000000FFFFFFFF) +#define DMANTISSA_MASK UINT64CONST(0x000FFFFFFFFFFFFF) + +/* 64-bits rotate left */ +static inline uint64_t rotl(const uint64_t x, const int bits) +{ + return (x << bits) | (x >> (64 - bits)); +} + +/* smallest mask holding a value */ +static inline uint64_t mask_u64(uint64_t u) +{ + return (((uint64_t) 1) << (pg_leftmost_one_pos64(u) + 1)) - 1; +} + +/* another 64-bits generator is used for state initialization or iterations */ +static uint64_t splitmix64(uint64_t * state) +{ + /* state update */ + uint64_t val = (*state += UINT64CONST(0x9E3779B97f4A7C15)); + + /* value extraction */ + val = (val ^ (val >> 30)) * UINT64CONST(0xBF58476D1CE4E5B9); + val = (val ^ (val >> 27)) * UINT64CONST(0x94D049BB133111EB); + + return val ^ (val >> 31); +} + +/* seed prng state from a 64 bits integer, ensuring non zero */ +void pg_prng_seed(pg_prng_state *state, uint64_t seed) +{ + state->s0 = splitmix64(&seed); + state->s1 = splitmix64(&seed); +} + +/* seed with 53 bits (mantissa & sign) from a float */ +void pg_prng_fseed(pg_prng_state *state, double fseed) +{ + uint64 seed = (int64) (((double) DMANTISSA_MASK) * fseed); + pg_prng_seed(state, seed); +} + +/* strong random seeding */ +void pg_prng_strong_seed(pg_prng_state *state) +{ + pg_strong_random((void *) state, sizeof(pg_prng_state)); + + /* avoid zero with Donald Knuth's LCG parameters */ + if (unlikely(state->s0 == 0 && state->s1 == 0)) + { + /* should it warn that something is amiss if we get there? */ + pg_prng_state def = PG_PRNG_DEFAULT_STATE; + *state = def; + } +} + +/* generator & state update */ +static uint64_t xoroshiro128ss(pg_prng_state *state) +{ + const uint64_t s0 = state->s0, + sx = state->s1 ^ s0, + val = rotl(s0 * 5, 7) * 9; + + /* update state */ + state->s0 = rotl(s0, 24) ^ sx ^ (sx << 16); + state->s1 = rotl(sx, 37); + + return val; +} + +/* u64 generator */ +uint64_t pg_prng_u64(pg_prng_state *state) +{ + return xoroshiro128ss(state); +} + +/* select in a range with bitmask rejection */ +uint64_t pg_prng_u64_range(pg_prng_state *state, uint64_t range) +{ + /* we always iterate over the state once, for determinism */ + uint64_t next = xoroshiro128ss(state); + uint64_t val = next; + + if (range >= 2) + { + uint64_t mask = mask_u64(range-1); + + /* iterate over splitmix till val in [0, range) */ + while ((val = val & mask) >= range) + val = splitmix64(&next); + } + else + val = 0; + + return val; +} + +/* i64 generator */ +int64_t pg_prng_i64(pg_prng_state *state) +{ + return (int64_t) xoroshiro128ss(state); +} + +/* u32 generator */ +uint32_t pg_prng_u32(pg_prng_state *state) +{ + const uint64_t v = xoroshiro128ss(state); + return (uint32_t) (((v >> 32) ^ v) & RIGHT_HALF_MASK); +} + +/* i32 generator */ +int32_t pg_prng_i32(pg_prng_state *state) +{ + const uint64_t v = xoroshiro128ss(state); + return (int32_t) (((v >> 32) ^ v) & RIGHT_HALF_MASK); +} + +/* double generator */ +double pg_prng_f64(pg_prng_state *state) +{ + uint64_t v = xoroshiro128ss(state); + return ldexp((double) (v & DMANTISSA_MASK), -52); +} + +/* bool generator */ +bool pg_prng_bool(pg_prng_state *state) +{ + uint64_t v = xoroshiro128ss(state); + return (v & FIRST_BIT_MASK) == FIRST_BIT_MASK; +} diff --git a/src/include/common/pg_prng.h b/src/include/common/pg_prng.h new file mode 100644 index 0000000000..6d582cc3dc --- /dev/null +++ b/src/include/common/pg_prng.h @@ -0,0 +1,34 @@ +/*------------------------------------------------------------------------- + * + * PRNG: internal Pseudo-Random Number Generator + * + * Copyright (c) 2021-2021, PostgreSQL Global Development Group + * + * src/include/common/pg_prng.h + * + *------------------------------------------------------------------------- + */ +#ifndef PG_PRNG_H +#define PG_PRNG_H + +/* 128 bits state */ +typedef struct pg_prng_state { + uint64_t s0, s1; +} pg_prng_state; + +/* use Donald Knuth's LCG constants for default state */ +#define PG_PRNG_DEFAULT_STATE \ + { UINT64CONST(0x5851F42D4C957F2D), UINT64CONST(0x14057B7EF767814F) } + +extern void pg_prng_seed(pg_prng_state *state, uint64_t seed); +extern void pg_prng_fseed(pg_prng_state *state, double fseed); +extern void pg_prng_strong_seed(pg_prng_state *state); +extern uint64_t pg_prng_u64(pg_prng_state *state); +extern uint64_t pg_prng_u64_range(pg_prng_state *state, uint64_t range); +extern int64_t pg_prng_i64(pg_prng_state *state); +extern uint32_t pg_prng_u32(pg_prng_state *state); +extern int32_t pg_prng_i32(pg_prng_state *state); +extern double pg_prng_f64(pg_prng_state *state); +extern bool pg_prng_bool(pg_prng_state *state); + +#endif /* PG_PRNG_H */ diff --git a/src/include/optimizer/geqo.h b/src/include/optimizer/geqo.h index 24dcdfb6cc..b28e4f084a 100644 --- a/src/include/optimizer/geqo.h +++ b/src/include/optimizer/geqo.h @@ -22,6 +22,7 @@ #ifndef GEQO_H #define GEQO_H +#include "common/pg_prng.h" #include "nodes/pathnodes.h" #include "optimizer/geqo_gene.h" @@ -72,8 +73,8 @@ extern double Geqo_seed; /* 0 .. 1 */ */ typedef struct { - List *initial_rels; /* the base relations we are joining */ - unsigned short random_state[3]; /* state for pg_erand48() */ + List *initial_rels; /* the base relations we are joining */ + pg_prng_state random_state; /* PRNG state */ } GeqoPrivateData; diff --git a/src/include/port.h b/src/include/port.h index 82f63de325..eb18665196 100644 --- a/src/include/port.h +++ b/src/include/port.h @@ -356,11 +356,6 @@ extern int gettimeofday(struct timeval *tp, struct timezone *tzp); #define pgoff_t off_t #endif -extern double pg_erand48(unsigned short xseed[3]); -extern long pg_lrand48(void); -extern long pg_jrand48(unsigned short xseed[3]); -extern void pg_srand48(long seed); - #ifndef HAVE_FLS extern int fls(int mask); #endif diff --git a/src/include/utils/sampling.h b/src/include/utils/sampling.h index a58d14281b..c0b1c6a645 100644 --- a/src/include/utils/sampling.h +++ b/src/include/utils/sampling.h @@ -13,15 +13,14 @@ #ifndef SAMPLING_H #define SAMPLING_H +#include "common/pg_prng.h" #include "storage/block.h" /* for typedef BlockNumber */ /* Random generator for sampling code */ -typedef unsigned short SamplerRandomState[3]; - extern void sampler_random_init_state(long seed, - SamplerRandomState randstate); -extern double sampler_random_fract(SamplerRandomState randstate); + pg_prng_state *randstate); +extern double sampler_random_fract(pg_prng_state *randstate); /* Block sampling methods */ @@ -32,7 +31,7 @@ typedef struct int n; /* desired sample size */ BlockNumber t; /* current block number */ int m; /* blocks selected so far */ - SamplerRandomState randstate; /* random generator state */ + pg_prng_state randstate; /* random generator state */ } BlockSamplerData; typedef BlockSamplerData *BlockSampler; @@ -46,8 +45,9 @@ extern BlockNumber BlockSampler_Next(BlockSampler bs); typedef struct { - double W; - SamplerRandomState randstate; /* random generator state */ + double W; + pg_prng_state randstate; /* random generator state */ + bool randstate_initialized; } ReservoirStateData; typedef ReservoirStateData *ReservoirState; diff --git a/src/port/Makefile b/src/port/Makefile index 52dbf5783f..b3754d8940 100644 --- a/src/port/Makefile +++ b/src/port/Makefile @@ -42,7 +42,6 @@ OBJS = \ $(PG_CRC32C_OBJS) \ bsearch_arg.o \ chklocale.o \ - erand48.o \ inet_net_ntop.o \ noblock.o \ path.o \ diff --git a/src/port/erand48.c b/src/port/erand48.c deleted file mode 100644 index a82ea94220..0000000000 --- a/src/port/erand48.c +++ /dev/null @@ -1,136 +0,0 @@ -/*------------------------------------------------------------------------- - * - * erand48.c - * - * This file supplies pg_erand48() and related functions, which except - * for the names are just like the POSIX-standard erand48() family. - * (We don't supply the full set though, only the ones we have found use - * for in Postgres. In particular, we do *not* implement lcong48(), so - * that there is no need for the multiplier and addend to be variable.) - * - * We used to test for an operating system version rather than - * unconditionally using our own, but (1) some versions of Cygwin have a - * buggy erand48() that always returns zero and (2) as of 2011, glibc's - * erand48() is strangely coded to be almost-but-not-quite thread-safe, - * which doesn't matter for the backend but is important for pgbench. - * - * Portions Copyright (c) 1996-2021, PostgreSQL Global Development Group - * - * Portions Copyright (c) 1993 Martin Birgmeier - * All rights reserved. - * - * You may redistribute unmodified or modified versions of this source - * code provided that the above copyright notice and this and the - * following conditions are retained. - * - * This software is provided ``as is'', and comes with no warranties - * of any kind. I shall in no event be liable for anything that happens - * to anyone/anything when using this software. - * - * IDENTIFICATION - * src/port/erand48.c - * - *------------------------------------------------------------------------- - */ - -#include "c.h" - -#include <math.h> - -/* These values are specified by POSIX */ -#define RAND48_MULT UINT64CONST(0x0005deece66d) -#define RAND48_ADD UINT64CONST(0x000b) - -/* POSIX specifies 0x330e's use in srand48, but the other bits are arbitrary */ -#define RAND48_SEED_0 (0x330e) -#define RAND48_SEED_1 (0xabcd) -#define RAND48_SEED_2 (0x1234) - -static unsigned short _rand48_seed[3] = { - RAND48_SEED_0, - RAND48_SEED_1, - RAND48_SEED_2 -}; - - -/* - * Advance the 48-bit value stored in xseed[] to the next "random" number. - * - * Also returns the value of that number --- without masking it to 48 bits. - * If caller uses the result, it must mask off the bits it wants. - */ -static uint64 -_dorand48(unsigned short xseed[3]) -{ - /* - * We do the arithmetic in uint64; any type wider than 48 bits would work. - */ - uint64 in; - uint64 out; - - in = (uint64) xseed[2] << 32 | (uint64) xseed[1] << 16 | (uint64) xseed[0]; - - out = in * RAND48_MULT + RAND48_ADD; - - xseed[0] = out & 0xFFFF; - xseed[1] = (out >> 16) & 0xFFFF; - xseed[2] = (out >> 32) & 0xFFFF; - - return out; -} - - -/* - * Generate a random floating-point value using caller-supplied state. - * Values are uniformly distributed over the interval [0.0, 1.0). - */ -double -pg_erand48(unsigned short xseed[3]) -{ - uint64 x = _dorand48(xseed); - - return ldexp((double) (x & UINT64CONST(0xFFFFFFFFFFFF)), -48); -} - -/* - * Generate a random non-negative integral value using internal state. - * Values are uniformly distributed over the interval [0, 2^31). - */ -long -pg_lrand48(void) -{ - uint64 x = _dorand48(_rand48_seed); - - return (x >> 17) & UINT64CONST(0x7FFFFFFF); -} - -/* - * Generate a random signed integral value using caller-supplied state. - * Values are uniformly distributed over the interval [-2^31, 2^31). - */ -long -pg_jrand48(unsigned short xseed[3]) -{ - uint64 x = _dorand48(xseed); - - return (int32) ((x >> 16) & UINT64CONST(0xFFFFFFFF)); -} - -/* - * Initialize the internal state using the given seed. - * - * Per POSIX, this uses only 32 bits from "seed" even if "long" is wider. - * Hence, the set of possible seed values is smaller than it could be. - * Better practice is to use caller-supplied state and initialize it with - * random bits obtained from a high-quality source of random bits. - * - * Note: POSIX specifies a function seed48() that allows all 48 bits - * of the internal state to be set, but we don't currently support that. - */ -void -pg_srand48(long seed) -{ - _rand48_seed[0] = RAND48_SEED_0; - _rand48_seed[1] = (unsigned short) seed; - _rand48_seed[2] = (unsigned short) (seed >> 16); -} diff --git a/src/port/random.c b/src/port/random.c deleted file mode 100644 index 2dd59a0829..0000000000 --- a/src/port/random.c +++ /dev/null @@ -1,25 +0,0 @@ -/*------------------------------------------------------------------------- - * - * random.c - * random() wrapper - * - * Portions Copyright (c) 1996-2021, PostgreSQL Global Development Group - * Portions Copyright (c) 1994, Regents of the University of California - * - * - * IDENTIFICATION - * src/port/random.c - * - *------------------------------------------------------------------------- - */ - -#include "c.h" - -#include <math.h> - - -long -random(void) -{ - return pg_lrand48(); -} diff --git a/src/port/srandom.c b/src/port/srandom.c deleted file mode 100644 index cf1007b2ef..0000000000 --- a/src/port/srandom.c +++ /dev/null @@ -1,25 +0,0 @@ -/*------------------------------------------------------------------------- - * - * srandom.c - * srandom() wrapper - * - * Portions Copyright (c) 1996-2021, PostgreSQL Global Development Group - * Portions Copyright (c) 1994, Regents of the University of California - * - * - * IDENTIFICATION - * src/port/srandom.c - * - *------------------------------------------------------------------------- - */ - -#include "c.h" - -#include <math.h> - - -void -srandom(unsigned int seed) -{ - pg_srand48((long int) seed); -} diff --git a/src/test/modules/test_integerset/test_integerset.c b/src/test/modules/test_integerset/test_integerset.c index 21c6f49b37..8935a81284 100644 --- a/src/test/modules/test_integerset/test_integerset.c +++ b/src/test/modules/test_integerset/test_integerset.c @@ -12,6 +12,7 @@ */ #include "postgres.h" +#include "common/pg_prng.h" #include "fmgr.h" #include "lib/integerset.h" #include "miscadmin.h" @@ -99,12 +100,16 @@ static void check_with_filler(IntegerSet *intset, uint64 x, uint64 value, uint64 static void test_single_value_and_filler(uint64 value, uint64 filler_min, uint64 filler_max); static void test_huge_distances(void); +static pg_prng_state pr_state; + /* * SQL-callable entry point to perform all tests. */ Datum test_integerset(PG_FUNCTION_ARGS) { + pg_prng_strong_seed(&pr_state); + /* Tests for various corner cases */ test_empty(); test_huge_distances(); @@ -248,7 +253,7 @@ test_pattern(const test_spec *spec) * only a small part of the integer space is used. We would very * rarely hit values that are actually in the set. */ - x = (pg_lrand48() << 31) | pg_lrand48(); + x = pg_prng_u64(&pr_state); x = x % (last_int + 1000); /* Do we expect this value to be present in the set? */ @@ -571,7 +576,7 @@ test_huge_distances(void) */ while (num_values < 1000) { - val += pg_lrand48(); + val += pg_prng_i32(&pr_state); values[num_values++] = val; } diff --git a/src/tools/msvc/Mkvcbuild.pm b/src/tools/msvc/Mkvcbuild.pm index 233ddbf4c2..29d0f467cb 100644 --- a/src/tools/msvc/Mkvcbuild.pm +++ b/src/tools/msvc/Mkvcbuild.pm @@ -103,9 +103,9 @@ sub mkvcbuild $solution = CreateSolution($vsVersion, $config); our @pgportfiles = qw( - chklocale.c explicit_bzero.c fls.c getpeereid.c getrusage.c inet_aton.c random.c - srandom.c getaddrinfo.c gettimeofday.c inet_net_ntop.c kill.c open.c - erand48.c snprintf.c strlcat.c strlcpy.c dirmod.c noblock.c path.c + chklocale.c explicit_bzero.c fls.c getpeereid.c getrusage.c inet_aton.c + getaddrinfo.c gettimeofday.c inet_net_ntop.c kill.c open.c + snprintf.c strlcat.c strlcpy.c dirmod.c noblock.c path.c dirent.c dlopen.c getopt.c getopt_long.c link.c pread.c preadv.c pwrite.c pwritev.c pg_bitutils.c pg_strong_random.c pgcheckdir.c pgmkdirp.c pgsleep.c pgstrcasecmp.c @@ -131,9 +131,9 @@ sub mkvcbuild config_info.c controldata_utils.c d2s.c encnames.c exec.c f2s.c file_perm.c file_utils.c hashfn.c hex.c ip.c jsonapi.c keywords.c kwlookup.c link-canary.c md5_common.c - pg_get_line.c pg_lzcompress.c pgfnames.c psprintf.c relpath.c rmtree.c - saslprep.c scram-common.c string.c stringinfo.c unicode_norm.c username.c - wait_error.c wchar.c); + pg_get_line.c pg_lzcompress.c pg_prng.c pgfnames.c psprintf.c relpath.c + rmtree.c saslprep.c scram-common.c string.c stringinfo.c unicode_norm.c + username.c wait_error.c wchar.c); if ($solution->{options}->{openssl}) {