Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
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)