diff options
-rw-r--r-- | doc/src/sgml/func.sgml | 14 | ||||
-rw-r--r-- | src/backend/utils/adt/float.c | 52 |
2 files changed, 53 insertions, 13 deletions
diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml index 37860996a6e..90d67f1acfd 100644 --- a/doc/src/sgml/func.sgml +++ b/doc/src/sgml/func.sgml @@ -1136,11 +1136,15 @@ </table> <para> - The characteristics of the values returned by - <literal><function>random()</function></literal> depend - on the system implementation. It is not suitable for cryptographic - applications; see <xref linkend="pgcrypto"/> module for an alternative. - </para> + The <function>random()</function> function uses a simple linear + congruential algorithm. It is fast but not suitable for cryptographic + applications; see the <xref linkend="pgcrypto"/> module for a more + secure alternative. + If <function>setseed()</function> is called, the results of + subsequent <function>random()</function> calls in the current session are + repeatable by re-issuing <function>setseed()</function> with the same + argument. + </para> <para> Finally, <xref linkend="functions-math-trig-table"/> shows the diff --git a/src/backend/utils/adt/float.c b/src/backend/utils/adt/float.c index cf9327f8853..add099ec9dc 100644 --- a/src/backend/utils/adt/float.c +++ b/src/backend/utils/adt/float.c @@ -22,10 +22,13 @@ #include "catalog/pg_type.h" #include "common/int.h" #include "libpq/pqformat.h" +#include "miscadmin.h" #include "utils/array.h" +#include "utils/backend_random.h" #include "utils/float.h" #include "utils/fmgrprotos.h" #include "utils/sortsupport.h" +#include "utils/timestamp.h" /* Configurable GUC parameter */ @@ -53,6 +56,10 @@ float8 degree_c_sixty = 60.0; float8 degree_c_one_half = 0.5; 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}; + /* Local function prototypes */ static double sind_q1(double x); static double cosd_q1(double x); @@ -2378,8 +2385,30 @@ drandom(PG_FUNCTION_ARGS) { float8 result; - /* result [0.0 - 1.0) */ - result = (double) random() / ((double) MAX_RANDOM_VALUE + 1); + /* Initialize random seed, if not done yet in this process */ + if (unlikely(!drandom_seed_set)) + { + /* + * If possible, initialize the seed 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 (!pg_backend_random((char *) 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); + } + drandom_seed_set = true; + } + + /* pg_erand48 produces desired result range [0.0 - 1.0) */ + result = pg_erand48(drandom_seed); PG_RETURN_FLOAT8(result); } @@ -2392,13 +2421,20 @@ Datum setseed(PG_FUNCTION_ARGS) { float8 seed = PG_GETARG_FLOAT8(0); - int iseed; + uint64 iseed; - if (seed < -1 || seed > 1) - elog(ERROR, "setseed parameter %f out of range [-1,1]", seed); - - iseed = (int) (seed * MAX_RANDOM_VALUE); - srandom((unsigned int) iseed); + 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))); + + /* 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); + drandom_seed_set = true; PG_RETURN_VOID(); } |