diff --git a/src/parser/bison_parser.y b/src/parser/bison_parser.y index be3c72a..88d5e23 100644 --- a/src/parser/bison_parser.y +++ b/src/parser/bison_parser.y @@ -131,6 +131,7 @@ int yyerror(YYLTYPE* llocp, SQLParserResult** result, yyscan_t scanner, const ch std::vector* column_vec; std::vector* update_vec; std::vector* expr_vec; + std::vector* order_vec; } @@ -146,7 +147,7 @@ int yyerror(YYLTYPE* llocp, SQLParserResult** result, yyscan_t scanner, const ch } } delete ($$); -} +} %destructor { delete ($$); } <*> @@ -197,8 +198,8 @@ int yyerror(YYLTYPE* llocp, SQLParserResult** result, yyscan_t scanner, const ch %type expr scalar_expr unary_expr binary_expr function_expr star_expr expr_alias placeholder_expr %type column_name literal int_literal num_literal string_literal %type comp_expr opt_where join_condition opt_having -%type opt_order -%type opt_limit +%type opt_limit opt_top +%type order_desc %type opt_order_type %type column_def %type update_clause @@ -207,6 +208,7 @@ int yyerror(YYLTYPE* llocp, SQLParserResult** result, yyscan_t scanner, const ch %type ident_commalist opt_column_list %type expr_list select_list literal_list %type table_ref_commalist +%type opt_order order_list %type update_clause_commalist %type column_def_commalist @@ -502,13 +504,14 @@ set_operator: ; select_clause: - SELECT opt_distinct select_list from_clause opt_where opt_group { + SELECT opt_top opt_distinct select_list from_clause opt_where opt_group { $$ = new SelectStatement(); - $$->selectDistinct = $2; - $$->selectList = $3; - $$->fromTable = $4; - $$->whereClause = $5; - $$->groupBy = $6; + $$->limit = $2; + $$->selectDistinct = $3; + $$->selectList = $4; + $$->fromTable = $5; + $$->whereClause = $6; + $$->groupBy = $7; } ; @@ -521,7 +524,6 @@ select_list: expr_list ; - from_clause: FROM table_ref { $$ = $2; } ; @@ -546,16 +548,31 @@ opt_having: | /* empty */ { $$ = NULL; } opt_order: - ORDER BY expr opt_order_type { $$ = new OrderDescription($4, $3); } + ORDER BY order_list { $$ = $3; } | /* empty */ { $$ = NULL; } ; +order_list: + order_desc { $$ = new std::vector(); $$->push_back($1); } + | order_list ',' order_desc { $1->push_back($3); $$ = $1; } + ; + +order_desc: + expr opt_order_type { $$ = new OrderDescription($2, $1); } + ; + opt_order_type: ASC { $$ = kOrderAsc; } | DESC { $$ = kOrderDesc; } | /* empty */ { $$ = kOrderAsc; } ; +// TODO: TOP and LIMIT can take more than just int literals. + +opt_top: + TOP int_literal { $$ = new LimitDescription($2->ival, kNoOffset); delete $2; } + | /* empty */ { $$ = NULL; } + ; opt_limit: LIMIT int_literal { $$ = new LimitDescription($2->ival, kNoOffset); delete $2; } diff --git a/src/sql/SelectStatement.h b/src/sql/SelectStatement.h index a9d169d..bb60836 100644 --- a/src/sql/SelectStatement.h +++ b/src/sql/SelectStatement.h @@ -63,7 +63,7 @@ namespace hsql { GroupByDescription* groupBy; SelectStatement* unionSelect; - OrderDescription* order; + std::vector* order; LimitDescription* limit; }; diff --git a/src/sqlhelper.cpp b/src/sqlhelper.cpp index 5a47df8..529cead 100644 --- a/src/sqlhelper.cpp +++ b/src/sqlhelper.cpp @@ -139,8 +139,8 @@ namespace hsql { if (stmt->order != NULL) { inprint("OrderBy:", numIndent + 1); - printExpression(stmt->order->expr, numIndent + 2); - if (stmt->order->type == kOrderAsc) inprint("ascending", numIndent + 2); + printExpression(stmt->order->at(0)->expr, numIndent + 2); + if (stmt->order->at(0)->type == kOrderAsc) inprint("ascending", numIndent + 2); else inprint("descending", numIndent + 2); } diff --git a/test/select_tests.cpp b/test/select_tests.cpp index e302a84..dead654 100644 --- a/test/select_tests.cpp +++ b/test/select_tests.cpp @@ -73,3 +73,23 @@ TEST(SelectGroupDistinctTest) { } +TEST(OrderByTest) { + TEST_PARSE_SINGLE_SQL( + "SELECT grade, city FROM students ORDER BY grade, city DESC;", + kStmtSelect, + SelectStatement, + result, + stmt); + + ASSERT_NULL(stmt->whereClause); + ASSERT_NOTNULL(stmt->order); + + ASSERT_EQ(stmt->order->size(), 2); + ASSERT_EQ(stmt->order->at(0)->type, kOrderAsc); + ASSERT_STREQ(stmt->order->at(0)->expr->name, "grade"); + + ASSERT_EQ(stmt->order->at(1)->type, kOrderDesc); + ASSERT_STREQ(stmt->order->at(1)->expr->name, "city"); + + delete result; +} diff --git a/test/valid_queries.sql b/test/valid_queries.sql index b211cbf..dfece3d 100644 --- a/test/valid_queries.sql +++ b/test/valid_queries.sql @@ -8,6 +8,7 @@ SELECT * FROM "table" LIMIT 10 OFFSET 10; SELECT * FROM second; SELECT * FROM t1 UNION SELECT * FROM t2 ORDER BY col1; SELECT * FROM (SELECT * FROM t1); SELECT * FROM t1 UNION (SELECT * FROM t2 UNION SELECT * FROM t3) ORDER BY col1; +SELECT TOP 10 * FROM t1 ORDER BY col1, col2; # JOIN SELECT t1.a, t1.b, t2.c FROM "table" AS t1 JOIN (SELECT * FROM foo JOIN bar ON foo.id = bar.id) t2 ON t1.a = t2.b WHERE (t1.b OR NOT t1.a) AND t2.c = 12.5 SELECT * FROM t1 JOIN t2 ON c1 = c2;