implement CASE WHEN expressions

This commit is contained in:
Pedro 2017-03-07 14:55:51 +01:00
parent 5605dbab7e
commit b7828e698e
7 changed files with 64 additions and 17 deletions

View File

@ -169,12 +169,11 @@ int yyerror(YYLTYPE* llocp, SQLParserResult** result, yyscan_t scanner, const ch
%token INSERT ISNULL OFFSET RENAME SCHEMA SELECT SORTED
%token TABLES UNIQUE UNLOAD UPDATE VALUES AFTER ALTER CROSS
%token DELTA GROUP INDEX INNER LIMIT LOCAL MERGE MINUS ORDER
%token OUTER RIGHT TABLE UNION USING WHERE CALL DATE DESC
%token DROP FILE FROM FULL HASH HINT INTO JOIN LEFT LIKE
%token LOAD NULL PART PLAN SHOW TEXT TIME VIEW WITH ADD ALL
%token AND ASC CSV FOR INT KEY NOT OFF SET TBL TOP AS BY IF
%token IN IS OF ON OR TO
%token OUTER RIGHT TABLE UNION USING WHERE CALL CASE DATE
%token DESC DROP ELSE FILE FROM FULL HASH HINT INTO JOIN
%token LEFT LIKE LOAD NULL PART PLAN SHOW TEXT THEN TIME
%token VIEW WHEN WITH ADD ALL AND ASC CSV END FOR INT KEY
%token NOT OFF SET TBL TOP AS BY IF IN IS OF ON OR TO
/*********************************
** Non-Terminal types (http://www.gnu.org/software/bison/manual/html_node/Type-Decl.html)
@ -198,7 +197,7 @@ int yyerror(YYLTYPE* llocp, SQLParserResult** result, yyscan_t scanner, const ch
%type <expr> expr operand scalar_expr unary_expr binary_expr logic_expr exists_expr
%type <expr> function_expr between_expr star_expr expr_alias placeholder_expr
%type <expr> column_name literal int_literal num_literal string_literal
%type <expr> comp_expr opt_where join_condition opt_having
%type <expr> comp_expr opt_where join_condition opt_having case_expr
%type <limit> opt_limit opt_top
%type <order> order_desc
%type <order_type> opt_order_type
@ -606,6 +605,7 @@ expr:
| between_expr
| logic_expr
| exists_expr
| case_expr
;
operand:
@ -645,6 +645,11 @@ logic_expr:
| expr OR expr { $$ = Expr::makeOpBinary($1, Expr::OR, $3); }
;
// TODO: allow no else specified
case_expr:
CASE WHEN operand THEN operand ELSE operand END { $$ = Expr::makeCase($3, $5, $7); }
;
exists_expr:
EXISTS '(' select_no_paren ')' { $$ = Expr::makeExists($3); }
;

View File

@ -54,11 +54,8 @@
<COMMENT>[^\n]* /* skipping comment content until a end of line is read */;
<COMMENT>\n BEGIN(INITIAL);
[ \t\n]+ /* skip whitespace */;
DEALLOCATE TOKEN(DEALLOCATE)
PARAMETERS TOKEN(PARAMETERS)
INTERSECT TOKEN(INTERSECT)
@ -127,9 +124,11 @@ UNION TOKEN(UNION)
USING TOKEN(USING)
WHERE TOKEN(WHERE)
CALL TOKEN(CALL)
CASE TOKEN(CASE)
DATE TOKEN(DATE)
DESC TOKEN(DESC)
DROP TOKEN(DROP)
ELSE TOKEN(ELSE)
FILE TOKEN(FILE)
FROM TOKEN(FROM)
FULL TOKEN(FULL)
@ -145,14 +144,17 @@ PART TOKEN(PART)
PLAN TOKEN(PLAN)
SHOW TOKEN(SHOW)
TEXT TOKEN(TEXT)
THEN TOKEN(THEN)
TIME TOKEN(TIME)
VIEW TOKEN(VIEW)
WHEN TOKEN(WHEN)
WITH TOKEN(WITH)
ADD TOKEN(ADD)
ALL TOKEN(ALL)
AND TOKEN(AND)
ASC TOKEN(ASC)
CSV TOKEN(CSV)
END TOKEN(END)
FOR TOKEN(FOR)
INT TOKEN(INT)
KEY TOKEN(KEY)
@ -171,15 +173,12 @@ ON TOKEN(ON)
OR TOKEN(OR)
TO TOKEN(TO)
"<>" TOKEN(NOTEQUALS)
"<=" TOKEN(LESSEQ)
">=" TOKEN(GREATEREQ)
[-+*/(){},.;<>=^%:?] { return yytext[0]; }
[0-9]+"."[0-9]* |
"."[0-9]* {
yylval->fval = atof(yytext);
@ -202,7 +201,6 @@ TO TOKEN(TO)
return SQL_IDENTIFIER;
}
'[^'\n]*' {
// Crop the leading and trailing quote char
yylval->sval = hsql::substr(yytext, 1, strlen(yytext)-1);

