Here is a v12 which implements the suggestions below.

Should we not allow for functions taking 0 arguments?  Since we're already
into some math here, how about pi()? ;-)

Hmmm, why not.

I understand requiring at least 1 arg simplifies the code a bit, but right
now it reports syntax error for "random()", while it correctly reports
unexpected number of arguments for "random(1,2,3)".  We would need another
check for min() and max() which expect >=1 arguments, but it's easy to add.

Indeed, I had to add a special check.

I would also argue that we should rename "random" to "rand" here to avoid
confusion with the familiar SQL function "random()" that doesn't take
arguments.

Why not, as it is also consistent with exporand() & gaussrand().

--
Fabien.
diff --git a/doc/src/sgml/ref/pgbench.sgml b/doc/src/sgml/ref/pgbench.sgml
index 0ac40f1..3278e77 100644
--- a/doc/src/sgml/ref/pgbench.sgml
+++ b/doc/src/sgml/ref/pgbench.sgml
@@ -771,17 +771,20 @@ 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.
+      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 * rand(1, 100000 * :scale)) % (100000 * :scale) + 1
 </programlisting></para>
     </listitem>
    </varlistentry>
@@ -931,18 +934,117 @@ pgbench <optional> <replaceable>options</> </optional> <replaceable>dbname</>
    </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>ddebug(<replaceable>x</>)</></></>
+       <entry>double</>
+       <entry>stderr print for debug and return argument</>
+       <entry><literal>ddebug(5432.1)</></>
+       <entry><literal>5432.1</></>
+      </row>
+      <row>
+       <entry><literal><function>double(<replaceable>i</>)</></></>
+       <entry>double</>
+       <entry>evaluate as int and cast to double</>
+       <entry><literal>double(5432)</></>
+       <entry><literal>5432.0</></>
+      </row>
+      <row>
+       <entry><literal><function>exporand(<replaceable>i</>, <replaceable>j</>, <replaceable>t</>)</></></>
+       <entry>integer</>
+       <entry>exponentially distributed random integer in the bounds, see below</>
+       <entry><literal>exporand(1, 10, 3.0)</></>
+       <entry>int between <literal>1</> and <literal>10</></>
+      </row>
+      <row>
+       <entry><literal><function>idebug(<replaceable>i</>)</></></>
+       <entry>integer</>
+       <entry>stderr print for debug and return argument</>
+       <entry><literal>idebug(5432)</></>
+       <entry><literal>5432</></>
+      </row>
+      <row>
+       <entry><literal><function>int(<replaceable>x</>)</></></>
+       <entry>integer</>
+       <entry>evaluate as double and cast to int</>
+       <entry><literal>int(5.4 + 3.8)</></>
+       <entry><literal>9</></>
+      </row>
+      <row>
+       <entry><literal><function>gaussrand(<replaceable>i</>, <replaceable>j</>, <replaceable>t</>)</></></>
+       <entry>integer</>
+       <entry>gaussian distributed random integer in the bounds, see below</>
+       <entry><literal>gaussrand(1, 10, 2.5)</></>
+       <entry>int between <literal>1</> and <literal>10</></>
+      </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>max(<replaceable>i</>, <replaceable>...</>)</></></>
+       <entry>integer</>
+       <entry>maximum value</>
+       <entry><literal>max(5, 4, 3, 2)</></>
+       <entry><literal>5</></>
+      </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>rand(<replaceable>i</>, <replaceable>j</>)</></></>
+       <entry>integer</>
+       <entry>uniformly distributed random integer in the bounds</>
+       <entry><literal>rand(1, 10)</></>
+       <entry>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>
    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 rand(1, 100000 * :scale)
+\set bid rand(1, 1 * :scale)
+\set tid rand(1, 10 * :scale)
+\set delta rand(-5000, 5000)
 BEGIN;
 UPDATE pgbench_accounts SET abalance = abalance + :delta WHERE aid = :aid;
 SELECT abalance FROM pgbench_accounts WHERE aid = :aid;
diff --git a/src/bin/pgbench/exprparse.y b/src/bin/pgbench/exprparse.y
index e68631e..35f6c58 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); }
 	;
 
 %%
@@ -74,6 +92,16 @@ make_integer_constant(int64 ival)
 }
 
 static PgBenchExpr *
+make_double_constant(double dval)
+{
+	PgBenchExpr *expr = pg_malloc(sizeof(PgBenchExpr));
+
+	expr->etype = ENODE_DOUBLE_CONSTANT;
+	expr->u.double_constant.dval = dval;
+	return expr;
+}
+
+static PgBenchExpr *
 make_variable(char *varname)
 {
 	PgBenchExpr *expr = pg_malloc(sizeof(PgBenchExpr));
@@ -95,4 +123,97 @@ make_op(char operator, PgBenchExpr *lexpr, PgBenchExpr *rexpr)
 	return expr;
 }
 
