diff --git a/src/lib/statements/SQLStatement.h b/src/lib/statements/SQLStatement.h index 5828e55..bafbeb6 100644 --- a/src/lib/statements/SQLStatement.h +++ b/src/lib/statements/SQLStatement.h @@ -57,6 +57,8 @@ public: bool isValid; const char* parser_msg; + int error_line; + int error_col; }; diff --git a/src/parser/bison_parser.y b/src/parser/bison_parser.y index 925ac75..a4f7559 100644 --- a/src/parser/bison_parser.y +++ b/src/parser/bison_parser.y @@ -19,13 +19,15 @@ using namespace hsql; -int yyerror(SQLStatementList** result, yyscan_t scanner, const char *msg) { +int yyerror(YYLTYPE* llocp, SQLStatementList** result, yyscan_t scanner, const char *msg) { SQLStatementList* list = new SQLStatementList(); list->isValid = false; list->parser_msg = strdup(msg); + list->error_line = llocp->first_line; + list->error_col = llocp->first_column; + *result = list; - return 0; } @@ -47,6 +49,8 @@ int yyerror(SQLStatementList** result, yyscan_t scanner, const char *msg) { %define api.token.prefix {SQL_} %define parse.error verbose +%locations + // Specify code that is included in the generated .h and .c files %code requires { @@ -57,6 +61,21 @@ typedef void* yyscan_t; #endif #define YYSTYPE HSQL_STYPE +#define YYLTYPE HSQL_LTYPE + + +#define YY_USER_ACTION \ + yylloc->first_line = yylloc->last_line; \ + yylloc->first_column = yylloc->last_column; \ + for(int i = 0; yytext[i] != '\0'; i++) { \ + if(yytext[i] == '\n') { \ + yylloc->last_line++; \ + yylloc->last_column = 0; \ + } \ + else { \ + yylloc->last_column++; \ + } \ + } } @@ -114,26 +133,27 @@ typedef void* yyscan_t; %token NOTEQUALS LESSEQ GREATEREQ /* SQL Keywords */ -%token PARAMETERS INTERSECT TEMPORARY TIMESTAMP DISTINCT -%token NVARCHAR RESTRICT TRUNCATE ANALYZE BETWEEN CASCADE -%token COLUMNS CONTROL DEFAULT EXPLAIN HISTORY INTEGER -%token NATURAL PRIMARY SCHEMAS SPATIAL VIRTUAL BEFORE COLUMN -%token CREATE DELETE DIRECT DOUBLE ESCAPE EXCEPT EXISTS -%token GLOBAL HAVING IMPORT INSERT ISNULL OFFSET RENAME -%token SCHEMA SELECT SORTED TABLES UNIQUE UNLOAD UPDATE -%token VALUES AFTER ALTER CROSS DELTA GROUP INDEX INNER -%token LIMIT LOCAL MERGE MINUS ORDER OUTER RIGHT TABLE UNION -%token USING WHERE CALL DATE DESC DROP FILE FROM FULL HASH -%token INTO JOIN LEFT LIKE LOAD NULL PART PLAN SHOW TEXT -%token TIME VIEW WITH ADD ALL AND ASC CSV FOR INT KEY NOT OFF -%token SET TBL TOP AS BY IF IN IS OF ON OR TO +%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 DATE DESC +%token DROP FILE FROM FULL HASH HINT INTO JOIN LEFT LIKE +%token LOAD NULL PART PLAN SHOW TEXT TIME VIEW WITH ADD ALL +%token AND ASC CSV FOR INT KEY NOT OFF SET TBL TOP AS BY IF +%token IN IS OF ON OR TO /********************************* ** Non-Terminal types (http://www.gnu.org/software/bison/manual/html_node/Type-Decl.html) *********************************/ %type statement_list -%type statement +%type statement preparable_statement prepare_statement %type select_statement select_with_paren select_no_paren select_clause %type import_statement %type create_statement @@ -200,6 +220,12 @@ statement_list: ; statement: + preparable_statement + | prepare_statement + ; + + +preparable_statement: select_statement { $$ = $1; } | import_statement { $$ = $1; } | create_statement { $$ = $1; } @@ -211,6 +237,14 @@ statement: ; +/****************************** + * Prepared Statement + ******************************/ +prepare_statement: + PREPARE IDENTIFIER ':' preparable_statement { $$ = NULL; } + ; + + /****************************** * Import Statement diff --git a/src/parser/flex_lexer.l b/src/parser/flex_lexer.l index 2193268..b3f657e 100644 --- a/src/parser/flex_lexer.l +++ b/src/parser/flex_lexer.l @@ -39,6 +39,7 @@ %option warn %option case-insensitive %option prefix="hsql_" +%option bison-locations /* %option nodefault */ @@ -58,6 +59,7 @@ +DEALLOCATE TOKEN(DEALLOCATE) PARAMETERS TOKEN(PARAMETERS) INTERSECT TOKEN(INTERSECT) TEMPORARY TOKEN(TEMPORARY) @@ -72,10 +74,12 @@ CASCADE TOKEN(CASCADE) COLUMNS TOKEN(COLUMNS) CONTROL TOKEN(CONTROL) DEFAULT TOKEN(DEFAULT) +EXECUTE TOKEN(EXECUTE) EXPLAIN TOKEN(EXPLAIN) HISTORY TOKEN(HISTORY) INTEGER TOKEN(INTEGER) NATURAL TOKEN(NATURAL) +PREPARE TOKEN(PREPARE) PRIMARY TOKEN(PRIMARY) SCHEMAS TOKEN(SCHEMAS) SPATIAL TOKEN(SPATIAL) @@ -130,6 +134,7 @@ FILE TOKEN(FILE) FROM TOKEN(FROM) FULL TOKEN(FULL) HASH TOKEN(HASH) +HINT TOKEN(HINT) INTO TOKEN(INTO) JOIN TOKEN(JOIN) LEFT TOKEN(LEFT) @@ -172,7 +177,7 @@ TO TOKEN(TO) ">=" TOKEN(GREATEREQ) -[-+*/(),.;<>=^%] { return yytext[0]; } +[-+*/(),.;<>=^%:?] { return yytext[0]; } [0-9]+"."[0-9]* | @@ -204,7 +209,7 @@ TO TOKEN(TO) return SQL_STRING; } - +. { fprintf(stderr, "[SQL-Lexer-Error] Unknown Character: %c\n", yytext[0]); return 0; } %% @@ -213,5 +218,5 @@ TO TOKEN(TO) ***************************/ int yyerror(const char *msg) { - fprintf(stderr, "[Error] SQL Lexer: %s\n",msg); return 0; + fprintf(stderr, "[SQL-Lexer-Error] %s\n",msg); return 0; } \ No newline at end of file diff --git a/src/parser/sql_keywords.txt b/src/parser/sql_keywords.txt index c38766f..2209fcf 100644 --- a/src/parser/sql_keywords.txt +++ b/src/parser/sql_keywords.txt @@ -101,6 +101,11 @@ UNLOAD DELETE +// Prepared Statements +DEALLOCATE +PREPARE +EXECUTE + /////////////////////////////// // other statements RENAME @@ -138,6 +143,7 @@ ESCAPE // With WITH +HINT PARAMETERS ON OFF diff --git a/src/sql_grammar_test.cpp b/src/sql_grammar_test.cpp index bdb901d..bfc4b24 100644 --- a/src/sql_grammar_test.cpp +++ b/src/sql_grammar_test.cpp @@ -76,7 +76,7 @@ int main(int argc, char *argv[]) { if (expect_false == stmt_list->isValid) { printf("\033[0;31m{ failed}\033[0m\n"); - printf("\t\033[0;31m%s\n\033[0m", stmt_list->parser_msg); + printf("\t\033[0;31m%s (L%d:%d)\n\033[0m", stmt_list->parser_msg, stmt_list->error_line, stmt_list->error_col); printf("\t%s\n", sql.c_str()); num_failed++; } else { diff --git a/test/valid_queries.sql b/test/valid_queries.sql index c3e5eb0..f5a1b96 100644 --- a/test/valid_queries.sql +++ b/test/valid_queries.sql @@ -29,4 +29,7 @@ UPDATE students SET grade = 1.3 WHERE name = 'Max Mustermann'; UPDATE students SET grade = 1.3, name='Felix Fürstenberg' WHERE name = 'Max Mustermann'; UPDATE students SET grade = 1.0; # DROP -DROP TABLE students; \ No newline at end of file +DROP TABLE students; +# PREPARE +PREPARE prep_inst: INSERT INTO test VALUES (?, ?, ?); SELECT * FROM test; +EXECUTE prep_inst(1, 2, 3); \ No newline at end of file