View File

@ -140,6 +140,11 @@ IS
ISNULL
BETWEEN
ESCAPE
CASE
WHEN
THEN
ELSE
END
// With
WITH

View File

@ -57,6 +57,16 @@ namespace hsql {
return e;
}
Expr* Expr::makeCase(Expr* expr, Expr* then, Expr* other) {
Expr* e = new Expr(kExprOperator);
e->expr = expr;
e->opType = CASE;
e->exprList = new std::vector<Expr*>();
e->exprList->push_back(then);
e->exprList->push_back(other);
return e;
}
Expr* Expr::makeLiteral(int64_t val) {
Expr* e = new Expr(kExprLiteralInt);
e->ival = val;

View File

@ -38,6 +38,7 @@ namespace hsql {
// Ternary operators
BETWEEN,
CASE,
// Binary operators.
SIMPLE_OP,
@ -111,6 +112,8 @@ namespace hsql {
static Expr* makeBetween(Expr* expr, Expr* left, Expr* right);
static Expr* makeCase(Expr* expr, Expr* then, Expr* other);
static Expr* makeLiteral(int64_t val);
static Expr* makeLiteral(double val);

View File

@ -1,7 +1,7 @@
-- http://www.sqlserver-dba.com/2011/09/this-is-a-followup-on-my-earlier-post-of-sql-server-test-data-generation-testing-tools-i-had-some-requests-for-my-set-up-pr.html
SELECT O_YEAR, SUM(CASE WHEN NATION = 'BRAZIL' THEN VOLUME ELSE 0 END)/SUM(VOLUME) AS MKT_SHARE
FROM (SELECT datepart(yy,O_ORDERDATE) AS O_YEAR, L_EXTENDEDPRICE*(1-L_DISCOUNT) AS VOLUME, N2.N_NAME AS NATION
FROM PART, SUPPLIER, LINEITEM, ORDERS, CUSTOMER, NATION N1, NATION N2, REGION
FROM "PART", SUPPLIER, LINEITEM, ORDERS, CUSTOMER, NATION N1, NATION N2, REGION
WHERE P_PARTKEY = L_PARTKEY AND S_SUPPKEY = L_SUPPKEY AND L_ORDERKEY = O_ORDERKEY
AND O_CUSTKEY = C_CUSTKEY AND C_NATIONKEY = N1.N_NATIONKEY AND
N1.N_REGIONKEY = R_REGIONKEY AND R_NAME = 'AMERICA' AND S_NATIONKEY = N2.N_NATIONKEY

View File

@ -184,7 +184,7 @@ TEST(SelectConditionalSelectTest) {
SelectStatement* select2 = cond1->expr2->select;
ASSERT_NOTNULL(select2);
ASSERT_STREQ(select2->fromTable->getName(), "tt")
ASSERT_STREQ(select2->fromTable->getName(), "tt");
// EXISTS (SELECT ...)
@ -193,7 +193,33 @@ TEST(SelectConditionalSelectTest) {
ASSERT_NOTNULL(cond2->select);
SelectStatement* ex_select = cond2->select;
ASSERT_STREQ(ex_select->fromTable->getName(), "test")
ASSERT_STREQ(ex_select->fromTable->getName(), "test");
delete result;
}
TEST(SelectCaseWhen) {
TEST_PARSE_SINGLE_SQL(
"SELECT MAX(CASE WHEN a = 'foo' THEN x ELSE 0 END) FROM test;",
kStmtSelect,
SelectStatement,
result,
stmt);
ASSERT_EQ(stmt->selectList->size(), 1);
Expr* func = stmt->selectList->at(0);
ASSERT_NOTNULL(func);
ASSERT(func->isType(kExprFunctionRef));
ASSERT_EQ(func->exprList->size(), 1);
Expr* caseExpr = func->exprList->at(0);
ASSERT_NOTNULL(caseExpr);
ASSERT(caseExpr->isType(kExprOperator));
ASSERT_EQ(caseExpr->opType, Expr::CASE);
ASSERT(caseExpr->expr->isType(kExprOperator));
ASSERT(caseExpr->expr->isSimpleOp('='));
ASSERT_EQ(caseExpr->exprList->size(), 2);
delete result;
}