+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 },
+	{ "rand", 2, PGBENCH_RANDOM },
+	{ "gaussrand", 3, PGBENCH_GAUSSRAND },
+	{ "exporand", 3, PGBENCH_EXPORAND },
+	{ "idebug", 1, PGBENCH_IDEBUG },
+	{ "ddebug", 1, PGBENCH_DDEBUG }
+};
+
+static int
+find_func(const char * fname)
+{
+	int nfunctions = sizeof(PGBENCH_FUNCTIONS) / sizeof(PGBENCH_FUNCTIONS[0]);
+	int i;
+
+	for (i = 0; i < nfunctions; i++)
+		if (pg_strcasecmp(fname, PGBENCH_FUNCTIONS[i].fname) == 0)
+			return i;
+
+	expr_yyerror_more("unexpected function name", fname);
+
+	/* not reached */
+	return -1;
+}
+
+static PgBenchExprList *
+make_elist(PgBenchExpr *expr, PgBenchExprList *list)
+{
+	PgBenchExprList *cons = pg_malloc(sizeof(PgBenchExprList));
+	cons->expr = expr;
+	cons->next = list;
+	return cons;
+}
+
+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;
+}
+
+static int
+elist_length(PgBenchExprList *list)
+{
+	int len = 0;
+
+	for (; list != NULL; list = list->next)
+		len++;
+
+	return len;
+}
+
+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)) ||
+		/* 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;
+	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..bf8aa0f 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-Z]+       {
+					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 f2d435b..9d5250a 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 rand(1, " CppAsString2(naccounts) " * :scale)\n"
+	"\\set bid rand(1, " CppAsString2(nbranches) " * :scale)\n"
+	"\\set tid rand(1, " CppAsString2(ntellers) " * :scale)\n"
+	"\\set delta rand(-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 rand(1, " CppAsString2(naccounts) " * :scale)\n"
+	"\\set bid rand(1, " CppAsString2(nbranches) " * :scale)\n"
+	"\\set tid rand(1, " CppAsString2(ntellers) " * :scale)\n"
+	"\\set delta rand(-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 rand(1, " CppAsString2(naccounts) " * :scale)\n"
 	"SELECT abalance FROM pgbench_accounts WHERE aid = :aid;\n"
 };
 
@@ -886,6 +879,145 @@ getQueryParams(CState *st, const Command *command, const char **params)
 		params[i] = getVariable(st, command->argv[i + 1]);
 }
 
+static bool evalInt(TState *, CState *, PgBenchExpr *, int64 *);
+
+static bool
+evalDouble(TState *thread, CState *st, PgBenchExpr *expr, double *retval)
+{
+	switch (expr->etype)
+	{
+		case ENODE_DOUBLE_CONSTANT:
+		{
+			*retval = expr->u.double_constant.dval;
+			return true;
+		}
+		case ENODE_OPERATOR:
+		{
+			double		lval, rval;
+
+			if (!evalDouble(thread, st, expr->u.operator.lexpr, &lval))
+				return false;
+			if (!evalDouble(thread, st, expr->u.operator.rexpr, &rval))
+				return false;
+
+			switch (expr->u.operator.operator)
+			{
+				case '+':
+					*retval = lval + rval;
+					return true;
+
+				case '-':
+					*retval = lval - rval;
+					return true;
+
+				case '*':
+					*retval = lval * rval;
+					return true;
+
+				case '/':
+					*retval = lval / rval;
+					return true;
+
+					/* cast any int operator */
+				case '%':
+				{
+					int64 ival;
+					if (!evalInt(thread, st, expr, &ival))
+						return false;
+					*retval = (double) ival;
+					return true;
+				}
+				default:
+					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;
+
+			switch (func)
+			{
+			case PGBENCH_PI:
+				*retval = M_PI;
+				return true;
+			case PGBENCH_ABS: /* also an integer function */
+			{
+				if (!evalDouble(thread, st, args->expr, retval))
+					return false;
+
+				if ((*retval) < 0.0)
+					*retval = - *retval;
+
+				return true;
+			}
+			case PGBENCH_SQRT:
+			{
+				double arg;
+
+				if (!evalDouble(thread, st, args->expr, &arg))
+					return false;
+
+				*retval = sqrt(arg);
+
+				return true;
+			}
+			case PGBENCH_DDEBUG:
+			{
+				if (!evalDouble(thread, st, args->expr, retval))
+					return false;
+
+				fprintf(stderr, "ddebug(script=%d,command=%d): %f\n",
+						st->use_file, st->state+1, *retval);
+
+				return true;
+			}
+			case PGBENCH_DOUBLE:
+			{
+				int64 ival;
+				if (!evalInt(thread, st, args->expr, &ival))
+					return false;
+				*retval = (double) ival;
+				return true;
+			}
+			/* integer-specific functions are evaluated as int and cast */
+			case PGBENCH_INT:
+			case PGBENCH_IDEBUG:
+			case PGBENCH_MIN:
+			case PGBENCH_MAX:
+			case PGBENCH_RANDOM:
+			case PGBENCH_GAUSSRAND:
+			case PGBENCH_EXPORAND:
+			{
+				int64 ival;
+				if (!evalInt(thread, st, expr, &ival))
+					return false;
+				*retval = (double) ival;
+				return true;
+			}
+			default:
+				fprintf(stderr, "unexpected function tag: %d\n", func);
+				exit(1);
+			}
+		}
+	case ENODE_INTEGER_CONSTANT:
+	case ENODE_VARIABLE:
+		{
+			int64 ival;
+			if (!evalInt(thread, st, expr, &ival))
+				return false;
+			*retval = (double) ival;
+			return true;
+		}
+	default:
+		fprintf(stderr, "unexpected enode type in double evaluation: %d\n",
+				expr->etype);
+		exit(1);
+	}
+}
+
 /*
  * Recursive evaluation of an expression in a pgbench script
  * using the current state of variables.
@@ -893,7 +1025,7 @@ getQueryParams(CState *st, const Command *command, const char **params)
  * the value itself is returned through the retval pointer.
  */
 static bool
