Skip to content

Commit 7e137f8

Browse files
committed
Extend pgbench's expression syntax to support a few built-in functions.
Fabien Coelho, reviewed mostly by Michael Paquier and me, but also by Heikki Linnakangas, BeomYong Lee, Kyotaro Horiguchi, Oleksander Shulgin, and Álvaro Herrera.
1 parent bd6cf3f commit 7e137f8

File tree

5 files changed

+409
-97
lines changed

5 files changed

+409
-97
lines changed

doc/src/sgml/ref/pgbench.sgml

Lines changed: 61 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -786,7 +786,7 @@ pgbench <optional> <replaceable>options</> </optional> <replaceable>dbname</>
786786
</para>
787787

788788
<variablelist>
789-
<varlistentry>
789+
<varlistentry id='pgbench-metacommand-set'>
790790
<term>
791791
<literal>\set <replaceable>varname</> <replaceable>expression</></literal>
792792
</term>
@@ -798,8 +798,10 @@ pgbench <optional> <replaceable>options</> </optional> <replaceable>dbname</>
798798
The expression may contain integer constants such as <literal>5432</>,
799799
references to variables <literal>:</><replaceable>variablename</>,
800800
and expressions composed of unary (<literal>-</>) or binary operators
801-
(<literal>+</>, <literal>-</>, <literal>*</>, <literal>/</>, <literal>%</>)
802-
with their usual associativity, and parentheses.
801+
(<literal>+</>, <literal>-</>, <literal>*</>, <literal>/</>,
802+
<literal>%</>) with their usual associativity,
803+
<link linkend="pgbench-builtin-functions">function calls</>, and
804+
parentheses.
803805
</para>
804806

805807
<para>
@@ -994,6 +996,62 @@ END;
994996

995997
</refsect2>
996998

999+
<refsect2 id="pgbench-builtin-functions">
1000+
<title>Built-In Functions</title>
1001+
1002+
<para>
1003+
The following functions are built into <application>pgbench</> and
1004+
may be used in conjunction with
1005+
<link linkend="pgbench-metacommand-set"><literal>\set</literal></link>.
1006+
</para>
1007+
1008+
<!-- list pgbench functions in alphabetical order -->
1009+
<table>
1010+
<title>pgbench Functions</title>
1011+
<tgroup cols="5">
1012+
<thead>
1013+
<row>
1014+
<entry>Function</entry>
1015+
<entry>Return Type</entry>
1016+
<entry>Description</entry>
1017+
<entry>Example</entry>
1018+
<entry>Result</entry>
1019+
</row>
1020+
</thead>
1021+
<tbody>
1022+
<row>
1023+
<entry><literal><function>abs(<replaceable>a</>)</></></>
1024+
<entry>same as <replaceable>a</></>
1025+
<entry>integer value</>
1026+
<entry><literal>abs(-17)</></>
1027+
<entry><literal>17</></>
1028+
</row>
1029+
<row>
1030+
<entry><literal><function>debug(<replaceable>a</>)</></></>
1031+
<entry>same as <replaceable>a</> </>
1032+
<entry>print to <systemitem>stderr</systemitem> the given argument</>
1033+
<entry><literal>debug(5432)</></>
1034+
<entry><literal>5432</></>
1035+
</row>
1036+
<row>
1037+
<entry><literal><function>max(<replaceable>i</> [, <replaceable>...</> ] )</></></>
1038+
<entry>integer</>
1039+
<entry>maximum value</>
1040+
<entry><literal>max(5, 4, 3, 2)</></>
1041+
<entry><literal>5</></>
1042+
</row>
1043+
<row>
1044+
<entry><literal><function>min(<replaceable>i</> [, <replaceable>...</> ] )</></></>
1045+
<entry>integer</>
1046+
<entry>minimum value</>
1047+
<entry><literal>min(5, 4, 3, 2)</></>
1048+
<entry><literal>2</></>
1049+
</row>
1050+
</tbody>
1051+
</tgroup>
1052+
</table>
1053+
</refsect2>
1054+
9971055
<refsect2>
9981056
<title>Per-Transaction Logging</title>
9991057

src/bin/pgbench/exprparse.y

Lines changed: 146 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,13 @@
1616

1717
PgBenchExpr *expr_parse_result;
1818

19+
static PgBenchExprList *make_elist(PgBenchExpr *exp, PgBenchExprList *list);
1920
static PgBenchExpr *make_integer_constant(int64 ival);
2021
static PgBenchExpr *make_variable(char *varname);
21-
static PgBenchExpr *make_op(char operator, PgBenchExpr *lexpr,
22+
static PgBenchExpr *make_op(const char *operator, PgBenchExpr *lexpr,
2223
PgBenchExpr *rexpr);
24+
static int find_func(const char *fname);
25+
static PgBenchExpr *make_func(const int fnumber, PgBenchExprList *args);
2326

2427
%}
2528

