Hello Jeevan,
I did the review of your patch and here are my views on your patch.
Thanks for this detailed review and debugging!
Documentation: [...] it be a good idea to have a table of operators similar to that of functions. We need not have several columns here like description, example etc., but a short table just categorizing the operators would be sufficient.
Ok, done.
Further testing and review: =========================== 1. Postgres treats '^' as exponentiation rather than XOR, and '#' as XOR. Personally, I think it can cause confusion, so it will be better if we can stick to the behavior of Postgres mathematical operators.
Ok. I agree to avoid '^'.
2. I could not see any tests for bitwise operators in the functions.sql file that you have attached.
Indeed. Included in attached version.
3. Precedence: [...]
Hmm. I got them all wrong, shame on me! I've followed C rules in the updated version.
5. Sorry, I was not able to understand the "should exist" comment in following snippet. +"xor" { return XOR_OP; } /* should exist */ +"^^" { return XOR_OP; } /* should exist */
There is no "logical exclusive or" operator in C nor in SQL. I do not see why not, so I put one...
7. You may want to reword following comment: [...] there -> here
Ok, fixed twice.
8. [...] if (coerceToBool(&vargs[0])) *retval = vargs[1]; else *retval = vargs[2];
Ok. Attached is an updated patch & test script. -- Fabien.
diff --git a/doc/src/sgml/ref/pgbench.sgml b/doc/src/sgml/ref/pgbench.sgml index 285608d..c958c1c 100644 --- a/doc/src/sgml/ref/pgbench.sgml +++ b/doc/src/sgml/ref/pgbench.sgml @@ -821,9 +821,8 @@ pgbench <optional> <replaceable>options</> </optional> <replaceable>dbname</> The expression may contain integer constants such as <literal>5432</>, double constants such as <literal>3.14159</>, references to variables <literal>:</><replaceable>variablename</>, - unary operators (<literal>+</>, <literal>-</>) and binary operators - (<literal>+</>, <literal>-</>, <literal>*</>, <literal>/</>, - <literal>%</>) with their usual precedence and associativity, + <link linkend="pgbench-builtin-operators">operators</> + with their usual precedence and associativity, <link linkend="pgbench-builtin-functions">function calls</>, and parentheses. </para> @@ -909,6 +908,84 @@ pgbench <optional> <replaceable>options</> </optional> <replaceable>dbname</> </variablelist> </refsect2> + <refsect2 id="pgbench-builtin-operators"> + <title>Built-In Operators</title> + + <para> + The arithmetic, bitwise, comparison and logical operators listed in + <xref linkend="pgbench-operators"> are built into <application>pgbench</> + and may be used in expressions appearing in + <link linkend="pgbench-metacommand-set"><literal>\set</literal></link>. + </para> + + <table id="pgbench-operators"> + <title>pgbench Operators</title> + <tgroup cols="3"> + <thead> + <row> + <entry>Operator Category</entry> + <entry>Result Type</entry> + <entry>List of Operators</entry> + </row> + </thead> + <tbody> + <row> + <entry>unary arithmetic</> + <entry>integer or double</> + <entry><literal>+</>, <literal>-</></> + </row> + <row> + <entry>binary arithmetic</> + <entry>integer or double</> + <entry><literal>+</>, <literal>-</>, <literal>*</>, <literal>/</></> + </row> + <row> + <entry>binary arithmetic</> + <entry>integer only</> + <entry><literal>%</></> + </row> + <row> + <entry>unary bitwise</> + <entry>integer</> + <entry><literal>~</></> + </row> + <row> + <entry>binary bitwise</> + <entry>integer</> + <entry><literal>&</>, <literal>|</>, <literal>#</></> + </row> + <row> + <entry>shifts</> + <entry>integer</> + <entry><literal><<</>, <literal>>></></> + </row> + <row> + <entry>comparison</> + <entry>boolean (0/1)</> + <entry> + <literal>=</>/<literal>==</>, <literal><></>/<literal>!=</>, + <literal><</>, <literal><=</>, <literal>></>, <literal>>=</> + </entry> + </row> + <row> + <entry>unary logical</> + <entry>boolean (0/1)</> + <entry><literal>not</>/<literal>!</></> + </row> + <row> + <entry>binary logical</> + <entry>boolean (0/1)</> + <entry> + <literal>and</>/<literal>&&</>, + <literal>or</>/<literal>||</>, + <literal>xor</>/<literal>^^</>, + </entry> + </row> + </tbody> + </tgroup> + </table> + </refsect2> + <refsect2 id="pgbench-builtin-functions"> <title>Built-In Functions</title> @@ -955,6 +1032,13 @@ pgbench <optional> <replaceable>options</> </optional> <replaceable>dbname</> <entry><literal>5432.0</></> </row> <row> + <entry><literal><function>exp(<replaceable>x</>)</></></> + <entry>double</> + <entry>exponential</> + <entry><literal>exp(1.0)</></> + <entry><literal>2.718281828459045</></> + </row> + <row> <entry><literal><function>greatest(<replaceable>a</> [, <replaceable>...</> ] )</></></> <entry>double if any <replaceable>a</> is double, else integer</> <entry>largest value among arguments</> @@ -962,6 +1046,13 @@ pgbench <optional> <replaceable>options</> </optional> <replaceable>dbname</> <entry><literal>5</></> </row> <row> + <entry><literal><function>if(<replaceable>c</>,<replaceable>e1</>,<replaceable>e2</>)</></></> + <entry>same as <replaceable>e*</></> + <entry>if <replaceable>c</> is not zero then <replaceable>e1</> else <replaceable>e2</></> + <entry><literal>if(0,1.0,2.0)</></> + <entry><literal>2.0</></> + </row> + <row> <entry><literal><function>int(<replaceable>x</>)</></></> <entry>integer</> <entry>cast to int</> @@ -976,6 +1067,13 @@ pgbench <optional> <replaceable>options</> </optional> <replaceable>dbname</> <entry><literal>2.1</></> </row> <row> + <entry><literal><function>ln(<replaceable>x</>)</></></> + <entry>double</> + <entry>natural logarithm</> + <entry><literal>ln(2.718281828459045)</></> + <entry><literal>1.0</></> + </row> + <row> <entry><literal><function>pi()</></></> <entry>double</> <entry>value of the constant PI</> diff --git a/src/bin/pgbench/exprparse.y b/src/bin/pgbench/exprparse.y index 0cc665b..f8dbbaf 100644 --- a/src/bin/pgbench/exprparse.y +++ b/src/bin/pgbench/exprparse.y @@ -52,11 +52,21 @@ static PgBenchExpr *make_func(yyscan_t yyscanner, int fnumber, PgBenchExprList * %type <str> VARIABLE FUNCTION %token INTEGER_CONST DOUBLE_CONST VARIABLE FUNCTION - -/* Precedence: lowest to highest */ +%token AND_OP OR_OP XOR_OP NE_OP LE_OP GE_OP LS_OP RS_OP + +/* Precedence: lowest to highest, taken from C */ +%left OR_OP +%left XOR_OP +%left AND_OP +%left '|' +%left '#' +%left '&' +%left '=' NE_OP +%left '<' '>' LE_OP GE_OP +%left LS_OP RS_OP %left '+' '-' %left '*' '/' '%' -%right UMINUS +%right UNARY %% @@ -68,14 +78,32 @@ elist: { $$ = NULL; } ; expr: '(' expr ')' { $$ = $2; } - | '+' expr %prec UMINUS { $$ = $2; } - | '-' expr %prec UMINUS { $$ = make_op(yyscanner, "-", + | '+' expr %prec UNARY { $$ = $2; } + | '-' expr %prec UNARY { $$ = make_op(yyscanner, "-", make_integer_constant(0), $2); } + | '~' expr %prec UNARY { $$ = make_op(yyscanner, "#", + make_integer_constant(-1), $2); } + | '!' expr %prec UNARY { $$ = make_op(yyscanner, "^^", + make_integer_constant(1), $2); } | expr '+' expr { $$ = make_op(yyscanner, "+", $1, $3); } | expr '-' expr { $$ = make_op(yyscanner, "-", $1, $3); } | expr '*' expr { $$ = make_op(yyscanner, "*", $1, $3); } | expr '/' expr { $$ = make_op(yyscanner, "/", $1, $3); } | expr '%' expr { $$ = make_op(yyscanner, "%", $1, $3); } + | expr '<' expr { $$ = make_op(yyscanner, "<", $1, $3); } + | expr LE_OP expr { $$ = make_op(yyscanner, "<=", $1, $3); } + | expr '>' expr { $$ = make_op(yyscanner, "<", $3, $1); } + | expr GE_OP expr { $$ = make_op(yyscanner, "<=", $3, $1); } + | expr '=' expr { $$ = make_op(yyscanner, "=", $1, $3); } + | expr NE_OP expr { $$ = make_op(yyscanner, "<>", $1, $3); } + | expr '&' expr { $$ = make_op(yyscanner, "&", $1, $3); } + | expr '|' expr { $$ = make_op(yyscanner, "|", $1, $3); } + | expr '#' expr { $$ = make_op(yyscanner, "#", $1, $3); } + | expr LS_OP expr { $$ = make_op(yyscanner, "<<", $1, $3); } + | expr RS_OP expr { $$ = make_op(yyscanner, ">>", $1, $3); } + | expr AND_OP expr { $$ = make_op(yyscanner, "&&", $1, $3); } + | expr OR_OP expr { $$ = make_op(yyscanner, "||", $1, $3); } + | expr XOR_OP expr { $$ = make_op(yyscanner, "^^", $1, $3); } | INTEGER_CONST { $$ = make_integer_constant($1); } | DOUBLE_CONST { $$ = make_double_constant($1); } | VARIABLE { $$ = make_variable($1); } @@ -177,6 +205,12 @@ static const struct "sqrt", 1, PGBENCH_SQRT }, { + "ln", 1, PGBENCH_LN + }, + { + "exp", 1, PGBENCH_EXP + }, + { "int", 1, PGBENCH_INT }, { @@ -191,6 +225,45 @@ static const struct { "random_exponential", 3, PGBENCH_RANDOM_EXPONENTIAL }, + { + "&&", 2, PGBENCH_AND + }, + { + "||", 2, PGBENCH_OR + }, + { + "^^", 2, PGBENCH_XOR + }, + { + "&", 2, PGBENCH_BITAND + }, + { + "|", 2, PGBENCH_BITOR + }, + { + "#", 2, PGBENCH_BITXOR + }, + { + "<<", 2, PGBENCH_LSHIFT + }, + { + ">>", 2, PGBENCH_RSHIFT + }, + { + "=", 2, PGBENCH_EQ + }, + { + "<>", 2, PGBENCH_NE + }, + { + "<=", 2, PGBENCH_LE + }, + { + "<", 2, PGBENCH_LT + }, + { + "if", 3, PGBENCH_IF + }, /* keep as last array element */ { NULL, 0, 0 diff --git a/src/bin/pgbench/exprscan.l b/src/bin/pgbench/exprscan.l index 20891a3..3941d5f 100644 --- a/src/bin/pgbench/exprscan.l +++ b/src/bin/pgbench/exprscan.l @@ -113,6 +113,28 @@ newline [\n] "*" { return '*'; } "/" { return '/'; } "%" { return '%'; } +"=" { return '='; } +"==" { return '='; } /* C version */ +"<>" { return NE_OP; } +"!=" { return NE_OP; } /* C version */ +"<=" { return LE_OP; } +">=" { return GE_OP; } +"<<" { return LS_OP; } +">>" { return RS_OP; } +"<" { return '<'; } +">" { return '>'; } +"|" { return '|'; } +"&" { return '&'; } +"#" { return '#'; } +"~" { return '~'; } +"and" { return AND_OP; } +"&&" { return AND_OP; } /* C version */ +"or" { return OR_OP; } +"||" { return OR_OP; } /* C version */ +"xor" { return XOR_OP; } /* should exist */ +"^^" { return XOR_OP; } /* should exist */ +"not" { return '!'; } +"!" { return '!'; } /* C version */ "(" { return '('; } ")" { return ')'; } "," { return ','; } diff --git a/src/bin/pgbench/pgbench.c b/src/bin/pgbench/pgbench.c index 1fb4ae4..20a841a 100644 --- a/src/bin/pgbench/pgbench.c +++ b/src/bin/pgbench/pgbench.c @@ -1262,6 +1262,19 @@ coerceToDouble(PgBenchValue *pval, double *dval) } } +static bool coerceToBool(PgBenchValue *pval) +{ + if (pval->type == PGBT_DOUBLE) + { + return pval->u.dval != 0.0; + } + else + { + Assert(pval->type == PGBT_INT); + return pval->u.ival != 0; + } +} + /* assign an integer value */ static void setIntValue(PgBenchValue *pv, int64 ival) @@ -1313,6 +1326,10 @@ evalFunc(TState *thread, CState *st, case PGBENCH_MUL: case PGBENCH_DIV: case PGBENCH_MOD: + case PGBENCH_EQ: + case PGBENCH_NE: + case PGBENCH_LE: + case PGBENCH_LT: { PgBenchValue *lval = &vargs[0], *rval = &vargs[1]; @@ -1348,6 +1365,22 @@ evalFunc(TState *thread, CState *st, setDoubleValue(retval, ld / rd); return true; + case PGBENCH_EQ: + setIntValue(retval, ld == rd); + return true; + + case PGBENCH_NE: + setIntValue(retval, ld != rd); + return true; + + case PGBENCH_LE: + setIntValue(retval, ld <= rd); + return true; + + case PGBENCH_LT: + setIntValue(retval, ld < rd); + return true; + default: /* cannot get here */ Assert(0); @@ -1376,6 +1409,22 @@ evalFunc(TState *thread, CState *st, setIntValue(retval, li * ri); return true; + case PGBENCH_EQ: + setIntValue(retval, li == ri); + return true; + + case PGBENCH_NE: + setIntValue(retval, li != ri); + return true; + + case PGBENCH_LE: + setIntValue(retval, li <= ri); + return true; + + case PGBENCH_LT: + setIntValue(retval, li < ri); + return true; + case PGBENCH_DIV: case PGBENCH_MOD: if (ri == 0) @@ -1416,7 +1465,57 @@ evalFunc(TState *thread, CState *st, } } - /* no arguments */ + /* integer operators */ + case PGBENCH_BITAND: + case PGBENCH_BITOR: + case PGBENCH_BITXOR: + case PGBENCH_LSHIFT: + case PGBENCH_RSHIFT: + { + int64 li, ri; + + if (!coerceToInt(&vargs[0], &li) || !coerceToInt(&vargs[1], &ri)) + return false; + + if (func == PGBENCH_BITAND) + setIntValue(retval, li & ri); + else if (func == PGBENCH_BITOR) + setIntValue(retval, li | ri); + else if (func == PGBENCH_BITXOR) + setIntValue(retval, li ^ ri); + else if (func == PGBENCH_LSHIFT) + setIntValue(retval, li << ri); + else if (func == PGBENCH_RSHIFT) + setIntValue(retval, li >> ri); + else /* cannot get here */ + Assert(0); + + return true; + } + + /* logical operators */ + case PGBENCH_AND: + case PGBENCH_OR: + case PGBENCH_XOR: + { + bool lb, rb; + + lb = coerceToBool(&vargs[0]); + rb = coerceToBool(&vargs[1]); + + if (func == PGBENCH_AND) + setIntValue(retval, lb && rb); + else if (func == PGBENCH_OR) + setIntValue(retval, lb || rb); + else if (func == PGBENCH_XOR) + setIntValue(retval, lb ^ rb); + else /* cannot get here */ + Assert(0); + + return true; + } + + /* no arguments */ case PGBENCH_PI: setDoubleValue(retval, M_PI); return true; @@ -1470,6 +1569,8 @@ evalFunc(TState *thread, CState *st, /* 1 double argument */ case PGBENCH_DOUBLE: case PGBENCH_SQRT: + case PGBENCH_LN: + case PGBENCH_EXP: { double dval; @@ -1480,6 +1581,11 @@ evalFunc(TState *thread, CState *st, if (func == PGBENCH_SQRT) dval = sqrt(dval); + else if (func == PGBENCH_LN) + dval = log(dval); + else if (func == PGBENCH_EXP) + dval = exp(dval); + /* else is cast: do nothing */ setDoubleValue(retval, dval); return true; @@ -1631,6 +1737,15 @@ evalFunc(TState *thread, CState *st, return true; } + case PGBENCH_IF: + /* should it do a lazy evaluation of the branch? */ + Assert(nargs == 3); + if (coerceToBool(&vargs[0])) + *retval = vargs[1]; + else + *retval = vargs[2]; + return true; + default: /* cannot get here */ Assert(0); diff --git a/src/bin/pgbench/pgbench.h b/src/bin/pgbench/pgbench.h index ab0f822..5306c40 100644 --- a/src/bin/pgbench/pgbench.h +++ b/src/bin/pgbench/pgbench.h @@ -73,9 +73,24 @@ typedef enum PgBenchFunction PGBENCH_DOUBLE, PGBENCH_PI, PGBENCH_SQRT, + PGBENCH_LN, + PGBENCH_EXP, PGBENCH_RANDOM, PGBENCH_RANDOM_GAUSSIAN, - PGBENCH_RANDOM_EXPONENTIAL + PGBENCH_RANDOM_EXPONENTIAL, + PGBENCH_AND, + PGBENCH_OR, + PGBENCH_XOR, + PGBENCH_BITAND, + PGBENCH_BITOR, + PGBENCH_BITXOR, + PGBENCH_LSHIFT, + PGBENCH_RSHIFT, + PGBENCH_EQ, + PGBENCH_NE, + PGBENCH_LE, + PGBENCH_LT, + PGBENCH_IF } PgBenchFunction; typedef struct PgBenchExpr PgBenchExpr;
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