%{ /** * bison_parser.y * defines bison_parser.h * outputs bison_parser.c * * Grammar File Spec: http://dinosaur.compilertools.net/bison/bison_6.html * */ /********************************* ** Section 1: C Declarations *********************************/ #include "bison_parser.h" #include "flex_lexer.h" #include #include using namespace hsql; int yyerror(YYLTYPE* llocp, SQLParserResult* result, yyscan_t scanner, const char *msg) { result->setIsValid(false); result->setErrorDetails(strdup(msg), llocp->first_line, llocp->first_column); return 0; } %} /********************************* ** Section 2: Bison Parser Declarations *********************************/ // Specify code that is included in the generated .h and .c files %code requires { // %code requires block #include "../sql/statements.h" #include "../SQLParserResult.h" #include "parser_typedef.h" // Auto update column and line number #define YY_USER_ACTION \ yylloc->first_line = yylloc->last_line; \ yylloc->first_column = yylloc->last_column; \ for(int i = 0; yytext[i] != '\0'; i++) { \ yylloc->total_column++; \ if(yytext[i] == '\n') { \ yylloc->last_line++; \ yylloc->last_column = 0; \ } \ else { \ yylloc->last_column++; \ } \ } } // Define the names of the created files (defined in Makefile) // %output "bison_parser.cpp" // %defines "bison_parser.h" // Tell bison to create a reentrant parser %define api.pure full // Prefix the parser %define api.prefix {hsql_} %define api.token.prefix {SQL_} %define parse.error verbose %locations %initial-action { // Initialize @$.first_column = 0; @$.last_column = 0; @$.first_line = 0; @$.last_line = 0; @$.total_column = 0; }; // Define additional parameters for yylex (http://www.gnu.org/software/bison/manual/html_node/Pure-Calling.html) %lex-param { yyscan_t scanner } // Define additional parameters for yyparse %parse-param { hsql::SQLParserResult* result } %parse-param { yyscan_t scanner } /********************************* ** Define all data-types (http://www.gnu.org/software/bison/manual/html_node/Union-Decl.html) *********************************/ %union { double fval; int64_t ival; char* sval; uintmax_t uval; bool bval; hsql::SQLStatement* statement; hsql::SelectStatement* select_stmt; hsql::ImportStatement* import_stmt; hsql::CreateStatement* create_stmt; hsql::InsertStatement* insert_stmt; hsql::DeleteStatement* delete_stmt; hsql::UpdateStatement* update_stmt; hsql::DropStatement* drop_stmt; hsql::PrepareStatement* prep_stmt; hsql::ExecuteStatement* exec_stmt; hsql::ShowStatement* show_stmt; hsql::TableRef* table; hsql::Expr* expr; hsql::OrderDescription* order; hsql::OrderType order_type; hsql::LimitDescription* limit; hsql::ColumnDefinition* column_t; hsql::GroupByDescription* group_t; hsql::UpdateClause* update_t; std::vector* stmt_vec; std::vector* str_vec; std::vector* table_vec; std::vector* column_vec; std::vector* update_vec; std::vector* expr_vec; std::vector* order_vec; } /********************************* ** Descrutor symbols *********************************/ %destructor { } %destructor { free( ($$) ); } %destructor { if (($$) != nullptr) { for (auto ptr : *($$)) { delete ptr; } } delete ($$); } %destructor { delete ($$); } <*> /********************************* ** Token Definition *********************************/ %token IDENTIFIER STRING %token FLOATVAL %token INTVAL /* SQL Keywords */ %token DEALLOCATE PARAMETERS INTERSECT TEMPORARY TIMESTAMP %token DISTINCT NVARCHAR RESTRICT TRUNCATE ANALYZE BETWEEN %token CASCADE COLUMNS CONTROL DEFAULT EXECUTE EXPLAIN %token HISTORY INTEGER NATURAL PREPARE PRIMARY SCHEMAS %token SPATIAL VIRTUAL BEFORE COLUMN CREATE DELETE DIRECT %token DOUBLE ESCAPE EXCEPT EXISTS GLOBAL HAVING IMPORT %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 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 %token ARRAY CONCAT ILIKE /********************************* ** Non-Terminal types (http://www.gnu.org/software/bison/manual/html_node/Type-Decl.html) *********************************/ %type statement_list %type statement preparable_statement %type execute_statement %type prepare_statement %type select_statement select_with_paren select_no_paren select_clause select_paren_or_clause %type import_statement %type create_statement %type insert_statement %type delete_statement truncate_statement %type update_statement %type drop_statement %type show_statement %type table_name opt_alias alias file_path prepare_target_query %type opt_not_exists opt_distinct %type import_file_type opt_join_type column_type %type from_clause table_ref table_ref_atomic table_ref_name nonjoin_table_ref_atomic %type
join_clause table_ref_name_no_alias %type expr operand scalar_expr unary_expr binary_expr logic_expr exists_expr %type function_expr between_expr expr_alias param_expr %type column_name literal int_literal num_literal string_literal %type comp_expr opt_where join_condition opt_having case_expr in_expr hint %type array_expr array_index null_literal %type opt_limit opt_top %type order_desc %type opt_order_type %type column_def %type update_clause %type opt_group %type ident_commalist opt_column_list %type expr_list select_list literal_list hint_list opt_hints %type table_ref_commalist %type opt_order order_list %type update_clause_commalist %type column_def_commalist /****************************** ** Token Precedence and Associativity ** Precedence: lowest to highest ******************************/ %left OR %left AND %right NOT %nonassoc '=' EQUALS NOTEQUALS LIKE ILIKE %nonassoc '<' '>' LESS GREATER LESSEQ GREATEREQ %nonassoc NOTNULL %nonassoc ISNULL %nonassoc IS /* sets precedence for IS NULL, etc */ %left '+' '-' %left '*' '/' '%' %left '^' %left CONCAT /* Unary Operators */ %right UMINUS %left '[' ']' %left '(' ')' %left '.' %left JOIN %% /********************************* ** Section 3: Grammar Definition *********************************/ // Defines our general input. input: statement_list opt_semicolon { for (SQLStatement* stmt : *$1) { // Transfers ownership of the statement. result->addStatement(stmt); } unsigned param_id = 0; for (void* param : yyloc.param_list) { if (param != nullptr) { Expr* expr = (Expr*) param; expr->ival = param_id; result->addParameter(expr); ++param_id; } } delete $1; } ; statement_list: statement { $$ = new std::vector(); $$->push_back($1); } | statement_list ';' statement { $1->push_back($3); $$ = $1; } ; statement: prepare_statement opt_hints { $$ = $1; $$->hints = $2; } | preparable_statement opt_hints { $$ = $1; $$->hints = $2; } | show_statement { $$ = $1; } ; preparable_statement: select_statement { $$ = $1; } | import_statement { $$ = $1; } | create_statement { $$ = $1; } | insert_statement { $$ = $1; } | delete_statement { $$ = $1; } | truncate_statement { $$ = $1; } | update_statement { $$ = $1; } | drop_statement { $$ = $1; } | execute_statement { $$ = $1; } ; /****************************** * Hints ******************************/ opt_hints: WITH HINT '(' hint_list ')' { $$ = $4; } | /* empty */ { $$ = nullptr; } ; hint_list: hint { $$ = new std::vector(); $$->push_back($1); } | hint_list ',' hint { $1->push_back($3); $$ = $1; } ; hint: IDENTIFIER { $$ = Expr::make(kExprHint); $$->name = $1; } | IDENTIFIER '(' literal_list ')' { $$ = Expr::make(kExprHint); $$->name = $1; $$->exprList = $3; } ; /****************************** * Prepared Statement ******************************/ prepare_statement: PREPARE IDENTIFIER FROM prepare_target_query { $$ = new PrepareStatement(); $$->name = $2; $$->query = $4; } ; prepare_target_query: STRING execute_statement: EXECUTE IDENTIFIER { $$ = new ExecuteStatement(); $$->name = $2; } | EXECUTE IDENTIFIER '(' literal_list ')' { $$ = new ExecuteStatement(); $$->name = $2; $$->parameters = $4; } ; /****************************** * Import Statement ******************************/ import_statement: IMPORT FROM import_file_type FILE file_path INTO table_name { $$ = new ImportStatement((ImportType) $3); $$->filePath = $5; $$->tableName = $7; } ; import_file_type: CSV { $$ = kImportCSV; } ; file_path: string_literal { $$ = strdup($1->name); delete $1; } ; /****************************** * Show Statement * SHOW TABLES; ******************************/ show_statement: SHOW TABLES { $$ = new ShowStatement(kShowTables); } | SHOW COLUMNS table_name { $$ = new ShowStatement(kShowColumns); $$->name = $3; } ; /****************************** * Create Statement * CREATE TABLE students (name TEXT, student_number INTEGER, city TEXT, grade DOUBLE) * CREATE TABLE students FROM TBL FILE 'test/students.tbl' ******************************/ create_statement: CREATE TABLE opt_not_exists table_name FROM TBL FILE file_path { $$ = new CreateStatement(kCreateTableFromTbl); $$->ifNotExists = $3; $$->tableName = $4; $$->filePath = $8; } | CREATE TABLE opt_not_exists table_name '(' column_def_commalist ')' { $$ = new CreateStatement(kCreateTable); $$->ifNotExists = $3; $$->tableName = $4; $$->columns = $6; } | CREATE VIEW opt_not_exists table_name opt_column_list AS select_statement { $$ = new CreateStatement(kCreateView); $$->ifNotExists = $3; $$->tableName = $4; $$->viewColumns = $5; $$->select = $7; } ; opt_not_exists: IF NOT EXISTS { $$ = true; } | /* empty */ { $$ = false; } ; column_def_commalist: column_def { $$ = new std::vector(); $$->push_back($1); } | column_def_commalist ',' column_def { $1->push_back($3); $$ = $1; } ; column_def: IDENTIFIER column_type { $$ = new ColumnDefinition($1, (ColumnDefinition::DataType) $2); } ; column_type: INT { $$ = ColumnDefinition::INT; } | INTEGER { $$ = ColumnDefinition::INT; } | DOUBLE { $$ = ColumnDefinition::DOUBLE; } | TEXT { $$ = ColumnDefinition::TEXT; } ; /****************************** * Drop Statement * DROP TABLE students; * DEALLOCATE PREPARE stmt; ******************************/ drop_statement: DROP TABLE table_name { $$ = new DropStatement(kDropTable); $$->name = $3; } | DROP VIEW table_name { $$ = new DropStatement(kDropView); $$->name = $3; } | DEALLOCATE PREPARE IDENTIFIER { $$ = new DropStatement(kDropPreparedStatement); $$->name = $3; } ; /****************************** * Delete Statement / Truncate statement * DELETE FROM students WHERE grade > 3.0 * DELETE FROM students <=> TRUNCATE students ******************************/ delete_statement: DELETE FROM table_name opt_where { $$ = new DeleteStatement(); $$->tableName = $3; $$->expr = $4; } ; truncate_statement: TRUNCATE table_name { $$ = new DeleteStatement(); $$->tableName = $2; } ; /****************************** * Insert Statement * INSERT INTO students VALUES ('Max', 1112233, 'Musterhausen', 2.3) * INSERT INTO employees SELECT * FROM stundents ******************************/ insert_statement: INSERT INTO table_name opt_column_list VALUES '(' literal_list ')' { $$ = new InsertStatement(kInsertValues); $$->tableName = $3; $$->columns = $4; $$->values = $7; } | INSERT INTO table_name opt_column_list select_no_paren { $$ = new InsertStatement(kInsertSelect); $$->tableName = $3; $$->columns = $4; $$->select = $5; } ; opt_column_list: '(' ident_commalist ')' { $$ = $2; } | /* empty */ { $$ = nullptr; } ; /****************************** * Update Statement * UPDATE students SET grade = 1.3, name='Felix Fürstenberg' WHERE name = 'Max Mustermann'; ******************************/ update_statement: UPDATE table_ref_name_no_alias SET update_clause_commalist opt_where { $$ = new UpdateStatement(); $$->table = $2; $$->updates = $4; $$->where = $5; } ; update_clause_commalist: update_clause { $$ = new std::vector(); $$->push_back($1); } | update_clause_commalist ',' update_clause { $1->push_back($3); $$ = $1; } ; update_clause: IDENTIFIER '=' expr { $$ = new UpdateClause(); $$->column = $1; $$->value = $3; } ; /****************************** * Select Statement ******************************/ select_statement: select_with_paren | select_no_paren | 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; // Limit could have been set by TOP. if ($5 != nullptr) { delete $$->limit; $$->limit = $5; } } ; select_with_paren: '(' select_no_paren ')' { $$ = $2; } | '(' select_with_paren ')' { $$ = $2; } ; select_paren_or_clause: select_with_paren | select_clause ; select_no_paren: select_clause opt_order opt_limit { $$ = $1; $$->order = $2; // Limit could have been set by TOP. if ($3 != nullptr) { delete $$->limit; $$->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 $$ = $1; $$->unionSelect = $3; $$->order = $4; // Limit could have been set by TOP. if ($5 != nullptr) { delete $$->limit; $$->limit = $5; } } ; set_operator: set_type opt_all ; set_type: UNION | INTERSECT | EXCEPT ; opt_all: ALL | /* empty */ ; select_clause: SELECT opt_top opt_distinct select_list from_clause opt_where opt_group { $$ = new SelectStatement(); $$->limit = $2; $$->selectDistinct = $3; $$->selectList = $4; $$->fromTable = $5; $$->whereClause = $6; $$->groupBy = $7; } ; opt_distinct: DISTINCT { $$ = true; } | /* empty */ { $$ = false; } ; select_list: expr_list ; from_clause: FROM table_ref { $$ = $2; } ; opt_where: WHERE expr { $$ = $2; } | /* empty */ { $$ = nullptr; } ; opt_group: GROUP BY expr_list opt_having { $$ = new GroupByDescription(); $$->columns = $3; $$->having = $4; } | /* empty */ { $$ = nullptr; } ; opt_having: HAVING expr { $$ = $2; } | /* empty */ { $$ = nullptr; } opt_order: ORDER BY order_list { $$ = $3; } | /* empty */ { $$ = nullptr; } ; 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 */ { $$ = nullptr; } ; opt_limit: LIMIT int_literal { $$ = new LimitDescription($2->ival, kNoOffset); delete $2; } | LIMIT int_literal OFFSET int_literal { $$ = new LimitDescription($2->ival, $4->ival); delete $2; delete $4; } | /* empty */ { $$ = nullptr; } ; /****************************** * Expressions ******************************/ expr_list: expr_alias { $$ = new std::vector(); $$->push_back($1); } | expr_list ',' expr_alias { $1->push_back($3); $$ = $1; } ; literal_list: literal { $$ = new std::vector(); $$->push_back($1); } | literal_list ',' literal { $1->push_back($3); $$ = $1; } ; expr_alias: expr opt_alias { $$ = $1; $$->alias = $2; } ; expr: operand | between_expr | logic_expr | exists_expr | in_expr ; operand: '(' expr ')' { $$ = $2; } | array_index | scalar_expr | unary_expr | binary_expr | case_expr | function_expr | array_expr | '(' select_no_paren ')' { $$ = Expr::makeSelect($2); } ; scalar_expr: column_name | literal ; unary_expr: '-' operand { $$ = Expr::makeOpUnary(kOpUnaryMinus, $2); } | NOT operand { $$ = Expr::makeOpUnary(kOpNot, $2); } | operand ISNULL { $$ = Expr::makeOpUnary(kOpIsNull, $1); } | operand IS NULL { $$ = Expr::makeOpUnary(kOpIsNull, $1); } | operand IS NOT NULL { $$ = Expr::makeOpUnary(kOpNot, Expr::makeOpUnary(kOpIsNull, $1)); } ; binary_expr: comp_expr | operand '-' operand { $$ = Expr::makeOpBinary($1, kOpMinus, $3); } | operand '+' operand { $$ = Expr::makeOpBinary($1, kOpPlus, $3); } | operand '/' operand { $$ = Expr::makeOpBinary($1, kOpSlash, $3); } | operand '*' operand { $$ = Expr::makeOpBinary($1, kOpAsterisk, $3); } | operand '%' operand { $$ = Expr::makeOpBinary($1, kOpPercentage, $3); } | operand '^' operand { $$ = Expr::makeOpBinary($1, kOpCaret, $3); } | operand LIKE operand { $$ = Expr::makeOpBinary($1, kOpLike, $3); } | operand NOT LIKE operand { $$ = Expr::makeOpBinary($1, kOpNotLike, $4); } | operand ILIKE operand { $$ = Expr::makeOpBinary($1, kOpILike, $3); } | operand CONCAT operand { $$ = Expr::makeOpBinary($1, kOpConcat, $3); } ; logic_expr: expr AND expr { $$ = Expr::makeOpBinary($1, kOpAnd, $3); } | expr OR expr { $$ = Expr::makeOpBinary($1, kOpOr, $3); } ; in_expr: operand IN '(' expr_list ')' { $$ = Expr::makeInOperator($1, $4); } | operand NOT IN '(' expr_list ')' { $$ = Expr::makeOpUnary(kOpNot, Expr::makeInOperator($1, $5)); } | operand IN '(' select_no_paren ')' { $$ = Expr::makeInOperator($1, $4); } | operand NOT IN '(' select_no_paren ')' { $$ = Expr::makeOpUnary(kOpNot, Expr::makeInOperator($1, $5)); } ; // TODO: allow no else specified case_expr: CASE WHEN expr THEN operand END { $$ = Expr::makeCase($3, $5); } | CASE WHEN expr THEN operand ELSE operand END { $$ = Expr::makeCase($3, $5, $7); } ; exists_expr: EXISTS '(' select_no_paren ')' { $$ = Expr::makeExists($3); } | NOT EXISTS '(' select_no_paren ')' { $$ = Expr::makeOpUnary(kOpNot, Expr::makeExists($4)); } ; comp_expr: operand '=' operand { $$ = Expr::makeOpBinary($1, kOpEquals, $3); } | operand NOTEQUALS operand { $$ = Expr::makeOpBinary($1, kOpNotEquals, $3); } | operand '<' operand { $$ = Expr::makeOpBinary($1, kOpLess, $3); } | operand '>' operand { $$ = Expr::makeOpBinary($1, kOpGreater, $3); } | operand LESSEQ operand { $$ = Expr::makeOpBinary($1, kOpLessEq, $3); } | operand GREATEREQ operand { $$ = Expr::makeOpBinary($1, kOpGreaterEq, $3); } ; function_expr: IDENTIFIER '(' ')' { $$ = Expr::makeFunctionRef($1, new std::vector(), false); } | IDENTIFIER '(' opt_distinct expr_list ')' { $$ = Expr::makeFunctionRef($1, $4, $3); } ; array_expr: ARRAY '[' expr_list ']' { $$ = Expr::makeArray($3); } ; array_index: operand '[' int_literal ']' { $$ = Expr::makeArrayIndex($1, $3->ival); } ; between_expr: operand BETWEEN operand AND operand { $$ = Expr::makeBetween($1, $3, $5); } ; column_name: IDENTIFIER { $$ = Expr::makeColumnRef($1); } | IDENTIFIER '.' IDENTIFIER { $$ = Expr::makeColumnRef($1, $3); } | '*' { $$ = Expr::makeStar(); } | IDENTIFIER '.' '*' { $$ = Expr::makeStar($1); } ; literal: string_literal | num_literal | null_literal | param_expr ; string_literal: STRING { $$ = Expr::makeLiteral($1); } ; num_literal: FLOATVAL { $$ = Expr::makeLiteral($1); } | int_literal ; int_literal: INTVAL { $$ = Expr::makeLiteral($1); } ; null_literal: NULL { $$ = Expr::makeNullLiteral(); } ; param_expr: '?' { $$ = Expr::makeParameter(yylloc.total_column); $$->ival2 = yyloc.param_list.size(); yyloc.param_list.push_back($$); } ; /****************************** * Table ******************************/ table_ref: table_ref_atomic | table_ref_atomic ',' table_ref_commalist { $3->push_back($1); auto tbl = new TableRef(kTableCrossProduct); tbl->list = $3; $$ = tbl; } ; table_ref_atomic: nonjoin_table_ref_atomic | join_clause ; nonjoin_table_ref_atomic: table_ref_name | '(' select_statement ')' opt_alias { auto tbl = new TableRef(kTableSelect); tbl->select = $2; tbl->alias = $4; $$ = tbl; } ; table_ref_commalist: table_ref_atomic { $$ = new std::vector(); $$->push_back($1); } | table_ref_commalist ',' table_ref_atomic { $1->push_back($3); $$ = $1; } ; table_ref_name: table_name opt_alias { auto tbl = new TableRef(kTableName); tbl->name = $1; tbl->alias = $2; $$ = tbl; } ; table_ref_name_no_alias: table_name { $$ = new TableRef(kTableName); $$->name = $1; } ; table_name: IDENTIFIER | IDENTIFIER '.' IDENTIFIER { $$ = $3; } ; alias: AS IDENTIFIER { $$ = $2; } | IDENTIFIER ; opt_alias: alias | /* empty */ { $$ = nullptr; } /****************************** * Join Statements ******************************/ join_clause: table_ref_atomic NATURAL JOIN nonjoin_table_ref_atomic { $$ = new TableRef(kTableJoin); $$->join = new JoinDefinition(); $$->join->type = kJoinNatural; $$->join->left = $1; $$->join->right = $4; } | table_ref_atomic opt_join_type JOIN table_ref_atomic ON join_condition { $$ = new TableRef(kTableJoin); $$->join = new JoinDefinition(); $$->join->type = (JoinType) $2; $$->join->left = $1; $$->join->right = $4; $$->join->condition = $6; } | table_ref_atomic opt_join_type JOIN table_ref_atomic USING '(' column_name ')' { $$ = new TableRef(kTableJoin); $$->join = new JoinDefinition(); $$->join->type = (JoinType) $2; $$->join->left = $1; $$->join->right = $4; auto left_col = Expr::makeColumnRef(strdup($7->name)); if ($7->alias != nullptr) left_col->alias = strdup($7->alias); if ($1->getName() != nullptr) left_col->table = strdup($1->getName()); auto right_col = Expr::makeColumnRef(strdup($7->name)); if ($7->alias != nullptr) right_col->alias = strdup($7->alias); if ($4->getName() != nullptr) right_col->table = strdup($4->getName()); $$->join->condition = Expr::makeOpBinary(left_col, kOpEquals, right_col); delete $7; } ; opt_join_type: INNER { $$ = kJoinInner; } | OUTER { $$ = kJoinOuter; } | LEFT OUTER { $$ = kJoinLeftOuter; } | RIGHT OUTER { $$ = kJoinRightOuter; } | LEFT { $$ = kJoinLeft; } | RIGHT { $$ = kJoinRight; } | CROSS { $$ = kJoinCross; } | /* empty, default */ { $$ = kJoinInner; } ; join_condition: expr ; /****************************** * Misc ******************************/ opt_semicolon: ';' | /* empty */ ; ident_commalist: IDENTIFIER { $$ = new std::vector(); $$->push_back($1); } | ident_commalist ',' IDENTIFIER { $1->push_back($3); $$ = $1; } ; %% /********************************* ** Section 4: Additional C code *********************************/ /* empty */