@@ -31,13 +34,15 @@ static PgBenchExpr *make_op(char operator, PgBenchExpr *lexpr,
3134
int64 ival;
3235
char *str;
3336
PgBenchExpr *expr;
37+
PgBenchExprList *elist;
3438
}
3539

40+
%type <elist> elist
3641
%type <expr> expr
37-
%type <ival> INTEGER
38-
%type <str> VARIABLE
42+
%type <ival> INTEGER function
43+
%type <str> VARIABLE FUNCTION
3944

40-
%token INTEGER VARIABLE
45+
%token INTEGER VARIABLE FUNCTION
4146
%token CHAR_ERROR /* never used, will raise a syntax error */
4247

4348
/* Precedence: lowest to highest */
@@ -49,16 +54,25 @@ static PgBenchExpr *make_op(char operator, PgBenchExpr *lexpr,
4954

5055
result: expr { expr_parse_result = $1; }
5156

57+
elist: { $$ = NULL; }
58+
| expr { $$ = make_elist($1, NULL); }
59+
| elist ',' expr { $$ = make_elist($3, $1); }
60+
;
61+
5262
expr: '(' expr ')' { $$ = $2; }
5363
| '+' expr %prec UMINUS { $$ = $2; }
54-
| '-' expr %prec UMINUS { $$ = make_op('-', make_integer_constant(0), $2); }
55-
| expr '+' expr { $$ = make_op('+', $1, $3); }
56-
| expr '-' expr { $$ = make_op('-', $1, $3); }
57-
| expr '*' expr { $$ = make_op('*', $1, $3); }
58-
| expr '/' expr { $$ = make_op('/', $1, $3); }
59-
| expr '%' expr { $$ = make_op('%', $1, $3); }
64+
| '-' expr %prec UMINUS { $$ = make_op("-", make_integer_constant(0), $2); }
65+
| expr '+' expr { $$ = make_op("+", $1, $3); }
66+
| expr '-' expr { $$ = make_op("-", $1, $3); }
67+
| expr '*' expr { $$ = make_op("*", $1, $3); }
68+
| expr '/' expr { $$ = make_op("/", $1, $3); }
69+
| expr '%' expr { $$ = make_op("%", $1, $3); }
6070
| INTEGER { $$ = make_integer_constant($1); }
6171
| VARIABLE { $$ = make_variable($1); }
72+
| function '(' elist ')'{ $$ = make_func($1, $3); }
73+
;
74+
75+
function: FUNCTION { $$ = find_func($1); pg_free($1); }
6276
;
6377

6478
%%
@@ -84,14 +98,131 @@ make_variable(char *varname)
8498
}
8599

