Support WITH (#125)

* Add struct WithDescription in SelectStatement.h

* Add test for With statements

* Implement With draft

* tm

* Fix Grammar

* Fix commented code

* naming improvements

* naming improvements

* introduce memory leak1

* removed memory leak

* Create two WITH-tests

* Add bad queries reg. WITH
This commit is contained in:
Julian Menzler 2019-05-24 16:42:28 +02:00 committed by mrks
parent ab1e6b4192
commit 6003ab58d1
7 changed files with 1351 additions and 1121 deletions

File diff suppressed because it is too large Load Diff

View File

@ -255,6 +255,7 @@ union HSQL_STYPE
hsql::Expr* expr;
hsql::OrderDescription* order;
hsql::OrderType order_type;
hsql::WithDescription* with_description_t;
hsql::DatetimeField datetime_field;
hsql::LimitDescription* limit;
hsql::ColumnDefinition* column_t;
@ -271,8 +272,9 @@ union HSQL_STYPE
std::vector<hsql::UpdateClause*>* update_vec;
std::vector<hsql::Expr*>* expr_vec;
std::vector<hsql::OrderDescription*>* order_vec;
std::vector<hsql::WithDescription*>* with_description_vec;
#line 276 "bison_parser.h" /* yacc.c:1909 */
#line 278 "bison_parser.h" /* yacc.c:1909 */
};
typedef union HSQL_STYPE HSQL_STYPE;

View File

