From 1483a4a95ac0c5f7cdc4e1baa409db1a951b9c5d Mon Sep 17 00:00:00 2001 From: Pedro Flemming Date: Tue, 6 Jun 2017 03:49:41 +0200 Subject: [PATCH] Add Hints per statement to SQL syntax. --- src/SQLParser.cpp | 2 +- src/SQLParserResult.cpp | 8 +-- src/parser/bison_parser.y | 71 +++++++++++++++------ src/sql/CreateStatement.h | 6 +- src/sql/DeleteStatement.h | 2 +- src/sql/Expr.cpp | 29 +++++---- src/sql/Expr.h | 5 +- src/sql/PrepareStatement.cpp | 4 +- src/sql/SQLStatement.cpp | 32 ++++++++++ src/sql/SQLStatement.h | 2 + src/sql/statements.cpp | 116 +++++++++++++++-------------------- src/util/sqlhelper.cpp | 18 +++--- test/sql_tests.cpp | 17 +++++ test/test.sh | 1 + test/valid_queries.sql | 4 ++ 15 files changed, 198 insertions(+), 119 deletions(-) create mode 100644 src/sql/SQLStatement.cpp diff --git a/src/SQLParser.cpp b/src/SQLParser.cpp index a33f84d..ce5aee1 100644 --- a/src/SQLParser.cpp +++ b/src/SQLParser.cpp @@ -48,7 +48,7 @@ namespace hsql { if (!SQLParser::parseSQLString(text, result)) { delete result; - return NULL; + return nullptr; } return result; diff --git a/src/SQLParserResult.cpp b/src/SQLParserResult.cpp index f266f0d..94fc07f 100644 --- a/src/SQLParserResult.cpp +++ b/src/SQLParserResult.cpp @@ -6,11 +6,11 @@ namespace hsql { SQLParserResult::SQLParserResult() : isValid_(false), - errorMsg_(NULL) {}; + errorMsg_(nullptr) {}; SQLParserResult::SQLParserResult(SQLStatement* stmt) : isValid_(false), - errorMsg_(NULL) { + errorMsg_(nullptr) { addStatement(stmt); }; @@ -20,7 +20,7 @@ namespace hsql { errorMsg_ = moved.errorMsg_; statements_ = std::move(moved.statements_); - moved.errorMsg_ = NULL; + moved.errorMsg_ = nullptr; moved.reset(); } @@ -91,7 +91,7 @@ namespace hsql { isValid_ = false; free(errorMsg_); - errorMsg_ = NULL; + errorMsg_ = nullptr; errorLine_ = -1; errorColumn_ = -1; } diff --git a/src/parser/bison_parser.y b/src/parser/bison_parser.y index 49adae8..4d8e224 100644 --- a/src/parser/bison_parser.y +++ b/src/parser/bison_parser.y @@ -133,7 +133,7 @@ int yyerror(YYLTYPE* llocp, SQLParserResult* result, yyscan_t scanner, const cha %destructor { } %destructor { free( ($$) ); } %destructor { - if (($$) != NULL) { + if (($$) != nullptr) { for (auto ptr : *($$)) { delete ptr; } @@ -149,7 +149,6 @@ int yyerror(YYLTYPE* llocp, SQLParserResult* result, yyscan_t scanner, const cha %token IDENTIFIER STRING %token FLOATVAL %token INTVAL -%token NOTEQUALS LESSEQ GREATEREQ /* SQL Keywords */ %token DEALLOCATE PARAMETERS INTERSECT TEMPORARY TIMESTAMP @@ -189,7 +188,7 @@ int yyerror(YYLTYPE* llocp, SQLParserResult* result, yyscan_t scanner, const cha %type expr operand scalar_expr unary_expr binary_expr logic_expr exists_expr %type function_expr between_expr star_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 +%type comp_expr opt_where join_condition opt_having case_expr in_expr hint %type opt_limit opt_top %type order_desc %type opt_order_type @@ -198,7 +197,7 @@ int yyerror(YYLTYPE* llocp, SQLParserResult* result, yyscan_t scanner, const cha %type opt_group %type ident_commalist opt_column_list -%type expr_list select_list literal_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 @@ -260,8 +259,14 @@ statement_list: ; statement: - prepare_statement - | preparable_statement + prepare_statement opt_hints { + $$ = $1; + $$->hints = $2; + } + | preparable_statement opt_hints { + $$ = $1; + $$->hints = $2; + } ; @@ -278,6 +283,34 @@ preparable_statement: ; +/****************************** + * 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 ******************************/ @@ -439,7 +472,7 @@ insert_statement: opt_column_list: '(' ident_commalist ')' { $$ = $2; } - | /* empty */ { $$ = NULL; } + | /* empty */ { $$ = nullptr; } ; @@ -490,7 +523,7 @@ select_no_paren: $$->order = $2; // Limit could have been set by TOP. - if ($3 != NULL) { + if ($3 != nullptr) { delete $$->limit; $$->limit = $3; } @@ -504,7 +537,7 @@ select_no_paren: $$->order = $4; // Limit could have been set by TOP. - if ($5 != NULL) { + if ($5 != nullptr) { delete $$->limit; $$->limit = $5; } @@ -515,7 +548,7 @@ select_no_paren: $$->order = $4; // Limit could have been set by TOP. - if ($5 != NULL) { + if ($5 != nullptr) { delete $$->limit; $$->limit = $5; } @@ -556,7 +589,7 @@ from_clause: opt_where: WHERE expr { $$ = $2; } - | /* empty */ { $$ = NULL; } + | /* empty */ { $$ = nullptr; } ; opt_group: @@ -565,16 +598,16 @@ opt_group: $$->columns = $3; $$->having = $4; } - | /* empty */ { $$ = NULL; } + | /* empty */ { $$ = nullptr; } ; opt_having: HAVING expr { $$ = $2; } - | /* empty */ { $$ = NULL; } + | /* empty */ { $$ = nullptr; } opt_order: ORDER BY order_list { $$ = $3; } - | /* empty */ { $$ = NULL; } + | /* empty */ { $$ = nullptr; } ; order_list: @@ -596,13 +629,13 @@ opt_order_type: opt_top: TOP int_literal { $$ = new LimitDescription($2->ival, kNoOffset); delete $2; } - | /* empty */ { $$ = NULL; } + | /* 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 */ { $$ = NULL; } + | /* empty */ { $$ = nullptr; } ; /****************************** @@ -731,7 +764,7 @@ int_literal: ; star_expr: - '*' { $$ = new Expr(kExprStar); } + '*' { $$ = Expr::make(kExprStar); } ; param_expr: @@ -795,7 +828,7 @@ table_ref_name_no_alias: table_name: IDENTIFIER - | IDENTIFIER '.' IDENTIFIER + | IDENTIFIER '.' IDENTIFIER { $$ = $3; } ; @@ -806,7 +839,7 @@ alias: opt_alias: alias - | /* empty */ { $$ = NULL; } + | /* empty */ { $$ = nullptr; } /****************************** diff --git a/src/sql/CreateStatement.h b/src/sql/CreateStatement.h index 6751f30..c341219 100644 --- a/src/sql/CreateStatement.h +++ b/src/sql/CreateStatement.h @@ -37,9 +37,9 @@ namespace hsql { CreateType type; bool ifNotExists; // default: false - char* filePath; // default: NULL - char* tableName; // default: NULL - std::vector* columns; // default: NULL + char* filePath; // default: nullptr + char* tableName; // default: nullptr + std::vector* columns; // default: nullptr std::vector* viewColumns; SelectStatement* select; }; diff --git a/src/sql/DeleteStatement.h b/src/sql/DeleteStatement.h index 4c7d8ab..2aed8a5 100644 --- a/src/sql/DeleteStatement.h +++ b/src/sql/DeleteStatement.h @@ -8,7 +8,7 @@ namespace hsql { // Represents SQL Delete statements. // Example: "DELETE FROM students WHERE grade > 3.0" - // Note: if (expr == NULL) => delete all rows (truncate) + // Note: if (expr == nullptr) => delete all rows (truncate) struct DeleteStatement : SQLStatement { DeleteStatement(); virtual ~DeleteStatement(); diff --git a/src/sql/Expr.cpp b/src/sql/Expr.cpp index 7c94d9b..2eb99b7 100644 --- a/src/sql/Expr.cpp +++ b/src/sql/Expr.cpp @@ -8,13 +8,13 @@ namespace hsql { Expr::Expr(ExprType type) : type(type), - expr(NULL), - expr2(NULL), - exprList(NULL), - select(NULL), - name(NULL), - table(NULL), - alias(NULL) {}; + expr(nullptr), + expr2(nullptr), + exprList(nullptr), + select(nullptr), + name(nullptr), + table(nullptr), + alias(nullptr) {}; Expr::~Expr() { delete expr; @@ -24,7 +24,7 @@ namespace hsql { free(table); free(alias); - if (exprList != NULL) { + if (exprList != nullptr) { for (Expr* e : *exprList) { delete e; } @@ -32,11 +32,16 @@ namespace hsql { } } + Expr* Expr::make(ExprType type) { + Expr* e = new Expr(type); + return e; + } + Expr* Expr::makeOpUnary(OperatorType op, Expr* expr) { Expr* e = new Expr(kExprOperator); e->opType = op; e->expr = expr; - e->expr2 = NULL; + e->expr2 = nullptr; return e; } @@ -164,15 +169,15 @@ namespace hsql { } bool Expr::hasAlias() const { - return alias != NULL; + return alias != nullptr; } bool Expr::hasTable() const { - return table != NULL; + return table != nullptr; } const char* Expr::getName() const { - if (alias != NULL) return alias; + if (alias != nullptr) return alias; else return name; } diff --git a/src/sql/Expr.h b/src/sql/Expr.h index 2e6d44e..3f2acd5 100644 --- a/src/sql/Expr.h +++ b/src/sql/Expr.h @@ -21,7 +21,8 @@ namespace hsql { kExprColumnRef, kExprFunctionRef, kExprOperator, - kExprSelect + kExprSelect, + kExprHint }; // Operator types. These are important for expressions of type kExprOperator. @@ -102,6 +103,8 @@ namespace hsql { // Static constructors. + static Expr* make(ExprType type); + static Expr* makeOpUnary(OperatorType op, Expr* expr); static Expr* makeOpBinary(Expr* expr1, char op, Expr* expr2); diff --git a/src/sql/PrepareStatement.cpp b/src/sql/PrepareStatement.cpp index 6484e2a..6ca272e 100644 --- a/src/sql/PrepareStatement.cpp +++ b/src/sql/PrepareStatement.cpp @@ -5,8 +5,8 @@ namespace hsql { // PrepareStatement PrepareStatement::PrepareStatement() : SQLStatement(kStmtPrepare), - name(NULL), - query(NULL) {} + name(nullptr), + query(nullptr) {} PrepareStatement::~PrepareStatement() { free(name); diff --git a/src/sql/SQLStatement.cpp b/src/sql/SQLStatement.cpp new file mode 100644 index 0000000..2d8252d --- /dev/null +++ b/src/sql/SQLStatement.cpp @@ -0,0 +1,32 @@ + +#include "SQLStatement.h" + +namespace hsql { + + // SQLStatement + SQLStatement::SQLStatement(StatementType type) : + hints(nullptr), + type_(type) {}; + + SQLStatement::~SQLStatement() { + if (hints != nullptr) { + for (Expr* hint : *hints) { + delete hint; + } + } + delete hints; + } + + StatementType SQLStatement::type() const { + return type_; + } + + bool SQLStatement::isType(StatementType type) const { + return (type_ == type); + } + + bool SQLStatement::is(StatementType type) const { + return isType(type); + } + +} \ No newline at end of file diff --git a/src/sql/SQLStatement.h b/src/sql/SQLStatement.h index ccfddf1..12387bd 100644 --- a/src/sql/SQLStatement.h +++ b/src/sql/SQLStatement.h @@ -35,6 +35,8 @@ namespace hsql { // Shorthand for isType(type). bool is(StatementType type) const; + std::vector* hints; + private: StatementType type_; diff --git a/src/sql/statements.cpp b/src/sql/statements.cpp index 2b91f11..ab4320a 100644 --- a/src/sql/statements.cpp +++ b/src/sql/statements.cpp @@ -3,24 +3,6 @@ namespace hsql { - // SQLStatement - SQLStatement::SQLStatement(StatementType type) : - type_(type) {}; - - SQLStatement::~SQLStatement() {} - - StatementType SQLStatement::type() const { - return type_; - } - - bool SQLStatement::isType(StatementType type) const { - return (type_ == type); - } - - bool SQLStatement::is(StatementType type) const { - return isType(type); - } - // ColumnDefinition ColumnDefinition::ColumnDefinition(char* name, DataType type) : name(name), @@ -35,25 +17,25 @@ namespace hsql { SQLStatement(kStmtCreate), type(type), ifNotExists(false), - filePath(NULL), - tableName(NULL), - columns(NULL), - viewColumns(NULL), - select(NULL) {}; + filePath(nullptr), + tableName(nullptr), + columns(nullptr), + viewColumns(nullptr), + select(nullptr) {}; CreateStatement::~CreateStatement() { free(filePath); free(tableName); delete select; - if (columns != NULL) { + if (columns != nullptr) { for (ColumnDefinition* def : *columns) { delete def; } delete columns; } - if (viewColumns != NULL) { + if (viewColumns != nullptr) { for (char* column : *viewColumns) { free(column); } @@ -64,8 +46,8 @@ namespace hsql { // DeleteStatement DeleteStatement::DeleteStatement() : SQLStatement(kStmtDelete), - tableName(NULL), - expr(NULL) {}; + tableName(nullptr), + expr(nullptr) {}; DeleteStatement::~DeleteStatement() { free(tableName); @@ -76,7 +58,7 @@ namespace hsql { DropStatement::DropStatement(DropType type) : SQLStatement(kStmtDrop), type(type), - name(NULL) {} + name(nullptr) {} DropStatement::~DropStatement() { free(name); @@ -85,13 +67,13 @@ namespace hsql { // ExecuteStatement ExecuteStatement::ExecuteStatement() : SQLStatement(kStmtExecute), - name(NULL), - parameters(NULL) {} + name(nullptr), + parameters(nullptr) {} ExecuteStatement::~ExecuteStatement() { free(name); - if (parameters != NULL) { + if (parameters != nullptr) { for (Expr* param : *parameters) { delete param; } @@ -103,8 +85,8 @@ namespace hsql { ImportStatement::ImportStatement(ImportType type) : SQLStatement(kStmtImport), type(type), - filePath(NULL), - tableName(NULL) {}; + filePath(nullptr), + tableName(nullptr) {}; ImportStatement::~ImportStatement() { delete filePath; @@ -115,23 +97,23 @@ namespace hsql { InsertStatement::InsertStatement(InsertType type) : SQLStatement(kStmtInsert), type(type), - tableName(NULL), - columns(NULL), - values(NULL), - select(NULL) {} + tableName(nullptr), + columns(nullptr), + values(nullptr), + select(nullptr) {} InsertStatement::~InsertStatement() { free(tableName); delete select; - if (columns != NULL) { + if (columns != nullptr) { for (char* column : *columns) { free(column); } delete columns; } - if (values != NULL) { + if (values != nullptr) { for (Expr* expr : *values) { delete expr; } @@ -157,13 +139,13 @@ namespace hsql { // GroypByDescription GroupByDescription::GroupByDescription() : - columns(NULL), - having(NULL) {} + columns(nullptr), + having(nullptr) {} GroupByDescription::~GroupByDescription() { delete having; - if (columns != NULL) { + if (columns != nullptr) { for (Expr* expr : *columns) { delete expr; } @@ -174,14 +156,14 @@ namespace hsql { // SelectStatement SelectStatement::SelectStatement() : SQLStatement(kStmtSelect), - fromTable(NULL), + fromTable(nullptr), selectDistinct(false), - selectList(NULL), - whereClause(NULL), - groupBy(NULL), - unionSelect(NULL), - order(NULL), - limit(NULL) {}; + selectList(nullptr), + whereClause(nullptr), + groupBy(nullptr), + unionSelect(nullptr), + order(nullptr), + limit(nullptr) {}; SelectStatement::~SelectStatement() { delete fromTable; @@ -191,14 +173,14 @@ namespace hsql { delete limit; // Delete each element in the select list. - if (selectList != NULL) { + if (selectList != nullptr) { for (Expr* expr : *selectList) { delete expr; } delete selectList; } - if (order != NULL) { + if (order != nullptr) { for (OrderDescription* desc : *order) { delete desc; } @@ -209,15 +191,15 @@ namespace hsql { // UpdateStatement UpdateStatement::UpdateStatement() : SQLStatement(kStmtUpdate), - table(NULL), - updates(NULL), - where(NULL) {} + table(nullptr), + updates(nullptr), + where(nullptr) {} UpdateStatement::~UpdateStatement() { delete table; delete where; - if (updates != NULL) { + if (updates != nullptr) { for (UpdateClause* update : *updates) { free(update->column); delete update->value; @@ -230,12 +212,12 @@ namespace hsql { // TableRef TableRef::TableRef(TableRefType type) : type(type), - schema(NULL), - name(NULL), - alias(NULL), - select(NULL), - list(NULL), - join(NULL) {} + schema(nullptr), + name(nullptr), + alias(nullptr), + select(nullptr), + list(nullptr), + join(nullptr) {} TableRef::~TableRef() { free(schema); @@ -245,7 +227,7 @@ namespace hsql { delete select; delete join; - if (list != NULL) { + if (list != nullptr) { for (TableRef* table : *list) { delete table; } @@ -254,19 +236,19 @@ namespace hsql { } bool TableRef::hasSchema() const { - return schema != NULL; + return schema != nullptr; } const char* TableRef::getName() const { - if (alias != NULL) return alias; + if (alias != nullptr) return alias; else return name; } // JoinDefinition JoinDefinition::JoinDefinition() : - left(NULL), - right(NULL), - condition(NULL), + left(nullptr), + right(nullptr), + condition(nullptr), type(kJoinInner) {} JoinDefinition::~JoinDefinition() { diff --git a/src/util/sqlhelper.cpp b/src/util/sqlhelper.cpp index 352990a..b6ad14b 100644 --- a/src/util/sqlhelper.cpp +++ b/src/util/sqlhelper.cpp @@ -50,14 +50,14 @@ namespace hsql { for (TableRef* tbl : *table->list) printTableRefInfo(tbl, numIndent); break; } - if (table->alias != NULL) { + if (table->alias != nullptr) { inprint("Alias", numIndent + 1); inprint(table->alias, numIndent + 2); } } void printOperatorExpression(Expr* expr, uintmax_t numIndent) { - if (expr == NULL) { + if (expr == nullptr) { inprint("null", numIndent); return; } @@ -80,7 +80,7 @@ namespace hsql { break; } printExpression(expr->expr, numIndent + 1); - if (expr->expr2 != NULL) printExpression(expr->expr2, numIndent + 1); + if (expr->expr2 != nullptr) printExpression(expr->expr2, numIndent + 1); } void printExpression(Expr* expr, uintmax_t numIndent) { @@ -112,7 +112,7 @@ namespace hsql { fprintf(stderr, "Unrecognized expression type %d\n", expr->type); return; } - if (expr->alias != NULL) { + if (expr->alias != nullptr) { inprint("Alias", numIndent + 1); inprint(expr->alias, numIndent + 2); } @@ -126,25 +126,25 @@ namespace hsql { inprint("Sources:", numIndent + 1); printTableRefInfo(stmt->fromTable, numIndent + 2); - if (stmt->whereClause != NULL) { + if (stmt->whereClause != nullptr) { inprint("Search Conditions:", numIndent + 1); printExpression(stmt->whereClause, numIndent + 2); } - if (stmt->unionSelect != NULL) { + if (stmt->unionSelect != nullptr) { inprint("Union:", numIndent + 1); printSelectStatementInfo(stmt->unionSelect, numIndent + 2); } - if (stmt->order != NULL) { + if (stmt->order != nullptr) { inprint("OrderBy:", numIndent + 1); printExpression(stmt->order->at(0)->expr, numIndent + 2); if (stmt->order->at(0)->type == kOrderAsc) inprint("ascending", numIndent + 2); else inprint("descending", numIndent + 2); } - if (stmt->limit != NULL) { + if (stmt->limit != nullptr) { inprint("Limit:", numIndent + 1); inprint(stmt->limit->limit, numIndent + 2); } @@ -167,7 +167,7 @@ namespace hsql { void printInsertStatementInfo(const InsertStatement* stmt, uintmax_t numIndent) { inprint("InsertStatment", numIndent); inprint(stmt->tableName, numIndent + 1); - if (stmt->columns != NULL) { + if (stmt->columns != nullptr) { inprint("Columns", numIndent + 1); for (char* col_name : *stmt->columns) { inprint(col_name, numIndent + 2); diff --git a/test/sql_tests.cpp b/test/sql_tests.cpp index 1546d71..ca73870 100644 --- a/test/sql_tests.cpp +++ b/test/sql_tests.cpp @@ -156,4 +156,21 @@ TEST(MoveSQLResultTest) { ASSERT_EQ(1, new_res.size()); } +TEST(HintTest) { + TEST_PARSE_SINGLE_SQL( + "SELECT * FROM students WITH HINT(NO_CACHE, SAMPLE_RATE(10));", + kStmtSelect, + SelectStatement, + result, + stmt); + + + ASSERT_NOTNULL(stmt->hints); + ASSERT_EQ(2, stmt->hints->size()); + ASSERT_STREQ("NO_CACHE", stmt->hints->at(0)->name); + ASSERT_STREQ("SAMPLE_RATE", stmt->hints->at(1)->name); + ASSERT_EQ(1, stmt->hints->at(1)->exprList->size()); + ASSERT_EQ(10, stmt->hints->at(1)->exprList->at(0)->ival); +} + TEST_MAIN(); diff --git a/test/test.sh b/test/test.sh index b9d2dc1..efe68ad 100755 --- a/test/test.sh +++ b/test/test.sh @@ -36,6 +36,7 @@ MEM_LEAK_RET=$? if [ $MEM_LEAK_RET -ne 200 ]; then printf "${GREEN}Memory leak check succeeded!${NC}\n" + MEM_LEAK_RET=0 else MEM_LEAK_RET=1 RET=1 diff --git a/test/valid_queries.sql b/test/valid_queries.sql index bfd7edd..ef08be0 100644 --- a/test/valid_queries.sql +++ b/test/valid_queries.sql @@ -43,6 +43,10 @@ PREPARE prep2 FROM 'INSERT INTO test VALUES (?, 0, 0); INSERT INTO test VALUES ( EXECUTE prep_inst(1, 2, 3); EXECUTE prep; DEALLOCATE PREPARE prep; +# HINTS +SELECT * FROM test WITH HINT(NO_CACHE); +SELECT * FROM test WITH HINT(NO_CACHE, NO_SAMPLING); +SELECT * FROM test WITH HINT(NO_CACHE, SAMPLE_RATE(0.1), OMW(1.0, 'test')); # Error expeced ! !1