Replace random(), pg_erand48(), etc with a better PRNG API and algorithm.
authorTom Lane <tgl@sss.pgh.pa.us>
Mon, 29 Nov 2021 02:32:36 +0000 (21:32 -0500)
committerTom Lane <tgl@sss.pgh.pa.us>
Mon, 29 Nov 2021 02:33:07 +0000 (21:33 -0500)
Standardize on xoroshiro128** as our basic PRNG algorithm, eliminating
a bunch of platform dependencies as well as fundamentally-obsolete PRNG
code.  In addition, this API replacement will ease replacing the
algorithm again in future, should that become necessary.

xoroshiro128** is a few percent slower than the drand48 family,
but it can produce full-width 64-bit random values not only 48-bit,
and it should be much more trustworthy.  It's likely to be noticeably
faster than the platform's random(), depending on which platform you
are thinking about; and we can have non-global state vectors easily,
unlike with random().  It is not cryptographically strong, but neither
are the functions it replaces.

Fabien Coelho, reviewed by Dean Rasheed, Aleksander Alekseev, and myself

Discussion: https://postgr.es/m/alpine.DEB.2.22.394.2105241211230.165418@pseudo

50 files changed:
configure
configure.ac
contrib/amcheck/verify_nbtree.c
contrib/auto_explain/auto_explain.c
contrib/file_fdw/file_fdw.c
contrib/postgres_fdw/postgres_fdw.c
contrib/tablefunc/tablefunc.c
contrib/tsm_system_rows/tsm_system_rows.c
contrib/tsm_system_time/tsm_system_time.c
src/backend/access/gin/ginget.c
src/backend/access/gist/gistutil.c
src/backend/access/nbtree/nbtinsert.c
src/backend/access/spgist/spgdoinsert.c
src/backend/access/transam/xact.c
src/backend/commands/analyze.c
src/backend/executor/nodeSamplescan.c
src/backend/lib/bloomfilter.c
src/backend/optimizer/geqo/geqo_random.c
src/backend/optimizer/geqo/geqo_selection.c
src/backend/postmaster/postmaster.c
src/backend/storage/file/fd.c
src/backend/storage/ipc/dsm.c
src/backend/storage/lmgr/Makefile
src/backend/storage/lmgr/s_lock.c
src/backend/tcop/postgres.c
src/backend/utils/adt/float.c
src/backend/utils/misc/sampling.c
src/bin/initdb/initdb.c
src/bin/pg_test_fsync/pg_test_fsync.c
src/bin/pgbench/pgbench.c
src/bin/pgbench/t/001_pgbench_with_server.pl
src/common/Makefile
src/common/pg_prng.c [new file with mode: 0644]
src/include/common/pg_prng.h [new file with mode: 0644]
src/include/optimizer/geqo.h
src/include/optimizer/geqo_random.h
src/include/pg_config.h.in
src/include/pg_config_manual.h
src/include/port.h
src/include/utils/sampling.h
src/port/Makefile
src/port/erand48.c [deleted file]
src/port/random.c [deleted file]
src/port/srandom.c [deleted file]
src/test/modules/test_bloomfilter/test_bloomfilter.c
src/test/modules/test_integerset/test_integerset.c
src/test/modules/test_rbtree/test_rbtree.c
src/tools/msvc/Mkvcbuild.pm
src/tools/msvc/Solution.pm
src/tools/testint128.c

index 896b7814731527f139ba172e8c567c4935f0f8fa..f8c81644280cf9a355cf299643cede4e619d264f 100755 (executable)
--- a/configure
+++ b/configure
@@ -16463,32 +16463,6 @@ esac
 
 fi
 
-ac_fn_c_check_func "$LINENO" "random" "ac_cv_func_random"
-if test "x$ac_cv_func_random" = xyes; then :
-  $as_echo "#define HAVE_RANDOM 1" >>confdefs.h
-
-else
-  case " $LIBOBJS " in
-  *" random.$ac_objext "* ) ;;
-  *) LIBOBJS="$LIBOBJS random.$ac_objext"
- ;;
-esac
-
-fi
-
-ac_fn_c_check_func "$LINENO" "srandom" "ac_cv_func_srandom"
-if test "x$ac_cv_func_srandom" = xyes; then :
-  $as_echo "#define HAVE_SRANDOM 1" >>confdefs.h
-
-else
-  case " $LIBOBJS " in
-  *" srandom.$ac_objext "* ) ;;
-  *) LIBOBJS="$LIBOBJS srandom.$ac_objext"
- ;;
-esac
-
-fi
-
 ac_fn_c_check_func "$LINENO" "strlcat" "ac_cv_func_strlcat"
 if test "x$ac_cv_func_strlcat" = xyes; then :
   $as_echo "#define HAVE_STRLCAT 1" >>confdefs.h
index b50130b32386b86573604de9a89097f6ccc57413..a5c10b8d56604e1b7340ec2fb74e8601505a901c 100644 (file)
@@ -1858,8 +1858,6 @@ AC_REPLACE_FUNCS(m4_normalize([
        mkdtemp
        pread
        pwrite
-       random
-       srandom
        strlcat
        strlcpy
        strnlen
index 42a830c33b502e3bb3a12e1307b521fcc6ad740e..dc1508b63619508590c826084549b275b274df3d 100644 (file)
@@ -32,6 +32,7 @@
 #include "catalog/index.h"
 #include "catalog/pg_am.h"
 #include "commands/tablecmds.h"
+#include "common/pg_prng.h"
 #include "lib/bloomfilter.h"
 #include "miscadmin.h"
 #include "storage/lmgr.h"
@@ -466,8 +467,8 @@ bt_check_every_level(Relation rel, Relation heaprel, bool heapkeyspace,
                total_pages = RelationGetNumberOfBlocks(rel);
                total_elems = Max(total_pages * (MaxTIDsPerBTreePage / 3),
                                                  (int64) state->rel->rd_rel->reltuples);
-               /* Random seed relies on backend srandom() call to avoid repetition */
-               seed = random();
+               /* Generate a random seed to avoid repetition */
+               seed = pg_prng_uint64(&pg_global_prng_state);
                /* Create Bloom filter to fingerprint index */
                state->filter = bloom_create(total_elems, maintenance_work_mem, seed);
                state->heaptuplespresent = 0;
index e9092ba359ad88d4e6d819a22fb46e47a4636be5..59ba63455fd18ebf790f334c630d53fde41ee8b8 100644 (file)
@@ -16,6 +16,7 @@
 
 #include "access/parallel.h"
 #include "commands/explain.h"
+#include "common/pg_prng.h"
 #include "executor/instrument.h"
 #include "jit/jit.h"
 #include "utils/guc.h"
@@ -275,8 +276,7 @@ explain_ExecutorStart(QueryDesc *queryDesc, int eflags)
        if (nesting_level == 0)
        {
                if (auto_explain_log_min_duration >= 0 && !IsParallelWorker())
-                       current_query_sampled = (random() < auto_explain_sample_rate *
-                                                                        ((double) MAX_RANDOM_VALUE + 1));
+                       current_query_sampled = (pg_prng_double(&pg_global_prng_state) < auto_explain_sample_rate);
                else
                        current_query_sampled = false;
        }
index 2c2f149fb015f23f346163211018bd4d2e3f069e..146b524076ad895a7c3682997d1a0c5dec206137 100644 (file)
@@ -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]);
index 45a09337d08e10cd98fa147507f95c6cb7251de5..f767fdcc6a10c7e54c488371e02ef1f42f7aa3f3 100644 (file)
@@ -5158,7 +5158,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]);
                }
index 52b272f2989ecb71ca4cb9dd7778dd2e60082609..63bb91a646f869540f81627821e7d728133b8bb3 100644 (file)
@@ -36,6 +36,7 @@
 
 #include "access/htup_details.h"
 #include "catalog/pg_type.h"
