Hello Robert,

1. It's really not appropriate to fold the documentation changes
raised on the other thread into this patch.  I'm not going to commit
something where the commit message is a laundry list of unrelated
changes.  Please separate out the documentation changes as a separate
patch.  Let's do that first, and then make this patch apply on top of
those changes.  The related changes in getGaussianRand etc. should
also be part of that patch, not this one.

Hmmm. Attached is a two-part v16.

2. Please reduce the churn in the pgbench output example.  Most of the
lines that you've changed don't actually need to be changed.

I did a real run to get consistant figures, especially as now more informations are shown. I did some computations to try to generate something consistent without changing too many lines, but just taking a real output would make more sense.

3. I think we should not have ENODE_INTEGER_CONSTANT and
ENODE_DOUBLE_CONSTANT.  We should just have ENODE_CONSTANT, and it
should store the same datatype we use to represent values in the
evaluator.

Why not. This induces a two level tag structure, which I do not find that great, but it simplifies the evaluator code to have one less case.

4. The way you've defined the value type is not great.  int64_t isn't
used anywhere in our source base.  Don't start here.

Indeed. I really meant "int64" which is used elsewhere in pgbench.

I think the is_none, is_int, is_double naming is significantly inferior to what I suggested, and unlike what we do through the rest of our code.

Hmmm. I think that it is rather a matter of taste, and PGBT_DOUBLE does not strike me as intrinsically superior, although possibly more in style.

I changed value_t and value_type_t to PgBenchValue and ~Type to blend in, even if I find it on the heavy side.

Similarly, the coercion functions are not very descriptive named,

I do not understand. "INT(value)" and "DOUBLE(value) both look pretty explicit to me...

The point a choosing short names is to avoid newlines to stay (or try to stay) under 80 columns, especially as pg indentations rules tend to move things quickly on the right in case constructs, which are used in the evaluation function.

I use "explicitely named" functions, but used short macros in the evaluator.

and I don't see why we'd want those to be macros rather than static functions.

Can be functions, sure. Switched.

5. The declaration of PgBenchExprList has a cuddled opening brace,
which is not PostgreSQL style.

Indeed. There were other instances.

--
Fabien.
diff --git a/doc/src/sgml/ref/pgbench.sgml b/doc/src/sgml/ref/pgbench.sgml
index 0ac40f1..da3c792 100644
--- a/doc/src/sgml/ref/pgbench.sgml
+++ b/doc/src/sgml/ref/pgbench.sgml
@@ -788,7 +788,7 @@ pgbench <optional> <replaceable>options</> </optional> <replaceable>dbname</>
 
    <varlistentry>
     <term>
-     <literal>\setrandom <replaceable>varname</> <replaceable>min</> <replaceable>max</> [ uniform | { gaussian | exponential } <replaceable>threshold</> ]</literal>
+     <literal>\setrandom <replaceable>varname</> <replaceable>min</> <replaceable>max</> [ uniform | { gaussian | exponential } <replaceable>param</> ]</literal>
      </term>
 
     <listitem>
@@ -804,54 +804,63 @@ pgbench <optional> <replaceable>options</> </optional> <replaceable>dbname</>
       By default, or when <literal>uniform</> is specified, all values in the
       range are drawn with equal probability.  Specifying <literal>gaussian</>
       or  <literal>exponential</> options modifies this behavior; each
-      requires a mandatory threshold which determines the precise shape of the
+      requires a mandatory parameter which determines the precise shape of the
       distribution.
      </para>
 
      <para>
       For a Gaussian distribution, the interval is mapped onto a standard
       normal distribution (the classical bell-shaped Gaussian curve) truncated
-      at <literal>-threshold</> on the left and <literal>+threshold</>
+      at <literal>-param</> on the left and <literal>+param</>
       on the right.
+      Values in the middle of the interval are more likely to be drawn.
       To be precise, if <literal>PHI(x)</> is the cumulative distribution
       function of the standard normal distribution, with mean <literal>mu</>
-      defined as <literal>(max + min) / 2.0</>, then value <replaceable>i</>
-      between <replaceable>min</> and <replaceable>max</> inclusive is drawn
-      with probability:
-      <literal>
-        (PHI(2.0 * threshold * (i - min - mu + 0.5) / (max - min + 1)) -
-         PHI(2.0 * threshold * (i - min - mu - 0.5) / (max - min + 1))) /
-         (2.0 * PHI(threshold) - 1.0)</>.
-      Intuitively, the larger the <replaceable>threshold</>, the more
+      defined as <literal>(max+min)/2</>, with
+<literallayout>
+f(x) = PHI(2 * param * (x-mu) / (max-min+1)) / (2 * PHI(param) - 1)
+</literallayout>
+      then value <replaceable>i</> between <replaceable>min</> and
+      <replaceable>max</> inclusive is drawn with probability:
+      <literal>f(i+0.5) - f(i-0.5)</>.
+      Intuitively, the larger the <replaceable>param</>, the more
       frequently values close to the middle of the interval are drawn, and the
       less frequently values close to the <replaceable>min</> and
       <replaceable>max</> bounds.
-      About 67% of values are drawn from the middle <literal>1.0 / threshold</>
-      and 95% in the middle <literal>2.0 / threshold</>; for instance, if
-      <replaceable>threshold</> is 4.0, 67% of values are drawn from the middle
-      quarter and 95% from the middle half of the interval.
-      The minimum <replaceable>threshold</> is 2.0 for performance of
+      About 67% of values are drawn from the middle <literal>1/param</>,
+      that is a relative <literal>0.5/param</> around the mean,
+      and 95% in the middle <literal>2/param</>, that is
+      a relative <literal>1/param</> around the mean;
+      for instance, if <replaceable>param</> is 4.0, 67% of values are drawn
+      from the middle quarter (1/4.0) of the interval
+      (i.e. from <literal>3/8</> to <literal>5/8</>)
+      and 95% from the middle half (2/4.0) of the interval (second and third
+      quartiles).
+      The minimum <replaceable>param</> is 2.0 for performance of
       the Box-Muller transform.
      </para>
 
      <para>
-      For an exponential distribution, the <replaceable>threshold</>
-      parameter controls the distribution by truncating a quickly-decreasing
-      exponential distribution at <replaceable>threshold</>, and then
+      For an exponential distribution, the <replaceable>param</> parameter
+      controls the distribution by truncating a quickly-decreasing
+      exponential distribution at <replaceable>param</>, and then
       projecting onto integers between the bounds.
-      To be precise, value <replaceable>i</> between <replaceable>min</> and
+      To be precise, with
+<literallayout>
+f(x) = exp(-param * (x-min) / (max-min+1)) / (1 - exp(-param))
+</literallayout>
+      Then value <replaceable>i</> between <replaceable>min</> and
       <replaceable>max</> inclusive is drawn with probability:
-      <literal>(exp(-threshold*(i-min)/(max+1-min)) -
-       exp(-threshold*(i+1-min)/(max+1-min))) / (1.0 - exp(-threshold))</>.
-      Intuitively, the larger the <replaceable>threshold</>, the more
+      <literal>f(x) - f(x+1)</>.
+      Intuitively, the larger the <replaceable>param</>, the more
       frequently values close to <replaceable>min</> are accessed, and the
       less frequently values close to <replaceable>max</> are accessed.
-      The closer to 0 the threshold, the flatter (more uniform) the access
+      The closer to 0 the parameter, the flatter (more uniform) the access
       distribution.
       A crude approximation of the distribution is that the most frequent 1%
       values in the range, close to <replaceable>min</>, are drawn
