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 INSERT ISNULL OFFSET RENAME SCHEMA SELECT SORTED
%token TABLES UNIQUE UNLOAD UPDATE VALUES AFTER ALTER CROSS %token TABLES UNIQUE UNLOAD UPDATE VALUES AFTER ALTER CROSS
%token DELTA GROUP INDEX INNER LIMIT LOCAL MERGE MINUS ORDER %token DELTA GROUP INDEX INNER LIMIT LOCAL MERGE MINUS ORDER
%token OUTER RIGHT TABLE UNION USING WHERE CALL DATE DESC %token OUTER RIGHT TABLE UNION USING WHERE CALL CASE DATE
%token DROP FILE FROM FULL HASH HINT INTO JOIN LEFT LIKE %token DESC DROP ELSE FILE FROM FULL HASH HINT INTO JOIN
%token LOAD NULL PART PLAN SHOW TEXT TIME VIEW WITH ADD ALL %token LEFT LIKE LOAD NULL PART PLAN SHOW TEXT THEN TIME
%token AND ASC CSV FOR INT KEY NOT OFF SET TBL TOP AS BY IF %token VIEW WHEN WITH ADD ALL AND ASC CSV END FOR INT KEY
%token IN IS OF ON OR TO %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) ** 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> 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> function_expr between_expr star_expr expr_alias placeholder_expr
%type <expr> column_name literal int_literal num_literal string_literal %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 <limit> opt_limit opt_top
%type <order> order_desc %type <order> order_desc
%type <order_type> opt_order_type %type <order_type> opt_order_type
@ -606,6 +605,7 @@ expr:
| between_expr | between_expr
| logic_expr | logic_expr
| exists_expr | exists_expr
| case_expr
; ;
operand: operand:
@ -645,6 +645,11 @@ logic_expr:
| expr OR expr { $$ = Expr::makeOpBinary($1, Expr::OR, $3); } | 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_expr:
EXISTS '(' select_no_paren ')' { $$ = Expr::makeExists($3); } 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]* /* skipping comment content until a end of line is read */;
<COMMENT>\n BEGIN(INITIAL); <COMMENT>\n BEGIN(INITIAL);
[ \t\n]+ /* skip whitespace */; [ \t\n]+ /* skip whitespace */;
DEALLOCATE TOKEN(DEALLOCATE) DEALLOCATE TOKEN(DEALLOCATE)
PARAMETERS TOKEN(PARAMETERS) PARAMETERS TOKEN(PARAMETERS)
INTERSECT TOKEN(INTERSECT) INTERSECT TOKEN(INTERSECT)
@ -127,9 +124,11 @@ UNION TOKEN(UNION)
USING TOKEN(USING) USING TOKEN(USING)
WHERE TOKEN(WHERE) WHERE TOKEN(WHERE)
CALL TOKEN(CALL) CALL TOKEN(CALL)
CASE TOKEN(CASE)
DATE TOKEN(DATE) DATE TOKEN(DATE)
DESC TOKEN(DESC) DESC TOKEN(DESC)
DROP TOKEN(DROP) DROP TOKEN(DROP)
ELSE TOKEN(ELSE)
FILE TOKEN(FILE) FILE TOKEN(FILE)
FROM TOKEN(FROM) FROM TOKEN(FROM)
FULL TOKEN(FULL) FULL TOKEN(FULL)
@ -145,14 +144,17 @@ PART TOKEN(PART)
PLAN TOKEN(PLAN) PLAN TOKEN(PLAN)
SHOW TOKEN(SHOW) SHOW TOKEN(SHOW)
TEXT TOKEN(TEXT) TEXT TOKEN(TEXT)
THEN TOKEN(THEN)
TIME TOKEN(TIME) TIME TOKEN(TIME)
VIEW TOKEN(VIEW) VIEW TOKEN(VIEW)
WHEN TOKEN(WHEN)
WITH TOKEN(WITH) WITH TOKEN(WITH)
ADD TOKEN(ADD) ADD TOKEN(ADD)
ALL TOKEN(ALL) ALL TOKEN(ALL)
AND TOKEN(AND) AND TOKEN(AND)
ASC TOKEN(ASC) ASC TOKEN(ASC)
CSV TOKEN(CSV) CSV TOKEN(CSV)
END TOKEN(END)
FOR TOKEN(FOR) FOR TOKEN(FOR)
INT TOKEN(INT) INT TOKEN(INT)
KEY TOKEN(KEY) KEY TOKEN(KEY)
@ -171,15 +173,12 @@ ON TOKEN(ON)
OR TOKEN(OR) OR TOKEN(OR)
TO TOKEN(TO) TO TOKEN(TO)
"<>" TOKEN(NOTEQUALS) "<>" TOKEN(NOTEQUALS)
"<=" TOKEN(LESSEQ) "<=" TOKEN(LESSEQ)
">=" TOKEN(GREATEREQ) ">=" TOKEN(GREATEREQ)
[-+*/(){},.;<>=^%:?] { return yytext[0]; } [-+*/(){},.;<>=^%:?] { return yytext[0]; }
[0-9]+"."[0-9]* | [0-9]+"."[0-9]* |
"."[0-9]* { "."[0-9]* {
yylval->fval = atof(yytext); yylval->fval = atof(yytext);
@ -202,7 +201,6 @@ TO TOKEN(TO)
return SQL_IDENTIFIER; return SQL_IDENTIFIER;
} }
'[^'\n]*' { '[^'\n]*' {
// Crop the leading and trailing quote char // Crop the leading and trailing quote char
yylval->sval = hsql::substr(yytext, 1, strlen(yytext)-1); yylval->sval = hsql::substr(yytext, 1, strlen(yytext)-1);

View File

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

View File

@ -57,6 +57,16 @@ namespace hsql {
return e; 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* Expr::makeLiteral(int64_t val) {
Expr* e = new Expr(kExprLiteralInt); Expr* e = new Expr(kExprLiteralInt);
e->ival = val; e->ival = val;

View File

@ -38,6 +38,7 @@ namespace hsql {
// Ternary operators // Ternary operators
BETWEEN, BETWEEN,
CASE,
// Binary operators. // Binary operators.
SIMPLE_OP, SIMPLE_OP,
@ -111,6 +112,8 @@ namespace hsql {
static Expr* makeBetween(Expr* expr, Expr* left, Expr* right); 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(int64_t val);
static Expr* makeLiteral(double 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 -- 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 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 (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 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 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 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; SelectStatement* select2 = cond1->expr2->select;
ASSERT_NOTNULL(select2); ASSERT_NOTNULL(select2);
ASSERT_STREQ(select2->fromTable->getName(), "tt") ASSERT_STREQ(select2->fromTable->getName(), "tt");
// EXISTS (SELECT ...) // EXISTS (SELECT ...)
@ -193,7 +193,33 @@ TEST(SelectConditionalSelectTest) {
ASSERT_NOTNULL(cond2->select); ASSERT_NOTNULL(cond2->select);
SelectStatement* ex_select = 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; delete result;
} }