LCOV - code coverage report
Current view: top level - src/backend/utils/adt - pseudorandomfuncs.c (source / functions) Hit Total Coverage
Test: PostgreSQL 19devel Lines: 69 73 94.5 %
Date: 2025-10-02 06:18:21 Functions: 10 10 100.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*-------------------------------------------------------------------------
       2             :  *
       3             :  * pseudorandomfuncs.c
       4             :  *    Functions giving SQL access to a pseudorandom number generator.
       5             :  *
       6             :  * Portions Copyright (c) 1996-2025, PostgreSQL Global Development Group
       7             :  * Portions Copyright (c) 1994, Regents of the University of California
       8             :  *
       9             :  * IDENTIFICATION
      10             :  *    src/backend/utils/adt/pseudorandomfuncs.c
      11             :  *
      12             :  *-------------------------------------------------------------------------
      13             :  */
      14             : #include "postgres.h"
      15             : 
      16             : #include <math.h>
      17             : 
      18             : #include "common/pg_prng.h"
      19             : #include "miscadmin.h"
      20             : #include "utils/date.h"
      21             : #include "utils/fmgrprotos.h"
      22             : #include "utils/numeric.h"
      23             : #include "utils/timestamp.h"
      24             : 
      25             : /* Shared PRNG state used by all the random functions */
      26             : static pg_prng_state prng_state;
      27             : static bool prng_seed_set = false;
      28             : 
      29             : /*
      30             :  * Macro for checking the range bounds of random(min, max) functions. Throws
      31             :  * an error if they're the wrong way round.
      32             :  */
      33             : #define CHECK_RANGE_BOUNDS(rmin, rmax) \
      34             :     do { \
      35             :         if ((rmin) > (rmax)) \
      36             :             ereport(ERROR, \
      37             :                     errcode(ERRCODE_INVALID_PARAMETER_VALUE), \
      38             :                     errmsg("lower bound must be less than or equal to upper bound")); \
      39             :     } while (0)
      40             : 
      41             : /*
      42             :  * initialize_prng() -
      43             :  *
      44             :  *  Initialize (seed) the PRNG, if not done yet in this process.
      45             :  */
      46             : static void
      47     2571830 : initialize_prng(void)
      48             : {
      49     2571830 :     if (unlikely(!prng_seed_set))
      50             :     {
      51             :         /*
      52             :          * If possible, seed the PRNG using high-quality random bits. Should
      53             :          * that fail for some reason, we fall back on a lower-quality seed
      54             :          * based on current time and PID.
      55             :          */
      56         178 :         if (unlikely(!pg_prng_strong_seed(&prng_state)))
      57             :         {
      58           0 :             TimestampTz now = GetCurrentTimestamp();
      59             :             uint64      iseed;
      60             : 
      61             :             /* Mix the PID with the most predictable bits of the timestamp */
      62           0 :             iseed = (uint64) now ^ ((uint64) MyProcPid << 32);
      63           0 :             pg_prng_seed(&prng_state, iseed);
      64             :         }
      65         178 :         prng_seed_set = true;
      66             :     }
      67     2571830 : }
      68             : 
      69             : /*
      70             :  * setseed() -
      71             :  *
      72             :  *  Seed the PRNG from a specified value in the range [-1.0, 1.0].
      73             :  */
      74             : Datum
      75          14 : setseed(PG_FUNCTION_ARGS)
      76             : {
      77          14 :     float8      seed = PG_GETARG_FLOAT8(0);
      78             : 
      79          14 :     if (seed < -1 || seed > 1 || isnan(seed))
      80           0 :         ereport(ERROR,
      81             :                 errcode(ERRCODE_INVALID_PARAMETER_VALUE),
      82             :                 errmsg("setseed parameter %g is out of allowed range [-1,1]",
      83             :                        seed));
      84             : 
      85          14 :     pg_prng_fseed(&prng_state, seed);
      86          14 :     prng_seed_set = true;
      87             : 
      88          14 :     PG_RETURN_VOID();
      89             : }
      90             : 
      91             : /*
      92             :  * drandom() -
      93             :  *
      94             :  *  Returns a random number chosen uniformly in the range [0.0, 1.0).
      95             :  */
      96             : Datum
      97     2416778 : drandom(PG_FUNCTION_ARGS)
      98             : {
      99             :     float8      result;
     100             : 
     101     2416778 :     initialize_prng();
     102             : 
     103             :     /* pg_prng_double produces desired result range [0.0, 1.0) */
     104     2416778 :     result = pg_prng_double(&prng_state);
     105             : 
     106     2416778 :     PG_RETURN_FLOAT8(result);
     107             : }
     108             : 
     109             : /*
     110             :  * drandom_normal() -
     111             :  *
     112             :  *  Returns a random number from a normal distribution.
     113             :  */
     114             : Datum
     115       25320 : drandom_normal(PG_FUNCTION_ARGS)
     116             : {
     117       25320 :     float8      mean = PG_GETARG_FLOAT8(0);
     118       25320 :     float8      stddev = PG_GETARG_FLOAT8(1);
     119             :     float8      result,
     120             :                 z;
     121             : 
     122       25320 :     initialize_prng();
     123             : 
     124             :     /* Get random value from standard normal(mean = 0.0, stddev = 1.0) */
     125       25320 :     z = pg_prng_double_normal(&prng_state);
     126             :     /* Transform the normal standard variable (z) */
     127             :     /* using the target normal distribution parameters */
     128       25320 :     result = (stddev * z) + mean;
     129             : 
     130       25320 :     PG_RETURN_FLOAT8(result);
     131             : }
     132             : 
     133             : /*
     134             :  * int4random() -
     135             :  *
     136             :  *  Returns a random 32-bit integer chosen uniformly in the specified range.
     137             :  */
     138             : Datum
     139       51144 : int4random(PG_FUNCTION_ARGS)
     140             : {
     141       51144 :     int32       rmin = PG_GETARG_INT32(0);
     142       51144 :     int32       rmax = PG_GETARG_INT32(1);
     143             :     int32       result;
     144             : 
     145       51144 :     CHECK_RANGE_BOUNDS(rmin, rmax);
     146             : 
     147       51138 :     initialize_prng();
     148             : 
     149       51138 :     result = (int32) pg_prng_int64_range(&prng_state, rmin, rmax);
     150             : 
     151       51138 :     PG_RETURN_INT32(result);
     152             : }
     153             : 
     154             : /*
     155             :  * int8random() -
     156             :  *
     157             :  *  Returns a random 64-bit integer chosen uniformly in the specified range.
     158             :  */
     159             : Datum
     160       45072 : int8random(PG_FUNCTION_ARGS)
     161             : {
     162       45072 :     int64       rmin = PG_GETARG_INT64(0);
     163       45072 :     int64       rmax = PG_GETARG_INT64(1);
     164             :     int64       result;
     165             : 
     166       45072 :     CHECK_RANGE_BOUNDS(rmin, rmax);
     167             : 
     168       45066 :     initialize_prng();
     169             : 
     170       45066 :     result = pg_prng_int64_range(&prng_state, rmin, rmax);
     171             : 
     172       45066 :     PG_RETURN_INT64(result);
     173             : }
     174             : 
     175             : /*
     176             :  * numeric_random() -
     177             :  *
     178             :  *  Returns a random numeric value chosen uniformly in the specified range.
     179             :  */
     180             : Datum
     181       33462 : numeric_random(PG_FUNCTION_ARGS)
     182             : {
     183       33462 :     Numeric     rmin = PG_GETARG_NUMERIC(0);
     184       33462 :     Numeric     rmax = PG_GETARG_NUMERIC(1);
     185             :     Numeric     result;
     186             : 
     187             :     /* Leave range bound checking to random_numeric() */
     188             : 
     189       33462 :     initialize_prng();
     190             : 
     191       33462 :     result = random_numeric(&prng_state, rmin, rmax);
     192             : 
     193       33432 :     PG_RETURN_NUMERIC(result);
     194             : }
     195             : 
     196             : 
     197             : /*
     198             :  * date_random() -
     199             :  *
     200             :  *  Returns a random date chosen uniformly in the specified range.
     201             :  */
     202             : Datum
     203          36 : date_random(PG_FUNCTION_ARGS)
     204             : {
     205          36 :     int32       rmin = (int32) PG_GETARG_DATEADT(0);
     206          36 :     int32       rmax = (int32) PG_GETARG_DATEADT(1);
     207             :     DateADT     result;
     208             : 
     209          36 :     CHECK_RANGE_BOUNDS(rmin, rmax);
     210             : 
     211          30 :     if (DATE_IS_NOBEGIN(rmin) || DATE_IS_NOEND(rmax))
     212          12 :         ereport(ERROR,
     213             :                 errcode(ERRCODE_INVALID_PARAMETER_VALUE),
     214             :                 errmsg("lower and upper bounds must be finite"));
     215             : 
     216          18 :     initialize_prng();
     217             : 
     218          18 :     result = (DateADT) pg_prng_int64_range(&prng_state, rmin, rmax);
     219             : 
     220          18 :     PG_RETURN_DATEADT(result);
     221             : }
     222             : 
     223             : /*
     224             :  * timestamp_random() -
     225             :  *
     226             :  *  Returns a random timestamp chosen uniformly in the specified range.
     227             :  */
     228             : Datum
     229          42 : timestamp_random(PG_FUNCTION_ARGS)
     230             : {
     231          42 :     int64       rmin = (int64) PG_GETARG_TIMESTAMP(0);
     232          42 :     int64       rmax = (int64) PG_GETARG_TIMESTAMP(1);
     233             :     Timestamp   result;
     234             : 
     235          42 :     CHECK_RANGE_BOUNDS(rmin, rmax);
     236             : 
     237          36 :     if (TIMESTAMP_IS_NOBEGIN(rmin) || TIMESTAMP_IS_NOEND(rmax))
     238          12 :         ereport(ERROR,
     239             :                 errcode(ERRCODE_INVALID_PARAMETER_VALUE),
     240             :                 errmsg("lower and upper bounds must be finite"));
     241             : 
     242          24 :     initialize_prng();
     243             : 
     244          24 :     result = (Timestamp) pg_prng_int64_range(&prng_state, rmin, rmax);
     245             : 
     246          24 :     PG_RETURN_TIMESTAMP(result);
     247             : }
     248             : 
     249             : /*
     250             :  * timestamptz_random() -
     251             :  *
     252             :  *  Returns a random timestamptz chosen uniformly in the specified range.
     253             :  */
     254             : Datum
     255          42 : timestamptz_random(PG_FUNCTION_ARGS)
     256             : {
     257          42 :     int64       rmin = (int64) PG_GETARG_TIMESTAMPTZ(0);
     258          42 :     int64       rmax = (int64) PG_GETARG_TIMESTAMPTZ(1);
     259             :     TimestampTz result;
     260             : 
     261          42 :     CHECK_RANGE_BOUNDS(rmin, rmax);
     262             : 
     263          36 :     if (TIMESTAMP_IS_NOBEGIN(rmin) || TIMESTAMP_IS_NOEND(rmax))
     264          12 :         ereport(ERROR,
     265             :                 errcode(ERRCODE_INVALID_PARAMETER_VALUE),
     266             :                 errmsg("lower and upper bounds must be finite"));
     267             : 
     268          24 :     initialize_prng();
     269             : 
     270          24 :     result = (TimestampTz) pg_prng_int64_range(&prng_state, rmin, rmax);
     271             : 
     272          24 :     PG_RETURN_TIMESTAMPTZ(result);
     273             : }

Generated by: LCOV version 1.16