-      <replaceable>threshold</>%  of the time.
-      The <replaceable>threshold</> value must be strictly positive.
+      <replaceable>param</>%  of the time.
+      The <replaceable>param</> value must be strictly positive.
      </para>
 
      <para>
diff --git a/src/bin/pgbench/pgbench.c b/src/bin/pgbench/pgbench.c
index f2d435b..60ad22e 100644
--- a/src/bin/pgbench/pgbench.c
+++ b/src/bin/pgbench/pgbench.c
@@ -90,7 +90,7 @@ static int	pthread_join(pthread_t th, void **thread_return);
 #define LOG_STEP_SECONDS	5	/* seconds between log messages */
 #define DEFAULT_NXACTS	10		/* default nxacts */
 
-#define MIN_GAUSSIAN_THRESHOLD		2.0 /* minimum threshold for gauss */
+#define MIN_GAUSSIAN_PARAM		2.0 /* minimum parameter for gauss */
 
 int			nxacts = 0;			/* number of transactions per client */
 int			duration = 0;		/* duration in seconds */
@@ -488,47 +488,47 @@ getrand(TState *thread, int64 min, int64 max)
 
 /*
  * random number generator: exponential distribution from min to max inclusive.
- * the threshold is so that the density of probability for the last cut-off max
- * value is exp(-threshold).
+ * the parameter is so that the density of probability for the last cut-off max
+ * value is exp(-param).
  */
 static int64
-getExponentialRand(TState *thread, int64 min, int64 max, double threshold)
+getExponentialRand(TState *thread, int64 min, int64 max, double param)
 {
 	double		cut,
 				uniform,
 				rand;
 
-	Assert(threshold > 0.0);
-	cut = exp(-threshold);
+	Assert(param > 0.0);
+	cut = exp(-param);
 	/* erand in [0, 1), uniform in (0, 1] */
 	uniform = 1.0 - pg_erand48(thread->random_state);
 
 	/*
-	 * inner expresion in (cut, 1] (if threshold > 0), rand in [0, 1)
+	 * inner expresion in (cut, 1] (if param > 0), rand in [0, 1)
 	 */
 	Assert((1.0 - cut) != 0.0);
-	rand = -log(cut + (1.0 - cut) * uniform) / threshold;
+	rand = -log(cut + (1.0 - cut) * uniform) / param;
 	/* return int64 random number within between min and max */
 	return min + (int64) ((max - min + 1) * rand);
 }
 
 /* random number generator: gaussian distribution from min to max inclusive */
 static int64
-getGaussianRand(TState *thread, int64 min, int64 max, double threshold)
+getGaussianRand(TState *thread, int64 min, int64 max, double param)
 {
 	double		stdev;
 	double		rand;
 
 	/*
-	 * Get user specified random number from this loop, with -threshold <
-	 * stdev <= threshold
+	 * Get user specified random number from this loop,
+	 * with -param < stdev <= param
 	 *
 	 * This loop is executed until the number is in the expected range.
 	 *
-	 * As the minimum threshold is 2.0, the probability of looping is low:
+	 * As the minimum parameter is 2.0, the probability of looping is low:
 	 * sqrt(-2 ln(r)) <= 2 => r >= e^{-2} ~ 0.135, then when taking the
 	 * average sinus multiplier as 2/pi, we have a 8.6% looping probability in
-	 * the worst case. For a 5.0 threshold value, the looping probability is
+	 * the worst case. For a 5.0 param value, the looping probability is
 	 * about e^{-5} * 2 / pi ~ 0.43%.
 	 */
 	do
@@ -553,10 +553,10 @@ getGaussianRand(TState *thread, int64 min, int64 max, double threshold)
 		 * over.
 		 */
 	}
-	while (stdev < -threshold || stdev >= threshold);
+	while (stdev < -param || stdev >= param);
 
-	/* stdev is in [-threshold, threshold), normalization to [0,1) */
-	rand = (stdev + threshold) / (threshold * 2.0);
+	/* stdev is in [-param, param), normalization to [0,1) */
+	rand = (stdev + param) / (param * 2.0);
 
 	/* return int64 random number within between min and max */
 	return min + (int64) ((max - min + 1) * rand);
@@ -1483,7 +1483,7 @@ top:
 			char	   *var;
 			int64		min,
 						max;
-			double		threshold = 0;
+			double		param = 0;
 			char		res[64];
 
 			if (*argv[2] == ':')
@@ -1554,41 +1554,41 @@ top:
 				{
 					if ((var = getVariable(st, argv[5] + 1)) == NULL)
 					{
-						fprintf(stderr, "%s: invalid threshold number: \"%s\"\n",
+						fprintf(stderr, "%s: invalid parameter: \"%s\"\n",
 								argv[0], argv[5]);
 						st->ecnt++;
 						return true;
 					}
-					threshold = strtod(var, NULL);
+					param = strtod(var, NULL);
 				}
 				else
-					threshold = strtod(argv[5], NULL);
+					param = strtod(argv[5], NULL);
 
 				if (pg_strcasecmp(argv[4], "gaussian") == 0)
 				{
-					if (threshold < MIN_GAUSSIAN_THRESHOLD)
+					if (param < MIN_GAUSSIAN_PARAM)
 					{
-						fprintf(stderr, "gaussian threshold must be at least %f (not \"%s\")\n", MIN_GAUSSIAN_THRESHOLD, argv[5]);
+						fprintf(stderr, "gaussian parameter must be at least %f (not \"%s\")\n", MIN_GAUSSIAN_PARAM, argv[5]);
 						st->ecnt++;
 						return true;
 					}
 #ifdef DEBUG
-					printf("min: " INT64_FORMAT " max: " INT64_FORMAT " random: " INT64_FORMAT "\n", min, max, getGaussianRand(thread, min, max, threshold));
+					printf("min: " INT64_FORMAT " max: " INT64_FORMAT " random: " INT64_FORMAT "\n", min, max, getGaussianRand(thread, min, max, param));
 #endif
-					snprintf(res, sizeof(res), INT64_FORMAT, getGaussianRand(thread, min, max, threshold));
+					snprintf(res, sizeof(res), INT64_FORMAT, getGaussianRand(thread, min, max, param));
 				}
 				else if (pg_strcasecmp(argv[4], "exponential") == 0)
 				{
-					if (threshold <= 0.0)
+					if (param <= 0.0)
 					{
-						fprintf(stderr, "exponential threshold must be greater than zero (not \"%s\")\n", argv[5]);
+						fprintf(stderr, "exponential parameter must be greater than zero (not \"%s\")\n", argv[5]);
 						st->ecnt++;
 						return true;
 					}
 #ifdef DEBUG
-					printf("min: " INT64_FORMAT " max: " INT64_FORMAT " random: " INT64_FORMAT "\n", min, max, getExponentialRand(thread, min, max, threshold));
+					printf("min: " INT64_FORMAT " max: " INT64_FORMAT " random: " INT64_FORMAT "\n", min, max, getExponentialRand(thread, min, max, param));
 #endif
-					snprintf(res, sizeof(res), INT64_FORMAT, getExponentialRand(thread, min, max, threshold));
+					snprintf(res, sizeof(res), INT64_FORMAT, getExponentialRand(thread, min, max, param));
 				}
 			}
 			else	/* this means an error somewhere in the parsing phase... */
@@ -2282,8 +2282,9 @@ process_commands(char *buf, const char *source, const int lineno)
 		if (pg_strcasecmp(my_commands->argv[0], "setrandom") == 0)
 		{
 			/*
-			 * parsing: \setrandom variable min max [uniform] \setrandom
-			 * variable min max (gaussian|exponential) threshold
+			 * parsing:
+			 *   \setrandom variable min max [uniform]
+			 *   \setrandom variable min max (gaussian|exponential) param
 			 */
 
 			if (my_commands->argc < 4)
@@ -2308,7 +2309,7 @@ process_commands(char *buf, const char *source, const int lineno)
 				if (my_commands->argc < 6)
 				{
 					syntax_error(source, lineno, my_commands->line, my_commands->argv[0],
-					 "missing threshold argument", my_commands->argv[4], -1);
+					 "missing parameter", my_commands->argv[4], -1);
 				}
 				else if (my_commands->argc > 6)
 				{
diff --git a/doc/src/sgml/ref/pgbench.sgml b/doc/src/sgml/ref/pgbench.sgml
index da3c792..b754bf3 100644
--- a/doc/src/sgml/ref/pgbench.sgml
+++ b/doc/src/sgml/ref/pgbench.sgml
@@ -771,17 +771,21 @@ pgbench <optional> <replaceable>options</> </optional> <replaceable>dbname</>
       Sets variable <replaceable>varname</> to an integer value calculated
       from <replaceable>expression</>.
       The expression may contain integer constants such as <literal>5432</>,
-      references to variables <literal>:</><replaceable>variablename</>,
+      double constants such as <literal>3.14156</>,
+      references to integer variables <literal>:</><replaceable>variablename</>,
       and expressions composed of unary (<literal>-</>) or binary operators
-      (<literal>+</>, <literal>-</>, <literal>*</>, <literal>/</>, <literal>%</>)
-      with their usual associativity, and parentheses.
+      (<literal>+</>, <literal>-</>, <literal>*</>, <literal>/</>,
+      <literal>%</>) with their usual associativity, function calls and
+      parentheses.
+      <xref linkend="functions-pgbench-func-table"> shows the available
+      functions.
      </para>
 
      <para>
       Examples:
 <programlisting>
 \set ntellers 10 * :scale
-\set aid (1021 * :aid) % (100000 * :scale) + 1
+\set aid (1021 * random(1, 100000 * :scale)) % (100000 * :scale) + 1
 </programlisting></para>
     </listitem>
    </varlistentry>
@@ -801,13 +805,252 @@ pgbench <optional> <replaceable>options</> </optional> <replaceable>dbname</>
      </para>
 
      <para>
-      By default, or when <literal>uniform</> is specified, all values in the
-      range are drawn with equal probability.  Specifying <literal>gaussian</>
-      or  <literal>exponential</> options modifies this behavior; each
-      requires a mandatory parameter which determines the precise shape of the
+      <itemizedlist>
+       <listitem>
+        <para>
+         <literal>\setrandom n 1 10</> or <literal>\setrandom n 1 10 uniform</>
+         is equivalent to <literal>\set n random(1, 10)</> and uses a uniform
+         distribution.
+        </para>
+       </listitem>
+
+      <listitem>
+       <para>
+        <literal>\setrandom n 1 10 exponential 3.0</> is equivalent to
+        <literal>\set n random_exponential(1, 10, 3.0)</> and uses an
+        exponential distribution.
+       </para>
+      </listitem>
+
+      <listitem>
+       <para>
+        <literal>\setrandom n 1 10 gaussian 2.0</> is equivalent to
+        <literal>\set n random_gaussian(1, 10, 2.0)</>, and uses a gaussian
+        distribution.
+       </para>
+      </listitem>
+     </itemizedlist>
+
+       See the documentation of these functions below for further information
+       about the precise shape of these distributions, depending on the value
+       of the parameter.
+     </para>
+
+     <para>
+      Example:
+<programlisting>
+\setrandom aid 1 :naccounts gaussian 5.0
+</programlisting></para>
+    </listitem>
+   </varlistentry>
+
+   <varlistentry>
+    <term>
+     <literal>\sleep <replaceable>number</> [ us | ms | s ]</literal>
+    </term>
+
+    <listitem>
+     <para>
+      Causes script execution to sleep for the specified duration in
+      microseconds (<literal>us</>), milliseconds (<literal>ms</>) or seconds
+      (<literal>s</>).  If the unit is omitted then seconds are the default.
+      <replaceable>number</> can be either an integer constant or a
+      <literal>:</><replaceable>variablename</> reference to a variable
+      having an integer value.
+     </para>
+
+     <para>
+      Example:
+<programlisting>
+\sleep 10 ms
+</programlisting></para>
+    </listitem>
+   </varlistentry>
+
+   <varlistentry>
+    <term>
+     <literal>\setshell <replaceable>varname</> <replaceable>command</> [ <replaceable>argument</> ... ]</literal>
+    </term>
+
+    <listitem>
+     <para>
+      Sets variable <replaceable>varname</> to the result of the shell command
+      <replaceable>command</>. The command must return an integer value
+      through its standard output.
+     </para>
+
+     <para><replaceable>argument</> can be either a text constant or a
+      <literal>:</><replaceable>variablename</> reference to a variable of
+      any types. If you want to use <replaceable>argument</> starting with
+      colons, you need to add an additional colon at the beginning of
+      <replaceable>argument</>.
+     </para>
+
+     <para>
+      Example:
+<programlisting>
+\setshell variable_to_be_assigned command literal_argument :variable ::literal_starting_with_colon
+</programlisting></para>
+    </listitem>
+   </varlistentry>
+
+   <varlistentry>
+    <term>
+     <literal>\shell <replaceable>command</> [ <replaceable>argument</> ... ]</literal>
+    </term>
+
+    <listitem>
+     <para>
+      Same as <literal>\setshell</literal>, but the result is ignored.
+     </para>
+
+     <para>
+      Example:
+<programlisting>
+\shell command literal_argument :variable ::literal_starting_with_colon
+</programlisting></para>
+    </listitem>
+   </varlistentry>
+  </variablelist>
+
+   <!-- list pgbench functions in alphabetical order -->
+   <table id="functions-pgbench-func-table">
+    <title>PgBench Functions</title>
+    <tgroup cols="5">
+     <thead>
+      <row>
+       <entry>Function</entry>
+       <entry>Return Type</entry>
+       <entry>Description</entry>
+       <entry>Example</entry>
+       <entry>Result</entry>
+      </row>
+     </thead>
+     <tbody>
+      <row>
+       <entry><literal><function>abs(<replaceable>a</>)</></></>
+       <entry>same as <replaceable>a</></>
+       <entry>integer or double absolute value</>
+       <entry><literal>abs(-17)</></>
+       <entry><literal>17</></>
+      </row>
+      <row>
+       <entry><literal><function>debug(<replaceable>a</>)</></></>
+       <entry>same as<replaceable>a</> </>
+       <entry>stderr print for debug and return argument</>
+       <entry><literal>debug(5432.1)</></>
+       <entry><literal>5432.1</></>
+      </row>
+      <row>
+       <entry><literal><function>double(<replaceable>i</>)</></></>
+       <entry>double</>
+       <entry>cast to double</>
+       <entry><literal>double(5432)</></>
+       <entry><literal>5432.0</></>
+      </row>
+      <row>
+       <entry><literal><function>int(<replaceable>x</>)</></></>
+       <entry>integer</>
+       <entry>cast to int</>
+       <entry><literal>int(5.4 + 3.8)</></>
+       <entry><literal>9</></>
+      </row>
+      <row>
+       <entry><literal><function>max(<replaceable>i</>, <replaceable>...</>)</></></>
+       <entry>integer</>
+       <entry>maximum value</>
+       <entry><literal>max(5, 4, 3, 2)</></>
+       <entry><literal>5</></>
+      </row>
+      <row>
+       <entry><literal><function>min(<replaceable>i</>, <replaceable>...</>)</></></>
+       <entry>integer</>
+       <entry>minimum value</>
+       <entry><literal>min(5, 4, 3, 2)</></>
+       <entry><literal>2</></>
+      </row>
+      <row>
+       <entry><literal><function>pi()</></></>
+       <entry>double</>
+       <entry>value of the PI constant</>
+       <entry><literal>pi()</></>
+       <entry><literal>3.14159265358979323846</></>
+      </row>
+      <row>
+       <entry><literal><function>random(<replaceable>lb</>, <replaceable>ub</>)</></></>
+       <entry>integer</>
+       <entry>uniformly distributed random integer in <literal>[lb,ub]</></>
+       <entry><literal>random(1, 10)</></>
+       <entry>an int between <literal>1</> and <literal>10</></>
+      </row>
+      <row>
+       <entry><literal><function>random_exponential(<replaceable>lb</>, <replaceable>ub</>, <replaceable>param</>)</></></>
+       <entry>integer</>
+       <entry>exponentially distributed random integer in <literal>[ub,lb]</>,
+              see below</>
+       <entry><literal>random_exponential(1, 10, 3.0)</></>
+       <entry>an int between <literal>1</> and <literal>10</></>
+      </row>
+      <row>
+       <entry><literal><function>random_gaussian(<replaceable>lb</>, <replaceable>ub</>, <replaceable>param</>)</></></>
+       <entry>integer</>
+       <entry>gaussian distributed random integer in <literal>[ub,lb]</>,
+              see below</>
+       <entry><literal>random_gaussian(1, 10, 2.5)</></>
+       <entry>an int between <literal>1</> and <literal>10</></>
+      </row>
+      <row>
+       <entry><literal><function>sqrt(<replaceable>x</>)</></></>
+       <entry>double</>
+       <entry>square root</>
+       <entry><literal>sqrt(2.0)</></>
+       <entry><literal>1.414213562</></>
+      </row>
+     </tbody>
+     </tgroup>
+   </table>
+
+   <para>
+    The <literal>random</> function generated values are uniform, that is
+    all values in the specified range are drawn with equal probability.
+   </para>
+
+   <para>
+     The <literal>random_exponential</> and <literal>random_gaussian</>
+     functions require an additional double parameter which determines the
+     precise shape of the  distribution.
+   </para>
+
+   <itemizedlist>
+    <listitem>
+     <para>
+      For an exponential distribution, the <replaceable>param</> parameter
+      controls the distribution by truncating a quickly-decreasing
+      exponential distribution at <replaceable>param</>, and then
+      projecting onto integers between the bounds.
+      To be precise, with
+<literallayout>
+f(x) = exp(-param * (x-min) / (max-min+1)) / (1 - exp(-param))
+</literallayout>
+      Then value <replaceable>i</> between <replaceable>min</> and
+      <replaceable>max</> inclusive is drawn with probability:
+      <literal>f(x) - f(x+1)</>.
+     </para>
+
+     <para>
+      Intuitively, the larger the <replaceable>param</>, the more
+      frequently values close to <replaceable>min</> are accessed, and the
+      less frequently values close to <replaceable>max</> are accessed.
+      The closer to 0 the parameter, the flatter (more uniform) the access
       distribution.
+      A crude approximation of the distribution is that the most frequent 1%
+      values in the range, close to <replaceable>min</>, are drawn
+      <replaceable>param</>%  of the time.
+      The <replaceable>param</> value must be strictly positive.
      </para>
+    </listitem>
 
+    <listitem>
      <para>
       For a Gaussian distribution, the interval is mapped onto a standard
       normal distribution (the classical bell-shaped Gaussian curve) truncated
@@ -823,6 +1066,8 @@ f(x) = PHI(2 * param * (x-mu) / (max-min+1)) / (2 * PHI(param) - 1)
       then value <replaceable>i</> between <replaceable>min</> and
       <replaceable>max</> inclusive is drawn with probability:
       <literal>f(i+0.5) - f(i-0.5)</>.
+     </para>
+     <para>
       Intuitively, the larger the <replaceable>param</>, the more
       frequently values close to the middle of the interval are drawn, and the
       less frequently values close to the <replaceable>min</> and
@@ -839,119 +1084,18 @@ f(x) = PHI(2 * param * (x-mu) / (max-min+1)) / (2 * PHI(param) - 1)
       The minimum <replaceable>param</> is 2.0 for performance of
       the Box-Muller transform.
      </para>
-
-     <para>
-      For an exponential distribution, the <replaceable>param</> parameter
-      controls the distribution by truncating a quickly-decreasing
-      exponential distribution at <replaceable>param</>, and then
-      projecting onto integers between the bounds.
-      To be precise, with
-<literallayout>
-f(x) = exp(-param * (x-min) / (max-min+1)) / (1 - exp(-param))
-</literallayout>
-      Then value <replaceable>i</> between <replaceable>min</> and
-      <replaceable>max</> inclusive is drawn with probability:
-      <literal>f(x) - f(x+1)</>.
-      Intuitively, the larger the <replaceable>param</>, the more
-      frequently values close to <replaceable>min</> are accessed, and the
-      less frequently values close to <replaceable>max</> are accessed.
-      The closer to 0 the parameter, the flatter (more uniform) the access
-      distribution.
-      A crude approximation of the distribution is that the most frequent 1%
-      values in the range, close to <replaceable>min</>, are drawn
-      <replaceable>param</>%  of the time.
-      The <replaceable>param</> value must be strictly positive.
-     </para>
-
-     <para>
-      Example:
-<programlisting>
-\setrandom aid 1 :naccounts gaussian 5.0
-</programlisting></para>
     </listitem>
-   </varlistentry>
-
-   <varlistentry>
-    <term>
-     <literal>\sleep <replaceable>number</> [ us | ms | s ]</literal>
-    </term>
-
-    <listitem>
-     <para>
-      Causes script execution to sleep for the specified duration in
-      microseconds (<literal>us</>), milliseconds (<literal>ms</>) or seconds
-      (<literal>s</>).  If the unit is omitted then seconds are the default.
-      <replaceable>number</> can be either an integer constant or a
-      <literal>:</><replaceable>variablename</> reference to a variable
-      having an integer value.
-     </para>
-
-     <para>
-      Example:
-<programlisting>
-\sleep 10 ms
-</programlisting></para>
-    </listitem>
-   </varlistentry>
-
-   <varlistentry>
-    <term>
-     <literal>\setshell <replaceable>varname</> <replaceable>command</> [ <replaceable>argument</> ... ]</literal>
-    </term>
-
-    <listitem>
-     <para>
-      Sets variable <replaceable>varname</> to the result of the shell command
-      <replaceable>command</>. The command must return an integer value
-      through its standard output.
-     </para>
-
-     <para><replaceable>argument</> can be either a text constant or a
-      <literal>:</><replaceable>variablename</> reference to a variable of
-      any types. If you want to use <replaceable>argument</> starting with
-      colons, you need to add an additional colon at the beginning of
-      <replaceable>argument</>.
-     </para>
-
-     <para>
-      Example:
-<programlisting>
-\setshell variable_to_be_assigned command literal_argument :variable ::literal_starting_with_colon
-</programlisting></para>
-    </listitem>
-   </varlistentry>
-
-   <varlistentry>
-    <term>
-     <literal>\shell <replaceable>command</> [ <replaceable>argument</> ... ]</literal>
-    </term>
-
-    <listitem>
-     <para>
-      Same as <literal>\setshell</literal>, but the result is ignored.
-     </para>
-
-     <para>
-      Example:
-<programlisting>
-\shell command literal_argument :variable ::literal_starting_with_colon
-</programlisting></para>
-    </listitem>
-   </varlistentry>
-  </variablelist>
+   </itemizedlist>
 
   <para>
    As an example, the full definition of the built-in TPC-B-like
    transaction is:
 
 <programlisting>
-\set nbranches :scale
-\set ntellers 10 * :scale
-\set naccounts 100000 * :scale
-\setrandom aid 1 :naccounts
-\setrandom bid 1 :nbranches
-\setrandom tid 1 :ntellers
-\setrandom delta -5000 5000
+\set aid random(1, 100000 * :scale)
+\set bid random(1, 1 * :scale)
+\set tid random(1, 10 * :scale)
+\set delta random(-5000, 5000)
 BEGIN;
 UPDATE pgbench_accounts SET abalance = abalance + :delta WHERE aid = :aid;
 SELECT abalance FROM pgbench_accounts WHERE aid = :aid;
@@ -1110,16 +1254,15 @@ number of clients: 10
 number of threads: 1
 number of transactions per client: 1000
 number of transactions actually processed: 10000/10000
+latency average: 16.052 ms
+latency stddev: 8.204 ms
 tps = 618.764555 (including connections establishing)
 tps = 622.977698 (excluding connections establishing)
 statement latencies in milliseconds:
-        0.004386        \set nbranches 1 * :scale
-        0.001343        \set ntellers 10 * :scale
-        0.001212        \set naccounts 100000 * :scale
-        0.001310        \setrandom aid 1 :naccounts
-        0.001073        \setrandom bid 1 :nbranches
-        0.001005        \setrandom tid 1 :ntellers
-        0.001078        \setrandom delta -5000 5000
+        0.002522        \set aid random(1, 100000 * :scale)
+        0.005459        \set bid random(1, 1 * :scale)
+        0.002348        \set tid random(1, 10 * :scale)
+        0.001078        \set delta random(-5000, 5000)
         0.326152        BEGIN;
         0.603376        UPDATE pgbench_accounts SET abalance = abalance + :delta WHERE aid = :aid;
         0.454643        SELECT abalance FROM pgbench_accounts WHERE aid = :aid;
diff --git a/src/bin/pgbench/exprparse.y b/src/bin/pgbench/exprparse.y
index e68631e..516c647 100644
--- a/src/bin/pgbench/exprparse.y
+++ b/src/bin/pgbench/exprparse.y
@@ -16,10 +16,14 @@
 
 PgBenchExpr *expr_parse_result;
 
+static PgBenchExprList *make_elist(PgBenchExpr *exp, PgBenchExprList *list);
 static PgBenchExpr *make_integer_constant(int64 ival);
+static PgBenchExpr *make_double_constant(double dval);
 static PgBenchExpr *make_variable(char *varname);
 static PgBenchExpr *make_op(char operator, PgBenchExpr *lexpr,
 		PgBenchExpr *rexpr);
+static int find_func(const char * fname);
+static PgBenchExpr *make_func(const int fnumber, PgBenchExprList *args);
 
 %}
 
