aboutsummaryrefslogtreecommitdiff
path: root/src/backend/utils/adt/pseudorandomfuncs.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/utils/adt/pseudorandomfuncs.c')
-rw-r--r--src/backend/utils/adt/pseudorandomfuncs.c185
1 files changed, 185 insertions, 0 deletions
diff --git a/src/backend/utils/adt/pseudorandomfuncs.c b/src/backend/utils/adt/pseudorandomfuncs.c
new file mode 100644
index 00000000000..8e82c7078c5
--- /dev/null
+++ b/src/backend/utils/adt/pseudorandomfuncs.c
@@ -0,0 +1,185 @@
+/*-------------------------------------------------------------------------
+ *
+ * pseudorandomfuncs.c
+ * Functions giving SQL access to a pseudorandom number generator.
+ *
+ * Portions Copyright (c) 1996-2024, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * IDENTIFICATION
+ * src/backend/utils/adt/pseudorandomfuncs.c
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include <math.h>
+
+#include "common/pg_prng.h"
+#include "miscadmin.h"
+#include "utils/fmgrprotos.h"
+#include "utils/numeric.h"
+#include "utils/timestamp.h"
+
+/* Shared PRNG state used by all the random functions */
+static pg_prng_state prng_state;
+static bool prng_seed_set = false;
+
+/*
+ * initialize_prng() -
+ *
+ * Initialize (seed) the PRNG, if not done yet in this process.
+ */
+static void
+initialize_prng(void)
+{
+ if (unlikely(!prng_seed_set))
+ {
+ /*
+ * If possible, seed the PRNG using high-quality random bits. Should
+ * that fail for some reason, we fall back on a lower-quality seed
+ * based on current time and PID.
+ */
+ if (unlikely(!pg_prng_strong_seed(&prng_state)))
+ {
+ TimestampTz now = GetCurrentTimestamp();
+ uint64 iseed;
+
+ /* Mix the PID with the most predictable bits of the timestamp */
+ iseed = (uint64) now ^ ((uint64) MyProcPid << 32);
+ pg_prng_seed(&prng_state, iseed);
+ }
+ prng_seed_set = true;
+ }
+}
+
+/*
+ * setseed() -
+ *
+ * Seed the PRNG from a specified value in the range [-1.0, 1.0].
+ */
+Datum
+setseed(PG_FUNCTION_ARGS)
+{
+ float8 seed = PG_GETARG_FLOAT8(0);
+
+ if (seed < -1 || seed > 1 || isnan(seed))
+ ereport(ERROR,
+ errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("setseed parameter %g is out of allowed range [-1,1]",
+ seed));
+
+ pg_prng_fseed(&prng_state, seed);
+ prng_seed_set = true;
+
+ PG_RETURN_VOID();
+}
+
+/*
+ * drandom() -
+ *
+ * Returns a random number chosen uniformly in the range [0.0, 1.0).
+ */
+Datum
+drandom(PG_FUNCTION_ARGS)
+{
+ float8 result;
+
+ initialize_prng();
+
+ /* pg_prng_double produces desired result range [0.0, 1.0) */
+ result = pg_prng_double(&prng_state);
+
+ PG_RETURN_FLOAT8(result);
+}
+
+/*
+ * drandom_normal() -
+ *
+ * Returns a random number from a normal distribution.
+ */
+Datum
+drandom_normal(PG_FUNCTION_ARGS)
+{
+ float8 mean = PG_GETARG_FLOAT8(0);
+ float8 stddev = PG_GETARG_FLOAT8(1);
+ float8 result,
+ z;
+
+ initialize_prng();
+
+ /* Get random value from standard normal(mean = 0.0, stddev = 1.0) */
+ z = pg_prng_double_normal(&prng_state);
+ /* Transform the normal standard variable (z) */
+ /* using the target normal distribution parameters */
+ result = (stddev * z) + mean;
+
+ PG_RETURN_FLOAT8(result);
+}
+
+/*
+ * int4random() -
+ *
+ * Returns a random 32-bit integer chosen uniformly in the specified range.
+ */
+Datum
+int4random(PG_FUNCTION_ARGS)
+{
+ int32 rmin = PG_GETARG_INT32(0);
+ int32 rmax = PG_GETARG_INT32(1);
+ int32 result;
+
+ if (rmin > rmax)
+ ereport(ERROR,
+ errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("lower bound must be less than or equal to upper bound"));
+
+ initialize_prng();
+
+ result = (int32) pg_prng_int64_range(&prng_state, rmin, rmax);
+
+ PG_RETURN_INT32(result);
+}
+
+/*
+ * int8random() -
+ *
+ * Returns a random 64-bit integer chosen uniformly in the specified range.
+ */
+Datum
+int8random(PG_FUNCTION_ARGS)
+{
+ int64 rmin = PG_GETARG_INT64(0);
+ int64 rmax = PG_GETARG_INT64(1);
+ int64 result;
+
+ if (rmin > rmax)
+ ereport(ERROR,
+ errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("lower bound must be less than or equal to upper bound"));
+
+ initialize_prng();
+
+ result = pg_prng_int64_range(&prng_state, rmin, rmax);
+
+ PG_RETURN_INT64(result);
+}
+
+/*
+ * numeric_random() -
+ *
+ * Returns a random numeric value chosen uniformly in the specified range.
+ */
+Datum
+numeric_random(PG_FUNCTION_ARGS)
+{
+ Numeric rmin = PG_GETARG_NUMERIC(0);
+ Numeric rmax = PG_GETARG_NUMERIC(1);
+ Numeric result;
+
+ initialize_prng();
+
+ result = random_numeric(&prng_state, rmin, rmax);
+
+ PG_RETURN_NUMERIC(result);
+}