86100
static PgBenchExpr *
87-
make_op(char operator, PgBenchExpr *lexpr, PgBenchExpr *rexpr)
101+
make_op(const char *operator, PgBenchExpr *lexpr, PgBenchExpr *rexpr)
102+
{
103+
return make_func(find_func(operator),
104+
make_elist(rexpr, make_elist(lexpr, NULL)));
105+
}
106+
107+
/*
108+
* List of available functions:
109+
* - fname: function name
110+
* - nargs: number of arguments
111+
* -1 is a special value for min & max meaning #args >= 1
112+
* - tag: function identifier from PgBenchFunction enum
113+
*/
114+
static struct
115+
{
116+
char * fname;
117+
int nargs;
118+
PgBenchFunction tag;
119+
} PGBENCH_FUNCTIONS[] = {
120+
/* parsed as operators, executed as functions */
121+
{ "+", 2, PGBENCH_ADD },
122+
{ "-", 2, PGBENCH_SUB },
123+
{ "*", 2, PGBENCH_MUL },
124+
{ "/", 2, PGBENCH_DIV },
125+
{ "%", 2, PGBENCH_MOD },
126+
/* actual functions */
127+
{ "abs", 1, PGBENCH_ABS },
128+
{ "min", -1, PGBENCH_MIN },
129+
{ "max", -1, PGBENCH_MAX },
130+
{ "debug", 1, PGBENCH_DEBUG },
131+
/* keep as last array element */
132+
{ NULL, 0, 0 }
133+
};
134+
135+
/*
136+
* Find a function from its name
137+
*
138+
* return the index of the function from the PGBENCH_FUNCTIONS array
139+
* or fail if the function is unknown.
140+
*/
141+
static int
142+
find_func(const char * fname)
143+
{
144+
int i = 0;
145+
146+
while (PGBENCH_FUNCTIONS[i].fname)
147+
{
148+
if (pg_strcasecmp(fname, PGBENCH_FUNCTIONS[i].fname) == 0)
149+
return i;
150+
i++;
151+
}
152+
153+
expr_yyerror_more("unexpected function name", fname);
154+
155+
/* not reached */
156+
return -1;
157+
}
158+
159+
/* Expression linked list builder */
160+
static PgBenchExprList *
161+
make_elist(PgBenchExpr *expr, PgBenchExprList *list)
162+
{
163+
PgBenchExprLink * cons;
164+
165+
if (list == NULL)
166+
{
167+
list = pg_malloc(sizeof(PgBenchExprList));
168+
list->head = NULL;
169+
list->tail = NULL;
170+
}
171+
172+
cons = pg_malloc(sizeof(PgBenchExprLink));
173+
cons->expr = expr;
174+
cons->next = NULL;
175+
176+
if (list->head == NULL)
177+
list->head = cons;
178+
else
179+
list->tail->next = cons;
180+
181+
list->tail = cons;
182+
183+
return list;
184+
}
185+
186+
/* Return the length of an expression list */
187+
static int
188+
elist_length(PgBenchExprList *list)
189+
{
190+
PgBenchExprLink *link = list != NULL? list->head: NULL;
191+
int len = 0;
192+
193+
for (; link != NULL; link = link->next)
194+
len++;
195+
196+
return len;
197+
}
198+
199+
/* Build function call expression */
200+
static PgBenchExpr *
201+
make_func(const int fnumber, PgBenchExprList *args)
88202
{
89203
PgBenchExpr *expr = pg_malloc(sizeof(PgBenchExpr));
90204

91-
expr->etype = ENODE_OPERATOR;
92-
expr->u.operator.operator = operator;
93-
expr->u.operator.lexpr = lexpr;
94-
expr->u.operator.rexpr = rexpr;
205+
Assert(fnumber >= 0);
206+
207+
if (PGBENCH_FUNCTIONS[fnumber].nargs >= 0 &&
208+
PGBENCH_FUNCTIONS[fnumber].nargs != elist_length(args))
209+
expr_yyerror_more("unexpected number of arguments",
210+
PGBENCH_FUNCTIONS[fnumber].fname);
211+
212+
/* check at least one arg for min & max */
213+
if (PGBENCH_FUNCTIONS[fnumber].nargs == -1 &&
214+
elist_length(args) == 0)
215+
expr_yyerror_more("at least one argument expected",
216+
PGBENCH_FUNCTIONS[fnumber].fname);
217+
218+
expr->etype = ENODE_FUNCTION;
219+
expr->u.function.function = PGBENCH_FUNCTIONS[fnumber].tag;
220+
221+
/* only the link is used, the head/tail is not useful anymore */
222+
expr->u.function.args = args != NULL? args->head: NULL;
223+
if (args)
224+
pg_free(args);
225+
95226
return expr;
96227
}
97228

src/bin/pgbench/exprscan.l

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ space [ \t\r\f]
4646
"%" { yycol += yyleng; return '%'; }
4747
"(" { yycol += yyleng; return '('; }
4848
")" { yycol += yyleng; return ')'; }
49+
"," { yycol += yyleng; return ','; }
4950

5051
:[a-zA-Z0-9_]+ {
5152
yycol += yyleng;
@@ -57,8 +58,14 @@ space [ \t\r\f]
5758
yylval.ival = strtoint64(yytext);
5859
return INTEGER;
5960
}
61+
[a-zA-Z0-9_]+ {
62+
yycol += yyleng;
63+
yylval.str = pg_strdup(yytext);
64+
return FUNCTION;
65+
}
6066

6167
[\n] { yycol = 0; yyline++; }
68+
6269
{space}+ { yycol += yyleng; /* ignore */ }
6370

6471
. {
@@ -71,10 +78,16 @@ space [ \t\r\f]
7178
%%
7279

7380
void
74-
yyerror(const char *message)
81+
expr_yyerror_more(const char *message, const char *more)
7582
{
7683
syntax_error(expr_source, expr_lineno, expr_full_line, expr_command,
77-
message, NULL, expr_col + yycol);
84+
message, more, expr_col + yycol);
85+
}
86+
87+
void
88+
yyerror(const char *message)
89+
{
90+
expr_yyerror_more(message, NULL);
7891
}
7992

8093
/*
@@ -94,6 +107,9 @@ expr_scanner_init(const char *str, const char *source,
94107
expr_command = (char *) cmd;
95108
expr_col = (int) ecol;
96109

110+
/* reset column count for this scan */
111+
yycol = 0;
112+
97113
/*
98114
* Might be left over after error
99115
*/

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy