Hello Heikki,

As an initial list of functions, I'd suggest:

abs(x)
min(x, y, ...)
max(x, y, ...)
random_uniform(min, max)
random_gaussian(min, max, threshold)
random_exponential(min, max, threshold)

Would that be enough to specify e.g. the

As soon as we add more functions, the way they are documented needs to be reworked too; we'll need to add a table in the manual to list them.

Here is a v8 with "abs", "min", "max", "random", "gaussian" et
"exponential".

There is no expression typing, but expressions are evaluated and cast to the expected type depending on what is needed (variables expect an int value, function arguments are usually int but for threshold...).

There is an evalDouble function to evaluate double arguments for gaussian & exponential thresholds.

There is a special "int" function which evaluates its argument as a double and cast the result to an int, for testing purpose.

Variables are only int values, and functions only return ints.

There is no real doc, WIP...

Also included are a set of sql stuff I used for minimal tests.

--
Fabien.
diff --git a/doc/src/sgml/ref/pgbench.sgml b/doc/src/sgml/ref/pgbench.sgml
index 2517a3a..ed99abc 100644
--- a/doc/src/sgml/ref/pgbench.sgml
+++ b/doc/src/sgml/ref/pgbench.sgml
@@ -758,10 +758,11 @@ 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, integer function calls and parentheses.
      </para>
 
      <para>
diff --git a/src/bin/pgbench/exprparse.y b/src/bin/pgbench/exprparse.y
index e68631e..a1b0afd 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,89 @@ make_op(char operator, PgBenchExpr *lexpr, PgBenchExpr *rexpr)
 	return expr;
 }
 
+static struct {
+	char * fname;
+	int nargs;
+	PgBenchFunction tag;
+} PGBENCH_FUNCTIONS[] = {
+	{ "abs", 1, PGBENCH_ABS },
+	{ "int", 1, PGBENCH_INT },
+	{ "min", -1, PGBENCH_MIN },
+	{ "max", -1, PGBENCH_MAX },
+	{ "random", 2, PGBENCH_RANDOM },
+	{ "gaussian", 3, PGBENCH_GAUSSIAN },
+	{ "exponential", 3, PGBENCH_EXPONENTIAL }
+};
+
+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 e839fa3..d22ec47 100644
--- a/src/bin/pgbench/pgbench.c
+++ b/src/bin/pgbench/pgbench.c
@@ -300,13 +300,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"
@@ -318,13 +315,9 @@ 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"
 	"BEGIN;\n"
 	"UPDATE pgbench_accounts SET abalance = abalance + :delta WHERE aid = :aid;\n"
 	"SELECT abalance FROM pgbench_accounts WHERE aid = :aid;\n"
@@ -334,8 +327,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"
 };
 
@@ -883,6 +875,71 @@ 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 '/':
+				case '%':
+					if (rval == 0.0)
+					{
+						fprintf(stderr, "division by zero\n");
+						return false;
+					}
+					if (expr->u.operator.operator == '/')
+						*retval = lval / rval;
+					else
+						*retval = (int64) lval % (int64) rval;
+					return true;
+
+				default:
+					fprintf(stderr, "unexpected operator %c\n",
+							expr->u.operator.operator);
+					exit(1);
+					return false;
+			}
+		}
+		default:
+		{
+			int64 ival;
+			bool ok = evalInt(thread, st, expr, &ival);
+			*retval = (double) ival;
+			return ok;
+		}
+	}
+}
+
 /*
  * Recursive evaluation of an expression in a pgbench script
  * using the current state of variables.
@@ -890,7 +947,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)
 	{
@@ -900,6 +957,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;
@@ -919,9 +982,9 @@ 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)
 				{
@@ -960,6 +1023,93 @@ evaluateExpr(CState *st, PgBenchExpr *expr, int64 *retval)
 				return false;
 			}
 
+		case ENODE_FUNCTION:
+			{
+				PgBenchFunction func = expr->u.function.function;
+				PgBenchExprList *args = expr->u.function.args;
+
+				switch (func)
+				{
+					case PGBENCH_RANDOM:
+					case PGBENCH_GAUSSIAN:
+					case PGBENCH_EXPONENTIAL:
+					{
+						int64 arg1, arg2;
+
+						if (!evalInt(thread, st, args->expr, &arg1))
+							return false;
+						if (!evalInt(thread, st, args->next->expr, &arg2))
+							return false;
+
+						if (func == PGBENCH_RANDOM)
+							*retval = getrand(thread, arg1, arg2);
+						else /* gaussian & exponential */
+						{
+							double threshold;
+							if (!evalDouble(thread, st, args->next->next->expr,
+											&threshold))
+								return false;
+							if (func == PGBENCH_GAUSSIAN)
+								*retval = getGaussianRand(thread, arg1, arg2, threshold);
+							else /* exponential */
+								*retval = getExponentialRand(thread, arg1, arg2, threshold);
+						}
+
+						return true;
+					}
+					case PGBENCH_ABS: /* unary function */
+					{
+						int64 arg;
+
+						if (!evalInt(thread, st, args->expr, &arg))
+							return false;
+
+						*retval = arg > 0? arg: -arg;
+						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;
+					}
+					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 = val;
+						return true;
+					}
+					default:
+						fprintf(stderr, "bad function (%d)\n",
+								expr->u.function.function);
+						return false;
+				}
+			}
+
 		default:
 			break;
 	}
@@ -1609,7 +1759,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..2239ae7 100644
--- a/src/bin/pgbench/pgbench.h
+++ b/src/bin/pgbench/pgbench.h
@@ -14,11 +14,26 @@
 typedef enum PgBenchExprType
 {
 	ENODE_INTEGER_CONSTANT,
+	ENODE_DOUBLE_CONSTANT,
 	ENODE_VARIABLE,
-	ENODE_OPERATOR
+	ENODE_OPERATOR,
+	ENODE_FUNCTION
 } PgBenchExprType;
 
+typedef enum PgBenchFunction
+{
+	PGBENCH_NONE,
+	PGBENCH_ABS,
+	PGBENCH_INT,
+	PGBENCH_MIN,
+	PGBENCH_MAX,
+	PGBENCH_RANDOM,
+	PGBENCH_GAUSSIAN,
+	PGBENCH_EXPONENTIAL
+} PgBenchFunction;
+
 typedef struct PgBenchExpr PgBenchExpr;
+typedef struct PgBenchExprList PgBenchExprList;
 
 struct PgBenchExpr
 {
@@ -31,6 +46,10 @@ struct PgBenchExpr
 		}			integer_constant;
 		struct
 		{
+			double		dval;
+		}			double_constant;
+		struct
+		{
 			char	   *varname;
 		}			variable;
 		struct
@@ -39,14 +58,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: func-init.sql
Description: application/sql

Attachment: func-reset.sql
Description: application/sql

Attachment: func-show.sql
Description: application/sql

Attachment: func.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