Hello Kyotaro-san,

My description should have been obscure. Indeed the call tree is
finite for *sane* expression node. But it makes infinit call for
a value of expr->etype unknown by both evalDouble and
evalInt.

Such issue would be detected if the function is actually tested, hopefully this should be the case... :-)

However I agree that relying implicitely on the "default" case is not very good practice, so I updated the code in the attached v11 to fail explicitely on such errors.

I also attached a small test script, which exercises most (all?) functions:

  ./pgbench -f functions.sql -t 1

[...]
By the way, the complexity comes from separating integer and
double. If there is no serios reason to separate them, handling
all values as double makes things far simpler.

Yep, but no.

Could you let me know the reason why it strictly separates integer and double? I don't see no problem in possible errors of floating point calculations for this purpose. Is there any?

Indeed it would make things simpler, but it would break large integers as the int64 -> double -> int64 casts would result in approximations. The integer type is the important one here because it is used for primary keys, and you do not want a key to be approximated in any way, so the int64 type must be fully and exactly supported.

--
Fabien.
diff --git a/doc/src/sgml/ref/pgbench.sgml b/doc/src/sgml/ref/pgbench.sgml
index 0ac40f1..11aa518 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 * random(1, 100000 * :scale)) % (100000 * :scale) + 1
 </programlisting></para>
     </listitem>
    </varlistentry>
@@ -931,18 +934,110 @@ 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>random(<replaceable>i</>, <replaceable>j</>)</></></>
+       <entry>integer</>
+       <entry>uniformly distributed random integer in the bounds</>
+       <entry><literal>random(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 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;
diff --git a/src/bin/pgbench/exprparse.y b/src/bin/pgbench/exprparse.y
index e68631e..97bb559 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,10 @@ static PgBenchExpr *make_op(char operator, PgBenchExpr *lexpr,
 
 result: expr				{ expr_parse_result = $1; }
 
+elist: 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 +70,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 +91,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 +122,93 @@ make_op(char operator, PgBenchExpr *lexpr, PgBenchExpr *rexpr)
 	return expr;
 }
 
+static struct {
+	char * fname;
+	int nargs;
+	PgBenchFunction tag;
+} PGBENCH_FUNCTIONS[] = {
+	{ "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 },
+	{ "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 != -1 &&
+		PGBENCH_FUNCTIONS[fnumber].nargs != elist_length(args))
+		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 6ae1b86..2f11eb7 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"
 };
 
@@ -886,6 +879,142 @@ 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_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 +1022,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 +1032,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 +1057,179 @@ 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_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 +1878,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..af7abcb 100644
--- a/src/bin/pgbench/pgbench.h
+++ b/src/bin/pgbench/pgbench.h
@@ -14,11 +14,30 @@
 typedef enum PgBenchExprType
 {
 	ENODE_INTEGER_CONSTANT,
+	ENODE_DOUBLE_CONSTANT,
 	ENODE_VARIABLE,
-	ENODE_OPERATOR
+	ENODE_OPERATOR,
+	ENODE_FUNCTION
 } PgBenchExprType;
 
+typedef enum PgBenchFunction
+{
+	PGBENCH_NONE,
+	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 +50,10 @@ struct PgBenchExpr
 		}			integer_constant;
 		struct
 		{
+			double		dval;
+		}			double_constant;
+		struct
+		{
 			char	   *varname;
 		}			variable;
 		struct
@@ -39,14 +62,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