+#include "common/pg_prng.h"
 #include "executor/spi.h"
 #include "funcapi.h"
 #include "lib/stringinfo.h"
@@ -290,8 +291,8 @@ get_normal_pair(float8 *x1, float8 *x2)
 
        do
        {
-               u1 = (float8) random() / (float8) MAX_RANDOM_VALUE;
-               u2 = (float8) random() / (float8) MAX_RANDOM_VALUE;
+               u1 = pg_prng_double(&pg_global_prng_state);
+               u2 = pg_prng_double(&pg_global_prng_state);
 
                v1 = (2.0 * u1) - 1.0;
                v2 = (2.0 * u2) - 1.0;
index 4996612902492f62043234d9651cfcec7ae86961..1a46d4b143a9292a1439670449f36343e4652fea 100644 (file)
@@ -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;
 
index 788d8f9a68d3d297d08c422386a33e719a995bad..36acc6c1060e55d7df5345dd2c75aa4e9c8166fa 100644 (file)
@@ -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;
 
index 03191e016ce5c4be0e5f59474789bd0114718991..e93bf2999909822861bd2c13e748a28cedc84c68 100644 (file)
@@ -16,6 +16,7 @@
 
 #include "access/gin_private.h"
 #include "access/relscan.h"
+#include "common/pg_prng.h"
 #include "miscadmin.h"
 #include "storage/predicate.h"
 #include "utils/datum.h"
@@ -787,7 +788,7 @@ entryLoadMoreItems(GinState *ginstate, GinScanEntry entry,
        }
 }
 
-#define gin_rand() (((double) random()) / ((double) MAX_RANDOM_VALUE))
+#define gin_rand() pg_prng_double(&pg_global_prng_state)
 #define dropItem(e) ( gin_rand() > ((double)GinFuzzySearchLimit)/((double)((e)->predictNumberResult)) )
 
 /*
index 43ba03b6eb97cd523ccee974c5a93356fd5f1679..94dbabc1988a7b5b02dce2a1665bf592c2c4acbc 100644 (file)
@@ -19,6 +19,7 @@
 #include "access/htup_details.h"
 #include "access/reloptions.h"
 #include "catalog/pg_opclass.h"
+#include "common/pg_prng.h"
 #include "storage/indexfsm.h"
 #include "storage/lmgr.h"
 #include "utils/float.h"
@@ -507,7 +508,7 @@ gistchoose(Relation r, Page p, IndexTuple it,       /* it has compressed entry */
                        if (keep_current_best == -1)
                        {
                                /* we didn't make the random choice yet for this old best */
-                               keep_current_best = (random() <= (MAX_RANDOM_VALUE / 2)) ? 1 : 0;
+                               keep_current_best = pg_prng_bool(&pg_global_prng_state) ? 1 : 0;
                        }
                        if (keep_current_best == 0)
                        {
@@ -529,7 +530,7 @@ gistchoose(Relation r, Page p, IndexTuple it,       /* it has compressed entry */
                        if (keep_current_best == -1)
                        {
                                /* we didn't make the random choice yet for this old best */
-                               keep_current_best = (random() <= (MAX_RANDOM_VALUE / 2)) ? 1 : 0;
+                               keep_current_best = pg_prng_bool(&pg_global_prng_state) ? 1 : 0;
                        }
                        if (keep_current_best == 1)
                                break;
index 0fe8c709395039883f941387810a32af50e2d869..37ee0b4d6ee68adbd943c821b655dfdc98316b6e 100644 (file)
@@ -19,6 +19,7 @@
 #include "access/nbtxlog.h"
 #include "access/transam.h"
 #include "access/xloginsert.h"
+#include "common/pg_prng.h"
 #include "lib/qunique.h"
 #include "miscadmin.h"
 #include "storage/lmgr.h"
@@ -965,7 +966,7 @@ _bt_findinsertloc(Relation rel,
 
                        if (P_RIGHTMOST(opaque) ||
                                _bt_compare(rel, itup_key, page, P_HIKEY) != 0 ||
-                               random() <= (MAX_RANDOM_VALUE / 100))
+                               pg_prng_uint32(&pg_global_prng_state) <= (PG_UINT32_MAX / 100))
                                break;
 
                        _bt_stepright(rel, insertstate, stack);
index 70557bcf3d0add000182cf751144870c38be3f36..e7afb2c242a39189a9c3bcbc5d02b026d658cf1a 100644 (file)
@@ -19,6 +19,7 @@
 #include "access/spgist_private.h"
 #include "access/spgxlog.h"
 #include "access/xloginsert.h"
+#include "common/pg_prng.h"
 #include "miscadmin.h"
 #include "storage/bufmgr.h"
 #include "utils/rel.h"
@@ -2210,7 +2211,9 @@ spgdoinsert(Relation index, SpGistState *state,
                                if (out.resultType == spgAddNode)
                                        elog(ERROR, "cannot add a node to an allTheSame inner tuple");
                                else if (out.resultType == spgMatchNode)
-                                       out.result.matchNode.nodeN = random() % innerTuple->nNodes;
+                                       out.result.matchNode.nodeN =
+                                               pg_prng_uint64_range(&pg_global_prng_state,
+                                                                                        0, innerTuple->nNodes - 1);
                        }
 
                        switch (out.resultType)
index 8e35c432f5c6090ec3712e6c3b5bcf454e0798ae..e7b0bc804d85fe7f728963f562b94413fa10204d 100644 (file)
@@ -37,6 +37,7 @@
 #include "commands/async.h"
 #include "commands/tablecmds.h"
 #include "commands/trigger.h"
+#include "common/pg_prng.h"
 #include "executor/spi.h"
 #include "libpq/be-fsstubs.h"
 #include "libpq/pqsignal.h"
@@ -1990,7 +1991,7 @@ StartTransaction(void)
        /* Determine if statements are logged in this transaction */
        xact_is_sampled = log_xact_sample_rate != 0 &&
                (log_xact_sample_rate == 1 ||
-                random() <= log_xact_sample_rate * MAX_RANDOM_VALUE);
+                pg_prng_double(&pg_global_prng_state) <= log_xact_sample_rate);
 
        /*
         * initialize current transaction state fields
index 4928702aec0f61263d7203f6d3cf6a5ca5f4c67b..cd77907fc74036a08a54c2db33d5c77f26d23131 100644 (file)
@@ -38,6 +38,7 @@
 #include "commands/progress.h"
 #include "commands/tablecmds.h"
 #include "commands/vacuum.h"
+#include "common/pg_prng.h"
 #include "executor/executor.h"
 #include "foreign/fdwapi.h"
 #include "miscadmin.h"
@@ -1140,7 +1141,7 @@ acquire_sample_rows(Relation onerel, int elevel,
        double          liverows = 0;   /* # live rows seen */
        double          deadrows = 0;   /* # dead rows seen */
        double          rowstoskip = -1;        /* -1 means not set yet */
-       long            randseed;               /* Seed for block sampler(s) */
+       uint32          randseed;               /* Seed for block sampler(s) */
        BlockNumber totalblocks;
        TransactionId OldestXmin;
        BlockSamplerData bs;
@@ -1162,7 +1163,7 @@ acquire_sample_rows(Relation onerel, int elevel,
        OldestXmin = GetOldestNonRemovableTransactionId(onerel);
 
        /* Prepare for sampling block numbers */
-       randseed = random();
+       randseed = pg_prng_uint32(&pg_global_prng_state);
        nblocks = BlockSampler_Init(&bs, totalblocks, targrows, randseed);
 
 #ifdef USE_PREFETCH
@@ -1279,7 +1280,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]);
index 44232d50d0a32294da0fe23c2178ca04ba086ac2..33b6fb13778e94b81f3ff73dcbcedfe3f9105556 100644 (file)
@@ -17,6 +17,7 @@
 #include "access/relscan.h"
 #include "access/tableam.h"
 #include "access/tsmapi.h"