@@ -29,15 +33,19 @@ static PgBenchExpr *make_op(char operator, PgBenchExpr *lexpr,
 %union
 {
 	int64		ival;
+	double		dval;
 	char	   *str;
 	PgBenchExpr *expr;
+	PgBenchExprList *elist;
 }
 
+%type <elist> elist
 %type <expr> expr
-%type <ival> INTEGER
-%type <str> VARIABLE
+%type <ival> INTEGER function
+%type <dval> DOUBLE
+%type <str> VARIABLE FUNCTION
 
-%token INTEGER VARIABLE
+%token INTEGER DOUBLE VARIABLE FUNCTION
 %token CHAR_ERROR /* never used, will raise a syntax error */
 
 /* Precedence: lowest to highest */
@@ -49,6 +57,11 @@ static PgBenchExpr *make_op(char operator, PgBenchExpr *lexpr,
 
 result: expr				{ expr_parse_result = $1; }
 
+elist:                  	{ $$ = NULL; }
+	| expr 					{ $$ = make_elist($1, NULL); }
+	| elist ',' expr		{ $$ = make_elist($3, $1); }
+	;
+
 expr: '(' expr ')'			{ $$ = $2; }
 	| '+' expr %prec UMINUS	{ $$ = $2; }
 	| '-' expr %prec UMINUS	{ $$ = make_op('-', make_integer_constant(0), $2); }
@@ -58,7 +71,12 @@ expr: '(' expr ')'			{ $$ = $2; }
 	| expr '/' expr			{ $$ = make_op('/', $1, $3); }
 	| expr '%' expr			{ $$ = make_op('%', $1, $3); }
 	| INTEGER				{ $$ = make_integer_constant($1); }
+	| DOUBLE				{ $$ = make_double_constant($1); }
 	| VARIABLE 				{ $$ = make_variable($1); }
+	| function '(' elist ')'{ $$ = make_func($1, $3); }
+	;
+
+function: FUNCTION			{ $$ = find_func($1); pg_free($1); }
 	;
 
 %%
@@ -68,8 +86,20 @@ make_integer_constant(int64 ival)
 {
 	PgBenchExpr *expr = pg_malloc(sizeof(PgBenchExpr));
 
-	expr->etype = ENODE_INTEGER_CONSTANT;
-	expr->u.integer_constant.ival = ival;
+	expr->etype = ENODE_CONSTANT;
+	expr->u.constant.type = PGBT_INT;
+	expr->u.constant.u.ival = ival;
+	return expr;
+}
+
+static PgBenchExpr *
+make_double_constant(double dval)
+{
+	PgBenchExpr *expr = pg_malloc(sizeof(PgBenchExpr));
+
+	expr->etype = ENODE_CONSTANT;
+	expr->u.constant.type = PGBT_DOUBLE;
+	expr->u.constant.u.dval = dval;
 	return expr;
 }
 
@@ -95,4 +125,123 @@ make_op(char operator, PgBenchExpr *lexpr, PgBenchExpr *rexpr)
 	return expr;
 }
 
+/* list of available functions
+ * - fname: function name
+ * - nargs: number of arguments (-1 is a special value for min & max)
+ * - tag: function identifier from PgBenchFunction enum
+ */
+static struct
+{
+	char * fname;
+	int nargs;
+	PgBenchFunction tag;
+} PGBENCH_FUNCTIONS[] = {
+	{ "pi", 0, PGBENCH_PI },
+	{ "abs", 1, PGBENCH_ABS },
+	{ "sqrt", 1, PGBENCH_SQRT },
+	{ "int", 1, PGBENCH_INT },
+	{ "double", 1, PGBENCH_DOUBLE },
+	{ "min", -1, PGBENCH_MIN },
+	{ "max", -1, PGBENCH_MAX },
+	{ "random", 2, PGBENCH_RANDOM },
+	{ "random_gaussian", 3, PGBENCH_RANDOM_GAUSSIAN },
+	{ "random_exponential", 3, PGBENCH_RANDOM_EXPONENTIAL },
+	{ "debug", 1, PGBENCH_DEBUG },
+
+	/* keep as last array element */
+	{ NULL, 0, 0 }
+};
+
+/*
+ * Find a function from its name
+ *
+ * return the index of the function from the PGBENCH_FUNCTIONS array
+ * or fail if the function is unknown.
+ */
+static int
+find_func(const char * fname)
+{
+	int i = 0;
+
+	while (PGBENCH_FUNCTIONS[i].fname)
+	{
+		if (pg_strcasecmp(fname, PGBENCH_FUNCTIONS[i].fname) == 0)
+			return i;
+		i++;
+	}
+
+	expr_yyerror_more("unexpected function name", fname);
+
+	/* not reached */
+	return -1;
+}
+
+/* Expression linked list builder */
+static PgBenchExprList *
+make_elist(PgBenchExpr *expr, PgBenchExprList *list)
+{
+	PgBenchExprList *cons = pg_malloc(sizeof(PgBenchExprList));
+	cons->expr = expr;
+	cons->next = list;
+	return cons;
+}
+
+/*
+ * Reverse expression linked list
+ *
+ * The list of function arguments is built in reverse order, and reversed once
+ * at the end so as to avoid appending repeatedly at the end of the list.
+ */
+static PgBenchExprList *
+reverse_elist(PgBenchExprList *list)
+{
+	PgBenchExprList *cur = list, *prec = NULL, *next = NULL;
+
+	while (cur != NULL)
+	{
+		next = cur->next;
+		cur->next = prec;
+		prec = cur;
+		cur = next;
+	}
+
+	return prec;
+}
+
+/* Return the length of an expression list */
+static int
+elist_length(PgBenchExprList *list)
+{
+	int len = 0;
+
+	for (; list != NULL; list = list->next)
+		len++;
+
+	return len;
+}
+
+/* Build function call expression */
+static PgBenchExpr *
+make_func(const int fnumber, PgBenchExprList *args)
+{
+	PgBenchExpr *expr = pg_malloc(sizeof(PgBenchExpr));
+
+	Assert(fnumber >= 0);
+
+	if ((PGBENCH_FUNCTIONS[fnumber].nargs >= 0 &&
+		 PGBENCH_FUNCTIONS[fnumber].nargs != elist_length(args)) ||
+		/* check at least one arg for min & max */
+		(PGBENCH_FUNCTIONS[fnumber].nargs == -1 &&
+		 elist_length(args) == 0))
+		expr_yyerror_more("unexpected number of arguments",
+						  PGBENCH_FUNCTIONS[fnumber].fname);
+
+	expr->etype = ENODE_FUNCTION;
+	expr->u.function.function = PGBENCH_FUNCTIONS[fnumber].tag;
+	/* the argument list has been built in reverse order, it is fixed here */
+	expr->u.function.args = reverse_elist(args);
+
+	return expr;
+}
+
 #include "exprscan.c"
diff --git a/src/bin/pgbench/exprscan.l b/src/bin/pgbench/exprscan.l
index 5331ab7..1f8fc65 100644
--- a/src/bin/pgbench/exprscan.l
+++ b/src/bin/pgbench/exprscan.l
@@ -46,6 +46,7 @@ space			[ \t\r\f]
 "%"				{ yycol += yyleng; return '%'; }
 "("				{ yycol += yyleng; return '('; }
 ")"				{ yycol += yyleng; return ')'; }
+","				{ yycol += yyleng; return ','; }
 
 :[a-zA-Z0-9_]+	{
 					yycol += yyleng;
@@ -57,8 +58,19 @@ space			[ \t\r\f]
 					yylval.ival = strtoint64(yytext);
 					return INTEGER;
 				}
+[0-9]+\.[0-9]+	{
+					yycol += yyleng;
+					yylval.dval = atof(yytext);
+					return DOUBLE;
+				}
+[a-zA-Z0-9_]+   {
+					yycol += yyleng;
+					yylval.str = pg_strdup(yytext);
+					return FUNCTION;
+				}
+
+[\n]			{ yycol = 0; yyline++; /* never occurs, input on one line */ }
 
-[\n]			{ yycol = 0; yyline++; }
 {space}+		{ yycol += yyleng; /* ignore */ }
 
 .				{
@@ -71,10 +83,16 @@ space			[ \t\r\f]
 %%
 
 void
-yyerror(const char *message)
+expr_yyerror_more(const char *message, const char *more)
 {
 	syntax_error(expr_source, expr_lineno, expr_full_line, expr_command,
-				 message, NULL, expr_col + yycol);
+				 message, more, expr_col + yycol);
+}
+
+void
+yyerror(const char *message)
+{
+	expr_yyerror_more(message, NULL);
 }
 
 /*
@@ -94,15 +112,14 @@ expr_scanner_init(const char *str, const char *source,
 	expr_command = (char *) cmd;
 	expr_col = (int) ecol;
 
-	/*
-	 * Might be left over after error
-	 */
+	/* reset column count for this scan */
+	yycol = 0;
+
+	/* Might be left over after error */
 	if (YY_CURRENT_BUFFER)
 		yy_delete_buffer(YY_CURRENT_BUFFER);
 
-	/*
-	 * Make a scan buffer with special termination needed by flex.
-	 */
+	/* Make a scan buffer with special termination needed by flex. */
 	scanbuflen = slen;
 	scanbuf = pg_malloc(slen + 2);
 	memcpy(scanbuf, str, slen);
diff --git a/src/bin/pgbench/pgbench.c b/src/bin/pgbench/pgbench.c
index 60ad22e..6ae1873 100644
--- a/src/bin/pgbench/pgbench.c
+++ b/src/bin/pgbench/pgbench.c
@@ -303,13 +303,10 @@ static int	debug = 0;			/* debug flag */
 
 /* default scenario */
 static char *tpc_b = {
-	"\\set nbranches " CppAsString2(nbranches) " * :scale\n"
-	"\\set ntellers " CppAsString2(ntellers) " * :scale\n"
-	"\\set naccounts " CppAsString2(naccounts) " * :scale\n"
-	"\\setrandom aid 1 :naccounts\n"
-	"\\setrandom bid 1 :nbranches\n"
-	"\\setrandom tid 1 :ntellers\n"
-	"\\setrandom delta -5000 5000\n"
+	"\\set aid random(1, " CppAsString2(naccounts) " * :scale)\n"
+	"\\set bid random(1, " CppAsString2(nbranches) " * :scale)\n"
+	"\\set tid random(1, " CppAsString2(ntellers) " * :scale)\n"
+	"\\set delta random(-5000, 5000)\n"
 	"BEGIN;\n"
 	"UPDATE pgbench_accounts SET abalance = abalance + :delta WHERE aid = :aid;\n"
 	"SELECT abalance FROM pgbench_accounts WHERE aid = :aid;\n"
@@ -321,13 +318,10 @@ static char *tpc_b = {
 
 /* -N case */
 static char *simple_update = {
-	"\\set nbranches " CppAsString2(nbranches) " * :scale\n"
-	"\\set ntellers " CppAsString2(ntellers) " * :scale\n"
-	"\\set naccounts " CppAsString2(naccounts) " * :scale\n"
-	"\\setrandom aid 1 :naccounts\n"
-	"\\setrandom bid 1 :nbranches\n"
-	"\\setrandom tid 1 :ntellers\n"
-	"\\setrandom delta -5000 5000\n"
+	"\\set aid random(1, " CppAsString2(naccounts) " * :scale)\n"
+	"\\set bid random(1, " CppAsString2(nbranches) " * :scale)\n"
+	"\\set tid random(1, " CppAsString2(ntellers) " * :scale)\n"
+	"\\set delta random(-5000, 5000)\n"
 	"BEGIN;\n"
 	"UPDATE pgbench_accounts SET abalance = abalance + :delta WHERE aid = :aid;\n"
 	"SELECT abalance FROM pgbench_accounts WHERE aid = :aid;\n"
@@ -337,8 +331,7 @@ static char *simple_update = {
 
 /* -S case */
 static char *select_only = {
-	"\\set naccounts " CppAsString2(naccounts) " * :scale\n"
-	"\\setrandom aid 1 :naccounts\n"
+	"\\set aid random(1, " CppAsString2(naccounts) " * :scale)\n"
 	"SELECT abalance FROM pgbench_accounts WHERE aid = :aid;\n"
 };
 
@@ -887,22 +880,72 @@ getQueryParams(CState *st, const Command *command, const char **params)
 }
 
 /*
+ * Recursive evaluation of int or double expressions
+ *
+ * Note that currently only integer variables are available, with values
+ * stored as text.
+ */
+
+static int64
+coerceToInt(PgBenchValue *pval)
+{
+	if (pval->type == PGBT_INT)
+		return pval->u.ival;
+	else if (pval->type == PGBT_DOUBLE)
+		return (int64) pval->u.dval;
+	fprintf(stderr, "unexpected value type %d\n", pval->type);
+	exit(1);
+	return 0;
+}
+
+static double
+coerceToDouble(PgBenchValue *pval)
+{
+	if (pval->type == PGBT_DOUBLE)
+		return pval->u.dval;
+	else if (pval->type == PGBT_INT)
+		return (double) pval->u.ival;
+	fprintf(stderr, "unexpected value type %d\n", pval->type);
+	exit(1);
+	return 0;
+}
+
+static void
+setIntValue(PgBenchValue *pv, int64 ival)
+{
+	pv->type = PGBT_INT;
+	pv->u.ival = ival;
+}
+
+static void
+setDoubleValue(PgBenchValue *pv, double dval)
+{
+	pv->type = PGBT_DOUBLE;
+	pv->u.dval = dval;
+}
+
+/* use short names in the evaluator */
+#define INT(v) coerceToInt(&v)
+#define DOUBLE(v) coerceToDouble(&v)
+#define SET_INT(pv, ival) setIntValue(pv, ival)
+#define SET_DOUBLE(pv, dval) setDoubleValue(pv, dval)
+
+/*
  * Recursive evaluation of an expression in a pgbench script
  * using the current state of variables.
  * Returns whether the evaluation was ok,
  * the value itself is returned through the retval pointer.
  */
 static bool
-evaluateExpr(CState *st, PgBenchExpr *expr, int64 *retval)
+evaluateExpr(TState *thread, CState *st, PgBenchExpr *expr, PgBenchValue *retval)
 {
 	switch (expr->etype)
 	{
-		case ENODE_INTEGER_CONSTANT:
+		case ENODE_CONSTANT:
 			{
-				*retval = expr->u.integer_constant.ival;
+				*retval = expr->u.constant;
 				return true;
 			}
-
 		case ENODE_VARIABLE:
 			{
 				char	   *var;
@@ -913,58 +956,267 @@ evaluateExpr(CState *st, PgBenchExpr *expr, int64 *retval)
 							expr->u.variable.varname);
 					return false;
 				}
-				*retval = strtoint64(var);
+
+				SET_INT(retval, strtoint64(var));
 				return true;
 			}
-
 		case ENODE_OPERATOR:
 			{
-				int64		lval;
-				int64		rval;
+				PgBenchValue		lval, rval;
 
-				if (!evaluateExpr(st, expr->u.operator.lexpr, &lval))
+				if (!evaluateExpr(thread, st, expr->u.operator.lexpr, &lval))
 					return false;
-				if (!evaluateExpr(st, expr->u.operator.rexpr, &rval))
+
+				if (!evaluateExpr(thread, st, expr->u.operator.rexpr, &rval))
 					return false;
-				switch (expr->u.operator.operator)
+
+				/* overloaded type management */
+				if (lval.type == PGBT_DOUBLE || rval.type == PGBT_DOUBLE)
 				{
-					case '+':
-						*retval = lval + rval;
-						return true;
+					switch (expr->u.operator.operator)
+					{
+						case '+':
+							SET_DOUBLE(retval, DOUBLE(lval) + DOUBLE(rval));
+							return true;
 
-					case '-':
-						*retval = lval - rval;
-						return true;
+						case '-':
+							SET_DOUBLE(retval, DOUBLE(lval) - DOUBLE(rval));
+							return true;
+
+						case '*':
+							SET_DOUBLE(retval, DOUBLE(lval) * DOUBLE(rval));
+							return true;
+
+						case '/':
+							SET_DOUBLE(retval, DOUBLE(lval) / DOUBLE(rval));
+							return true;
+
+						case '%': /* no overloading for modulo */
+							if (INT(rval) == 0)
+							{
+								fprintf(stderr, "division by zero\n");
+								return false;
+							}
+							SET_INT(retval, INT(lval) % INT(rval));
+							return true;
+					}
+				}
+				else /* both operands are integers */
+				{
+					switch (expr->u.operator.operator)
+					{
+						case '+':
+							SET_INT(retval, INT(lval) + INT(rval));
+							return true;
+
+						case '-':
+							SET_INT(retval, INT(lval) - INT(rval));
+							return true;
+
+						case '*':
+							SET_INT(retval, INT(lval) * INT(rval));
+							return true;
+
+						case '/':
+							if (INT(rval) == 0)
+							{
+								fprintf(stderr, "division by zero\n");
+								return false;
+							}
+
+							SET_INT(retval, INT(lval) / INT(rval));
+							return true;
+
+						case '%':
+							if (INT(rval) == 0)
+							{
+								fprintf(stderr, "division by zero\n");
+								return false;
+							}
+
+							SET_INT(retval, INT(lval) % INT(rval));
+							return true;
+					}
+				}
+
+				fprintf(stderr, "unexpected operator '%c'\n",
+						expr->u.operator.operator);
+				exit(1);
+			}
+		case ENODE_FUNCTION:
+			{
+				PgBenchFunction func = expr->u.function.function;
+				PgBenchExprList *args = expr->u.function.args;
 
-					case '*':
-						*retval = lval * rval;
+				switch (func)
+				{
+					case PGBENCH_PI:
+						SET_DOUBLE(retval, M_PI);
 						return true;
 
-					case '/':
-						if (rval == 0)
+					case PGBENCH_ABS:
 						{
-							fprintf(stderr, "division by zero\n");
-							return false;
+							PgBenchValue arg;
+
+							if (!evaluateExpr(thread, st, args->expr, &arg))
+								return false;
+
+							if (arg.type == PGBT_DOUBLE)
+							{
+								if (DOUBLE(arg) < 0.0)
+									SET_DOUBLE(retval, - DOUBLE(arg));
+								else
+									*retval = arg;
+							}
+							else if (arg.type == PGBT_INT)
+							{
+								if (INT(arg) < 0)
+									SET_INT(retval, - INT(arg));
+								else
+									*retval = arg;
+							}
+
+							return true;
 						}
-						*retval = lval / rval;
-						return true;
+					case PGBENCH_SQRT:
+						{
+							PgBenchValue arg;
+
+							if (!evaluateExpr(thread, st, args->expr, &arg))
+								return false;
 
-					case '%':
-						if (rval == 0)
+							SET_DOUBLE(retval, sqrt(DOUBLE(arg)));
+
+							return true;
+						}
+					case PGBENCH_DEBUG:
 						{
-							fprintf(stderr, "division by zero\n");
-							return false;
+							if (!evaluateExpr(thread, st, args->expr, retval))
+								return false;
+
+							fprintf(stderr,	"debug(script=%d,command=%d): ",
+									st->use_file, st->state+1);
+
+							if (retval->type == PGBT_INT)
+								fprintf(stderr,	"int " INT64_FORMAT "\n", retval->u.ival);
+							else if (retval->type == PGBT_DOUBLE)
+								fprintf(stderr, "double %f\n", retval->u.dval);
+							else
+								fprintf(stderr, "none\n");
+
+							return true;
 						}
-						*retval = lval % rval;
-						return true;
-				}
+					case PGBENCH_DOUBLE:
+						{
+							PgBenchValue arg;
 
-				fprintf(stderr, "bad operator\n");
-				return false;
-			}
+							if (!evaluateExpr(thread, st, args->expr, &arg))
+								return false;
+
+							SET_DOUBLE(retval, DOUBLE(arg));
+
+							return true;
+						}
+					case PGBENCH_INT:
+						{
+							PgBenchValue arg;
+
+							if (!evaluateExpr(thread, st, args->expr, &arg))
+								return false;
+
+							SET_INT(retval, INT(arg));
+
+							return true;
+						}
+					case PGBENCH_MIN:
+					case PGBENCH_MAX:
+						{
+							int64 val = -1;
+							bool first = true;
+							while (args != NULL)
+							{
+								PgBenchValue arg;
+
+								if (!evaluateExpr(thread, st, args->expr, &arg))
+									return false;
+
+								if (first)
+									val = INT(arg);
+								else if (func == PGBENCH_MIN)
+									val = val < INT(arg)? val: INT(arg);
+								else if (func == PGBENCH_MAX)
+									val = val > INT(arg)? val: INT(arg);
 
+								args = args->next;
+								first = false;
+							}
+
+							SET_INT(retval, val);
+							return true;
+						}
+					case PGBENCH_RANDOM:
+					case PGBENCH_RANDOM_EXPONENTIAL:
+					case PGBENCH_RANDOM_GAUSSIAN:
+						{
+							PgBenchValue varg1, varg2;
+							int64 arg1, arg2;
+
+							if (!evaluateExpr(thread, st, args->expr, &varg1))
+								return false;
+
+							if (!evaluateExpr(thread, st, args->next->expr, &varg2))
+								return false;
+
+							arg1 = INT(varg1);
+							arg2 = INT(varg2);
+
+							/* check random range */
+							if (arg1 > arg2)
+							{
+								fprintf(stderr, "empty range given to random\n");
+								st->ecnt++;
+								return false;
+							}
+							else if (arg2 - arg1 < 0 || (arg2 - arg1) + 1 < 0)
+							{
+								/* prevent int overflows in random functions */
+								fprintf(stderr, "random range is too large\n");
+								st->ecnt++;
+								return false;
+							}
+
+							if (func == PGBENCH_RANDOM)
+								SET_INT(retval, getrand(thread, arg1, arg2));
+							else /* gaussian & exponential */
+							{
+								PgBenchValue param;
+
+								if (!evaluateExpr(thread, st,
+												  args->next->next->expr,
+												  &param))
+									return false;
+
+								if (func == PGBENCH_RANDOM_GAUSSIAN)
+									SET_INT(retval,
+											getGaussianRand(thread, arg1, arg2,
+															DOUBLE(param)));
+								else /* exponential */
+									SET_INT(retval,
+											getExponentialRand(thread, arg1, arg2,
+															   DOUBLE(param)));
+							}
+
+							return true;
+						}
+				default:
+					fprintf(stderr, "unexpected function tag: %d\n", func);
+					exit(1);
+				}
+			}
 		default:
-			break;
+			fprintf(stderr, "unexpected enode type in evaluation: %d\n",
+					expr->etype);
+			exit(1);
 	}
 
 	fprintf(stderr, "bad expression\n");
@@ -1610,15 +1862,15 @@ top:
 		else if (pg_strcasecmp(argv[0], "set") == 0)
 		{
 			char		res[64];
-			PgBenchExpr *expr = commands[st->state]->expr;
-			int64		result;
+			PgBenchExpr 	*expr = commands[st->state]->expr;
+			PgBenchValue	result;
 
-			if (!evaluateExpr(st, expr, &result))
+			if (!evaluateExpr(thread, st, expr, &result))
 			{
 				st->ecnt++;
 				return true;
 			}
-			sprintf(res, INT64_FORMAT, result);
+			sprintf(res, INT64_FORMAT, INT(result));
 
 			if (!putVariable(st, argv[0], argv[1], res))
 			{
diff --git a/src/bin/pgbench/pgbench.h b/src/bin/pgbench/pgbench.h
index 42e2aae..edce567 100644
--- a/src/bin/pgbench/pgbench.h
+++ b/src/bin/pgbench/pgbench.h
@@ -11,24 +11,56 @@
 #ifndef PGBENCH_H
 #define PGBENCH_H
 
+typedef enum
+{
+	PGBT_NONE,
+	PGBT_INT,
+	PGBT_DOUBLE
+} PgBenchValueType;
+
+typedef struct
+{
+	PgBenchValueType type;
+	union
+	{
+		int64 ival;
+		double dval;
+	} u;
+} PgBenchValue;
+
 typedef enum PgBenchExprType
 {
-	ENODE_INTEGER_CONSTANT,
+	ENODE_CONSTANT,
 	ENODE_VARIABLE,
-	ENODE_OPERATOR
+	ENODE_OPERATOR,
+	ENODE_FUNCTION
 } PgBenchExprType;
 
+typedef enum PgBenchFunction
+{
+	PGBENCH_NONE,
+	PGBENCH_PI,
+	PGBENCH_INT,
+	PGBENCH_DOUBLE,
+	PGBENCH_DEBUG,
+	PGBENCH_ABS,
+	PGBENCH_SQRT,
+	PGBENCH_MIN,
+	PGBENCH_MAX,
+	PGBENCH_RANDOM,
+	PGBENCH_RANDOM_GAUSSIAN,
+	PGBENCH_RANDOM_EXPONENTIAL
+} PgBenchFunction;
+
 typedef struct PgBenchExpr PgBenchExpr;
+typedef struct PgBenchExprList PgBenchExprList;
 
 struct PgBenchExpr
 {
 	PgBenchExprType etype;
 	union
 	{
-		struct
-		{
-			int64		ival;
-		}			integer_constant;
+		PgBenchValue	constant;
 		struct
 		{
 			char	   *varname;
@@ -39,14 +71,26 @@ struct PgBenchExpr
 			PgBenchExpr *lexpr;
 			PgBenchExpr *rexpr;
 		}			operator;
+		struct
+		{
+			PgBenchFunction function;
+			PgBenchExprList *args;
+		}			function;
 	}			u;
 };
 
+struct PgBenchExprList
+{
+	PgBenchExpr *expr;
+	PgBenchExprList *next;
+};
+
 extern PgBenchExpr *expr_parse_result;
 
 extern int	expr_yyparse(void);
 extern int	expr_yylex(void);
 extern void expr_yyerror(const char *str);
+extern void expr_yyerror_more(const char *str, const char *more);
 extern void expr_scanner_init(const char *str, const char *source,
 				  const int lineno, const char *line,
 				  const char *cmd, const int ecol);
-- 
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

Reply via email to