From b4711985b3c8d88e02e7d39cf1653165185867df Mon Sep 17 00:00:00 2001 From: VaclavT Date: Sun, 4 Jul 2021 15:03:13 +0200 Subject: [PATCH] another ugly basic implementation --- CMakeLists.txt | 2 +- Readme.md | 10 +++++ executor.cpp | 79 ++++++++++++++++++++++++-------- lexer.cpp | 14 ++++-- lexer.h | 7 ++- main.cpp | 5 ++- parser.cpp | 79 +++++++++++++++++++++++--------- parser.h | 70 +++++++++++++++++++++++++++-- row.cpp | 48 ++++++++++++++++++++ row.h | 119 +++++++++++++++++++++++++++++++++++++++++++++++++ table.cpp | 28 +++++++++--- table.h | 16 +++++-- 12 files changed, 417 insertions(+), 60 deletions(-) create mode 100644 Readme.md create mode 100644 row.cpp create mode 100644 row.h diff --git a/CMakeLists.txt b/CMakeLists.txt index e09d4c5..30407fc 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -12,7 +12,7 @@ project(msql) set(PROJECT_NAME msql) set(SOURCE - exception.cpp lexer.cpp parser.cpp executor.cpp main.cpp table.cpp table.h) + exception.cpp lexer.cpp parser.cpp executor.cpp main.cpp table.cpp table.h row.cpp row.h) add_executable(${PROJECT_NAME} ${SOURCE}) diff --git a/Readme.md b/Readme.md new file mode 100644 index 0000000..09d4834 --- /dev/null +++ b/Readme.md @@ -0,0 +1,10 @@ + +### TODO +- rename it to usql +- rename Exception to UException, Table to UTable, Row to URow etc +- unify using of float and double keywords +- add constructors +- add exceptions +- class members should have prefix m_O +- add pipe | token +- add logging \ No newline at end of file diff --git a/executor.cpp b/executor.cpp index c0356dc..fe09c46 100644 --- a/executor.cpp +++ b/executor.cpp @@ -14,7 +14,7 @@ Table* Executor::find_table(const std::string name) { if (table_def != std::end(m_tables)) { return table_def.operator->(); } else { - // TODO throw exception + throw Exception("table not found (" + name + ")"); } } @@ -50,25 +50,28 @@ bool Executor::execute_insert_into_table(InsertIntoTableNode& node) { Table* table_def = find_table(node.table_name); // prepare empty new_row - std::vector new_row; - new_row.reserve(table_def->columns_count()); - for(size_t i=0; icolumns_count(); i++) { - new_row.push_back(std::string {""}); - } + Row new_row = table_def->createEmptyRow(); // copy values for(size_t i=0; iget_column_def(colNameNode.name); - // TODO validate - new_row[col_def.order] = node.cols_values[i].value; + // TODO validate value + + if (col_def.type == ColumnType::integer_type) { + new_row.setColumnValue(col_def.order, std::stoi(node.cols_values[i].value)); + } else if (col_def.type == ColumnType::float_type) { + new_row.setColumnValue(col_def.order, std::stof(node.cols_values[i].value)); + } else { + new_row.setColumnValue(col_def.order, node.cols_values[i].value); + } } // TODO check not null columns // append new_row - table_def->m_rows.push_back(new_row); + table_def->addRow(new_row); return true; } @@ -99,18 +102,56 @@ bool Executor::execute_select(SelectFromTableNode& node) { for (auto row = begin (table->m_rows); row != end (table->m_rows); ++row) { - // eval there for row - bool where_true = true; - - if (where_true) { - // prepare empty row - std::vector new_row; - new_row.reserve(result.columns_count()); - for(auto i=0; iat(source_table_col_index[i])); + // eval where for row + bool where_true = true; + if (node.where->node_type == NodeType::true_node) { // no where clause + where_true = true; + } else { // evaluate where + RelationalOperatorNode& filter = static_cast(*node.where); + if (filter.op == RelationalOperatorType::greater) { + std::unique_ptr left_value; + if ((*filter.left).node_type == NodeType::database_value) { + DatabaseValueNode *dvl = static_cast(filter.left.get()); + ColDefNode col_def = table->get_column_def(dvl->col_name); // TODO optimize it to just get this def once + auto db_value = row->ithColum(col_def.order); + if (col_def.type == ColumnType::integer_type) { + left_value = std::make_unique(db_value->integerValue()); + } + // TODO other cases } - result.m_rows.push_back(new_row); + + std::unique_ptr right_value; + if ((*filter.right).node_type == NodeType::int_value) { + IntValueNode *ivl = static_cast(filter.right.get()); + right_value = std::make_unique(ivl->value); + } + + IntValueNode* left_int_value = static_cast(left_value.get()); + IntValueNode* right_int_value = static_cast(right_value.get()); + + where_true = left_int_value->value > right_int_value->value; } + } + + if (where_true) { + // prepare empty row + Row new_row = result.createEmptyRow(); + + // copy column values + for(auto idx=0; idxithColum(row_col_index); + if (result_tbl_col_defs[idx].type == ColumnType::integer_type) + new_row.setColumnValue(idx, ((ColIntegerValue*)col_value)->integerValue()); + if (result_tbl_col_defs[idx].type == ColumnType::float_type) + new_row.setColumnValue(idx, col_value->floatValue()); + if (result_tbl_col_defs[idx].type == ColumnType::varchar_type) + new_row.setColumnValue(idx, col_value->stringValue()); + } + + // add row to result + result.m_rows.push_back(new_row); + } } result.print(); diff --git a/lexer.cpp b/lexer.cpp index e95e847..a9dbec3 100644 --- a/lexer.cpp +++ b/lexer.cpp @@ -24,7 +24,7 @@ void Lexer::parse(const std::string &code) { // TODO make it constant std::regex words_regex("[0-9]+\\.[0-9]+|[0-9][0-9_]+[0-9]|[0-9]+|[A-Za-z]+[A-Za-z0-9_#]*|[\\(\\)\\[\\]\\{\\}]|[-\\+\\*/" - ",;:\?]|==|>=|<=|~=|>|<|=|;|~|\\|\\||&&|\n|\r|\r\n|'([^']|'')*'|\".*?\"|%.*?\n"); + ",;:\?]|==|>=|<=|~=|>|<|=|;|~|\\||or|and|\n|\r|\r\n|'([^']|'')*'|\".*?\"|%.*?\n"); auto words_begin = std::sregex_iterator(m_code_str.begin(), m_code_str.end(), words_regex); auto words_end = std::sregex_iterator(); @@ -55,6 +55,12 @@ void Lexer::debugTokens() { Token Lexer::currentToken() { return m_tokens[m_index]; } +Token Lexer::consumeCurrentToken() { + int i = m_index; + nextToken(); + return m_tokens[i]; +} + void Lexer::nextToken() { if (m_index < m_tokens.size()) { m_index++; @@ -65,7 +71,7 @@ void Lexer::skipToken(TokenType type) { if (tokenType() == type) { nextToken(); } else { - throw Exception("ERROR unexpected token " + currentToken().token_string + ", instead of " + typeToString(type)); + throw Exception("ERROR unexpected token " + consumeCurrentToken().token_string + ", instead of " + typeToString(type)); } } @@ -177,10 +183,10 @@ TokenType Lexer::type(const std::string &token) { if (token == "varchar") return TokenType::keyword_varchar; - if (token == "||") + if (token == "or") return TokenType::logical_or; - if (token == "&&") + if (token == "and") return TokenType::logical_and; if (token == ",") diff --git a/lexer.h b/lexer.h index 16fbe39..4098266 100644 --- a/lexer.h +++ b/lexer.h @@ -40,6 +40,7 @@ enum class TokenType { close_paren, logical_and, logical_or, + pipe, semicolon, comma, newline, @@ -61,7 +62,8 @@ public: void debugTokens(); - Token currentToken(); + Token currentToken(); + Token consumeCurrentToken(); void nextToken(); @@ -72,7 +74,8 @@ public: TokenType nextTokenType(); TokenType prevTokenType(); - static bool isRelationalOperator(TokenType token_type); + static bool isRelationalOperator(TokenType token_type); + static bool isLogicalOperator(TokenType token_type); private: TokenType type(const std::string &token); diff --git a/main.cpp b/main.cpp index 2c2c328..685f397 100644 --- a/main.cpp +++ b/main.cpp @@ -16,7 +16,8 @@ int main(int argc, char *argv[]) { "insert into a (i, s) values(1, 'one')", "insert into a (i, s) values(2, 'two')", "insert into a (i, s) values(3, 'two')", - "select i, s from a where i > 0" + "insert into a (i, s) values(4, 'four')", + "select i, s from a where i > 2" // "update a set s = 'three' where i = 3" // "delete from a where i = 3" // "select i, s from a where i > 0" @@ -25,7 +26,7 @@ int main(int argc, char *argv[]) { for(auto command : sql_commands) { auto node = parser.parse(command); - executor.execute(*node.get()); + executor.execute(*node); } return 0; diff --git a/parser.cpp b/parser.cpp index a3cf028..61a5ba8 100644 --- a/parser.cpp +++ b/parser.cpp @@ -19,6 +19,7 @@ std::unique_ptr Parser::parse(const std::string &code) { return parse_select_from_table(); } + std::cout << "ERROR, token:" << lexer.currentToken().token_string << std::endl; return std::make_unique(NodeType::error); } @@ -29,9 +30,8 @@ std::unique_ptr Parser::parse_create_table() { lexer.skipToken(TokenType::keyword_table); if (lexer.tokenType() != TokenType::identifier) { /* TODO handle error */ } - std::string table_name = lexer.currentToken().token_string; - lexer.nextToken(); - + std::string table_name = lexer.consumeCurrentToken().token_string; + lexer.skipToken(TokenType::open_paren); int column_order = 0; do { @@ -42,8 +42,7 @@ std::unique_ptr Parser::parse_create_table() { // column name if (lexer.tokenType() != TokenType::identifier) { /* TODO handle error */ } - column_name = lexer.currentToken().token_string; - lexer.nextToken(); + column_name = lexer.consumeCurrentToken().token_string; // column type and optionaly len if (lexer.tokenType() == TokenType::keyword_int) { @@ -57,8 +56,7 @@ std::unique_ptr Parser::parse_create_table() { lexer.nextToken(); lexer.skipToken(TokenType::open_paren); if (lexer.tokenType() == TokenType::int_number) { - column_len = std::stoi(lexer.currentToken().token_string); - lexer.nextToken(); + column_len = std::stoi(lexer.consumeCurrentToken().token_string); } else { /* TODO handle error */ } lexer.skipToken(TokenType::close_paren); } else { /* TODO handle error */ } @@ -94,15 +92,13 @@ std::unique_ptr Parser::parse_insert_into_table() { // table name if (lexer.tokenType() != TokenType::identifier) { /* TODO handle error */ } - std::string table_name = lexer.currentToken().token_string; - lexer.nextToken(); + std::string table_name = lexer.consumeCurrentToken().token_string; // column names lexer.skipToken(TokenType::open_paren); do { if (lexer.tokenType() != TokenType::identifier) { /* TODO handle error */ } - cols_names.push_back(lexer.currentToken().token_string); - lexer.nextToken(); + cols_names.push_back(lexer.consumeCurrentToken().token_string); lexer.skipTokenOptional(TokenType::comma); } while (lexer.tokenType() != TokenType::close_paren); @@ -113,8 +109,7 @@ std::unique_ptr Parser::parse_insert_into_table() { // column values lexer.skipToken(TokenType::open_paren); do { - cols_values.push_back(lexer.currentToken().token_string); - lexer.nextToken(); + cols_values.push_back(lexer.consumeCurrentToken().token_string); lexer.skipTokenOptional(TokenType::comma); } while (lexer.tokenType() != TokenType::close_paren); @@ -124,25 +119,69 @@ std::unique_ptr Parser::parse_insert_into_table() { } std::unique_ptr Parser::parse_select_from_table() { - std::vector where {}; std::vector cols_names {}; + std::unique_ptr where_node; lexer.skipToken(TokenType::keyword_select); // TODO support also numbers and expressions while (lexer.tokenType() != TokenType::keyword_from) { // TODO add consumeToken() which returns token and advances to next token - cols_names.push_back(lexer.currentToken().token_string); - lexer.nextToken(); + cols_names.push_back(lexer.consumeCurrentToken().token_string); lexer.skipTokenOptional(TokenType::comma); } lexer.skipToken(TokenType::keyword_from); - std::string table_name = lexer.currentToken().token_string; - lexer.nextToken(); + std::string table_name = lexer.consumeCurrentToken().token_string; - if (lexer.tokenType() == TokenType::keyword_where) {} + if (lexer.tokenType() == TokenType::keyword_where) { + lexer.skipToken(TokenType::keyword_where); + where_node = parse_where_clause(); + } else { + where_node = std::make_unique(); + } // if (lexer.tokenType() == TokenType::keyword_order_by) {} // if (lexer.tokenType() == TokenType::keyword_offset) {} // if (lexer.tokenType() == TokenType::keyword_limit) {} - return std::make_unique(table_name, cols_names, where); + return std::make_unique(table_name, cols_names, std::move(where_node)); +} + +std::unique_ptr Parser::parse_where_clause() { + // TODO add support for multiple filters + // TODO add support for parenthises + + auto left = parse_operand_node(); + auto operation = parse_operator(); + auto right = parse_operand_node(); + + return std::make_unique(operation, std::move(left), std::move(right)); +} + +std::unique_ptr Parser::parse_operand_node() { + // while not end or order or limit + auto token_type = lexer.tokenType(); + std::string tokenString = lexer.consumeCurrentToken().token_string; + switch (token_type) { + case TokenType::int_number: + return std::make_unique(std::stoi(tokenString)); + case TokenType::double_number: + return std::make_unique(std::stod(tokenString)); + case TokenType::string_literal: + return std::make_unique(tokenString); + case TokenType::identifier: + return std::make_unique(tokenString); + default: ; + // Throw exception + } +} + +RelationalOperatorType Parser::parse_operator() { + auto op = lexer.consumeCurrentToken(); + switch (op.type) { + case TokenType::equal: + return RelationalOperatorType::equal; + case TokenType::greater: + return RelationalOperatorType::greater; + default: ; + // Throw exception + } } \ No newline at end of file diff --git a/parser.h b/parser.h index 535a928..bedca1e 100644 --- a/parser.h +++ b/parser.h @@ -14,6 +14,13 @@ enum class ColumnType { }; enum class NodeType { + true_node, + int_value, + float_value, + string_value, + database_value, + logical_operator, + relational_operator, create_table, insert_into, select_from, @@ -56,6 +63,59 @@ struct ColDefNode : Node { Node(NodeType::column_def), name(col_name), type(col_type), order(col_order), length(col_len), null(nullable) {} }; +struct TrueNode : Node { + TrueNode() : Node(NodeType::true_node) {} +}; + +struct IntValueNode : Node { + int value; + + IntValueNode(int value) : Node(NodeType::int_value), value(value) {} +}; + +struct FloatValueNode : Node { + double value; + + FloatValueNode(double value) : Node(NodeType::float_value), value(value) {} +}; + +struct StringValueNode : Node { + std::string value; + + StringValueNode(std::string value) : Node(NodeType::string_value), value(value) {} +}; + +struct DatabaseValueNode : Node { + std::string col_name; + + DatabaseValueNode(std::string name) : Node(NodeType::database_value), col_name(name) {} +}; + +struct LogicalOperatorNode : Node { +// and_operator, +// or_operator, +// not_operator, + // and / or / not + std::unique_ptr left; + std::unique_ptr right; +}; + +enum class RelationalOperatorType { + equal, + greater + // =, !=, >, >=, <, <=, like +}; + +struct RelationalOperatorNode : Node { + RelationalOperatorType op; + + std::unique_ptr left; + std::unique_ptr right; + + RelationalOperatorNode(RelationalOperatorType op, std::unique_ptr left, std::unique_ptr right) : + Node(NodeType::relational_operator), op(op), left(std::move(left)), right(std::move(right)) {}; +}; + struct CreateTableNode : Node { std::string table_name; std::vector cols_defs; @@ -76,10 +136,10 @@ struct InsertIntoTableNode : Node { struct SelectFromTableNode : Node { std::string table_name; std::vector cols_names; - std::vector where; + std::unique_ptr where; - SelectFromTableNode(const std::string name, std::vector names, std::vector where_clause) : - Node(NodeType::select_from), table_name(name), cols_names(names), where(where_clause) {} + SelectFromTableNode(const std::string name, std::vector names, std::unique_ptr where_clause) : + Node(NodeType::select_from), table_name(name), cols_names(names), where(std::move(where_clause)) {} }; struct UpdateTableNode : Node { }; @@ -101,6 +161,10 @@ private: std::unique_ptr parse_insert_into_table(); std::unique_ptr parse_select_from_table(); + std::unique_ptr parse_where_clause(); + std::unique_ptr parse_operand_node(); + RelationalOperatorType parse_operator(); + private: Lexer lexer; }; diff --git a/row.cpp b/row.cpp new file mode 100644 index 0000000..ee4ec59 --- /dev/null +++ b/row.cpp @@ -0,0 +1,48 @@ + +#include "row.h" + + +Row::Row(int cols_count) { + m_columns.reserve(cols_count); + for (int i = 0; i < cols_count; i++) { + m_columns.push_back(std::make_unique()); + } +} + +Row::Row(const Row &other) { + m_columns.reserve(other.m_columns.size()); + // TODO fixme this is nonsense + for (int i = 0; i < other.m_columns.size(); i++) { + m_columns.push_back(std::make_unique()); + } + + for (int i = 0; i < other.m_columns.size(); i++) { + if (ColIntegerValue* other_v = dynamic_cast(other.m_columns[i].get())) { + setColumnValue(i, other_v->integerValue()); + } + if (ColFloatValue* other_v = dynamic_cast(other.m_columns[i].get())) { + setColumnValue(i, other_v->floatValue()); + } + if (ColStringValue* other_v = dynamic_cast(other.m_columns[i].get())) { + setColumnValue(i, other_v->stringValue()); + } + } +} + +void Row::setColumnValue(int col_index, int value) { + m_columns[col_index] = std::make_unique(value); +} + +void Row::setColumnValue(int col_index, double value) { + m_columns[col_index] = std::make_unique(value); +} + +void Row::setColumnValue(int col_index, std::string value) { + m_columns[col_index] = std::make_unique(value); +}; + +void Row::print() { + for(int i=0; iprint(); + } +} diff --git a/row.h b/row.h new file mode 100644 index 0000000..474cb6e --- /dev/null +++ b/row.h @@ -0,0 +1,119 @@ +#pragma once + +#include "exception.h" +#include "parser.h" + +#include + +class ColumnValue { + + +private: + ColumnType m_type; + union { + int int_value; + double float_value; + + }; +}; + + + +struct ColValue { + + virtual bool isNull() { return false; }; + virtual bool isInteger() { return false; }; + virtual bool isFloat() { return false; }; + virtual bool isString() { return false; }; + + virtual int integerValue() { throw Exception("Not supported"); }; + virtual double floatValue() { throw Exception("Not supported"); }; + virtual std::string stringValue() { throw Exception("Not supported"); }; + + virtual void print() {std::cout << "ColValue:" << std::endl; }; +}; + + +struct ColNullValue : ColValue { + + virtual bool isNull() { return true; }; + + virtual void print() {std::cout << "ColNullValue:" << std::endl; }; +}; + + +struct ColIntegerValue : ColValue { + + ColIntegerValue(int value) : m_integer(value) {}; + ColIntegerValue(const ColIntegerValue &other) : m_integer(other.m_integer) {} + + virtual bool isInteger() { return true; }; + + virtual int integerValue() { return m_integer; }; + virtual double floatValue() { return (double) m_integer; }; + virtual std::string stringValue() { return std::to_string(m_integer); }; + + virtual void print() {std::cout << "ColIntegerValue: " << m_integer <> m_columns; +}; diff --git a/table.cpp b/table.cpp index 0c4b984..577ae7b 100644 --- a/table.cpp +++ b/table.cpp @@ -7,22 +7,40 @@ Table::Table(const std::string name, const std::vector columns) { m_rows.clear(); } -ColDefNode Table::get_column_def(const std::string col_name) { +ColDefNode Table::get_column_def(const std::string& col_name) { auto name_cmp = [col_name](ColDefNode cd){ return cd.name == col_name; }; auto col_def = std::find_if(begin(m_col_defs), end(m_col_defs), name_cmp ); if (col_def != std::end(m_col_defs)) { return *col_def; } else { - // TODO throw exception + throw Exception("column not exists (" + col_name + ")"); } } + +Row Table::createEmptyRow() { + return Row(columns_count()); +} + + void Table::print() { std::cout << "** " << m_name << " **" << std::endl; for(auto row : m_rows) { - for( auto col : row) { - std::cout << col << ","; + for(int ci = 0; ci < columns_count(); ci++) { + auto v = row[ci].stringValue(); + std::cout << v << ","; } std::cout << std::endl; } -} \ No newline at end of file +} + +Table::Table(const Table& other) { + m_name = other.m_name; + m_col_defs = other.m_col_defs; + m_rows.clear(); // row not copied now +} + +void Table::addRow(const Row &row) { + m_rows.push_back(row); +} + diff --git a/table.h b/table.h index d2415f0..afa8d97 100644 --- a/table.h +++ b/table.h @@ -1,6 +1,7 @@ #pragma once #include "parser.h" +#include "row.h" #include @@ -8,14 +9,21 @@ struct Table { // public: + Table(const Table& other); + Table(const std::string name, const std::vector columns); - ColDefNode get_column_def(const std::string col_name); + ColDefNode get_column_def(const std::string& col_name); int columns_count() { return m_col_defs.size(); }; + + Row createEmptyRow(); // TODO this means unnecessary copying + void addRow(const Row &row); + void print(); + // private: - std::string m_name; - std::vector m_col_defs; - std::vector> m_rows; + std::string m_name; + std::vector m_col_defs; + std::vector m_rows; };