+#include "common/pg_prng.h"
 #include "executor/executor.h"
 #include "executor/nodeSamplescan.h"
 #include "miscadmin.h"
@@ -154,7 +155,7 @@ ExecInitSampleScan(SampleScan *node, EState *estate, int eflags)
         * do this just once, since the seed shouldn't change over rescans.
         */
        if (tsc->repeatable == NULL)
-               scanstate->seed = random();
+               scanstate->seed = pg_prng_uint32(&pg_global_prng_state);
 
        /*
         * Finally, initialize the TABLESAMPLE method handler.
index daf2c40ebf54910b75cf9adcc58c185d584df433..ac6001b712d268d032404d94f0d08d65d4d43b83 100644 (file)
@@ -81,8 +81,7 @@ static inline uint32 mod_m(uint32 a, uint64 m);
  * distinct seed value on every call makes it unlikely that the same false
  * positives will reoccur when the same set is fingerprinted a second time.
  * Callers that don't care about this pass a constant as their seed, typically
- * 0.  Callers can use a pseudo-random seed in the range of 0 - INT_MAX by
- * calling random().
+ * 0.  Callers can also use a pseudo-random seed, eg from pg_prng_uint64().
  */
 bloom_filter *
 bloom_create(int64 total_elems, int bloom_work_mem, uint64 seed)
index f21bc047e68b5156e195e939eb0ece3e5e4b03f1..4d9432c0dfe87106f579296352f1baf35ac2e969 100644 (file)
@@ -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,17 @@ geqo_rand(PlannerInfo *root)
 {
        GeqoPrivateData *private = (GeqoPrivateData *) root->join_search_private;
 
-       return pg_erand48(private->random_state);
+       return pg_prng_double(&private->random_state);
+}
+
+int
+geqo_randint(PlannerInfo *root, int upper, int lower)
+{
+       GeqoPrivateData *private = (GeqoPrivateData *) root->join_search_private;
+
+       /*
+        * In current usage, "lower" is never negative so we can just use
+        * pg_prng_uint64_range directly.
+        */
+       return (int) pg_prng_uint64_range(&private->random_state, lower, upper);
 }