@ -116,6 +116,7 @@ int yyerror(YYLTYPE* llocp, SQLParserResult* result, yyscan_t scanner, const cha
hsql::Expr* expr;
hsql::OrderDescription* order;
hsql::OrderType order_type;
hsql::WithDescription* with_description_t;
hsql::DatetimeField datetime_field;
hsql::LimitDescription* limit;
hsql::ColumnDefinition* column_t;
@ -132,6 +133,7 @@ int yyerror(YYLTYPE* llocp, SQLParserResult* result, yyscan_t scanner, const cha
std::vector<hsql::UpdateClause*>* update_vec;
std::vector<hsql::Expr*>* expr_vec;
std::vector<hsql::OrderDescription*>* order_vec;
std::vector<hsql::WithDescription*>* with_description_vec;
}
@ -212,13 +214,15 @@ int yyerror(YYLTYPE* llocp, SQLParserResult* result, yyscan_t scanner, const cha
%type <update_t> update_clause
%type <group_t> opt_group
%type <alias_t> opt_table_alias table_alias opt_alias alias
%type <with_description_t> with_description
%type <str_vec> ident_commalist opt_column_list
%type <expr_vec> expr_list select_list opt_literal_list literal_list hint_list opt_hints
%type <table_vec> table_ref_commalist
%type <order_vec> opt_order order_list
%type <update_vec> update_clause_commalist
%type <column_vec> column_def_commalist
%type <str_vec> ident_commalist opt_column_list
%type <expr_vec> expr_list select_list opt_literal_list literal_list hint_list opt_hints
%type <table_vec> table_ref_commalist
%type <order_vec> opt_order order_list
%type <with_description_vec> opt_with_clause with_clause with_description_list
%type <update_vec> update_clause_commalist
%type <column_vec> column_def_commalist
/******************************
** Token Precedence and Associativity
@ -583,20 +587,27 @@ update_clause:
******************************/
select_statement:
select_with_paren
| select_no_paren
| select_with_paren set_operator select_paren_or_clause opt_order opt_limit {
opt_with_clause select_with_paren {
$$ = $2;
$$->withDescriptions = $1;
}
| opt_with_clause select_no_paren {
$$ = $2;
$$->withDescriptions = $1;
}
| opt_with_clause select_with_paren set_operator select_paren_or_clause opt_order opt_limit {
// TODO: allow multiple unions (through linked list)
// TODO: capture type of set_operator
// TODO: might overwrite order and limit of first select here
$$ = $1;
$$->unionSelect = $3;
$$->order = $4;
$$ = $2;
$$->withDescriptions = $1;
$$->unionSelect = $4;
$$->order = $5;
// Limit could have been set by TOP.
if ($5 != nullptr) {
if ($6 != nullptr) {
delete $$->limit;
$$->limit = $5;
$$->limit = $6;
}
}
;
@ -1012,6 +1023,39 @@ opt_alias:
| /* empty */ { $$ = nullptr; }
/******************************
* With Descriptions
******************************/
opt_with_clause:
with_clause
| /* empty */ { $$ = nullptr; }
;
with_clause:
WITH with_description_list { $$ = $2; }
;
with_description_list:
with_description {
$$ = new std::vector<WithDescription*>();
$$->push_back($1);
}
| with_description_list ',' with_description {
$1->push_back($3);
$$ = $1;
}
;
with_description:
IDENTIFIER AS select_with_paren {
$$ = new WithDescription();
$$->alias = $1;
$$->select = $3;
}
;
/******************************
* Join Statements
******************************/

View File

@ -39,6 +39,13 @@ namespace hsql {
Expr* having;
};
struct WithDescription {
~WithDescription();
char* alias;
SelectStatement* select;
};
// Representation of a full SQL select statement.
// TODO: add union_order and union_limit.
struct SelectStatement : SQLStatement {
@ -53,9 +60,11 @@ namespace hsql {
SelectStatement* unionSelect;
std::vector<OrderDescription*>* order;
std::vector<WithDescription*>* withDescriptions;
LimitDescription* limit;
};
} // namespace hsql
#endif

View File

@ -227,6 +227,11 @@ namespace hsql {
}
}
WithDescription::~WithDescription() {
free(alias);
delete select;
}
// SelectStatement
SelectStatement::SelectStatement() :
SQLStatement(kStmtSelect),
@ -237,6 +242,7 @@ namespace hsql {
groupBy(nullptr),
unionSelect(nullptr),
order(nullptr),
withDescriptions(nullptr),
limit(nullptr) {};
SelectStatement::~SelectStatement() {
@ -260,6 +266,13 @@ namespace hsql {
}
delete order;
}
if (withDescriptions != nullptr) {
for (WithDescription* desc : *withDescriptions) {
delete desc;
}
delete withDescriptions;
}
}
// UpdateStatement

View File

@ -10,3 +10,7 @@
!SELECT * FROM t WHERE a = ? AND b = ?;gibberish;
!SHOW COLUMNS;
!select a + 2 as b(spam, eggs) from B;
!WITH a AS SELECT 1 SELECT 1;
!WITH a AS (SELECT ) SELECT 1;
!WITH a AS (WITH b AS (SELECT 1) SELECT 1) SELECT 1; # We do not support nested WITH clauses
!WITH a AS (SELECT ) b AS (SELECT ) SELECT 1; # Missing comma between WITH descriptions

View File

@ -681,3 +681,60 @@ TEST(NoFromClause) {
ASSERT_EQ(stmt->selectList->at(0)->expr->type, kExprLiteralInt);
ASSERT_EQ(stmt->selectList->at(0)->expr2->type, kExprLiteralInt);
}
TEST(WithClauseSingle) {
TEST_PARSE_SINGLE_SQL("WITH "
"a AS (SELECT name FROM peopleA)"
"SELECT name FROM a;",
kStmtSelect,
SelectStatement,
result,
stmt)
// with_description_list count
ASSERT_EQ(stmt->withDescriptions->size(), 1);
// with_description alias
ASSERT_STREQ(stmt->withDescriptions->at(0)->alias, "a");
// with_description select stmt
ASSERT_EQ(stmt->withDescriptions->at(0)->select->selectList->size(), 1)
ASSERT_STREQ(stmt->withDescriptions->at(0)->select->selectList->at(0)->name, std::string("name"));
ASSERT_STREQ(stmt->withDescriptions->at(0)->select->fromTable->name, std::string("peopleA"));
// main select
ASSERT_EQ(stmt->selectList->size(), 1);
ASSERT_STREQ(stmt->selectList->at(0)->name, "name");
ASSERT_STREQ(stmt->fromTable->name, "a");
}
TEST(WithClauseDouble) {
TEST_PARSE_SINGLE_SQL("WITH "
"a AS (SELECT nameA FROM peopleA), "
"b AS (SELECT nameB, cityB FROM peopleB) "
"SELECT nameA FROM a;",
kStmtSelect,
SelectStatement,
result,
stmt)
// with_description_list count
ASSERT_EQ(stmt->withDescriptions->size(), 2);
// with_description aliases
ASSERT_STREQ(stmt->withDescriptions->at(0)->alias, "a");
ASSERT_STREQ(stmt->withDescriptions->at(1)->alias, "b");
// with_description select stmts
ASSERT_EQ(stmt->withDescriptions->at(0)->select->selectList->size(), 1)
ASSERT_STREQ(stmt->withDescriptions->at(0)->select->fromTable->name, "peopleA");
ASSERT_EQ(stmt->withDescriptions->at(1)->select->selectList->size(), 2)
ASSERT_STREQ(stmt->withDescriptions->at(1)->select->fromTable->name, "peopleB");
// main select
ASSERT_EQ(stmt->selectList->size(), 1);
ASSERT_STREQ(stmt->selectList->at(0)->name, "nameA");
ASSERT_STREQ(stmt->fromTable->name, "a");
}