-evaluateExpr(CState *st, PgBenchExpr *expr, int64 *retval)
+evalInt(TState *thread, CState *st, PgBenchExpr *expr, int64 *retval)
 {
 	switch (expr->etype)
 	{
@@ -903,6 +1035,12 @@ evaluateExpr(CState *st, PgBenchExpr *expr, int64 *retval)
 				return true;
 			}
 
+		case ENODE_DOUBLE_CONSTANT:
+			{
+				*retval = (int64) expr->u.double_constant.dval;
+				return true;
+			}
+
 		case ENODE_VARIABLE:
 			{
 				char	   *var;
@@ -922,49 +1060,180 @@ evaluateExpr(CState *st, PgBenchExpr *expr, int64 *retval)
 				int64		lval;
 				int64		rval;
 
-				if (!evaluateExpr(st, expr->u.operator.lexpr, &lval))
+				if (!evalInt(thread, st, expr->u.operator.lexpr, &lval))
 					return false;
-				if (!evaluateExpr(st, expr->u.operator.rexpr, &rval))
+				if (!evalInt(thread, st, expr->u.operator.rexpr, &rval))
 					return false;
+
 				switch (expr->u.operator.operator)
 				{
-					case '+':
-						*retval = lval + rval;
-						return true;
+				case '+':
+					*retval = lval + rval;
+					return true;
 
-					case '-':
-						*retval = lval - rval;
-						return true;
+				case '-':
+					*retval = lval - rval;
+					return true;
 
-					case '*':
-						*retval = lval * rval;
-						return true;
+				case '*':
+					*retval = lval * rval;
+					return true;
 
-					case '/':
-						if (rval == 0)
+				case '/':
+					if (rval == 0)
+					{
+						fprintf(stderr, "division by zero\n");
+						return false;
+					}
+					*retval = lval / rval;
+					return true;
+
+				case '%':
+					if (rval == 0)
+					{
+						fprintf(stderr, "division by zero\n");
+						return false;
+					}
+					*retval = lval % rval;
+					return true;
+
+				default:
+					fprintf(stderr, "unexpected integer operator '%c'\n",
+							expr->u.operator.operator);
+					return false;
+				}
+			}
+
+		case ENODE_FUNCTION:
+			{
+				PgBenchFunction func = expr->u.function.function;
+				PgBenchExprList *args = expr->u.function.args;
+
+				switch (func)
+				{
+					case PGBENCH_RANDOM:
+					case PGBENCH_GAUSSRAND:
+					case PGBENCH_EXPORAND:
+					{
+						int64 arg1, arg2;
+
+						if (!evalInt(thread, st, args->expr, &arg1))
+							return false;
+						if (!evalInt(thread, st, args->next->expr, &arg2))
+							return false;
+
+						/* check random range */
+						if (arg1 > arg2)
 						{
-							fprintf(stderr, "division by zero\n");
+							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;
 						}
-						*retval = lval / rval;
-						return true;
 
-					case '%':
-						if (rval == 0)
+						if (func == PGBENCH_RANDOM)
+							*retval = getrand(thread, arg1, arg2);
+						else /* gaussian & exponential */
 						{
-							fprintf(stderr, "division by zero\n");
+							double threshold;
+							if (!evalDouble(thread, st, args->next->next->expr,
+											&threshold))
+								return false;
+							if (func == PGBENCH_GAUSSRAND)
+								*retval = getGaussianRand(thread, arg1, arg2, threshold);
+							else /* exponential */
+								*retval = getExponentialRand(thread, arg1, arg2, threshold);
+						}
+
+						return true;
+					}
+					case PGBENCH_IDEBUG: /* unary functions */
+					{
+						if (!evalInt(thread, st, args->expr, retval))
+							return false;
+
+						fprintf(stderr, "idebug(script=%d,command=%d): "
+								INT64_FORMAT "\n", st->use_file, st->state+1, *retval);
+
+						return true;
+					}
+					case PGBENCH_ABS: /* both an int & double function */
+					{
+						if (!evalInt(thread, st, args->expr, retval))
 							return false;
+
+						if ((*retval) < 0)
+							*retval = - *retval;
+
+						return true;
+					}
+					case PGBENCH_MIN: /* n-ary, at least one argument */
+					case PGBENCH_MAX:
+					{
+						int64 val = -1;
+						bool first = true;
+						while (args != NULL)
+						{
+							int64 arg;
+
+							if (!evalInt(thread, st, args->expr, &arg))
+								return false;
+
+							if (first)
+								val = arg;
+							else if (func == PGBENCH_MIN)
+								val = val < arg? val: arg;
+							else if (func == PGBENCH_MAX)
+								val = val > arg? val: arg;
+
+							args = args->next;
+							first = false;
 						}
-						*retval = lval % rval;
+
+						*retval = val;
 						return true;
-				}
+					}
+					case PGBENCH_INT: /* eval as double & cast to int */
+					{
+						double arg;
+
+						if (!evalDouble(thread, st, args->expr, &arg))
+							return false;
+
+						*retval = (int64) arg;
+						return true;
+					}
 
-				fprintf(stderr, "bad operator\n");
-				return false;
+					/* cast double specific functions to int */
+					case PGBENCH_PI:
+					case PGBENCH_DOUBLE:
+					case PGBENCH_DDEBUG:
+					case PGBENCH_SQRT:
+					{
+						double arg;
+
+						if (!evalDouble(thread, st, expr, &arg))
+							return false;
+
+						*retval = (int64) arg;
+						return true;
+					}
+				default:
+					fprintf(stderr, "unexpected function tag %d\n", func);
+					exit(1);
+				}
 			}
 