index 66b6c8ae38e454366408d95a3c2031a7e7ebf8d3..68a2e4e9f15c81a9df82efffd79afd29019bca7d 100644 (file)
@@ -63,10 +63,6 @@ geqo_selection(PlannerInfo *root, Chromosome *momma, Chromosome *daddy,
        /*
         * Ensure we have selected different genes, except if pool size is only
         * one, when we can't.
-        *
-        * This code was observed to hang up in an infinite loop when the
-        * platform's implementation of erand48() was broken.  We now always use
-        * our own version.
         */
        if (pool->size > 1)
        {
@@ -95,11 +91,11 @@ linear_rand(PlannerInfo *root, int pool_size, double bias)
        double          max = (double) pool_size;
 
        /*
-        * If geqo_rand() returns exactly 1.0 then we will get exactly max from
-        * this equation, whereas we need 0 <= index < max.  Also it seems
-        * possible that roundoff error might deliver values slightly outside the
-        * range; in particular avoid passing a value slightly less than 0 to
-        * sqrt(). If we get a bad value just try again.
+        * geqo_rand() is not supposed to return 1.0, but if it does then we will
+        * get exactly max from this equation, whereas we need 0 <= index < max.
+        * Also it seems possible that roundoff error might deliver values
+        * slightly outside the range; in particular avoid passing a value
+        * slightly less than 0 to sqrt().  If we get a bad value just try again.
         */
        do
        {
index db797c040bf51da934eac96fe4fed17e0d04067b..328ecafa8cbcd43f1d796b89d547dae15e4c770a 100644 (file)
@@ -98,6 +98,7 @@
 #include "catalog/pg_control.h"
 #include "common/file_perm.h"
 #include "common/ip.h"
+#include "common/pg_prng.h"
 #include "common/string.h"
 #include "lib/ilist.h"
 #include "libpq/auth.h"
@@ -2699,19 +2700,19 @@ ClosePostmasterPorts(bool am_syslogger)
 void
 InitProcessGlobals(void)
 {
-       unsigned int rseed;
-
        MyProcPid = getpid();
        MyStartTimestamp = GetCurrentTimestamp();
        MyStartTime = timestamptz_to_time_t(MyStartTimestamp);
 
        /*
-        * Set a different seed for random() in every process.  We want something
+        * Set a different global seed in every process.  We want something
         * unpredictable, so if possible, use high-quality random bits for the
         * seed.  Otherwise, fall back to a seed based on timestamp and PID.
         */
-       if (!pg_strong_random(&rseed, sizeof(rseed)))
+       if (unlikely(!pg_prng_strong_seed(&pg_global_prng_state)))
        {
+               uint64          rseed;
+
                /*
                 * Since PIDs and timestamps tend to change more frequently in their
                 * least significant bits, shift the timestamp left to allow a larger
@@ -2722,8 +2723,17 @@ InitProcessGlobals(void)
                rseed = ((uint64) MyProcPid) ^
                        ((uint64) MyStartTimestamp << 12) ^
                        ((uint64) MyStartTimestamp >> 20);
+
+               pg_prng_seed(&pg_global_prng_state, rseed);
        }
-       srandom(rseed);
+
+       /*
+        * Also make sure that we've set a good seed for random(3).  Use of that
+        * is deprecated in core Postgres, but extensions might use it.
+        */
+#ifndef WIN32
+       srandom(pg_prng_uint32(&pg_global_prng_state));
+#endif
 }
 
 
index cb1a8dd34f2f3e79756e4b996e0c7a9a54282ad7..263057841ddc329806eaf9825ae16da6d7dbf29a 100644 (file)
@@ -92,6 +92,7 @@
 #include "catalog/pg_tablespace.h"
 #include "common/file_perm.h"
 #include "common/file_utils.h"
+#include "common/pg_prng.h"
 #include "miscadmin.h"
 #include "pgstat.h"
 #include "port/pg_iovec.h"
@@ -2939,7 +2940,8 @@ SetTempTablespaces(Oid *tableSpaces, int numSpaces)
         * available tablespaces.
         */
        if (numSpaces > 1)
-               nextTempTableSpace = random() % numSpaces;
+               nextTempTableSpace = pg_prng_uint64_range(&pg_global_prng_state,
+                                                                                                 0, numSpaces - 1);
        else
                nextTempTableSpace = 0;
 }
index b461a5f7e96e069828a5164ab5d95078f1426595..074dff4e6d8ad414e42e5d43d6eb5cc67a7948d7 100644 (file)
@@ -33,6 +33,7 @@
 #endif
 #include <sys/stat.h>
 
+#include "common/pg_prng.h"
 #include "lib/ilist.h"
 #include "miscadmin.h"
 #include "port/pg_bitutils.h"
@@ -180,7 +181,8 @@ dsm_postmaster_startup(PGShmemHeader *shim)
        {
                Assert(dsm_control_address == NULL);
                Assert(dsm_control_mapped_size == 0);
-               dsm_control_handle = random() << 1; /* Even numbers only */
+               /* Use even numbers only */
+               dsm_control_handle = pg_prng_uint32(&pg_global_prng_state) << 1;
                if (dsm_control_handle == DSM_HANDLE_INVALID)
                        continue;
                if (dsm_impl_op(DSM_OP_CREATE, dsm_control_handle, segsize,
@@ -536,7 +538,8 @@ dsm_create(Size size, int flags)
                for (;;)
                {
                        Assert(seg->mapped_address == NULL && seg->mapped_size == 0);
-                       seg->handle = random() << 1;    /* Even numbers only */
+                       /* Use even numbers only */
+                       seg->handle = pg_prng_uint32(&pg_global_prng_state) << 1;
                        if (seg->handle == DSM_HANDLE_INVALID)  /* Reserve sentinel */
                                continue;
                        if (dsm_impl_op(DSM_OP_CREATE, seg->handle, size, &seg->impl_private,
@@ -1237,7 +1240,7 @@ make_main_region_dsm_handle(int slot)
         */
        handle = 1;
        handle |= slot << 1;
-       handle |= random() << (pg_leftmost_one_pos32(dsm_control->maxitems) + 1);
+       handle |= pg_prng_uint32(&pg_global_prng_state) << (pg_leftmost_one_pos32(dsm_control->maxitems) + 1);
        return handle;
 }
 
index 829b792fcb1cd5fc85b14a7459c902b34631c6ac..b25b7ee421d34ee496da541e5429b01c82fb96c8 100644 (file)
@@ -30,9 +30,10 @@ ifdef TAS
 TASPATH = $(top_builddir)/src/backend/port/tas.o
 endif
 
-s_lock_test: s_lock.c $(top_builddir)/src/port/libpgport.a
+s_lock_test: s_lock.c $(top_builddir)/src/common/libpgcommon.a $(top_builddir)/src/port/libpgport.a
        $(CC) $(CPPFLAGS) $(CFLAGS) -DS_LOCK_TEST=1 $(srcdir)/s_lock.c \
-               $(TASPATH) -L $(top_builddir)/src/port -lpgport -o s_lock_test
+               $(TASPATH) -L $(top_builddir)/src/common -lpgcommon \
+               -L $(top_builddir)/src/port -lpgport -o s_lock_test
 
 # see notes in src/backend/parser/Makefile
 lwlocknames.c: lwlocknames.h
index 91322a40c1cb571c87bb268d45ebbd6be083b6f8..75c0f4535cd5e299a8400d526f59c8b50465694a 100644 (file)
@@ -50,6 +50,7 @@
 #include <time.h>
 #include <unistd.h>
 
+#include "common/pg_prng.h"
 #include "port/atomics.h"
 #include "storage/s_lock.h"
 
@@ -144,7 +145,7 @@ perform_spin_delay(SpinDelayStatus *status)
 
                /* increase delay by a random fraction between 1X and 2X */
                status->cur_delay += (int) (status->cur_delay *
-                                                                       ((double) random() / (double) MAX_RANDOM_VALUE) + 0.5);
+                                                                       pg_prng_double(&pg_global_prng_state) + 0.5);
                /* wrap back to minimum delay when max is exceeded */
                if (status->cur_delay > MAX_DELAY_USEC)
                        status->cur_delay = MIN_DELAY_USEC;
@@ -303,7 +304,7 @@ volatile struct test_lock_struct test_lock;
 int
 main()
 {
-       srandom((unsigned int) time(NULL));
+       pg_prng_seed(&pg_global_prng_state, (uint64) time(NULL));
 
        test_lock.pad1 = test_lock.pad2 = 0x44;
 
index 0775abe35de497a82654a3d54b4bdc2a48a9a85b..82de01cdc67b1e7010e80eb870ff9823de3f708c 100644 (file)
@@ -42,6 +42,7 @@
 #include "catalog/pg_type.h"
 #include "commands/async.h"
 #include "commands/prepare.h"
+#include "common/pg_prng.h"
 #include "executor/spi.h"
 #include "jit/jit.h"
 #include "libpq/libpq.h"
@@ -2355,13 +2356,13 @@ check_log_duration(char *msec_str, bool was_logged)
 
                /*
                 * Do not log if log_statement_sample_rate = 0. Log a sample if
-                * log_statement_sample_rate <= 1 and avoid unnecessary random() call
-                * if log_statement_sample_rate = 1.
+                * log_statement_sample_rate <= 1 and avoid unnecessary PRNG call if
+                * log_statement_sample_rate = 1.
                 */
                if (exceeded_sample_duration)
                        in_sample = log_statement_sample_rate != 0 &&
                                (log_statement_sample_rate == 1 ||
-                                random() <= log_statement_sample_rate * MAX_RANDOM_VALUE);
+                                pg_prng_double(&pg_global_prng_state) <= log_statement_sample_rate);
 
                if (exceeded_duration || in_sample || log_duration || xact_is_sampled)
                {
index 098bbb372bfc5d5bce9716aabdcf9bf721132968..455e5d8cbea6812b436e34075aa69d28d88e71b1 100644 (file)
@@ -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;
 
 /* 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 (unlikely(!pg_prng_strong_seed(&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);
+       /* pg_prng_double produces desired result range [0.0 - 1.0) */
+       result = pg_prng_double(&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();
index 0c327e823f7155ea7bd5c5e189d68331a986a34e..d1a2416e8b7b8309663f50f94a511f76ac4e6288 100644 (file)
@@ -37,7 +37,7 @@
  */
 BlockNumber
 BlockSampler_Init(BlockSampler bs, BlockNumber nblocks, int samplesize,
-                                 long randseed)
+                                 uint32 randseed)
 {
        bs->N = nblocks;                        /* measured table size */
 
@@ -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,11 @@ 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(pg_prng_uint32(&pg_global_prng_state),
+                                                         &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 +155,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 +187,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 +216,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 +231,22 @@ reservoir_get_next_S(ReservoirState rs, double t, int n)
  *----------
  */
 void
-sampler_random_init_state(long seed, SamplerRandomState randstate)
+sampler_random_init_state(uint32 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) 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_double returns a value in [0.0 - 1.0), so we must reject 0.0 */
        do
        {
-               res = pg_erand48(randstate);
-       } while (res == 0.0);
+               res = pg_prng_double(randstate);
+       } while (unlikely(res == 0.0));
        return res;
 }
 
@@ -261,27 +260,36 @@ sampler_random_fract(SamplerRandomState randstate)
  * except that a common random state is used across all callers.
  */
 static ReservoirStateData oldrs;
+static bool oldrs_initialized = false;
 
 double
 anl_random_fract(void)
 {
        /* initialize if first time through */
-       if (oldrs.randstate[0] == 0)
-               sampler_random_init_state(random(), oldrs.randstate);
+       if (unlikely(!oldrs_initialized))
+       {
+               sampler_random_init_state(pg_prng_uint32(&pg_global_prng_state),
+                                                                 &oldrs.randstate);
+               oldrs_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 (unlikely(!oldrs_initialized))
+       {
+               sampler_random_init_state(pg_prng_uint32(&pg_global_prng_state),
+                                                                 &oldrs.randstate);
+               oldrs_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
index 3c61c789e46e0e6992e4adebea5f5500127e12cb..403adb0ee7850d98b097c4ad8fc247cc0fea5386 100644 (file)
@@ -66,6 +66,7 @@
 #include "common/file_perm.h"
 #include "common/file_utils.h"
 #include "common/logging.h"
+#include "common/pg_prng.h"
 #include "common/restricted_token.h"
 #include "common/string.h"
 #include "common/username.h"
@@ -880,9 +881,10 @@ choose_dsm_implementation(void)
 {
 #ifdef HAVE_SHM_OPEN
        int                     ntries = 10;
+       pg_prng_state prng_state;
 
-       /* Initialize random(); this function is its only user in this program. */
-       srandom((unsigned int) (getpid() ^ time(NULL)));
+       /* Initialize prng; this function is its only user in this program. */
+       pg_prng_seed(&prng_state, (uint64) (getpid() ^ time(NULL)));
 
        while (ntries > 0)
        {
@@ -890,7 +892,7 @@ choose_dsm_implementation(void)
                char            name[64];
                int                     fd;
 
-               handle = random();
+               handle = pg_prng_uint32(&prng_state);
                snprintf(name, 64, "/PostgreSQL.%u", handle);
                if ((fd = shm_open(name, O_CREAT | O_RDWR | O_EXCL, 0600)) != -1)
                {
index fef31844fa0a01932414fa2f8d1cc51d6683deab..ddabf64c583d5d46332b5b98833ceba6c2516d3d 100644 (file)
@@ -15,6 +15,7 @@
 
 #include "access/xlogdefs.h"
 #include "common/logging.h"
+#include "common/pg_prng.h"
 #include "getopt_long.h"
 
 /*
@@ -117,6 +118,8 @@ main(int argc, char *argv[])
        pqsignal(SIGHUP, signal_cleanup);
 #endif
 
+       pg_prng_seed(&pg_global_prng_state, (uint64) time(NULL));
+
        prepare_buf();
 
        test_open();
@@ -233,7 +236,7 @@ prepare_buf(void)
 
        /* write random data into buffer */
        for (ops = 0; ops < DEFAULT_XLOG_SEG_SIZE; ops++)
-               full_buf[ops] = random();
+               full_buf[ops] = (char) pg_prng_int32(&pg_global_prng_state);
 
        buf = (char *) TYPEALIGN(XLOG_BLCKSZ, full_buf);
 }
index c12b6f0615914df64bad4401a2b4bc90ba9a60e0..ea9639984cf9eb66676b9a5ca80db2327eb5f84e 100644 (file)
@@ -59,6 +59,7 @@
 
 #include "common/int.h"
 #include "common/logging.h"
+#include "common/pg_prng.h"
 #include "common/string.h"
 #include "common/username.h"
 #include "fe_utils/cancel.h"
@@ -350,16 +351,8 @@ typedef struct StatsData
  */
 pg_time_usec_t epoch_shift;
 
-/*
- * 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;
@@ -461,7 +454,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 */
@@ -498,9 +491,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 */
@@ -898,42 +891,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)
-{
-       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);
+initRandomState(pg_prng_state *state)
+{
+       pg_prng_seed(state, pg_prng_uint64(&base_random_sequence));
 }
 
+
 /*
- * Random number generator: uniform distribution from min to max inclusive.
+ * random number generator: 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.  This is not checked.
  */
 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_uint64_range(state, 0, max - min);
 }
 
 /*
@@ -942,7 +921,7 @@ getrand(RandomState *random_state, int64 min, int64 max)
  * value is exp(-parameter).
  */
 static int64
-getExponentialRand(RandomState *random_state, int64 min, int64 max,
+getExponentialRand(pg_prng_state *state, int64 min, int64 max,
                                   double parameter)
 {
        double          cut,
@@ -952,8 +931,8 @@ getExponentialRand(RandomState *random_state, int64 min, int64 max,
        /* abort if wrong parameter, but must really be checked beforehand */
        Assert(parameter > 0.0);
        cut = exp(-parameter);
-       /* erand in [0, 1), uniform in (0, 1] */
-       uniform = 1.0 - pg_erand48(random_state->xseed);
+       /* pg_prng_double value in [0, 1), uniform in (0, 1] */
+       uniform = 1.0 - pg_prng_double(state);
 
        /*
         * inner expression in (cut, 1] (if parameter > 0), rand in [0, 1)
@@ -966,7 +945,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,
+getGaussianRand(pg_prng_state *state, int64 min, int64 max,
                                double parameter)
 {
        double          stdev;
@@ -990,13 +969,13 @@ getGaussianRand(RandomState *random_state, int64 min, int64 max,
        do
        {
                /*
-                * pg_erand48 generates [0,1), but for the basic version of the
+                * pg_prng_double 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
+                * are expected to be 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_double(state);
+               double          rand2 = 1.0 - pg_prng_double(state);
 
                /* Box-Muller basic form transform */
                double          var_sqrt = sqrt(-2.0 * log(rand1));
@@ -1026,7 +1005,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
@@ -1034,8 +1013,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);
+       /* pg_prng_double value in [0, 1), uniform in (0, 1] */
+       uniform = 1.0 - pg_prng_double(state);
 
        return (int64) (-log(uniform) * center + 0.5);
 }
@@ -1048,7 +1027,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,
@@ -1063,8 +1042,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_double(state);
+               v = pg_prng_double(state);
 
                x = floor(pow(u, -1.0 / (s - 1.0)));
 
@@ -1078,14 +1057,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);
 }
 
 /*
@@ -1142,7 +1121,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.
  *
@@ -1152,8 +1131,8 @@ getHashMurmur2(int64 val, uint64 seed)
 static int64
 permute(const int64 val, const int64 isize, const int64 seed)
 {
-       RandomState random_state1;
-       RandomState random_state2;
+       /* using a high-end PRNG is probably overkill */
+       pg_prng_state state;
        uint64          size;
        uint64          v;
        int                     masklen;
@@ -1163,14 +1142,8 @@ permute(const int64 val, const int64 isize, const int64 seed)
        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;
@@ -1216,8 +1189,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_uint64(&state) & mask) | 1;
+               r = pg_prng_uint64(&state) & mask;
                if (v <= mask)
                {
                        v = ((v * m) ^ r) & mask;
@@ -1225,8 +1198,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_uint64(&state) & mask) | 1;
+               r = pg_prng_uint64(&state) & mask;
                t = size - 1 - v;
                if (t <= mask)
                {
@@ -1236,7 +1209,7 @@ permute(const int64 val, const int64 isize, const int64 seed)
                }
 
                /* Random offset */
-               r = (uint64) getrand(&random_state2, 0, size - 1);
+               r = pg_prng_uint64_range(&state, 0, size - 1);
                v = (v + r) % size;
        }
 
@@ -3831,7 +3804,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_double(&thread->ts_sample_rs) > sample_rate)
                return;
 
        /* should we aggregate the results or not? */
@@ -5770,12 +5743,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;
+       /* Initialize base_random_sequence using seed */
+       pg_prng_seed(&base_random_sequence, (uint64) iseed);
 
        return true;
 }
@@ -6449,9 +6421,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_uint64(&base_random_sequence);
 
                for (i = 0; i < nclients; i++)
                        if (!putVariableInt(&state[i], "startup", "default_seed", (int64) seed))
index 69ffa595dd4b9c76eaff91abb7b487952dd41a7a..97ed6c71edf3bb74ddccc63b670c7693777eef2b 100644 (file)
@@ -369,7 +369,6 @@ $node->append_conf('postgresql.conf',
 $node->reload;
 
 # test expressions
-# command 1..3 and 23 depend on random seed which is used to call srandom.
 $node->pgbench(
        '--random-seed=5432 -t 1 -Dfoo=-10.1 -Dbla=false -Di=+3 -Dn=null -Dt=t -Df=of -Dd=1.0',
        0,
@@ -378,9 +377,9 @@ $node->pgbench(
                qr{setting random seed to 5432\b},
 
                # 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
+               # deterministic; but see also magic values in checks 111,113.
+               qr{command=1.: int 17\b},      # uniform random
+               qr{command=2.: int 104\b},     # exponential random
                qr{command=3.: int 1498\b},    # gaussian random
                qr{command=4.: int 4\b},
                qr{command=5.: int 5\b},
@@ -394,7 +393,7 @@ $node->pgbench(
                qr{command=15.: double 15\b},
                qr{command=16.: double 16\b},
                qr{command=17.: double 17\b},
-               qr{command=20.: int 1\b},    # zipfian random
+               qr{command=20.: int 3\b},    # zipfian random
                qr{command=21.: double -27\b},
                qr{command=22.: double 1024\b},
                qr{command=23.: double 1\b},
@@ -448,6 +447,7 @@ $node->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',
        {
@@ -591,8 +591,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) = 0 and permute(1, 2, 5431) = 1 and \
+             permute(0, 2, 5433) = 1 and permute(1, 2, 5433) = 0)
+-- check permute's portability across architectures
+\set size debug(:max - 10)
+\set t debug(permute(:size-1, :size, 5432) = 520382784483822430 and \
+             permute(:size-2, :size, 5432) = 1143715004660802862 and \
+             permute(:size-3, :size, 5432) = 447293596416496998 and \
+             permute(:size-4, :size, 5432) = 916527772266572956 and \
+             permute(:size-5, :size, 5432) = 2763809008686028849 and \
+             permute(:size-6, :size, 5432) = 8648551549198294572 and \
+             permute(:size-7, :size, 5432) = 4542876852200565125)
 }
        });
 
index 880722fcf535a9a6af58b7131f5c271cf50a364a..31c0dd366df8678bdd67fb512d5d4d68becce702 100644 (file)
@@ -66,6 +66,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 (file)
index 0000000..a0b6bcb
--- /dev/null
@@ -0,0 +1,247 @@
+/*-------------------------------------------------------------------------
+ *
+ * Pseudo-Random Number Generator
+ *
+ * We use Blackman and Vigna's xoroshiro128** 1.0 algorithm
+ * to have a small, fast PRNG suitable for generating reasonably
+ * good-quality 64-bit data.  This should not be considered
+ * cryptographically strong, however.
+ *
+ * About these generators: https://prng.di.unimi.it/
+ * See also https://en.wikipedia.org/wiki/List_of_random_number_generators
+ *
+ * Copyright (c) 2021, PostgreSQL Global Development Group
+ *
+ * src/common/pg_prng.c
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#include "c.h"
+
+#include <math.h>                              /* for ldexp() */
+
+#include "common/pg_prng.h"
+#include "port/pg_bitutils.h"
+
+/* process-wide state vector */
+pg_prng_state pg_global_prng_state;
+
+
+/*
+ * 64-bit rotate left
+ */
+static inline uint64
+rotl(uint64 x, int bits)
+{
+       return (x << bits) | (x >> (64 - bits));
+}
+
+/*
+ * The basic xoroshiro128** algorithm.
+ * Generates and returns a 64-bit uniformly distributed number,
+ * updating the state vector for next time.
+ *
+ * Note: the state vector must not be all-zeroes, as that is a fixed point.
+ */
+static uint64
+xoroshiro128ss(pg_prng_state *state)
+{
+       uint64          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;
+}
+
+/*
+ * We use this generator just to fill the xoroshiro128** state vector
+ * from a 64-bit seed.
+ */
+static uint64
+splitmix64(uint64 *state)
+{
+       /* state update */
+       uint64          val = (*state += UINT64CONST(0x9E3779B97f4A7C15));
+
+       /* value extraction */
+       val = (val ^ (val >> 30)) * UINT64CONST(0xBF58476D1CE4E5B9);
+       val = (val ^ (val >> 27)) * UINT64CONST(0x94D049BB133111EB);
+
+       return val ^ (val >> 31);
+}
+
+/*
+ * Initialize the PRNG state from a 64-bit integer,
+ * taking care that we don't produce all-zeroes.
+ */
+void
+pg_prng_seed(pg_prng_state *state, uint64 seed)
+{
+       state->s0 = splitmix64(&seed);
+       state->s1 = splitmix64(&seed);
+       /* Let's just make sure we didn't get all-zeroes */
+       (void) pg_prng_seed_check(state);
+}
+
+/*
+ * Initialize the PRNG state from a double in the range [-1.0, 1.0],
+ * taking care that we don't produce all-zeroes.
+ */
+void
+pg_prng_fseed(pg_prng_state *state, double fseed)
+{
+       /* Assume there's about 52 mantissa bits; the sign contributes too. */
+       int64           seed = ((double) ((UINT64CONST(1) << 52) - 1)) * fseed;
+
+       pg_prng_seed(state, (uint64) seed);
+}
+
+/*
+ * Validate a PRNG seed value.
+ */
+bool
+pg_prng_seed_check(pg_prng_state *state)
+{
+       /*
+        * If the seeding mechanism chanced to produce all-zeroes, insert
+        * something nonzero.  Anything would do; use Knuth's LCG parameters.
+        */
+       if (unlikely(state->s0 == 0 && state->s1 == 0))
+       {
+               state->s0 = UINT64CONST(0x5851F42D4C957F2D);
+               state->s1 = UINT64CONST(0x14057B7EF767814F);
+       }
+
+       /* As a convenience for the pg_prng_strong_seed macro, return true */
+       return true;
+}
+
+/*
+ * Select a random uint64 uniformly from the range [0, PG_UINT64_MAX].
+ */
+uint64
+pg_prng_uint64(pg_prng_state *state)
+{
+       return xoroshiro128ss(state);
+}
+
+/*
+ * Select a random uint64 uniformly from the range [rmin, rmax].
+ * If the range is empty, rmin is always produced.
+ */
+uint64
+pg_prng_uint64_range(pg_prng_state *state, uint64 rmin, uint64 rmax)
+{
+       uint64          val;
+
+       if (likely(rmax > rmin))
+       {
+               /*
+                * Use bitmask rejection method to generate an offset in 0..range.
+                * Each generated val is less than twice "range", so on average we
+                * should not have to iterate more than twice.
+                */
+               uint64          range = rmax - rmin;
+               uint32          rshift = 63 - pg_leftmost_one_pos64(range);
+
+               do
+               {
+                       val = xoroshiro128ss(state) >> rshift;
+               } while (val > range);
+       }
+       else
+               val = 0;
+
+       return rmin + val;
+}
+
+/*
+ * Select a random int64 uniformly from the range [PG_INT64_MIN, PG_INT64_MAX].
+ */
+int64
+pg_prng_int64(pg_prng_state *state)
+{
+       return (int64) xoroshiro128ss(state);
+}
+
+/*
+ * Select a random int64 uniformly from the range [0, PG_INT64_MAX].
+ */
+int64
+pg_prng_int64p(pg_prng_state *state)
+{
+       return (int64) (xoroshiro128ss(state) & UINT64CONST(0x7FFFFFFFFFFFFFFF));
+}
+
+/*
+ * Select a random uint32 uniformly from the range [0, PG_UINT32_MAX].
+ */
+uint32
+pg_prng_uint32(pg_prng_state *state)
+{
+       /*
+        * Although xoroshiro128** is not known to have any weaknesses in
+        * randomness of low-order bits, we prefer to use the upper bits of its
+        * result here and below.
+        */
+       uint64          v = xoroshiro128ss(state);
+
+       return (uint32) (v >> 32);
+}
+
+/*
+ * Select a random int32 uniformly from the range [PG_INT32_MIN, PG_INT32_MAX].
+ */
+int32
+pg_prng_int32(pg_prng_state *state)
+{
+       uint64          v = xoroshiro128ss(state);
+
+       return (int32) (v >> 32);
+}
+
+/*
+ * Select a random int32 uniformly from the range [0, PG_INT32_MAX].
+ */
+int32
+pg_prng_int32p(pg_prng_state *state)
+{
+       uint64          v = xoroshiro128ss(state);
+
+       return (int32) (v >> 33);
+}
+
+/*
+ * Select a random double uniformly from the range [0.0, 1.0).
+ *
+ * Note: if you want a result in the range (0.0, 1.0], the standard way
+ * to get that is "1.0 - pg_prng_double(state)".
+ */
+double
+pg_prng_double(pg_prng_state *state)
+{
+       uint64          v = xoroshiro128ss(state);
+
+       /*
+        * As above, assume there's 52 mantissa bits in a double.  This result
+        * could round to 1.0 if double's precision is less than that; but we
+        * assume IEEE float arithmetic elsewhere in Postgres, so this seems OK.
+        */
+       return ldexp((double) (v >> (64 - 52)), -52);
+}
+
+/*
+ * Select a random boolean value.
+ */
+bool
+pg_prng_bool(pg_prng_state *state)
+{
+       uint64          v = xoroshiro128ss(state);
+
+       return (bool) (v >> 63);
+}
diff --git a/src/include/common/pg_prng.h b/src/include/common/pg_prng.h
new file mode 100644 (file)
index 0000000..623c65a
--- /dev/null
@@ -0,0 +1,60 @@
+/*-------------------------------------------------------------------------
+ *
+ * Pseudo-Random Number Generator
+ *
+ * Copyright (c) 2021, PostgreSQL Global Development Group
+ *
+ * src/include/common/pg_prng.h
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef PG_PRNG_H
+#define PG_PRNG_H
+
+/*
+ * State vector for PRNG generation.  Callers should treat this as an
+ * opaque typedef, but we expose its definition to allow it to be
+ * embedded in other structs.
+ */
+typedef struct pg_prng_state
+{
+       uint64          s0,
+                               s1;
+} pg_prng_state;
+
+/*
+ * Callers not needing local PRNG series may use this global state vector,
+ * after initializing it with one of the pg_prng_...seed functions.
+ */
+extern PGDLLIMPORT pg_prng_state pg_global_prng_state;
+
+extern void pg_prng_seed(pg_prng_state *state, uint64 seed);
+extern void pg_prng_fseed(pg_prng_state *state, double fseed);
+extern bool pg_prng_seed_check(pg_prng_state *state);
+
+/*
+ * Initialize the PRNG state from the pg_strong_random source,
+ * taking care that we don't produce all-zeroes.  If this returns false,
+ * caller should initialize the PRNG state from some other random seed,
+ * using pg_prng_[f]seed.
+ *
+ * We implement this as a macro, so that the pg_strong_random() call is
+ * in the caller.  If it were in pg_prng.c, programs using pg_prng.c
+ * but not needing strong seeding would nonetheless be forced to pull in
+ * pg_strong_random.c and thence OpenSSL.
+ */
+#define pg_prng_strong_seed(state) \
+       (pg_strong_random((void *) (state), sizeof(pg_prng_state)) ? \
+        pg_prng_seed_check(state) : false)
+
+extern uint64 pg_prng_uint64(pg_prng_state *state);
+extern uint64 pg_prng_uint64_range(pg_prng_state *state, uint64 rmin, uint64 rmax);
+extern int64 pg_prng_int64(pg_prng_state *state);
+extern int64 pg_prng_int64p(pg_prng_state *state);
+extern uint32 pg_prng_uint32(pg_prng_state *state);
+extern int32 pg_prng_int32(pg_prng_state *state);
+extern int32 pg_prng_int32p(pg_prng_state *state);
+extern double pg_prng_double(pg_prng_state *state);
+extern bool pg_prng_bool(pg_prng_state *state);
+
+#endif                                                 /* PG_PRNG_H */
index 24dcdfb6cce408cd9e3998c6e9430a43a5a032f8..b9635a2170a03d497cdb16c16deefa02a7e7bef1 100644 (file)
@@ -22,6 +22,7 @@
 #ifndef GEQO_H
 #define GEQO_H
 
+#include "common/pg_prng.h"
 #include "nodes/pathnodes.h"
 #include "optimizer/geqo_gene.h"
 
@@ -73,7 +74,7 @@ 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() */
+       pg_prng_state random_state; /* PRNG state */
 } GeqoPrivateData;
 
 
index 7f4aad44397f0739cf40362cefb92e99d6d11d9b..958fdaf2997571482bda18b67c06f56b243378bf 100644 (file)
 
 extern void geqo_set_seed(PlannerInfo *root, double seed);
 
-/* geqo_rand returns a random float value between 0 and 1 inclusive */
+/* geqo_rand returns a random float value in the range [0.0, 1.0) */
 extern double geqo_rand(PlannerInfo *root);
 
 /* geqo_randint returns integer value between lower and upper inclusive */
-#define geqo_randint(root, upper, lower) \
-       ( (int) floor( geqo_rand(root)*(((upper)-(lower))+0.999999) ) + (lower) )
+extern int     geqo_randint(PlannerInfo *root, int upper, int lower);
 
 #endif                                                 /* GEQO_RANDOM_H */
index ca3592465ede7b86ea416e74c262e5aeac6dd8fc..d793734fe10c0473930a961cb81de30fabfc8b4f 100644 (file)
 /* Define to 1 if you have the `pwrite' function. */
 #undef HAVE_PWRITE
 
-/* Define to 1 if you have the `random' function. */
-#undef HAVE_RANDOM
-
 /* Define to 1 if you have the <readline.h> header file. */
 #undef HAVE_READLINE_H
 
 /* Define to 1 if you have spinlocks. */
 #undef HAVE_SPINLOCKS
 
-/* Define to 1 if you have the `srandom' function. */
-#undef HAVE_SRANDOM
-
 /* Define to 1 if stdbool.h conforms to C99. */
 #undef HAVE_STDBOOL_H
 
index 225c5b87cc799f3e1ada8c085582c6b5ca757cc7..427ffabd143c3a7f049d4b9d221c3ce981827c8c 100644 (file)
  */
 #define DEFAULT_EVENT_SOURCE  "PostgreSQL"
 
-/*
- * The random() function is expected to yield values between 0 and
- * MAX_RANDOM_VALUE.  Currently, all known implementations yield
- * 0..2^31-1, so we just hardwire this constant.  We could do a
- * configure test if it proves to be necessary.  CAUTION: Think not to
- * replace this with RAND_MAX.  RAND_MAX defines the maximum value of
- * the older rand() function, which is often different from --- and
- * considerably inferior to --- random().
- */
-#define MAX_RANDOM_VALUE  PG_INT32_MAX
-
 /*
  * On PPC machines, decide whether to use the mutex hint bit in LWARX
  * instructions.  Setting the hint bit will slightly improve spinlock
index 49b4d38131bf84a2bf5173a41ff942e9630be2be..806fb795edc217f13cf71e9baa57db97d2d6158c 100644 (file)
@@ -362,11 +362,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
@@ -452,10 +447,6 @@ extern size_t strlcpy(char *dst, const char *src, size_t siz);
 extern size_t strnlen(const char *str, size_t maxlen);
 #endif
 
-#if !defined(HAVE_RANDOM)
-extern long random(void);
-#endif
-
 #ifndef HAVE_SETENV
 extern int     setenv(const char *name, const char *value, int overwrite);
 #endif
@@ -464,10 +455,6 @@ extern int setenv(const char *name, const char *value, int overwrite);
 extern int     unsetenv(const char *name);
 #endif
 
-#ifndef HAVE_SRANDOM
-extern void srandom(unsigned int seed);
-#endif
-
 #ifndef HAVE_DLOPEN
 extern void *dlopen(const char *file, int mode);
 extern void *dlsym(void *handle, const char *symbol);
index a58d14281bc3eb74bc1ab882f552441f162a151e..74ce8f9dc3cd240a4e06188662f7be993ac4a540 100644 (file)
 #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);
+extern void sampler_random_init_state(uint32 seed,
+                                                                         pg_prng_state *randstate);
+extern double sampler_random_fract(pg_prng_state *randstate);
 
 /* Block sampling methods */
 
@@ -32,13 +31,13 @@ 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;
 
 extern BlockNumber BlockSampler_Init(BlockSampler bs, BlockNumber nblocks,
-                                                                        int samplesize, long randseed);
+                                                                        int samplesize, uint32 randseed);
 extern bool BlockSampler_HasMore(BlockSampler bs);
 extern BlockNumber BlockSampler_Next(BlockSampler bs);
 
@@ -47,7 +46,7 @@ extern BlockNumber BlockSampler_Next(BlockSampler bs);
 typedef struct
 {
        double          W;
-       SamplerRandomState randstate;   /* random generator state */
+       pg_prng_state randstate;        /* random generator state */
 } ReservoirStateData;
 
 typedef ReservoirStateData *ReservoirState;
index 52dbf5783f00aec620ba15b66df6b4017e0cfc6e..b3754d8940a8959900f9d4a07769779d101d329b 100644 (file)
@@ -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 (file)
index a82ea94..0000000
+++ /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 (file)
index 2dd59a0..0000000
+++ /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 (file)
index cf1007b..0000000
+++ /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);
-}
index 96c50114286a5892275bf81ac68f219677dd3616..7109f0476c43d40c8a9b9d4958e316163b569c0e 100644 (file)
@@ -12,6 +12,7 @@
  */
 #include "postgres.h"
 
+#include "common/pg_prng.h"
 #include "fmgr.h"
 #include "lib/bloomfilter.h"
 #include "miscadmin.h"
@@ -83,9 +84,8 @@ create_and_test_bloom(int power, int64 nelements, int callerseed)
         * Generate random seed, or use caller's.  Seed should always be a
         * positive value less than or equal to PG_INT32_MAX, to ensure that any
         * random seed can be recreated through callerseed if the need arises.
-        * (Don't assume that RAND_MAX cannot exceed PG_INT32_MAX.)
         */
-       seed = callerseed < 0 ? random() % PG_INT32_MAX : callerseed;
+       seed = callerseed < 0 ? pg_prng_int32p(&pg_global_prng_state) : callerseed;
 
        /* Create Bloom filter, populate it, and report on false positive rate */
        filter = bloom_create(nelements, bloom_work_mem, seed);
index 21c6f49b3789a98a1436a98293179de7f85cc2c5..58ffff89b38a6669c3b9224a2fe068001961113d 100644 (file)
@@ -12,6 +12,7 @@
  */
 #include "postgres.h"
 
+#include "common/pg_prng.h"
 #include "fmgr.h"
 #include "lib/integerset.h"
 #include "miscadmin.h"
@@ -248,8 +249,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 = x % (last_int + 1000);
+               x = pg_prng_uint64_range(&pg_global_prng_state, 0, last_int + 1000);
 
                /* Do we expect this value to be present in the set? */
                if (x >= last_int)
@@ -571,7 +571,7 @@ test_huge_distances(void)
         */
        while (num_values < 1000)
        {
-               val += pg_lrand48();
+               val += pg_prng_uint32(&pg_global_prng_state);
                values[num_values++] = val;
        }
 
index 713ebd1b26d921c8f0a4623cf0eaa856f628a687..646c6e42fa8551ecad014fb6dd5f50407223c13f 100644 (file)
@@ -13,6 +13,7 @@
 
 #include "postgres.h"
 
+#include "common/pg_prng.h"
 #include "fmgr.h"
 #include "lib/rbtree.h"
 #include "utils/memutils.h"
@@ -108,7 +109,7 @@ GetPermutation(int size)
         */
        for (i = 1; i < size; i++)
        {
-               int                     j = random() % (i + 1);
+               int                     j = pg_prng_uint64_range(&pg_global_prng_state, 0, i);
 
                if (j < i)                              /* avoid fetching undefined data if j=i */
                        permutation[i] = permutation[j];
@@ -320,7 +321,7 @@ testdelete(int size, int delsize)
 
        for (i = 0; i < delsize; i++)
        {
-               int                     k = random() % size;
+               int                     k = pg_prng_uint64_range(&pg_global_prng_state, 0, size - 1);
 
                while (chosen[k])
                        k = (k + 1) % size;
index 41172eab36421cc7fdb54f5f851aef6ecd63f269..5a374a4727403ae08a12c1d304bc750c458a500a 100644 (file)
@@ -99,9 +99,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
@@ -127,9 +127,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 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})
        {
index b0726c2ebab32986d05184b575d78710ab89cc2c..1cb31d929fea8a5c30d9a5a979ca2d64cf99a1d4 100644 (file)
@@ -342,7 +342,6 @@ sub GenerateFiles
                HAVE_PTHREAD_IS_THREADED_NP => undef,
                HAVE_PTHREAD_PRIO_INHERIT   => undef,
                HAVE_PWRITE                 => undef,
-               HAVE_RANDOM                 => undef,
                HAVE_READLINE_H             => undef,
                HAVE_READLINE_HISTORY_H     => undef,
                HAVE_READLINE_READLINE_H    => undef,
@@ -363,7 +362,6 @@ sub GenerateFiles
                HAVE_SHM_OPEN                            => undef,
                HAVE_SOCKLEN_T                           => 1,
                HAVE_SPINLOCKS                           => 1,
-               HAVE_SRANDOM                             => undef,
                HAVE_STDBOOL_H                           => 1,
                HAVE_STDINT_H                            => 1,
                HAVE_STDLIB_H                            => 1,
index 71c345969a854b286870bd41d41f170cc96e27e3..47cc685ab4585f45a9442df699a48e6ad747591a 100644 (file)
@@ -27,6 +27,7 @@
 #endif
 
 #include "common/int128.h"
+#include "common/pg_prng.h"
 
 /*
  * We assume the parts of this union are laid out compatibly.
@@ -61,27 +62,11 @@ my_int128_compare(int128 x, int128 y)
        return 0;
 }
 
-/*
- * Get a random uint64 value.
- * We don't assume random() is good for more than 16 bits.
- */
-static uint64
-get_random_uint64(void)
-{
-       uint64          x;
-
-       x = (uint64) (random() & 0xFFFF) << 48;
-       x |= (uint64) (random() & 0xFFFF) << 32;
-       x |= (uint64) (random() & 0xFFFF) << 16;
-       x |= (uint64) (random() & 0xFFFF);
-       return x;
-}
-
 /*
  * Main program.
  *
  * Generates a lot of random numbers and tests the implementation for each.
- * The results should be reproducible, since we don't call srandom().
+ * The results should be reproducible, since we use a fixed PRNG seed.
  *
  * You can give a loop count if you don't like the default 1B iterations.
  */
@@ -90,6 +75,8 @@ main(int argc, char **argv)
 {
        long            count;
 
+       pg_prng_seed(&pg_global_prng_state, 0);
+
        if (argc >= 2)
                count = strtol(argv[1], NULL, 0);
        else
@@ -97,9 +84,9 @@ main(int argc, char **argv)
 
        while (count-- > 0)
        {
-               int64           x = get_random_uint64();
-               int64           y = get_random_uint64();
-               int64           z = get_random_uint64();
+               int64           x = pg_prng_uint64(&pg_global_prng_state);
+               int64           y = pg_prng_uint64(&pg_global_prng_state);
+               int64           z = pg_prng_uint64(&pg_global_prng_state);
                test128         t1;
                test128         t2;
 
@@ -151,7 +138,7 @@ main(int argc, char **argv)
                t1.hl.hi = x;
                t1.hl.lo = y;
                t2.hl.hi = z;
-               t2.hl.lo = get_random_uint64();
+               t2.hl.lo = pg_prng_uint64(&pg_global_prng_state);
 
                if (my_int128_compare(t1.i128, t2.i128) !=
                        int128_compare(t1.I128, t2.I128))