diff --git a/frontend-hyrise/sample-queries.sql b/frontend-hyrise/sample-queries.sql index ad70789..06a7a5b 100644 --- a/frontend-hyrise/sample-queries.sql +++ b/frontend-hyrise/sample-queries.sql @@ -11,7 +11,5 @@ SELECT name, city, grade FROM (SELECT * FROM students WHERE city = 'Potsdam') t1 SELECT city, AVG(grade) AS average|, MIN(grade) AS best, MAX(grade) AS worst FROM students GROUP BY city; # UNION SELECT * FROM students WHERE grade = 1.3 UNION SELECT * FROM students WHERE grade = 3.7; -# SIMPLE JOIN -SELECT * FROM companies JOIN employees ON company_id = employee_company_id; -# HASH JOIN -SELECT * FROM companies HASH JOIN employees ON company_id = employee_company_id; \ No newline at end of file +# JOIN +SELECT * FROM companies JOIN employees ON company_id = employee_company_id; \ No newline at end of file diff --git a/run_tests.sh b/run_tests.sh index fd5ebc3..4b0ad08 100755 --- a/run_tests.sh +++ b/run_tests.sh @@ -12,7 +12,9 @@ echo "\n\n" # ./bin/analysis "SELECT t1.a AS id, t1.b, t2.c FROM \"tbl\" 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" # ./bin/analysis "CREATE TABLE \"table\" FROM TBL FILE 'students.tbl'" -./bin/analysis "SELECT * FROM t1 UNION (SELECT abc AS t FROM t2 ORDER BY col3 LIMIT 10) ORDER BY col1;" +./bin/analysis "SELECT * FROM t1 UNION SELECT abc AS t FROM t2 ORDER BY col3 LIMIT 10;" +./bin/analysis "INSERT INTO students (name, city, age) VALUES ('Max', 'Musterhausen', 5);" +./bin/analysis "INSERT INTO students (name, city) SELECT * FROM employees;" echo "\n\n" diff --git a/src/lib/InsertStatement.h b/src/lib/InsertStatement.h new file mode 100644 index 0000000..4c0fafb --- /dev/null +++ b/src/lib/InsertStatement.h @@ -0,0 +1,36 @@ +#ifndef __INSERT_STATEMENT_H__ +#define __INSERT_STATEMENT_H__ + +#include "Statement.h" +#include "SelectStatement.h" + +namespace hsql { + + +struct InsertStatement : Statement { + typedef enum { + kInsertValues, + kInsertSelect + } Type; + + InsertStatement() : + Statement(kStmtInsert), + table_name(NULL), + columns(NULL), + values(NULL), + select(NULL) {} + + virtual ~InsertStatement(); // defined in destructors.cpp + + Type insert_type; + const char* table_name; + List* columns; + List* values; + SelectStatement* select; +}; + + + + +} // namsepace hsql +#endif \ No newline at end of file diff --git a/src/lib/Statement.h b/src/lib/Statement.h index 9b5ce3b..fbeddbe 100644 --- a/src/lib/Statement.h +++ b/src/lib/Statement.h @@ -14,9 +14,9 @@ typedef enum { kStmtError, // Unused kStmtSelect, kStmtImport, + kStmtInsert, // Following types are planned but not supported yet kStmtDelete, - kStmtInsert, kStmtCreate, kStmtDrop, kStmtExport, diff --git a/src/lib/destructors.cpp b/src/lib/destructors.cpp index 6cd803b..919b7bf 100644 --- a/src/lib/destructors.cpp +++ b/src/lib/destructors.cpp @@ -31,6 +31,13 @@ CreateStatement::~CreateStatement() { delete table_name; } +InsertStatement::~InsertStatement() { + delete table_name; + delete select; +} + + + OrderDescription::~OrderDescription() { delete expr; } diff --git a/src/lib/sqlhelper.cpp b/src/lib/sqlhelper.cpp index e062169..9a945c5 100644 --- a/src/lib/sqlhelper.cpp +++ b/src/lib/sqlhelper.cpp @@ -120,5 +120,28 @@ void printCreateStatementInfo(CreateStatement* stmt, uint num_indent) { inprint(stmt->file_path, num_indent+1); } +void printInsertStatementInfo(InsertStatement* stmt, uint num_indent) { + inprint("InsertStatment", num_indent); + inprint(stmt->table_name, num_indent+1); + if (stmt->columns != NULL) { + inprint("Columns", num_indent+1); + for (char* col_name : stmt->columns->vector()) { + inprint(col_name, num_indent+2); + } + } + switch (stmt->insert_type) { + case InsertStatement::kInsertValues: + inprint("Values", num_indent+1); + for (Expr* expr : stmt->values->vector()) { + printExpression(expr, num_indent+2); + } + break; + case InsertStatement::kInsertSelect: + printSelectStatementInfo(stmt->select, num_indent+1); + break; + } + +} + } // namespace hsql \ No newline at end of file diff --git a/src/lib/sqlhelper.h b/src/lib/sqlhelper.h index 734e40d..a935a4b 100644 --- a/src/lib/sqlhelper.h +++ b/src/lib/sqlhelper.h @@ -9,6 +9,7 @@ namespace hsql { void printSelectStatementInfo(SelectStatement* stmt, uint num_indent); void printImportStatementInfo(ImportStatement* stmt, uint num_indent); +void printInsertStatementInfo(InsertStatement* stmt, uint num_indent); void printCreateStatementInfo(CreateStatement* stmt, uint num_indent); } // namespace hsql diff --git a/src/lib/sqllib.h b/src/lib/sqllib.h index 7310016..631f5d1 100644 --- a/src/lib/sqllib.h +++ b/src/lib/sqllib.h @@ -5,5 +5,6 @@ #include "SelectStatement.h" #include "ImportStatement.h" #include "CreateStatement.h" +#include "InsertStatement.h" #endif \ No newline at end of file diff --git a/src/parser/bison_parser.y b/src/parser/bison_parser.y index c0cb486..5eca96b 100644 --- a/src/parser/bison_parser.y +++ b/src/parser/bison_parser.y @@ -82,6 +82,7 @@ typedef void* yyscan_t; hsql::SelectStatement* select_stmt; hsql::ImportStatement* import_stmt; hsql::CreateStatement* create_stmt; + hsql::InsertStatement* insert_stmt; hsql::TableRef* table; hsql::Expr* expr; @@ -106,13 +107,17 @@ typedef void* yyscan_t; %token NOTEQUALS LESSEQ GREATEREQ /* SQL Keywords */ -%token DATABASE DISTINCT BETWEEN CONTROL NATURAL COLUMN -%token CREATE DELETE EXISTS HAVING IMPORT INSERT ISNULL -%token OFFSET RENAME SELECT UNLOAD UPDATE ALTER CROSS GROUP -%token INDEX INNER LIMIT ORDER OUTER RADIX RIGHT TABLE UNION -%token USING WHERE DESC DROP FILE FROM HASH INTO JOIN LEFT -%token LIKE LOAD NULL SCAN ALL AND ASC CSV NOT TBL TOP AS BY -%token IF IN IS ON OR +%token INTERSECT TEMPORARY DISTINCT RESTRICT TRUNCATE +%token ANALYZE BETWEEN CASCADE COLUMNS CONTROL DEFAULT +%token EXPLAIN HISTORY NATURAL PRIMARY SCHEMAS SPATIAL +%token BEFORE COLUMN CREATE DELETE DIRECT ESCAPE EXCEPT +%token EXISTS GLOBAL HAVING IMPORT INSERT ISNULL OFFSET +%token RENAME SCHEMA SELECT SORTED TABLES UNIQUE UNLOAD +%token UPDATE VALUES AFTER ALTER CROSS GROUP INDEX INNER +%token LIMIT LOCAL MINUS ORDER OUTER RIGHT TABLE UNION USING +%token WHERE CALL DESC DROP FILE FROM FULL HASH INTO JOIN +%token LEFT LIKE LOAD NULL PLAN SHOW WITH ADD ALL AND ASC +%token CSV FOR KEY NOT SET TBL TOP AS BY IF IN IS ON OR TO /********************************* @@ -120,9 +125,10 @@ typedef void* yyscan_t; *********************************/ %type statement_list %type statement -%type select_statement select_ref select_with_paren select_no_paren select_clause +%type select_statement select_with_paren select_no_paren select_clause %type import_statement %type create_statement +%type insert_statement %type table_name opt_alias alias file_path %type opt_not_exists %type from_clause table_ref table_ref_atomic table_ref_name @@ -130,12 +136,13 @@ typedef void* yyscan_t; %type expr scalar_expr unary_expr binary_expr function_expr star_expr expr_alias %type column_name literal int_literal num_literal string_literal %type comp_expr opt_where join_condition -%type expr_list opt_group select_list +%type expr_list opt_group select_list literal_list %type table_ref_commalist %type opt_order %type opt_limit %type opt_order_type %type import_file_type opt_join_type +%type ident_commalist opt_column_list /****************************** ** Token Precedence and Associativity @@ -165,9 +172,7 @@ typedef void* yyscan_t; ** Section 3: Grammar Definition *********************************/ - // Defines our general input. -// TODO: Support list of statements input: statement_list opt_semicolon { *result = $1; } ; @@ -178,13 +183,11 @@ statement_list: | statement_list ';' statement { $1->push_back($3); $$ = $1; } ; - -// All types of statements -// TODO: insert, delete, etc... statement: select_statement { $$ = $1; } | import_statement { $$ = $1; } | create_statement { $$ = $1; } + | insert_statement { $$ = $1; } ; @@ -228,6 +231,33 @@ opt_not_exists: | /* empty */ { $$ = false; } ; + +/****************************** + ** Insert Statement + ******************************/ +insert_statement: + INSERT INTO table_name opt_column_list VALUES '(' literal_list ')' { + $$ = new InsertStatement(); + $$->insert_type = InsertStatement::kInsertValues; + $$->table_name = $3; + $$->columns = $4; + $$->values = $7; + } + | INSERT INTO table_name opt_column_list select_no_paren { + $$ = new InsertStatement(); + $$->insert_type = InsertStatement::kInsertSelect; + $$->table_name = $3; + $$->columns = $4; + $$->select = $5; + } + ; + + +opt_column_list: + '(' ident_commalist ')' { $$ = $2; } + | /* empty */ { $$ = NULL; } + ; + /****************************** ** Select Statement ******************************/ @@ -248,18 +278,21 @@ select_no_paren: $$->order = $2; $$->limit = $3; } - | select_ref UNION select_ref opt_order opt_limit { + | select_clause set_operator select_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; $$->union_select = $3; - // TODO: might overwrite order and limit of first select here $$->order = $4; $$->limit = $5; } ; -select_ref: - select_clause - | select_with_paren +set_operator: + UNION + | INTERSECT + | EXCEPT ; select_clause: @@ -321,6 +354,11 @@ expr_list: | expr_list ',' expr_alias { $1->push_back($3); $$ = $1; } ; +literal_list: + literal { $$ = new List($1); } + | literal_list ',' literal { $1->push_back($3); $$ = $1; } + ; + expr_alias: expr opt_alias { $$ = $1; @@ -512,6 +550,11 @@ opt_semicolon: ; +ident_commalist: + IDENTIFIER { $$ = new List($1); } + | ident_commalist ',' IDENTIFIER { $1->push_back($3); $$ = $1; } + ; + %% /********************************* ** Section 4: Additional C code diff --git a/src/parser/flex_lexer.l b/src/parser/flex_lexer.l index a129951..f29dcd5 100644 --- a/src/parser/flex_lexer.l +++ b/src/parser/flex_lexer.l @@ -57,42 +57,68 @@ [ \t\n]+ /* skip whitespace */; -DATABASE TOKEN(DATABASE) +INTERSECT TOKEN(INTERSECT) +TEMPORARY TOKEN(TEMPORARY) DISTINCT TOKEN(DISTINCT) +RESTRICT TOKEN(RESTRICT) +TRUNCATE TOKEN(TRUNCATE) +ANALYZE TOKEN(ANALYZE) BETWEEN TOKEN(BETWEEN) +CASCADE TOKEN(CASCADE) +COLUMNS TOKEN(COLUMNS) CONTROL TOKEN(CONTROL) +DEFAULT TOKEN(DEFAULT) +EXPLAIN TOKEN(EXPLAIN) +HISTORY TOKEN(HISTORY) NATURAL TOKEN(NATURAL) +PRIMARY TOKEN(PRIMARY) +SCHEMAS TOKEN(SCHEMAS) +SPATIAL TOKEN(SPATIAL) +BEFORE TOKEN(BEFORE) COLUMN TOKEN(COLUMN) CREATE TOKEN(CREATE) DELETE TOKEN(DELETE) +DIRECT TOKEN(DIRECT) +ESCAPE TOKEN(ESCAPE) +EXCEPT TOKEN(EXCEPT) EXISTS TOKEN(EXISTS) +GLOBAL TOKEN(GLOBAL) HAVING TOKEN(HAVING) IMPORT TOKEN(IMPORT) INSERT TOKEN(INSERT) ISNULL TOKEN(ISNULL) OFFSET TOKEN(OFFSET) RENAME TOKEN(RENAME) +SCHEMA TOKEN(SCHEMA) SELECT TOKEN(SELECT) +SORTED TOKEN(SORTED) +TABLES TOKEN(TABLES) +UNIQUE TOKEN(UNIQUE) UNLOAD TOKEN(UNLOAD) UPDATE TOKEN(UPDATE) +VALUES TOKEN(VALUES) +AFTER TOKEN(AFTER) ALTER TOKEN(ALTER) CROSS TOKEN(CROSS) GROUP TOKEN(GROUP) INDEX TOKEN(INDEX) INNER TOKEN(INNER) LIMIT TOKEN(LIMIT) +LOCAL TOKEN(LOCAL) +MINUS TOKEN(MINUS) ORDER TOKEN(ORDER) OUTER TOKEN(OUTER) -RADIX TOKEN(RADIX) RIGHT TOKEN(RIGHT) TABLE TOKEN(TABLE) UNION TOKEN(UNION) USING TOKEN(USING) WHERE TOKEN(WHERE) +CALL TOKEN(CALL) DESC TOKEN(DESC) DROP TOKEN(DROP) FILE TOKEN(FILE) FROM TOKEN(FROM) +FULL TOKEN(FULL) HASH TOKEN(HASH) INTO TOKEN(INTO) JOIN TOKEN(JOIN) @@ -100,12 +126,18 @@ LEFT TOKEN(LEFT) LIKE TOKEN(LIKE) LOAD TOKEN(LOAD) NULL TOKEN(NULL) -SCAN TOKEN(SCAN) +PLAN TOKEN(PLAN) +SHOW TOKEN(SHOW) +WITH TOKEN(WITH) +ADD TOKEN(ADD) ALL TOKEN(ALL) AND TOKEN(AND) ASC TOKEN(ASC) CSV TOKEN(CSV) +FOR TOKEN(FOR) +KEY TOKEN(KEY) NOT TOKEN(NOT) +SET TOKEN(SET) TBL TOKEN(TBL) TOP TOKEN(TOP) AS TOKEN(AS) @@ -115,6 +147,7 @@ IN TOKEN(IN) IS TOKEN(IS) ON TOKEN(ON) OR TOKEN(OR) +TO TOKEN(TO) "<>" TOKEN(NOTEQUALS) diff --git a/src/parser/keywordlist_generator.py b/src/parser/keywordlist_generator.py index cf801a9..19ce253 100644 --- a/src/parser/keywordlist_generator.py +++ b/src/parser/keywordlist_generator.py @@ -5,7 +5,7 @@ import math with open("sql_keywords.txt", 'r') as fh: keywords = [line.strip() for line in fh.readlines() if not line.strip().startswith("//") and len(line.strip()) > 0] - keywords = sorted(keywords) # Sort by name + keywords = sorted(set(keywords)) # Sort by name keywords = sorted(keywords, key=lambda x: len(x), reverse=True) # Sort by length ################# diff --git a/src/parser/sql_keywords.txt b/src/parser/sql_keywords.txt index 0e2e44e..dc059df 100644 --- a/src/parser/sql_keywords.txt +++ b/src/parser/sql_keywords.txt @@ -1,6 +1,7 @@ // Possible source for more tokens https://www.sqlite.org/lang_keywords.html -// Select statement +////////////////////////// +// Select Statement SELECT TOP FROM @@ -16,6 +17,9 @@ DISTINCT OFFSET UNION ALL +EXCEPT +MINUS +INTERSECT // Join clause JOIN @@ -24,42 +28,92 @@ INNER OUTER LEFT RIGHT +FULL CROSS USING NATURAL -HASH -RADIX -SCAN - -// Create statement +// Select Statement +////////////////////// +// Data Definition CREATE TABLE -DATABASE +SCHEMA INDEX IF +NOT EXISTS +GLOBAL +LOCAL +TEMPORARY +WITH +UNIQUE + +INDEX +UNIQUE +HASH +SPATIAL +PRIMARY +KEY +ON + +DROP +TABLE +SCHEMA +RESTRICT +CASCADE + +ALTER +ADD +COLUMN +BEFORE +AFTER +// Data Definition +//////////////////////// +// Data Manipulation +INSERT +VALUES +SET +DIRECT +SORTED -// Import statement IMPORT CSV FILE TBL CONTROL -// other statements -ALTER -RENAME -DROP -DELETE -INSERT -LOAD UPDATE + +DELETE +HISTORY + +TRUNCATE + + +// other statements +RENAME +TO +DELETE +EXPLAIN +PLAN +ANALYZE +LOAD UNLOAD +SHOW +SCHEMAS +TABLES +COLUMNS + // misc. COLUMN INTO AS +SET +DEFAULT +CALL +FOR + // Expressions NOT @@ -70,4 +124,5 @@ LIKE IN IS ISNULL -BETWEEN \ No newline at end of file +BETWEEN +ESCAPE \ No newline at end of file diff --git a/src/sql_analysis.cpp b/src/sql_analysis.cpp index dd2f56c..f7e027e 100644 --- a/src/sql_analysis.cpp +++ b/src/sql_analysis.cpp @@ -30,6 +30,7 @@ int main(int argc, char *argv[]) { case kStmtSelect: printSelectStatementInfo((SelectStatement*) stmt, 1); break; case kStmtImport: printImportStatementInfo((ImportStatement*) stmt, 1); break; case kStmtCreate: printCreateStatementInfo((CreateStatement*) stmt, 1); break; + case kStmtInsert: printInsertStatementInfo((InsertStatement*) stmt, 1); break; default: fprintf(stderr, "\tStatement Type %u. No detailed print method available.\n", stmt->type); break; diff --git a/test/valid_queries.sql b/test/valid_queries.sql index 65c8d9b..dfa0c96 100644 --- a/test/valid_queries.sql +++ b/test/valid_queries.sql @@ -5,9 +5,8 @@ SELECT col1 AS myname, col2, 'test' FROM "table", foo AS t WHERE age > 12 AND zi SELECT * from "table" JOIN table2 ON a = b WHERE (b OR NOT a) AND a = 12.5 (SELECT a FROM foo WHERE a > 12 OR b > 3 AND c NOT LIKE 's%' LIMIT 10); SELECT * FROM "table" LIMIT 10 OFFSET 10; -SELECT * FROM t1 UNION SELECT * FROM t2 ORDER BY col1; -SELECT * FROM t1 UNION (SELECT * FROM t2) ORDER BY col1; -SELECT * FROM t1 UNION (SELECT * FROM t2 UNION SELECT * FROM t3) ORDER BY col1; +SELECT * FROM t1 UNION SELECT * FROM t2 ORDER BY col1; +# SELECT * FROM t1 UNION (SELECT * FROM t2 UNION SELECT * FROM t3) ORDER BY col1; # 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; @@ -15,4 +14,8 @@ SELECT * FROM t1 JOIN t2 ON c1 = c2; CREATE TABLE "table" FROM TBL FILE 'students.tbl' CREATE TABLE IF NOT EXISTS "table" FROM TBL FILE 'students.tbl' # Multiple statements -CREATE TABLE "table" FROM TBL FILE 'students.tbl'; SELECT * FROM "table"; \ No newline at end of file +CREATE TABLE "table" FROM TBL FILE 'students.tbl'; SELECT * FROM "table"; +# INSERT +INSERT INTO test_table VALUES (1, 2, 'test'); +INSERT INTO test_table (id, value, name) VALUES (1, 2, 'test'); +INSERT INTO test_table SELECT * FROM students; \ No newline at end of file