Add support to identify different set operators & allow chain of multiple set operators (#138)

This commit is contained in:
Tobias Nack 2020-02-18 14:26:10 +01:00 committed by GitHub
parent 4b617bca96
commit e8ce1c4caf
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 2018 additions and 1613 deletions

File diff suppressed because it is too large Load Diff

View File

@ -1,4 +1,4 @@
/* A Bison parser, made by GNU Bison 3.4.1. */
/* A Bison parser, made by GNU Bison 3.4.2. */
/* Bison interface for Yacc-like parsers in C
@ -273,6 +273,7 @@ union HSQL_STYPE
hsql::GroupByDescription* group_t;
hsql::UpdateClause* update_t;
hsql::Alias* alias_t;
hsql::SetOperation* set_operator_t;
std::vector<hsql::SQLStatement*>* stmt_vec;
@ -284,7 +285,7 @@ union HSQL_STYPE
std::vector<hsql::OrderDescription*>* order_vec;
std::vector<hsql::WithDescription*>* with_description_vec;
#line 288 "bison_parser.h"
#line 289 "bison_parser.h"
};
typedef union HSQL_STYPE HSQL_STYPE;

View File

@ -127,6 +127,7 @@ int yyerror(YYLTYPE* llocp, SQLParserResult* result, yyscan_t scanner, const cha
hsql::GroupByDescription* group_t;
hsql::UpdateClause* update_t;
hsql::Alias* alias_t;
hsql::SetOperation* set_operator_t;
std::vector<hsql::SQLStatement*>* stmt_vec;
@ -191,7 +192,7 @@ int yyerror(YYLTYPE* llocp, SQLParserResult* result, yyscan_t scanner, const cha
%type <exec_stmt> execute_statement
%type <transaction_stmt> transaction_statement
%type <prep_stmt> prepare_statement
%type <select_stmt> select_statement select_with_paren select_no_paren select_clause select_paren_or_clause
%type <select_stmt> select_statement select_with_paren select_no_paren select_clause select_within_set_operation select_within_set_operation_no_parentheses
%type <import_stmt> import_statement
%type <export_stmt> export_statement
%type <create_stmt> create_statement
@ -202,7 +203,7 @@ int yyerror(YYLTYPE* llocp, SQLParserResult* result, yyscan_t scanner, const cha
%type <show_stmt> show_statement
%type <table_name> table_name
%type <sval> file_path prepare_target_query
%type <bval> opt_not_exists opt_exists opt_distinct opt_column_nullable
%type <bval> opt_not_exists opt_exists opt_distinct opt_column_nullable opt_all
%type <uval> opt_join_type
%type <table> opt_from_clause from_clause table_ref table_ref_atomic table_ref_name nonjoin_table_ref_atomic
%type <table> join_clause table_ref_name_no_alias
@ -221,6 +222,7 @@ int yyerror(YYLTYPE* llocp, SQLParserResult* result, yyscan_t scanner, const cha
%type <group_t> opt_group
%type <alias_t> opt_table_alias table_alias opt_alias alias
%type <with_description_t> with_description
%type <set_operator_t> set_operator set_type
// ImportType is used for compatibility reasons
%type <import_type_t> opt_file_type file_type
@ -689,20 +691,32 @@ select_statement:
$$ = $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
| opt_with_clause select_with_paren set_operator select_within_set_operation opt_order opt_limit {
$$ = $2;
$$->withDescriptions = $1;
$$->unionSelect = $4;
$$->order = $5;
// Limit could have been set by TOP.
if ($6 != nullptr) {
delete $$->limit;
$$->limit = $6;
if ($$->setOperations == nullptr) {
$$->setOperations = new std::vector<SetOperation*>();
}
$$->setOperations->push_back($3);
$$->setOperations->back()->nestedSelectStatement = $4;
$$->setOperations->back()->resultOrder = $5;
$$->setOperations->back()->resultLimit = $6;
$$->withDescriptions = $1;
}
;
select_within_set_operation:
select_with_paren
| select_within_set_operation_no_parentheses;
select_within_set_operation_no_parentheses:
select_clause { $$ = $1; }
| select_clause set_operator select_within_set_operation {
$$ = $1;
if ($$->setOperations == nullptr) {
$$->setOperations = new std::vector<SetOperation*>();
}
$$->setOperations->push_back($2);
$$->setOperations->back()->nestedSelectStatement = $3;
}
;
@ -711,11 +725,6 @@ select_with_paren:
| '(' select_with_paren ')' { $$ = $2; }
;
select_paren_or_clause:
select_with_paren
| select_clause
;
select_no_paren:
select_clause opt_order opt_limit {
$$ = $1;
@ -727,35 +736,47 @@ select_no_paren:
$$->limit = $3;
}
}
| select_clause 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
| select_clause set_operator select_within_set_operation opt_order opt_limit {
$$ = $1;
$$->unionSelect = $3;
$$->order = $4;
// Limit could have been set by TOP.
if ($5 != nullptr) {
delete $$->limit;
$$->limit = $5;
if ($$->setOperations == nullptr) {
$$->setOperations = new std::vector<SetOperation*>();
}
$$->setOperations->push_back($2);
$$->setOperations->back()->nestedSelectStatement = $3;
$$->setOperations->back()->resultOrder = $4;
$$->setOperations->back()->resultLimit = $5;
}
;
set_operator:
set_type opt_all
set_type opt_all {
$$ = $1;
$$->isAll = $2;
}
;
set_type:
UNION
| INTERSECT
| EXCEPT
UNION {
$$ = new SetOperation();
$$->setType = SetType::kSetUnion;
}
| INTERSECT {
$$ = new SetOperation();
$$->setType = SetType::kSetIntersect;
}
| EXCEPT {
$$ = new SetOperation();
$$->setType = SetType::kSetExcept;
}
;
opt_all:
ALL
| /* empty */
ALL {
$$ = true;
}
| /* empty */ {
$$ = false;
}
;
select_clause:

View File

@ -1,6 +1,6 @@
#line 2 "flex_lexer.cpp"
#line 1 "flex_lexer.cpp"
#line 4 "flex_lexer.cpp"
#line 3 "flex_lexer.cpp"
#define YY_INT_ALIGNED short int
@ -573,8 +573,8 @@ static void yynoreturn yy_fatal_error ( const char* msg , yyscan_t yyscanner );
yyg->yy_hold_char = *yy_cp; \
*yy_cp = '\0'; \
yyg->yy_c_buf_p = yy_cp;
#define YY_NUM_RULES 156
#define YY_END_OF_BUFFER 157
#define YY_NUM_RULES 158
#define YY_END_OF_BUFFER 159
/* This struct is not used in this scanner,
but its presence is necessary. */
struct yy_trans_info
@ -584,25 +584,25 @@ struct yy_trans_info
};
static const flex_int16_t yy_accept[1072] =
{ 0,
0, 0, 153, 153, 2, 2, 157, 155, 4, 4,
155, 155, 145, 151, 145, 145, 148, 145, 145, 145,
150, 150, 150, 150, 150, 150, 150, 150, 150, 150,
150, 150, 150, 150, 150, 150, 150, 150, 150, 150,
150, 150, 150, 150, 145, 153, 154, 2, 2, 3,
0, 0, 155, 155, 2, 2, 159, 157, 4, 4,
157, 157, 147, 153, 147, 147, 150, 147, 147, 147,
152, 152, 152, 152, 152, 152, 152, 152, 152, 152,
152, 152, 152, 152, 152, 152, 152, 152, 152, 152,
152, 152, 152, 152, 147, 155, 156, 2, 2, 3,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 4, 140, 0, 1, 148,
147, 146, 142, 141, 139, 143, 150, 150, 150, 150,
2, 2, 2, 2, 2, 4, 142, 0, 1, 150,
149, 148, 144, 143, 141, 145, 152, 152, 152, 152,
150, 150, 118, 150, 119, 150, 150, 150, 150, 150,
150, 150, 150, 150, 150, 150, 150, 150, 150, 150,
150, 150, 150, 150, 150, 150, 150, 150, 150, 120,
150, 150, 121, 122, 150, 150, 150, 150, 150, 150,
150, 150, 150, 150, 150, 150, 123, 124, 125, 150,
150, 150, 150, 150, 150, 150, 150, 150, 150, 150,
150, 150, 150, 150, 150, 126, 150, 150, 150, 150,
150, 150, 150, 150, 150, 144, 153, 152, 2, 2,
152, 152, 120, 152, 121, 152, 152, 152, 152, 152,
152, 152, 152, 152, 152, 152, 152, 152, 152, 152,
152, 152, 152, 152, 152, 152, 152, 152, 152, 122,
152, 152, 123, 124, 152, 152, 152, 152, 152, 152,
152, 152, 152, 152, 152, 152, 125, 126, 127, 152,
152, 152, 152, 152, 152, 152, 152, 152, 152, 152,
152, 152, 152, 152, 152, 128, 152, 152, 152, 152,
152, 152, 152, 152, 152, 146, 155, 154, 2, 2,
2, 2, 1, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
@ -613,19 +613,19 @@ static const flex_int16_t yy_accept[1072] =
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
149, 146, 106, 150, 107, 150, 150, 108, 150, 109,
150, 150, 150, 150, 150, 150, 150, 150, 150, 150,
150, 150, 150, 130, 150, 150, 150, 150, 150, 150,
151, 148, 108, 152, 109, 152, 152, 110, 152, 111,
152, 152, 152, 152, 152, 152, 152, 152, 152, 152,
152, 152, 152, 132, 152, 152, 152, 152, 152, 152,
150, 150, 150, 110, 150, 150, 150, 150, 150, 150,
150, 150, 150, 111, 150, 150, 150, 150, 150, 150,
150, 150, 150, 150, 150, 150, 150, 112, 150, 150,
113, 150, 150, 150, 150, 150, 150, 150, 150, 150,
150, 114, 150, 150, 115, 150, 150, 150, 150, 150,
150, 150, 150, 150, 150, 150, 150, 150, 116, 150,
150, 150, 150, 150, 150, 150, 150, 117, 150, 150,
150, 150, 150, 150, 150, 150, 150, 150, 150, 150,
150, 2, 2, 2, 2, 2, 2, 2, 2, 2,
152, 152, 152, 112, 152, 152, 152, 152, 152, 152,
152, 152, 152, 113, 152, 152, 152, 152, 152, 152,
152, 152, 152, 152, 152, 152, 152, 114, 152, 152,
115, 152, 152, 152, 152, 152, 152, 152, 152, 152,
152, 116, 152, 152, 117, 152, 152, 152, 152, 152,
152, 152, 152, 152, 152, 152, 152, 152, 118, 152,
152, 152, 152, 152, 152, 152, 152, 119, 152, 152,
152, 152, 152, 152, 152, 152, 152, 152, 152, 152,
152, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
@ -637,18 +637,18 @@ static const flex_int16_t yy_accept[1072] =
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 150, 150, 150, 150, 150, 150, 150, 77,
2, 2, 152, 152, 152, 152, 152, 152, 152, 79,
150, 78, 79, 150, 150, 150, 80, 150, 150, 81,
150, 150, 150, 150, 82, 150, 150, 150, 83, 84,
150, 150, 150, 150, 150, 150, 150, 85, 150, 150,
86, 87, 150, 150, 88, 150, 89, 129, 150, 150,
150, 150, 150, 150, 90, 150, 91, 92, 93, 150,
95, 150, 96, 150, 150, 150, 150, 97, 150, 150,
150, 150, 150, 98, 150, 150, 150, 150, 150, 150,
150, 150, 150, 99, 150, 150, 150, 150, 100, 101,
102, 150, 133, 150, 150, 150, 150, 150, 150, 150,
150, 103, 150, 104, 150, 105, 132, 2, 2, 2,
152, 80, 81, 152, 152, 152, 82, 152, 152, 83,
152, 152, 152, 152, 84, 152, 152, 152, 85, 86,
152, 152, 152, 152, 152, 152, 152, 87, 152, 152,
88, 89, 152, 152, 90, 152, 91, 131, 152, 152,
152, 152, 152, 152, 92, 152, 93, 94, 95, 152,
97, 152, 98, 152, 152, 152, 152, 99, 152, 152,
152, 152, 152, 100, 152, 152, 152, 152, 152, 152,
152, 152, 152, 101, 152, 152, 152, 152, 102, 103,
104, 152, 135, 152, 152, 152, 152, 152, 152, 152,
152, 105, 152, 106, 152, 107, 134, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
@ -661,14 +661,14 @@ static const flex_int16_t yy_accept[1072] =
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 57, 58, 150, 59, 150, 136, 150, 150,
150, 150, 150, 150, 60, 150, 150, 150, 61, 150,
150, 150, 150, 150, 150, 150, 150, 150, 150, 134,
62, 150, 150, 63, 150, 94, 150, 64, 65, 150,
150, 150, 150, 66, 67, 68, 69, 150, 131, 150,
150, 150, 70, 71, 150, 150, 150, 150, 150, 72,
150, 150, 150, 150, 150, 150, 73, 150, 150, 150,
150, 74, 150, 150, 150, 75, 150, 150, 150, 76,
2, 2, 57, 58, 152, 59, 152, 138, 152, 152,
152, 152, 152, 152, 60, 152, 152, 152, 61, 152,
152, 152, 152, 152, 152, 152, 152, 152, 152, 136,
62, 152, 152, 63, 152, 96, 152, 64, 65, 152,
152, 152, 152, 66, 67, 68, 69, 152, 133, 152,
152, 152, 70, 71, 152, 152, 152, 152, 152, 72,
152, 152, 152, 152, 152, 152, 73, 152, 152, 152,
152, 74, 152, 152, 152, 77, 152, 152, 152, 78,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
@ -677,30 +677,30 @@ static const flex_int16_t yy_accept[1072] =
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 150, 31,
150, 150, 32, 138, 150, 33, 150, 150, 34, 150,
35, 150, 36, 37, 38, 150, 39, 150, 150, 41,
42, 43, 44, 45, 150, 150, 46, 128, 150, 150,
47, 150, 150, 150, 48, 150, 150, 49, 127, 50,
2, 2, 2, 2, 2, 2, 2, 2, 152, 31,
152, 152, 32, 140, 152, 33, 152, 152, 34, 152,
35, 152, 36, 37, 38, 152, 39, 152, 152, 41,
42, 43, 44, 45, 152, 152, 46, 130, 152, 152,
47, 152, 152, 152, 48, 152, 152, 49, 129, 50,
51, 150, 52, 150, 150, 150, 150, 53, 54, 55,
56, 150, 150, 2, 2, 2, 2, 2, 2, 2,
51, 152, 52, 152, 152, 152, 152, 53, 54, 55,
56, 152, 152, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 15, 16,
17, 18, 19, 150, 20, 150, 150, 21, 22, 40,
23, 150, 24, 150, 150, 25, 26, 150, 150, 27,
28, 150, 150, 150, 150, 29, 30, 2, 2, 2,
17, 18, 19, 152, 20, 152, 152, 21, 22, 40,
23, 152, 24, 152, 152, 25, 26, 152, 152, 27,
28, 152, 152, 152, 152, 29, 30, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 150, 10, 11, 150,
12, 150, 13, 137, 150, 150, 150, 14, 2, 2,
2, 2, 2, 2, 2, 2, 152, 10, 11, 152,
12, 152, 13, 139, 152, 152, 152, 14, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
150, 7, 150, 8, 9, 150, 2, 2, 2, 2,
2, 2, 5, 6, 150, 2, 2, 2, 135, 2,
152, 7, 152, 8, 9, 152, 2, 2, 2, 2,
2, 2, 5, 6, 152, 2, 2, 2, 137, 2,
0
} ;
@ -1982,7 +1982,7 @@ static const flex_int16_t yy_chk[4346] =
static thread_local std::stringstream strbuf;
#line 1986 "flex_lexer.cpp"
#line 1985 "flex_lexer.cpp"
/***************************
** Section 2: Rules
@ -1996,7 +1996,7 @@ static thread_local std::stringstream strbuf;
/***************************
** Section 3: Rules
***************************/
#line 2000 "flex_lexer.cpp"
#line 1999 "flex_lexer.cpp"
#define INITIAL 0
#define singlequotedstring 1
@ -2283,7 +2283,7 @@ YY_DECL
#line 56 "flex_lexer.l"
#line 2287 "flex_lexer.cpp"
#line 2286 "flex_lexer.cpp"
while ( /*CONSTCOND*/1 ) /* loops until end-of-file is reached */
{
@ -2711,430 +2711,440 @@ TOKEN(UNION)
case 75:
YY_RULE_SETUP
#line 134 "flex_lexer.l"
TOKEN(USING)
TOKEN(INTERSECT)
YY_BREAK
case 76:
YY_RULE_SETUP
#line 135 "flex_lexer.l"
TOKEN(WHERE)
TOKEN(EXCEPT)
YY_BREAK
case 77:
YY_RULE_SETUP
#line 136 "flex_lexer.l"
TOKEN(CALL)
TOKEN(USING)
YY_BREAK
case 78:
YY_RULE_SETUP
#line 137 "flex_lexer.l"
TOKEN(CASE)
TOKEN(WHERE)
YY_BREAK
case 79:
YY_RULE_SETUP
#line 138 "flex_lexer.l"
TOKEN(CHAR)
TOKEN(CALL)
YY_BREAK
case 80:
YY_RULE_SETUP
#line 139 "flex_lexer.l"
TOKEN(COPY)
TOKEN(CASE)
YY_BREAK
case 81:
YY_RULE_SETUP
#line 140 "flex_lexer.l"
TOKEN(DATE)
TOKEN(CHAR)
YY_BREAK
case 82:
YY_RULE_SETUP
#line 141 "flex_lexer.l"
TOKEN(DESC)
TOKEN(COPY)
YY_BREAK
case 83:
YY_RULE_SETUP
#line 142 "flex_lexer.l"
TOKEN(DROP)
TOKEN(DATE)
YY_BREAK
case 84:
YY_RULE_SETUP
#line 143 "flex_lexer.l"
TOKEN(ELSE)
TOKEN(DESC)
YY_BREAK
case 85:
YY_RULE_SETUP
#line 144 "flex_lexer.l"
TOKEN(FILE)
TOKEN(DROP)
YY_BREAK
case 86:
YY_RULE_SETUP
#line 145 "flex_lexer.l"
TOKEN(FROM)
TOKEN(ELSE)
YY_BREAK
case 87:
YY_RULE_SETUP
#line 146 "flex_lexer.l"
TOKEN(FULL)
TOKEN(FILE)
YY_BREAK
case 88:
YY_RULE_SETUP
#line 147 "flex_lexer.l"
TOKEN(HASH)
TOKEN(FROM)
YY_BREAK
case 89:
YY_RULE_SETUP
#line 148 "flex_lexer.l"
TOKEN(HINT)
TOKEN(FULL)
YY_BREAK
case 90:
YY_RULE_SETUP
#line 149 "flex_lexer.l"
TOKEN(INTO)
TOKEN(HASH)
YY_BREAK
case 91:
YY_RULE_SETUP
#line 150 "flex_lexer.l"
TOKEN(JOIN)
TOKEN(HINT)
YY_BREAK
case 92:
YY_RULE_SETUP
#line 151 "flex_lexer.l"
TOKEN(LEFT)
TOKEN(INTO)
YY_BREAK
case 93:
YY_RULE_SETUP
#line 152 "flex_lexer.l"
TOKEN(LIKE)
TOKEN(JOIN)
YY_BREAK
case 94:
YY_RULE_SETUP
#line 153 "flex_lexer.l"
TOKEN(ILIKE)
TOKEN(LEFT)
YY_BREAK
case 95:
YY_RULE_SETUP
#line 154 "flex_lexer.l"
TOKEN(LOAD)
TOKEN(LIKE)
YY_BREAK
case 96:
YY_RULE_SETUP
#line 155 "flex_lexer.l"
TOKEN(LONG)
TOKEN(ILIKE)
YY_BREAK
case 97:
YY_RULE_SETUP
#line 156 "flex_lexer.l"
TOKEN(NULL)
TOKEN(LOAD)
YY_BREAK
case 98:
YY_RULE_SETUP
#line 157 "flex_lexer.l"
TOKEN(PLAN)
TOKEN(LONG)
YY_BREAK
case 99:
YY_RULE_SETUP
#line 158 "flex_lexer.l"
TOKEN(SHOW)
TOKEN(NULL)
YY_BREAK
case 100:
YY_RULE_SETUP
#line 159 "flex_lexer.l"
TOKEN(TEXT)
TOKEN(PLAN)
YY_BREAK
case 101:
YY_RULE_SETUP
#line 160 "flex_lexer.l"
TOKEN(THEN)
TOKEN(SHOW)
YY_BREAK
case 102:
YY_RULE_SETUP
#line 161 "flex_lexer.l"
TOKEN(TIME)
TOKEN(TEXT)
YY_BREAK
case 103:
YY_RULE_SETUP
#line 162 "flex_lexer.l"
TOKEN(VIEW)
TOKEN(THEN)
YY_BREAK
case 104:
YY_RULE_SETUP
#line 163 "flex_lexer.l"
TOKEN(WHEN)
TOKEN(TIME)
YY_BREAK
case 105:
YY_RULE_SETUP
#line 164 "flex_lexer.l"
TOKEN(WITH)
TOKEN(VIEW)
YY_BREAK
case 106:
YY_RULE_SETUP
#line 165 "flex_lexer.l"
TOKEN(ADD)
TOKEN(WHEN)
YY_BREAK
case 107:
YY_RULE_SETUP
#line 166 "flex_lexer.l"
TOKEN(ALL)
TOKEN(WITH)
YY_BREAK
case 108:
YY_RULE_SETUP
#line 167 "flex_lexer.l"
TOKEN(AND)
TOKEN(ADD)
YY_BREAK
case 109:
YY_RULE_SETUP
#line 168 "flex_lexer.l"
TOKEN(ASC)
TOKEN(ALL)
YY_BREAK
case 110:
YY_RULE_SETUP
#line 169 "flex_lexer.l"
TOKEN(END)
TOKEN(AND)
YY_BREAK
case 111:
YY_RULE_SETUP
#line 170 "flex_lexer.l"
TOKEN(FOR)
TOKEN(ASC)
YY_BREAK
case 112:
YY_RULE_SETUP
#line 171 "flex_lexer.l"
TOKEN(INT)
TOKEN(END)
YY_BREAK
case 113:
YY_RULE_SETUP
#line 172 "flex_lexer.l"
TOKEN(KEY)
TOKEN(FOR)
YY_BREAK
case 114:
YY_RULE_SETUP
#line 173 "flex_lexer.l"
TOKEN(NOT)
TOKEN(INT)
YY_BREAK
case 115:
YY_RULE_SETUP
#line 174 "flex_lexer.l"
TOKEN(OFF)
TOKEN(KEY)
YY_BREAK
case 116:
YY_RULE_SETUP
#line 175 "flex_lexer.l"
TOKEN(SET)
TOKEN(NOT)
YY_BREAK
case 117:
YY_RULE_SETUP
#line 176 "flex_lexer.l"
TOKEN(TOP)
TOKEN(OFF)
YY_BREAK
case 118:
YY_RULE_SETUP
#line 177 "flex_lexer.l"
TOKEN(AS)
TOKEN(SET)
YY_BREAK
case 119:
YY_RULE_SETUP
#line 178 "flex_lexer.l"
TOKEN(BY)
TOKEN(TOP)
YY_BREAK
case 120:
YY_RULE_SETUP
#line 179 "flex_lexer.l"
TOKEN(IF)
TOKEN(AS)
YY_BREAK
case 121:
YY_RULE_SETUP
#line 180 "flex_lexer.l"
TOKEN(IN)
TOKEN(BY)
YY_BREAK
case 122:
YY_RULE_SETUP
#line 181 "flex_lexer.l"
TOKEN(IS)
TOKEN(IF)
YY_BREAK
case 123:
YY_RULE_SETUP
#line 182 "flex_lexer.l"
TOKEN(OF)
TOKEN(IN)
YY_BREAK
case 124:
YY_RULE_SETUP
#line 183 "flex_lexer.l"
TOKEN(ON)
TOKEN(IS)
YY_BREAK
case 125:
YY_RULE_SETUP
#line 184 "flex_lexer.l"
TOKEN(OR)
TOKEN(OF)
YY_BREAK
case 126:
YY_RULE_SETUP
#line 185 "flex_lexer.l"
TOKEN(TO)
TOKEN(ON)
YY_BREAK
case 127:
YY_RULE_SETUP
#line 186 "flex_lexer.l"
TOKEN(SECOND)
TOKEN(OR)
YY_BREAK
case 128:
YY_RULE_SETUP
#line 187 "flex_lexer.l"
TOKEN(MINUTE)
TOKEN(TO)
YY_BREAK
case 129:
YY_RULE_SETUP
#line 188 "flex_lexer.l"
TOKEN(HOUR)
TOKEN(SECOND)
YY_BREAK
case 130:
YY_RULE_SETUP
#line 189 "flex_lexer.l"
TOKEN(DAY)
TOKEN(MINUTE)
YY_BREAK
case 131:
YY_RULE_SETUP
#line 190 "flex_lexer.l"
TOKEN(MONTH)
TOKEN(HOUR)
YY_BREAK
case 132:
YY_RULE_SETUP
#line 191 "flex_lexer.l"
TOKEN(YEAR)
TOKEN(DAY)
YY_BREAK
case 133:
YY_RULE_SETUP
#line 192 "flex_lexer.l"
TOKEN(TRUE)
TOKEN(MONTH)
YY_BREAK
case 134:
YY_RULE_SETUP
#line 193 "flex_lexer.l"
TOKEN(FALSE)
TOKEN(YEAR)
YY_BREAK
case 135:
YY_RULE_SETUP
#line 194 "flex_lexer.l"
TOKEN(TRANSACTION)
TOKEN(TRUE)
YY_BREAK
case 136:
YY_RULE_SETUP
#line 195 "flex_lexer.l"
TOKEN(BEGIN)
TOKEN(FALSE)
YY_BREAK
case 137:
YY_RULE_SETUP
#line 196 "flex_lexer.l"
TOKEN(ROLLBACK)
TOKEN(TRANSACTION)
YY_BREAK
case 138:
YY_RULE_SETUP
#line 197 "flex_lexer.l"
TOKEN(COMMIT)
TOKEN(BEGIN)
YY_BREAK
/* Allow =/== see https://sqlite.org/lang_expr.html#collateop */
case 139:
YY_RULE_SETUP
#line 200 "flex_lexer.l"
TOKEN(EQUALS)
#line 198 "flex_lexer.l"
TOKEN(ROLLBACK)
YY_BREAK
case 140:
YY_RULE_SETUP
#line 201 "flex_lexer.l"
TOKEN(NOTEQUALS)
#line 199 "flex_lexer.l"
TOKEN(COMMIT)
YY_BREAK
/* Allow =/== see https://sqlite.org/lang_expr.html#collateop */
case 141:
YY_RULE_SETUP
#line 202 "flex_lexer.l"
TOKEN(NOTEQUALS)
TOKEN(EQUALS)
YY_BREAK
case 142:
YY_RULE_SETUP
#line 203 "flex_lexer.l"
TOKEN(LESSEQ)
TOKEN(NOTEQUALS)
YY_BREAK
case 143:
YY_RULE_SETUP
#line 204 "flex_lexer.l"
TOKEN(GREATEREQ)
TOKEN(NOTEQUALS)
YY_BREAK
case 144:
YY_RULE_SETUP
#line 205 "flex_lexer.l"
TOKEN(CONCAT)
TOKEN(LESSEQ)
YY_BREAK
case 145:
YY_RULE_SETUP
#line 207 "flex_lexer.l"
{ return yytext[0]; }
#line 206 "flex_lexer.l"
TOKEN(GREATEREQ)
YY_BREAK
case 146:
#line 210 "flex_lexer.l"
YY_RULE_SETUP
#line 207 "flex_lexer.l"
TOKEN(CONCAT)
YY_BREAK
case 147:
YY_RULE_SETUP
#line 210 "flex_lexer.l"
#line 209 "flex_lexer.l"
{ return yytext[0]; }
YY_BREAK
case 148:
#line 212 "flex_lexer.l"
case 149:
YY_RULE_SETUP
#line 212 "flex_lexer.l"
{
yylval->fval = atof(yytext);
return SQL_FLOATVAL;
}
YY_BREAK
case 148:
case 150:
YY_RULE_SETUP
#line 215 "flex_lexer.l"
#line 217 "flex_lexer.l"
{
yylval->ival = atol(yytext);
return SQL_INTVAL;
}
YY_BREAK
case 149:
case 151:
YY_RULE_SETUP
#line 220 "flex_lexer.l"
#line 222 "flex_lexer.l"
{
// Crop the leading and trailing quote char
yylval->sval = hsql::substr(yytext, 1, strlen(yytext)-1);
return SQL_IDENTIFIER;
}
YY_BREAK
case 150:
case 152:
YY_RULE_SETUP
#line 226 "flex_lexer.l"
#line 228 "flex_lexer.l"
{
yylval->sval = strdup(yytext);
return SQL_IDENTIFIER;
}
YY_BREAK
case 151:
YY_RULE_SETUP
#line 231 "flex_lexer.l"
{ BEGIN singlequotedstring; strbuf = std::stringstream{}; }
YY_BREAK
case 152:
YY_RULE_SETUP
#line 232 "flex_lexer.l"
{ strbuf << '\''; }
YY_BREAK
case 153:
/* rule 153 can match eol */
YY_RULE_SETUP
#line 233 "flex_lexer.l"
{ strbuf << yytext; }
{ BEGIN singlequotedstring; strbuf = std::stringstream{}; }
YY_BREAK
case 154:
YY_RULE_SETUP
#line 234 "flex_lexer.l"
{ BEGIN 0; yylval->sval = strdup(strbuf.str().c_str()); return SQL_STRING; }
YY_BREAK
case YY_STATE_EOF(singlequotedstring):
#line 235 "flex_lexer.l"
{ fprintf(stderr, "[SQL-Lexer-Error] Unterminated string\n"); return 0; }
{ strbuf << '\''; }
YY_BREAK
case 155:
/* rule 155 can match eol */
YY_RULE_SETUP
#line 237 "flex_lexer.l"
{ fprintf(stderr, "[SQL-Lexer-Error] Unknown Character: %c\n", yytext[0]); return 0; }
#line 235 "flex_lexer.l"
{ strbuf << yytext; }
YY_BREAK
case 156:
YY_RULE_SETUP
#line 240 "flex_lexer.l"
#line 236 "flex_lexer.l"
{ BEGIN 0; yylval->sval = strdup(strbuf.str().c_str()); return SQL_STRING; }
YY_BREAK
case YY_STATE_EOF(singlequotedstring):
#line 237 "flex_lexer.l"
{ fprintf(stderr, "[SQL-Lexer-Error] Unterminated string\n"); return 0; }
YY_BREAK
case 157:
YY_RULE_SETUP
#line 239 "flex_lexer.l"
{ fprintf(stderr, "[SQL-Lexer-Error] Unknown Character: %c\n", yytext[0]); return 0; }
YY_BREAK
case 158:
YY_RULE_SETUP
#line 242 "flex_lexer.l"
ECHO;
YY_BREAK
#line 3138 "flex_lexer.cpp"
#line 3147 "flex_lexer.cpp"
case YY_STATE_EOF(INITIAL):
case YY_STATE_EOF(COMMENT):
yyterminate();
@ -4291,7 +4301,7 @@ void yyfree (void * ptr , yyscan_t yyscanner)
#define YYTABLES_NAME "yytables"
#line 240 "flex_lexer.l"
#line 242 "flex_lexer.l"
/***************************
** Section 3: User code

View File

@ -2,9 +2,9 @@
#define hsql_HEADER_H 1
#define hsql_IN_HEADER 1
#line 6 "flex_lexer.h"
#line 5 "flex_lexer.h"
#line 8 "flex_lexer.h"
#line 7 "flex_lexer.h"
#define YY_INT_ALIGNED short int
@ -730,9 +730,9 @@ extern int yylex \
#undef yyTABLES_NAME
#endif
#line 240 "flex_lexer.l"
#line 242 "flex_lexer.l"
#line 737 "flex_lexer.h"
#line 736 "flex_lexer.h"
#undef hsql_IN_HEADER
#endif /* hsql_HEADER_H */

View File

@ -131,6 +131,8 @@ OUTER TOKEN(OUTER)
RIGHT TOKEN(RIGHT)
TABLE TOKEN(TABLE)
UNION TOKEN(UNION)
INTERSECT TOKEN(INTERSECT)
EXCEPT TOKEN(EXCEPT)
USING TOKEN(USING)
WHERE TOKEN(WHERE)
CALL TOKEN(CALL)

View File

@ -11,6 +11,11 @@ namespace hsql {
kOrderDesc
};
enum SetType {
kSetUnion,
kSetIntersect,
kSetExcept
};
// Description of the order by clause within a select statement.
struct OrderDescription {
@ -46,8 +51,20 @@ namespace hsql {
SelectStatement* select;
};
struct SetOperation {
SetOperation();
virtual ~SetOperation();
SetType setType;
bool isAll;
SelectStatement* nestedSelectStatement;
std::vector<OrderDescription*>* resultOrder;
LimitDescription* resultLimit;
};
// Representation of a full SQL select statement.
// TODO: add union_order and union_limit.
struct SelectStatement : SQLStatement {
SelectStatement();
virtual ~SelectStatement();
@ -58,7 +75,32 @@ namespace hsql {
Expr* whereClause;
GroupByDescription* groupBy;
SelectStatement* unionSelect;
// Note that a SetOperation is always connected to a
// different SelectStatement. This statement can itself
// have SetOperation connections to other SelectStatements.
// To evaluate the operations in the correct order:
// Iterate over the setOperations vector:
// 1. Fully evaluate the nestedSelectStatement within the SetOperation
// 2. Connect the original statement with the
// evaluated nestedSelectStatement
// 3. Apply the resultOrder and the resultLimit
// 4. The result now functions as the the original statement
// for the next iteration
//
// Example:
//
// (SELECT * FROM students INTERSECT SELECT * FROM students_2) UNION SELECT * FROM students_3 ORDER BY grade ASC;
//
// 1. We evaluate `Select * FROM students`
// 2. Then we iterate over the setOperations vector
// 3. We evalute the nestedSelectStatement of the first entry, which is: `SELECT * FROM students_2`
// 4. We connect the result of 1. with the results of 3. using the setType, which is INTERSECT
// 5. We continue the iteration of the setOperations vector
// 6. We evaluate the new nestedSelectStatement which is: `SELECT * FROM students_3`
// 7. We apply a Union-Operation to connect the results of 4. and 6.
// 8. Finally, we apply the resultOrder of the last SetOperation (ORDER BY grade ASC)
std::vector<SetOperation*>* setOperations;
std::vector<OrderDescription*>* order;
std::vector<WithDescription*>* withDescriptions;
LimitDescription* limit;

View File

@ -262,7 +262,7 @@ namespace hsql {
selectList(nullptr),
whereClause(nullptr),
groupBy(nullptr),
unionSelect(nullptr),
setOperations(nullptr),
order(nullptr),
withDescriptions(nullptr),
limit(nullptr) {};
@ -271,7 +271,6 @@ namespace hsql {
delete fromTable;
delete whereClause;
delete groupBy;
delete unionSelect;
delete limit;
// Delete each element in the select list.
@ -295,6 +294,13 @@ namespace hsql {
}
delete withDescriptions;
}
if (setOperations != nullptr) {
for (SetOperation* setOperation : *setOperations) {
delete setOperation;
}
delete setOperations;
}
}
// UpdateStatement
@ -381,4 +387,21 @@ namespace hsql {
delete condition;
}
SetOperation::SetOperation() :
nestedSelectStatement(nullptr),
resultOrder(nullptr),
resultLimit(nullptr) {}
SetOperation::~SetOperation() {
delete nestedSelectStatement;
delete resultLimit;
if (resultOrder != nullptr) {
for (OrderDescription* desc: *resultOrder) {
delete desc;
}
delete resultOrder;
}
}
} // namespace hsql

View File

@ -168,9 +168,41 @@ namespace hsql {
}
}
if (stmt->unionSelect != nullptr) {
if (stmt->setOperations != nullptr) {
for (SetOperation* setOperation : *stmt->setOperations) {
switch (setOperation->setType) {
case SetType::kSetIntersect:
inprint("Intersect:", numIndent + 1);
break;
case SetType::kSetUnion:
inprint("Union:", numIndent + 1);
printSelectStatementInfo(stmt->unionSelect, numIndent + 2);
break;
case SetType::kSetExcept:
inprint("Except:", numIndent + 1);
break;
}
printSelectStatementInfo(setOperation->nestedSelectStatement, numIndent + 2);
if (setOperation->resultOrder != nullptr) {
inprint("SetResultOrderBy:", numIndent + 1);
printExpression(setOperation->resultOrder->at(0)->expr, numIndent + 2);
if (setOperation->resultOrder->at(0)->type == kOrderAsc) inprint("ascending", numIndent + 2);
else inprint("descending", numIndent + 2);
}
if (setOperation->resultLimit != nullptr) {
if (setOperation->resultLimit->limit != nullptr) {
inprint("SetResultLimit:", numIndent + 1);
printExpression(setOperation->resultLimit->limit, numIndent + 2);
}
if (setOperation->resultLimit->offset != nullptr) {
inprint("SetResultOffset:", numIndent + 1);
printExpression(setOperation->resultLimit->offset, numIndent + 2);
}
}
}
}
if (stmt->order != nullptr) {

View File

@ -105,7 +105,6 @@ TEST(UpdateStatementTest) {
ASSERT_STREQ(stmt->where->expr2->name, "Max O'Mustermann");
}
TEST(InsertStatementTest) {
TEST_PARSE_SINGLE_SQL(
"INSERT INTO students VALUES ('Max Mustermann', 12345, 'Musterhausen', 2.0)",
@ -118,7 +117,6 @@ TEST(InsertStatementTest) {
// TODO
}
TEST(DropTableStatementTest) {
TEST_PARSE_SINGLE_SQL(
"DROP TABLE students",
@ -281,7 +279,6 @@ TEST(HintTest) {
result,
stmt);
ASSERT_NOTNULL(stmt->hints);
ASSERT_EQ(2, stmt->hints->size());
ASSERT_STREQ("NO_CACHE", stmt->hints->at(0)->name);
@ -301,6 +298,190 @@ TEST(StringLengthTest) {
ASSERT_EQ(result.getStatement(2)->stringLength, 21);
}
TEST(ExceptOperatorTest) {
TEST_PARSE_SINGLE_SQL(
"SELECT * FROM students EXCEPT SELECT * FROM students_2;",
kStmtSelect,
SelectStatement,
result,
stmt);
ASSERT_STREQ(stmt->setOperations->back()->nestedSelectStatement->fromTable->name, "students_2");
ASSERT_STREQ(stmt->fromTable->name, "students");
ASSERT_EQ(stmt->setOperations->back()->setType, kSetExcept);
}
TEST(IntersectOperatorTest) {
TEST_PARSE_SINGLE_SQL(
"SELECT * FROM students INTERSECT SELECT * FROM students_2;",
kStmtSelect,
SelectStatement,
result,
stmt);
ASSERT_STREQ(stmt->setOperations->back()->nestedSelectStatement->fromTable->name, "students_2");
ASSERT_STREQ(stmt->fromTable->name, "students");
ASSERT_EQ(stmt->setOperations->back()->setType, kSetIntersect);
}
TEST(UnionOperatorTest) {
TEST_PARSE_SINGLE_SQL(
"SELECT * FROM students UNION SELECT * FROM students_2;",
kStmtSelect,
SelectStatement,
result,
stmt);
ASSERT_STREQ(stmt->setOperations->back()->nestedSelectStatement->fromTable->name, "students_2");
ASSERT_STREQ(stmt->fromTable->name, "students");
ASSERT_EQ(stmt->setOperations->back()->setType, kSetUnion);
ASSERT_FALSE(stmt->setOperations->back()->isAll);
}
TEST(UnionAllOperatorTest) {
TEST_PARSE_SINGLE_SQL(
"SELECT * FROM students UNION ALL SELECT * FROM students_2;",
kStmtSelect,
SelectStatement,
result,
stmt);
ASSERT_STREQ(stmt->setOperations->back()->nestedSelectStatement->fromTable->name, "students_2");
ASSERT_STREQ(stmt->fromTable->name, "students");
ASSERT_TRUE(stmt->setOperations->back()->isAll);
}
TEST(NestedSetOperationTest) {
TEST_PARSE_SINGLE_SQL(
"SELECT * FROM students INTERSECT SELECT grade FROM students_2 UNION SELECT * FROM employees;",
kStmtSelect,
SelectStatement,
result,
stmt);
ASSERT_STREQ(stmt->setOperations->back()->nestedSelectStatement->setOperations->back()->nestedSelectStatement->fromTable->name, "employees");
ASSERT_STREQ(stmt->setOperations->back()->nestedSelectStatement->fromTable->name, "students_2");
ASSERT_STREQ(stmt->fromTable->name, "students");
ASSERT_EQ(stmt->setOperations->back()->setType, kSetIntersect);
ASSERT_EQ(stmt->setOperations->back()->nestedSelectStatement->setOperations->back()->setType, kSetUnion);
ASSERT_FALSE(stmt->setOperations->back()->isAll);
}
TEST(OrderByFullStatementTest) {
TEST_PARSE_SINGLE_SQL(
"SELECT * FROM students INTERSECT SELECT grade FROM students_2 UNION SELECT * FROM employees ORDER BY grade ASC;",
kStmtSelect,
SelectStatement,
result,
stmt);
ASSERT_EQ(stmt->setOperations->back()->resultOrder->at(0)->type, kOrderAsc);
ASSERT_STREQ(stmt->setOperations->back()->resultOrder->at(0)->expr->name, "grade");
ASSERT_FALSE(stmt->setOperations->back()->isAll);
}
TEST(SetOperationSubQueryOrder) {
TEST_PARSE_SINGLE_SQL(
"(SELECT * FROM students ORDER BY name DESC) INTERSECT SELECT grade FROM students_2 UNION SELECT * FROM employees ORDER BY grade ASC;",
kStmtSelect,
SelectStatement,
result,
stmt);
ASSERT_EQ(stmt->order->at(0)->type, kOrderDesc);
ASSERT_STREQ(stmt->order->at(0)->expr->name, "name");
ASSERT_EQ(stmt->setOperations->back()->resultOrder->at(0)->type, kOrderAsc);
ASSERT_STREQ(stmt->setOperations->back()->resultOrder->at(0)->expr->name, "grade");
ASSERT_FALSE(stmt->setOperations->back()->isAll);
}
TEST(SetOperationLastSubQueryOrder) {
TEST_PARSE_SINGLE_SQL(
"SELECT * FROM students INTERSECT SELECT grade FROM students_2 UNION (SELECT * FROM employees ORDER BY name DESC) ORDER BY grade ASC;",
kStmtSelect,
SelectStatement,
result,
stmt);
ASSERT_EQ(stmt->setOperations->back()->nestedSelectStatement->setOperations->back()->nestedSelectStatement->order->at(0)->type, kOrderDesc);
ASSERT_STREQ(stmt->setOperations->back()->nestedSelectStatement->setOperations->back()->nestedSelectStatement->order->at(0)->expr->name, "name");
ASSERT_EQ(stmt->setOperations->back()->resultOrder->at(0)->type, kOrderAsc);
ASSERT_STREQ(stmt->setOperations->back()->resultOrder->at(0)->expr->name, "grade");
ASSERT_FALSE(stmt->setOperations->back()->isAll);
}
TEST(NestedDifferentSetOperationsWithWithClause) {
TEST_PARSE_SINGLE_SQL("WITH UNION_FIRST AS (SELECT * FROM A UNION SELECT * FROM B) SELECT * FROM UNION_FIRST EXCEPT SELECT * FROM C",
kStmtSelect,
SelectStatement,
result,
stmt);
ASSERT_STREQ(stmt->withDescriptions->back()->alias, "UNION_FIRST");
ASSERT_EQ(stmt->withDescriptions->back()->select->setOperations->back()->setType, kSetUnion);
ASSERT_STREQ(stmt->withDescriptions->back()->select->fromTable->name, "A");
ASSERT_STREQ(stmt->withDescriptions->back()->select->setOperations->back()->nestedSelectStatement->fromTable->name, "B");
ASSERT_EQ(stmt->setOperations->back()->setType, kSetExcept);
ASSERT_STREQ(stmt->fromTable->name, "UNION_FIRST");
ASSERT_STREQ(stmt->setOperations->back()->nestedSelectStatement->fromTable->name, "C");
}
TEST(NestedAllSetOperationsWithWithClause) {
TEST_PARSE_SINGLE_SQL("WITH UNION_FIRST AS (SELECT * FROM A UNION SELECT * FROM B) SELECT * FROM UNION_FIRST EXCEPT SELECT * FROM (SELECT * FROM C INTERSECT SELECT * FROM D)",
kStmtSelect,
SelectStatement,
result,
stmt);
ASSERT_STREQ(stmt->withDescriptions->back()->alias, "UNION_FIRST");
ASSERT_EQ(stmt->withDescriptions->back()->select->setOperations->back()->setType, kSetUnion);
ASSERT_STREQ(stmt->withDescriptions->back()->select->fromTable->name, "A");
ASSERT_STREQ(stmt->withDescriptions->back()->select->setOperations->back()->nestedSelectStatement->fromTable->name, "B");
ASSERT_EQ(stmt->setOperations->back()->setType, kSetExcept);
ASSERT_STREQ(stmt->fromTable->name, "UNION_FIRST");
ASSERT_EQ(stmt->setOperations->back()->nestedSelectStatement->fromTable->select->setOperations->back()->setType, kSetIntersect);
ASSERT_STREQ(stmt->setOperations->back()->nestedSelectStatement->fromTable->select->fromTable->name, "C");
ASSERT_STREQ(stmt->setOperations->back()->nestedSelectStatement->fromTable->select->setOperations->back()->nestedSelectStatement->fromTable->name, "D");
}
TEST(NestedSetOperationsWithMultipleWithClauses) {
TEST_PARSE_SINGLE_SQL(
"WITH UNION_FIRST AS (SELECT * FROM A UNION SELECT * FROM B),INTERSECT_SECOND AS (SELECT * FROM UNION_FIRST INTERSECT SELECT * FROM C) SELECT * FROM UNION_FIRST EXCEPT SELECT * FROM INTERSECT_SECOND",
kStmtSelect,
SelectStatement,
result,
stmt);
ASSERT_STREQ(stmt->withDescriptions->at(0)->alias, "UNION_FIRST");
ASSERT_STREQ(stmt->withDescriptions->back()->alias, "INTERSECT_SECOND");
ASSERT_EQ(stmt->withDescriptions->at(0)->select->setOperations->back()->setType, kSetUnion);
ASSERT_STREQ(stmt->withDescriptions->at(0)->select->fromTable->name, "A");
ASSERT_STREQ(stmt->withDescriptions->at(0)->select->setOperations->back()->nestedSelectStatement->fromTable->name, "B");
ASSERT_EQ(stmt->withDescriptions->back()->select->setOperations->back()->setType, kSetIntersect);
ASSERT_STREQ(stmt->withDescriptions->back()->select->fromTable->name, "UNION_FIRST");
ASSERT_STREQ(stmt->withDescriptions->back()->select->setOperations->back()->nestedSelectStatement->fromTable->name, "C");
ASSERT_EQ(stmt->setOperations->back()->setType, kSetExcept);
ASSERT_STREQ(stmt->fromTable->name, "UNION_FIRST");
ASSERT_STREQ(stmt->setOperations->back()->nestedSelectStatement->fromTable->name, "INTERSECT_SECOND");
}
TEST(WrongOrderByStatementTest) {
SQLParserResult res = parse_and_move("SELECT * FROM students ORDER BY name INTERSECT SELECT grade FROM students_2;");
ASSERT_FALSE(res.isValid());
}
TEST(BeginTransactionTest) {
{
TEST_PARSE_SINGLE_SQL(