diff --git a/Radme.md b/Radme.md deleted file mode 100644 index 8887230..0000000 --- a/Radme.md +++ /dev/null @@ -1,12 +0,0 @@ - -### TODO -- unify using of float and double keywords to double -- use long data type for int -- stoi -> stol, stof -> stod -- add exceptions -- class members should have prefix m_ -- add pipe | token -- add to_date a to_number functions -- add min and max functions -- add logging -- add const wherever should be \ No newline at end of file diff --git a/Readme.md b/Readme.md index 895b698..5903342 100644 --- a/Readme.md +++ b/Readme.md @@ -1,10 +1,13 @@ ### TODO -- rename Exception to UException, Table to UTable, Row to URow etc -- remove newlines from lexed string tokens -- unify using of float and double keywords -- add constructors -- add exceptions +- save table command +- unify using of float and double keywords to double +- use long data type for int +- stoi -> stol, stof -> stod +- add exceptions - class members should have prefix m_ -- add pipe | token -- add logging \ No newline at end of file +- add pipe | token +- add to_date a to_number functions +- add min and max functions, eg aggregate functions +- add logging +- add const wherever should be \ No newline at end of file diff --git a/lexer.cpp b/lexer.cpp index a9477ee..ab802bb 100644 --- a/lexer.cpp +++ b/lexer.cpp @@ -156,6 +156,9 @@ namespace usql { if (token == "<=") return TokenType::lesser_equal; + if (token == "as") + return TokenType::keyword_as; + if (token == "create") return TokenType::keyword_create; @@ -326,6 +329,9 @@ namespace usql { case TokenType::lesser_equal: txt = "<="; break; + case TokenType::keyword_as: + txt = "as"; + break; case TokenType::keyword_create: txt = "create"; break; diff --git a/lexer.h b/lexer.h index 7fba978..aef057d 100644 --- a/lexer.h +++ b/lexer.h @@ -20,6 +20,7 @@ namespace usql { greater_equal, lesser, lesser_equal, + keyword_as, keyword_create, keyword_table, keyword_where, diff --git a/main.cpp b/main.cpp index 5c7b7e0..5ae4bce 100644 --- a/main.cpp +++ b/main.cpp @@ -15,20 +15,23 @@ int main(int argc, char *argv[]) { "insert into a (i, s) values(3, 'two')", "insert into a (i, s) values(4, lower('FOUR'))", "insert into a (i, s) values(5, 'five')", - "select i, s from a where i > 2", - "select i, s from a where i = 1", - "select i, s from a where s = 'two'", - "select i, s from a where i <= 3 and s = 'one'", - "select i, s from a where i > 0", - "delete from a where i = 4", - "select i, s from a where i > 0", - "update a set f = 9.99 where i = 3", - "select i, s, f from a where i = 3", - "update a set s = 'three', f = f + 0.01 where i = 3", - "select i, s, f from a where i = 3", - "create table data (ticker varchar(8), price float null)", - "load data from '/Users/vaclavt/Library/Mobile Documents/com~apple~CloudDocs/Development/usql/data.csv')", - "select ticker, price from data" +// "select i, s from a where i > 2", +// "select i, s from a where i = 1", +// "select i, s from a where s = 'two'", +// "select i, s from a where i <= 3 and s = 'one'", +// "select i, s from a where i > 0", +// "delete from a where i = 4", +// "select i, s from a where i > 0", +// "update a set f = 9.99 where i = 3", +// "select i, s, f from a where i = 3", +// "update a set s = 'three', f = f + 0.01 where i = 3", +// "select i, s, f from a where i = 3", +// "create table data (ticker varchar(8), price float null)", +// "load data from '/Users/vaclavt/Library/Mobile Documents/com~apple~CloudDocs/Development/usql/data.csv')", +// "select ticker, price from data", + "select i, s, f from a where i < 300", + "create table x as select i, s, f from a where i < 300", + "select i, s, f from x where i < 300" }; diff --git a/parser.cpp b/parser.cpp index 85dd894..913d6eb 100644 --- a/parser.cpp +++ b/parser.cpp @@ -45,53 +45,60 @@ namespace usql { if (lexer.tokenType() != TokenType::identifier) { /* TODO handle error */ } std::string table_name = lexer.consumeCurrentToken().token_string; - lexer.skipToken(TokenType::open_paren); - int column_order = 0; - do { - std::string column_name; - ColumnType column_type; - int column_len{1}; - bool column_nullable{true}; + // create as select + if (lexer.tokenType() == TokenType::keyword_as) { + lexer.skipToken(TokenType::keyword_as); - // column name - if (lexer.tokenType() != TokenType::identifier) { /* TODO handle error */ } - column_name = lexer.consumeCurrentToken().token_string; + std::unique_ptr select = parse_select_from_table(); + + return std::make_unique(table_name, std::move(select)); + } else { + lexer.skipToken(TokenType::open_paren); + int column_order = 0; + do { + std::string column_name; + ColumnType column_type; + int column_len{1}; + bool column_nullable{true}; - // column type and optionally len - if (lexer.tokenType() == TokenType::keyword_int) { - column_type = ColumnType::integer_type; - lexer.nextToken(); - } else if (lexer.tokenType() == TokenType::keyword_float) { - column_type = ColumnType::float_type; - lexer.nextToken(); - } else if (lexer.tokenType() == TokenType::keyword_varchar) { - column_type = ColumnType::varchar_type; - lexer.nextToken(); - lexer.skipToken(TokenType::open_paren); - if (lexer.tokenType() == TokenType::int_number) { - column_len = std::stoi(lexer.consumeCurrentToken().token_string); - } else { /* TODO handle error */ } - lexer.skipToken(TokenType::close_paren); - } else { /* TODO handle error */ } + // column name + if (lexer.tokenType() != TokenType::identifier) { /* TODO handle error */ } + column_name = lexer.consumeCurrentToken().token_string; - if (lexer.tokenType() == TokenType::keyword_not) { - lexer.nextToken(); - lexer.skipToken(TokenType::keyword_null); - column_nullable = false; - } else if (lexer.tokenType() == TokenType::keyword_null) { - lexer.nextToken(); - } + // column type and optionally len + if (lexer.tokenType() == TokenType::keyword_int) { + column_type = ColumnType::integer_type; + lexer.nextToken(); + } else if (lexer.tokenType() == TokenType::keyword_float) { + column_type = ColumnType::float_type; + lexer.nextToken(); + } else if (lexer.tokenType() == TokenType::keyword_varchar) { + column_type = ColumnType::varchar_type; + lexer.nextToken(); + lexer.skipToken(TokenType::open_paren); + if (lexer.tokenType() == TokenType::int_number) { + column_len = std::stoi(lexer.consumeCurrentToken().token_string); + } else { /* TODO handle error */ } + lexer.skipToken(TokenType::close_paren); + } else { /* TODO handle error */ } - cols_def.push_back( ColDefNode(column_name, column_type, column_order++, column_len, column_nullable)); + if (lexer.tokenType() == TokenType::keyword_not) { + lexer.nextToken(); + lexer.skipToken(TokenType::keyword_null); + column_nullable = false; + } else if (lexer.tokenType() == TokenType::keyword_null) { + lexer.nextToken(); + } - lexer.skipTokenOptional(TokenType::comma); + cols_def.push_back( ColDefNode(column_name, column_type, column_order++, column_len, column_nullable)); - // TODO in future constraints + lexer.skipTokenOptional(TokenType::comma); - } while (lexer.tokenType() != TokenType::close_paren); + // TODO in future constraints + } while (lexer.tokenType() != TokenType::close_paren); - - return std::make_unique(table_name, cols_def); + return std::make_unique(table_name, cols_def); + } } diff --git a/parser.h b/parser.h index ff29726..14a5f46 100644 --- a/parser.h +++ b/parser.h @@ -25,6 +25,7 @@ namespace usql { relational_operator, arithmetical_operator, create_table, + create_table_as_select, insert_into, select_from, delete_from, @@ -63,13 +64,6 @@ namespace usql { null(nullable) {} }; - struct ColValueNode : Node { - std::string value; - - ColValueNode(const std::string col_value) : - Node(NodeType::column_value), value(col_value) {} - }; - struct FunctionNode : Node { std::string function; std::vector> params; @@ -198,6 +192,7 @@ namespace usql { Node(NodeType::create_table), table_name(name), cols_defs(defs) {} }; + struct InsertIntoTableNode : Node { std::string table_name; std::vector cols_names; @@ -216,6 +211,16 @@ namespace usql { Node(NodeType::select_from), table_name(name), cols_names(names), where(std::move(where_clause)) {} }; + + struct CreateTableAsSelectNode : Node { + std::string table_name; + std::unique_ptr select_table; + + CreateTableAsSelectNode(const std::string name, std::unique_ptr table) : + Node(NodeType::create_table_as_select), table_name(name), select_table(std::move(table)) {} + }; + + struct UpdateTableNode : Node { std::string table_name; std::vector cols_names; diff --git a/row.cpp b/row.cpp index 27f5a04..d6b57c1 100644 --- a/row.cpp +++ b/row.cpp @@ -35,6 +35,10 @@ namespace usql { return *this; } + void Row::setColumnNull(int col_index) { + m_columns[col_index] = std::make_unique(); + } + void Row::setColumnValue(int col_index, int value) { m_columns[col_index] = std::make_unique(value); } diff --git a/row.h b/row.h index 19596b2..e42b54c 100644 --- a/row.h +++ b/row.h @@ -10,7 +10,7 @@ namespace usql { struct ColValue { - virtual bool isNull() { return false; };;;; + virtual bool isNull() { return false; }; virtual int integerValue() { throw Exception("Not supported"); }; @@ -80,22 +80,20 @@ namespace usql { public: Row(int cols_count); - Row(const Row &other); Row &operator=(Row other); + void setColumnNull(int col_index); void setColumnValue(int col_index, int value); - void setColumnValue(int col_index, double value); - void setColumnValue(int col_index, const std::string &value); ColValue &operator[](int i) { return *m_columns[i]; } - ColValue *ithColumn(int i) { + ColValue * ithColumn(int i) const { return m_columns[i].get(); } diff --git a/table.cpp b/table.cpp index 373abfc..2a8ed91 100644 --- a/table.cpp +++ b/table.cpp @@ -3,45 +3,69 @@ namespace usql { - Table::Table(const std::string name, const std::vector columns) { - m_name = name; - m_col_defs = columns; - m_rows.clear(); - } +Table::Table(const std::string name, const std::vector columns) { + m_name = name; + m_col_defs = columns; + m_rows.clear(); +} - 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 { - throw Exception("column not exists (" + 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 { + throw Exception("column not exists (" + col_name + ")"); + } +} - Row Table::createEmptyRow() { - return Row(columns_count()); - } +Row Table::createEmptyRow() { + return Row(columns_count()); +} - void Table::print() { - std::cout << "** " << m_name << " **" << std::endl; - for (auto row : m_rows) { - row.print(); - } - } +void Table::print() { + std::cout << "** " << m_name << " **" << std::endl; + for (auto row : m_rows) { + row.print(); + } +} - Table::Table(const Table &other) { - m_name = other.m_name; - m_col_defs = other.m_col_defs; - m_rows.clear(); // row not copied now - } +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) { - // TODO validate for not null values - // todo validate for length etc - m_rows.push_back(row); - } +void Table::addRow(const Row &row) { + // TODO validate for not null values + // todo validate for length etc + m_rows.push_back(row); +} -} \ No newline at end of file +void Table::addCopyOfRow(const Row &row) { + // TODO validate for not null values + // todo validate for length etc + + Row new_row = createEmptyRow(); + + for(int i = 0; i < m_col_defs.size(); i++) { + ColValue *ct = row.ithColumn(i); + + if (ct->isNull()) { + new_row.setColumnNull(i); + } else { + if (m_col_defs[i].type == ColumnType::integer_type) { + new_row.setColumnValue(i, row.ithColumn(i)->integerValue()); + } else if (m_col_defs[i].type == ColumnType::float_type) { + new_row.setColumnValue(i, row.ithColumn(i)->floatValue()); + } else if (m_col_defs[i].type == ColumnType::varchar_type) { + new_row.setColumnValue(i, row.ithColumn(i)->stringValue()); + } + } + } + m_rows.push_back(row); +} + +} // namespace \ No newline at end of file diff --git a/table.h b/table.h index dda6a03..8961244 100644 --- a/table.h +++ b/table.h @@ -19,6 +19,7 @@ namespace usql { Row createEmptyRow(); // TODO this means unnecessary copying void addRow(const Row &row); + void addCopyOfRow(const Row &row); void print(); diff --git a/usql.cpp b/usql.cpp index 065a80e..6e2ae55 100644 --- a/usql.cpp +++ b/usql.cpp @@ -18,18 +18,20 @@ std::unique_ptr USql::execute(Node &node) { switch (node.node_type) { case NodeType::create_table: return execute_create_table(static_cast(node)); - case NodeType::insert_into: - return execute_insert_into_table(static_cast(node)); - case NodeType::select_from: - return execute_select(static_cast(node)); - case NodeType::delete_from: - return execute_delete(static_cast(node)); - case NodeType::update_table: - return execute_update(static_cast(node)); - case NodeType::load_table: - return execute_load(static_cast(node)); - default: - return create_stmt_result_table(-1, "unknown statement"); + case NodeType::create_table_as_select: + return execute_create_table_as_table(static_cast(node)); + case NodeType::insert_into: + return execute_insert_into_table(static_cast(node)); + case NodeType::select_from: + return execute_select(static_cast(node)); + case NodeType::delete_from: + return execute_delete(static_cast(node)); + case NodeType::update_table: + return execute_update(static_cast(node)); + case NodeType::load_table: + return execute_load(static_cast(node)); + default: + return create_stmt_result_table(-1, "unknown statement"); } } @@ -43,6 +45,28 @@ std::unique_ptr
USql::execute_create_table(CreateTableNode &node) { } +std::unique_ptr
USql::execute_create_table_as_table(CreateTableAsSelectNode &node) { + // TODO check table does not exists + + auto select = execute_select((SelectFromTableNode &) *node.select_table.get()); + + // create table + Table new_table{node.table_name, select.get()->m_col_defs}; + m_tables.push_back(new_table); + + // copy rows + // must be here, if rows are put into new_table, they are lost during m_tables.push_table + Table *table = find_table(node.table_name); + for( Row& orig_row : select.get()->m_rows) { + table->addCopyOfRow(orig_row); + } + + select.release(); // is it correct? hoping not to release select table here and then when releasing CreateTableAsSelectNode + + return create_stmt_result_table(0, "table created"); +} + + std::unique_ptr
USql::execute_insert_into_table(InsertIntoTableNode &node) { // TODO check column names.size = values.size @@ -107,13 +131,14 @@ std::unique_ptr
USql::execute_select(SelectFromTableNode &node) { for (auto idx = 0; idx < result->columns_count(); idx++) { auto row_col_index = source_table_col_index[idx]; ColValue *col_value = row->ithColumn(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()); + if (!col_value->isNull()) { + 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 diff --git a/usql.h b/usql.h index eeefeae..bbfc4a1 100644 --- a/usql.h +++ b/usql.h @@ -18,6 +18,7 @@ private: std::unique_ptr
execute(Node &node); std::unique_ptr
execute_create_table(CreateTableNode &node); + std::unique_ptr
execute_create_table_as_table(CreateTableAsSelectNode &node); std::unique_ptr
execute_insert_into_table(InsertIntoTableNode &node); std::unique_ptr
execute_select(SelectFromTableNode &node); std::unique_ptr
execute_delete(DeleteFromTableNode &node);