From 4665705c3d3cadd115d0e2303c2c0196e72d51bb Mon Sep 17 00:00:00 2001 From: VaclavT Date: Sat, 14 Aug 2021 11:33:02 +0200 Subject: [PATCH] some TODOs removed.. --- Readme.md | 3 --- lexer.cpp | 15 +++++++-------- main.cpp | 43 ++++++++++++++++++++++--------------------- ml_date.cpp | 2 +- parser.cpp | 24 ++++++++++++++++++------ parser.h | 10 +++++----- row.cpp | 51 ++++++++++++++++++++++++++++++--------------------- row.h | 25 ++++++++++++++++--------- table.cpp | 2 +- usql.cpp | 16 +++++++++++++--- 10 files changed, 113 insertions(+), 78 deletions(-) diff --git a/Readme.md b/Readme.md index 6c48cd2..38dcbad 100644 --- a/Readme.md +++ b/Readme.md @@ -1,6 +1,5 @@ ### TODO -- better support for values in update and insert - date functions - now, add_date... - string functions rtrim, ltrim, rpad, lpad - add pipe | concatenation @@ -9,7 +8,5 @@ - support for uniqueue indexes (primary key) - support for btree indexes - add count min and max functions, eg aggregate functions -- support for parenthesis -- class members should have prefix m_ - add const wherever should be - PERF in Row::Row(const Row &other), could be more efficient (memory and cpu) diff --git a/lexer.cpp b/lexer.cpp index c21eee6..61d9345 100644 --- a/lexer.cpp +++ b/lexer.cpp @@ -22,16 +22,15 @@ namespace usql { } void Lexer::parse(const std::string &code) { - // TODO handle empty code - m_tokens.clear(); + if (code.empty()) + throw Exception("empty code"); + + m_tokens.clear(); + m_tokens.reserve(64); - // PERF something like this to preallocate ?? - if (code.size() > 100) { - m_tokens.reserve(code.size() / 10); - } m_code_str = code; if (!m_code_str.empty() && m_code_str.back() != '\n') { - m_code_str.append("\n"); // TODO temp solution to prevent possible situation when last line is a comment + m_code_str.append("\n"); // temp solution to prevent possible situation when last line is a comment } auto words_begin = std::sregex_iterator(m_code_str.begin(), m_code_str.end(), k_words_regex); @@ -116,7 +115,7 @@ namespace usql { } TokenType Lexer::type(const std::string &token) { - // TODO, FIXME 'one is evaluated as identifier + // FIXME 'one is evaluated as identifier if (token == ";") return TokenType::semicolon; if (token == "+") diff --git a/main.cpp b/main.cpp index 0f07e44..2bf35cc 100644 --- a/main.cpp +++ b/main.cpp @@ -140,30 +140,31 @@ int main(int argc, char *argv[]) { "create table a (i integer not null, s varchar(64), f float null, d date null, b boolean)", "insert into a (i, s, b) values(1, upper('one'), 'Y')", "insert into a (i, s, b) values(1 + 10000, upper('one'), 'Y')", + "update a set i = i * 100, f = f + 0.01 where i > 1", // "select to_string(i, '%d.%m.%Y %H:%M:%S'), i, s from a where i < to_date('20.12.2019', '%d.%m.%Y')", - "select i + 2, s, b from a where i >=1 order by 1 desc offset 0 limit 1", + "select i + 2, i, s, b, f from a where i >=1 order by 1 desc offset 0 limit 1", // "update table a set s = 'null string aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'", - "update table a set i = null", - "insert into a (i, s) values(2, 'two')", - "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')", - "insert into a (i, s) values(to_date('20.12.1973', '%d.%m.%Y'), 'six')", +// "update table a set i = null", +// "insert into a (i, s) values(2, 'two')", +// "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')", +// "insert into a (i, s) values(to_date('20.12.1973', '%d.%m.%Y'), 'six')", // tohle zpusobi kresh "insert into a (i, d) values(6', '2006-10-04')", - "insert into a (i, d) values(6, '2006-10-04')", - "save table a into '/tmp/a.csv'", - "select i, s from a where i > 2 order by 1 desc offset 1 limit 1", - "select distinct s, d from a", - "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", +// "insert into a (i, d) values(6, '2006-10-04')", +// "save table a into '/tmp/a.csv'", +// "select i, s from a where i > 2 order by 1 desc offset 1 limit 1", +// "select distinct s, d from a", +// "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", diff --git a/ml_date.cpp b/ml_date.cpp index e6ff561..ec56e7d 100644 --- a/ml_date.cpp +++ b/ml_date.cpp @@ -20,7 +20,7 @@ std::string date_to_string(const long datetime, const std::string format) { return result; } // TODO exception here - return "invalid argument"; + return "invalid argument"; } long string_to_date(const std::string &datestr, const std::string &format) { diff --git a/parser.cpp b/parser.cpp index 22cac5d..ca5788e 100644 --- a/parser.cpp +++ b/parser.cpp @@ -47,7 +47,9 @@ namespace usql { m_lexer.skipToken(TokenType::keyword_create); m_lexer.skipToken(TokenType::keyword_table); - if (m_lexer.tokenType() != TokenType::identifier) { /* TODO handle error */ } + if (m_lexer.tokenType() != TokenType::identifier) + throw Exception("syntax error, expecting identifier but found " + m_lexer.currentToken().token_string); + std::string table_name = m_lexer.consumeCurrentToken().token_string; // create as select @@ -111,7 +113,8 @@ namespace usql { m_lexer.skipTokenOptional(TokenType::comma); - // TODO in future constraints + //constraints + //defaults } while (m_lexer.tokenType() != TokenType::close_paren); return std::make_unique(table_name, cols_def); @@ -155,9 +158,13 @@ namespace usql { std::unique_ptr Parser::parse_set() { m_lexer.skipToken(TokenType::keyword_set); - // TODO check these are string literals + + if (m_lexer.currentToken().type!=TokenType::string_literal) throw Exception("Expecting literal in set name"); std::string name = m_lexer.consumeCurrentToken().token_string; + m_lexer.skipTokenOptional(TokenType::equal); + + if (m_lexer.currentToken().type!=TokenType::string_literal) throw Exception("Expecting literal in set value"); std::string value = m_lexer.consumeCurrentToken().token_string; return std::make_unique(name, value); @@ -165,7 +172,8 @@ namespace usql { std::unique_ptr Parser::parse_show() { m_lexer.skipToken(TokenType::keyword_show); - // TODO check these are string literals + + if (m_lexer.currentToken().type!=TokenType::string_literal) throw Exception("Expecting literal on show parameter name"); std::string name = m_lexer.consumeCurrentToken().token_string; return std::make_unique(name); @@ -179,13 +187,17 @@ namespace usql { m_lexer.skipToken(TokenType::keyword_into); // table name - if (m_lexer.tokenType() != TokenType::identifier) { /* TODO handle error */ } + if (m_lexer.tokenType() != TokenType::identifier) + throw Exception("syntax error, expecting identifier but found " + m_lexer.currentToken().token_string); + std::string table_name = m_lexer.consumeCurrentToken().token_string; // column names m_lexer.skipToken(TokenType::open_paren); do { - if (m_lexer.tokenType() != TokenType::identifier) { /* TODO handle error */ } + if (m_lexer.tokenType() != TokenType::identifier) + throw Exception("syntax error, expecting identifier but found " + m_lexer.currentToken().token_string); + database_values.emplace_back(m_lexer.consumeCurrentToken().token_string); m_lexer.skipTokenOptional(TokenType::comma); diff --git a/parser.h b/parser.h index 1fcb00d..0354d1e 100644 --- a/parser.h +++ b/parser.h @@ -125,11 +125,11 @@ namespace usql { bool isNull() override { return true; } - long getIntegerValue() override { throw Exception("not supported on null value"); }; - double getDoubleValue() override { throw Exception("not supported on null value"); }; - std::string getStringValue() override { throw Exception("not supported on null value"); }; - long getDateValue() override { throw Exception("not supported on null value"); }; - bool getBooleanValue() override { return false; }; + long getIntegerValue() override { throw Exception("getIntegerValue not supported on NullValueNode"); }; + double getDoubleValue() override { throw Exception("getDoubleValue not supported on NullValueNode"); }; + std::string getStringValue() override { throw Exception("getStringValue not supported on NullValueNode"); }; + long getDateValue() override { throw Exception("getDateValue not supported on NullValueNode"); }; + bool getBooleanValue() override { throw Exception("getBooleanValue not supported on NullValueNode"); }; }; struct IntValueNode : ValueNode { diff --git a/row.cpp b/row.cpp index 35267ac..e2738fb 100644 --- a/row.cpp +++ b/row.cpp @@ -8,7 +8,8 @@ namespace usql { } int ColIntegerValue::compare(ColValue * other) { - return other->isNull() ? 1 : m_integer - other->getIntValue(); // TODO implicit conversion from long to int + long r = m_integer - other->getIntValue(); + return other->isNull() ? 1 : r > 0 ? 1 : r == 0 ? 0 : -1; } int ColDoubleValue::compare(ColValue * other) { @@ -23,7 +24,8 @@ namespace usql { } int ColDateValue::compare(ColValue * other) { - return other->isNull() ? 1 : m_date - other->getIntValue(); // TODO implicit conversion from long to int + long r = m_date - other->getIntValue(); + return other->isNull() ? 1 : r > 0 ? 1 : r == 0 ? 0 : -1; } int ColBooleanValue::compare(ColValue * other) { @@ -41,28 +43,35 @@ namespace usql { Row::Row(const Row &other) { m_columns.reserve(other.m_columns.size()); - // TODO fixme, here first set cols null and then immediately replace it + // PERF here we first set cols null and then immediately replace it for (int i = 0; i < other.m_columns.size(); i++) { - m_columns.push_back(std::make_unique()); + m_columns.emplace_back(std::make_unique()); } - // TODO get rid of dynamic_cast for (int i = 0; i < other.m_columns.size(); i++) { - if (auto *other_v = dynamic_cast(other.m_columns[i].get())) { - setIntColumnValue(i, other_v->getIntValue()); - } else if (auto *other_v = dynamic_cast(other.m_columns[i].get())) { - setFloatColumnValue(i, other_v->getDoubleValue()); - } else if (auto *other_v = dynamic_cast(other.m_columns[i].get())) { - setStringColumnValue(i, other_v->getStringValue()); - } else if (auto *other_v = dynamic_cast(other.m_columns[i].get())) { - setDateColumnValue(i, other_v->getDateValue()); - } else if (auto *other_v = dynamic_cast(other.m_columns[i].get())) { - setBoolColumnValue(i, other_v->getBoolValue()); - } else if (auto *other_v = dynamic_cast(other.m_columns[i].get())) { - // NOP - } else { - throw Exception("unsupported data type"); - } + if (other.m_columns[i]->isNull()) + continue; // for null NOP + + ColumnType col_type = other.m_columns[i]->getColType(); + switch (col_type) { + case ColumnType::integer_type : + setIntColumnValue(i, (static_cast(other.m_columns[i].get())->getIntValue())); + break; + case ColumnType::float_type : + setFloatColumnValue(i, (static_cast(other.m_columns[i].get())->getDoubleValue())); + break; + case ColumnType::varchar_type : + setStringColumnValue(i, (static_cast(other.m_columns[i].get())->getStringValue())); + break; + case ColumnType::date_type : + setDateColumnValue(i, (static_cast(other.m_columns[i].get())->getDateValue())); + break; + case ColumnType::bool_type : + setBoolColumnValue(i, (static_cast(other.m_columns[i].get())->getBoolValue())); + break; + default: + throw Exception("unsupported column type"); + } } } @@ -155,7 +164,7 @@ void Row::setColumnValue(ColDefNode *col_def, ValueNode *col_value) { for (int ci = 0; ci < m_columns.size(); ci++) { auto value = m_columns[ci]->getStringValue(); - // TODO use string functions handle len + // TODO use rpad string function handle len out.append(value + std::string(col_char_sizes[ci] - value.size(), ' ') + " | "); } diff --git a/row.h b/row.h index a50b99a..723b6a2 100644 --- a/row.h +++ b/row.h @@ -10,6 +10,7 @@ namespace usql { struct ColValue { virtual bool isNull() { return false; }; + virtual ColumnType getColType() = 0; virtual long getIntValue() = 0; virtual double getDoubleValue() = 0; virtual std::string getStringValue() = 0; @@ -25,11 +26,12 @@ namespace usql { struct ColNullValue : ColValue { bool isNull() override { return true; }; - long getIntValue() override { throw Exception("Not supported"); }; - double getDoubleValue() override { throw Exception("Not supported"); }; + ColumnType getColType() override { throw Exception("getColType not supported on ColNullValue"); } + long getIntValue() override { throw Exception("getIntValue not supported on ColNullValue"); }; + double getDoubleValue() override { throw Exception("getDoubleValue not supported on ColNullValue"); }; std::string getStringValue() override { return "null"; }; - long getDateValue() override { throw Exception("Not supported"); }; - bool getBoolValue() override { throw Exception("Not supported"); }; + long getDateValue() override { throw Exception("getDateValue not supported on ColNullValue"); }; + bool getBoolValue() override { throw Exception("getDateValue not supported on ColNullValue"); }; int compare(ColValue * other) override; }; @@ -39,11 +41,12 @@ namespace usql { ColIntegerValue(long value) : m_integer(value) {}; ColIntegerValue(const ColIntegerValue &other) : m_integer(other.m_integer) {}; + ColumnType getColType() override { return ColumnType::integer_type; }; long getIntValue() override { return m_integer; }; double getDoubleValue() override { return (double) m_integer; }; std::string getStringValue() override { return std::to_string(m_integer); }; long getDateValue() override { return m_integer; }; - bool getBoolValue() override { throw Exception("Not supported"); }; + bool getBoolValue() override { throw Exception("Not supported on ColIntegerValue"); }; int compare(ColValue * other) override; @@ -55,11 +58,12 @@ namespace usql { ColDoubleValue(double value) : m_double(value) {}; ColDoubleValue(const ColDoubleValue &other) : m_double(other.m_double) {} + ColumnType getColType() override { return ColumnType::float_type; }; long getIntValue() override { return (long) m_double; }; double getDoubleValue() override { return m_double; }; std::string getStringValue() override { return std::to_string(m_double); }; long getDateValue() override { return (long) m_double; }; - bool getBoolValue() override { throw Exception("Not supported"); }; + bool getBoolValue() override { throw Exception("Not supported on ColDoubleValue"); }; int compare(ColValue * other) override; @@ -71,11 +75,12 @@ namespace usql { ColStringValue(const std::string &value) : m_string(value) {}; ColStringValue(const ColStringValue &other) : m_string(other.m_string) {}; + ColumnType getColType() override { return ColumnType::varchar_type; }; long getIntValue() override { return std::stoi(m_string); }; double getDoubleValue() override { return std::stod(m_string); }; std::string getStringValue() override { return m_string; }; long getDateValue() override { return std::stoi(m_string); }; - bool getBoolValue() override { throw Exception("Not supported"); }; + bool getBoolValue() override { throw Exception("Not supported on ColStringValue"); }; int compare(ColValue * other) override; @@ -86,11 +91,12 @@ namespace usql { ColDateValue(long value) : m_date(value) {}; ColDateValue(const ColDateValue &other) : m_date(other.m_date) {}; + ColumnType getColType() override { return ColumnType::date_type; }; long getIntValue() override { return m_date; }; double getDoubleValue() override { return (double) m_date; }; std::string getStringValue() override { return Settings::date_to_string(m_date); }; long getDateValue() override { return m_date; }; - bool getBoolValue() override { throw Exception("Not supported"); }; + bool getBoolValue() override { throw Exception("Not supported on ColDateValue"); }; int compare(ColValue * other) override; @@ -101,10 +107,11 @@ namespace usql { ColBooleanValue(bool value) : m_bool(value) {}; ColBooleanValue(const ColBooleanValue &other) : m_bool(other.m_bool) {}; + ColumnType getColType() override { return ColumnType::bool_type; }; long getIntValue() override { return (long) m_bool; }; double getDoubleValue() override { return (double) m_bool; }; std::string getStringValue() override { return m_bool ? "Y" : "N"; }; - long getDateValue() override { throw Exception("Not supported"); }; + long getDateValue() override { throw Exception("Not supported on ColBooleanValue"); }; bool getBoolValue() override { return m_bool; }; int compare(ColValue * other) override; diff --git a/table.cpp b/table.cpp index eafa6b4..c724a04 100644 --- a/table.cpp +++ b/table.cpp @@ -62,7 +62,7 @@ std::string Table::csv_string() { auto col = m_row.ith_column(i); if (!col->isNull()) { - csv_line += col->getStringValue(); // TODO handle enclosing commas etc + csv_line += col->getStringValue(); // TODO handle enclosing commas etc } } out_string += csv_line; diff --git a/usql.cpp b/usql.cpp index 6f03902..0377944 100644 --- a/usql.cpp +++ b/usql.cpp @@ -236,7 +236,8 @@ void USql::execute_order_by(SelectFromTableNode &node, Table *table, Table *resu auto compare_rows = [&node, &result](const Row &a, const Row &b) { for(const auto& order_by_col_def : node.order_by) { - ColDefNode col_def = result->get_column_def(order_by_col_def.col_index - 1); // TODO validate index + // TODO validate index + ColDefNode col_def = result->get_column_def(order_by_col_def.col_index - 1); ColValue *a_val = a.ith_column(col_def.order); ColValue *b_val = b.ith_column(col_def.order); @@ -282,6 +283,7 @@ std::tuple USql::get_column_definition(Table *table, SelectColN } else if (select_col_node->value->node_type == NodeType::arithmetical_operator) { // TODO return correct type + // hierarchicaly go throuhg and deduce right type ColDefNode cdef = ColDefNode{new_col_name, ColumnType::float_type, col_order, 1, true}; return std::make_tuple(-1, cdef); } @@ -289,6 +291,7 @@ std::tuple USql::get_column_definition(Table *table, SelectColN } + std::unique_ptr USql::execute_delete(DeleteFromTableNode &node) { // find source table Table *table = find_table(node.table_name); @@ -318,7 +321,8 @@ std::unique_ptr
USql::execute_update(UpdateTableNode &node) { if (eval_where(node.where.get(), table, *row)) { int i = 0; for (const auto& col : node.cols_names) { - ColDefNode col_def = table->get_column_def(col.col_name); // TODO cache it like in select + // TODO cache it like in select + ColDefNode col_def = table->get_column_def(col.col_name); std::unique_ptr new_val = eval_arithmetic_operator(col_def.type, static_cast(*node.values[i]), table, *row); @@ -372,7 +376,7 @@ bool USql::eval_relational_operator(const RelationalOperatorNode &filter, Table comparator = bl == br ? 0 : 1; // TODO define it // TODO handle dates } else { - // TODO throw exception + throw Exception("Undefined combination of types"); } switch (filter.op) { @@ -416,6 +420,9 @@ std::unique_ptr USql::eval_database_value_node(Table *table, Row &row ColDefNode col_def = table->get_column_def( dvl->col_name); // TODO optimize it to just get this def once auto db_value = row.ith_column(col_def.order); + if (db_value->isNull()) + return std::make_unique(); + if (col_def.type == ColumnType::integer_type) return std::make_unique(db_value->getIntValue()); if (col_def.type == ColumnType::float_type) @@ -513,6 +520,9 @@ std::unique_ptr USql::eval_arithmetic_operator(ColumnType outType, Ar std::unique_ptr left = eval_value_node(table, row, node.left.get()); std::unique_ptr right = eval_value_node(table, row, node.right.get()); + if (left->isNull() || right->isNull()) + return std::make_unique(); + if (outType == ColumnType::float_type) { double l = ((ValueNode *) left.get())->getDoubleValue(); double r = ((ValueNode *) right.get())->getDoubleValue();