-		default:
-			break;
+		default: /* abort on internal error */
+			fprintf(stderr, "unexpected enode type in int evaluation: %d\n",
+					expr->etype);
+			exit(1);
 	}
 
 	fprintf(stderr, "bad expression\n");
@@ -1613,7 +1882,7 @@ top:
 			PgBenchExpr *expr = commands[st->state]->expr;
 			int64		result;
 
-			if (!evaluateExpr(st, expr, &result))
+			if (!evalInt(thread, st, expr, &result))
 			{
 				st->ecnt++;
 				return true;
diff --git a/src/bin/pgbench/pgbench.h b/src/bin/pgbench/pgbench.h
index 42e2aae..2b0487c 100644
--- a/src/bin/pgbench/pgbench.h
+++ b/src/bin/pgbench/pgbench.h
@@ -14,11 +14,31 @@
 typedef enum PgBenchExprType
 {
 	ENODE_INTEGER_CONSTANT,
+	ENODE_DOUBLE_CONSTANT,
 	ENODE_VARIABLE,
-	ENODE_OPERATOR
+	ENODE_OPERATOR,
+	ENODE_FUNCTION
 } PgBenchExprType;
 
+typedef enum PgBenchFunction
+{
+	PGBENCH_NONE,
+	PGBENCH_PI,
+	PGBENCH_INT,
+	PGBENCH_DOUBLE,
+	PGBENCH_IDEBUG,
+	PGBENCH_DDEBUG,
+	PGBENCH_ABS,
+	PGBENCH_SQRT,
+	PGBENCH_MIN,
+	PGBENCH_MAX,
+	PGBENCH_RANDOM,
+	PGBENCH_GAUSSRAND,
+	PGBENCH_EXPORAND
+} PgBenchFunction;
+
 typedef struct PgBenchExpr PgBenchExpr;
+typedef struct PgBenchExprList PgBenchExprList;
 
 struct PgBenchExpr
 {
@@ -31,6 +51,10 @@ struct PgBenchExpr
 		}			integer_constant;
 		struct
 		{
+			double		dval;
+		}			double_constant;
+		struct
+		{
 			char	   *varname;
 		}			variable;
 		struct
@@ -39,14 +63,25 @@ 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);

Attachment: functions.sql
Description: application/sql

-- 
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