fixes and improvements

exceptions related code needs work
This commit is contained in:
VaclavT 2021-07-22 23:57:42 +02:00
parent 977a9cd198
commit 6d4fe43a3b
15 changed files with 174 additions and 102 deletions

1
.gitignore vendored
View File

@ -1,2 +1,3 @@
build build
cmake-build-debug cmake-build-debug
.DS_Store

View File

@ -1,11 +1,11 @@
### TODO ### TODO
- save table command - drop table command
- move csv generation from usql(save_table) to table class - move csv generation from usql(save_table) to table class
- add exceptions - add exceptions and rename it to UsqlException
- class members should have prefix m_ - class members should have prefix m_
- maybe to create iterator on table
- add pipe | token - add pipe | token
- add to_date a to_string functions - add count min and max functions, eg aggregate functions
- add min and max functions, eg aggregate functions
- add logging - add logging
- add const wherever should be - add const wherever should be

View File

@ -2,7 +2,7 @@
namespace usql { namespace usql {
Exception::Exception(const std::string &msg) { Exception::Exception(const std::string &msg) : std::runtime_error(msg) {
cause = msg; cause = msg;
} }

View File

@ -6,7 +6,7 @@
namespace usql { namespace usql {
class Exception : public std::exception { class Exception : public std::runtime_error {
private: private:
std::string cause; std::string cause;

View File

@ -13,12 +13,12 @@ namespace usql {
Lexer::Lexer() { Lexer::Lexer() {
k_words_regex = k_words_regex =
"[0-9]+\\.[0-9]+|[0-9][0-9_]+[0-9]|[0-9]+|[A-Za-z]+[A-Za-z0-9_#]*|[\\(\\)\\[\\]\\{\\}]|[-\\+\\*/" "[-+]?[0-9]+\\.[0-9]+|[-+]?[0-9][0-9_]+[0-9]|[0-9]+|[A-Za-z]+[A-Za-z0-9_#]*|[\\(\\)\\[\\]\\{\\}]|[-\\+\\*/"
",;:\?]|==|>=|<=|~=|>|<|=|;|~|\\||or|and|\n|\r|\r\n|'([^']|'')*'|\".*?\"|%.*?\n"; ",;:\?]|==|>=|<=|~=|>|<|=|;|~|\\||or|and|\n|\r|\r\n|'([^']|'')*'|\".*?\"|%.*?\n";
k_int_regex = "[0-9]+"; k_int_regex = "[-+]?[0-9]+";
k_int_underscored_regex = "[0-9][0-9_]+[0-9]"; k_int_underscored_regex = "[-+]?[0-9][0-9_]+[0-9]";
k_double_regex = "[0-9]+\\.[0-9]+"; k_double_regex = "[-+]?[0-9]+\\.[0-9]+";
k_identifier_regex = "[A-Za-z]+[A-Za-z0-9_#]*"; k_identifier_regex = "[A-Za-z]+[A-Za-z0-9_#]*";
} }
void Lexer::parse(const std::string &code) { void Lexer::parse(const std::string &code) {
@ -208,7 +208,7 @@ namespace usql {
return TokenType::keyword_null; return TokenType::keyword_null;
if (token == "integer") if (token == "integer")
return TokenType::keyword_int; return TokenType::keyword_integer;
if (token == "float") if (token == "float")
return TokenType::keyword_float; return TokenType::keyword_float;
@ -374,7 +374,7 @@ namespace usql {
case TokenType::keyword_null: case TokenType::keyword_null:
txt = "null"; txt = "null";
break; break;
case TokenType::keyword_int: case TokenType::keyword_integer:
txt = "integer"; txt = "integer";
break; break;
case TokenType::keyword_float: case TokenType::keyword_float:

View File

@ -37,7 +37,7 @@ namespace usql {
keyword_copy, keyword_copy,
keyword_not, keyword_not,
keyword_null, keyword_null,
keyword_int, keyword_integer,
keyword_float, keyword_float,
keyword_varchar, keyword_varchar,
int_number, int_number,

View File

@ -11,6 +11,8 @@ int main(int argc, char *argv[]) {
std::vector<std::string> sql_commands{ std::vector<std::string> sql_commands{
"create table a (i integer not null, s varchar(64), f float null)", "create table a (i integer not null, s varchar(64), f float null)",
"insert into a (i, s) values(1, upper('one'))", "insert into a (i, s) values(1, upper('one'))",
"update table a set s = 'null string aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'",
// "update table a set i = null",
"insert into a (i, s) values(2, 'two')", "insert into a (i, s) values(2, 'two')",
"insert into a (i, s) values(3, 'two')", "insert into a (i, s) values(3, 'two')",
"insert into a (i, s) values(4, lower('FOUR'))", "insert into a (i, s) values(4, lower('FOUR'))",
@ -35,7 +37,10 @@ int main(int argc, char *argv[]) {
"create table x as 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", "select i, s, f from x where i < 300",
"select i, s, f from a where i > 300", "select i, s, f from a where i > 300",
"select i, to_string(i, '%d.%m.%Y'), s, f from a where i > 300" "select i, to_string(i, '%d.%m.%Y'), s, f from a where i > 300",
"create table prices (datetime integer, symbol varchar(8), prev_close float, open float, price float, change float, change_prct varchar(16))",
"insert into prices (datetime, symbol, prev_close, open, price, change, change_prct) values (1626979443, 'MPC', 54.08, 53.82, 53.63, -0.832101, '-0.83 %')",
"select to_string(datetime, '%d.%m.%Y %H:%M:%S'), symbol, prev_close, open, price, change, change_prct from prices"
}; };

View File

@ -65,11 +65,13 @@ namespace usql {
bool column_nullable{true}; bool column_nullable{true};
// column name // column name
if (m_lexer.tokenType() != TokenType::identifier) { /* TODO handle error */ } if (m_lexer.tokenType() != TokenType::identifier) {
throw Exception("syntax error, expected identifier");
}
column_name = m_lexer.consumeCurrentToken().token_string; column_name = m_lexer.consumeCurrentToken().token_string;
// column type and optionally len // column type and optionally len
if (m_lexer.tokenType() == TokenType::keyword_int) { if (m_lexer.tokenType() == TokenType::keyword_integer) {
column_type = ColumnType::integer_type; column_type = ColumnType::integer_type;
m_lexer.nextToken(); m_lexer.nextToken();
} else if (m_lexer.tokenType() == TokenType::keyword_float) { } else if (m_lexer.tokenType() == TokenType::keyword_float) {
@ -81,9 +83,13 @@ namespace usql {
m_lexer.skipToken(TokenType::open_paren); m_lexer.skipToken(TokenType::open_paren);
if (m_lexer.tokenType() == TokenType::int_number) { if (m_lexer.tokenType() == TokenType::int_number) {
column_len = std::stoi(m_lexer.consumeCurrentToken().token_string); column_len = std::stoi(m_lexer.consumeCurrentToken().token_string);
} else { /* TODO handle error */ } } else {
throw Exception("syntax error, expected int number");
}
m_lexer.skipToken(TokenType::close_paren); m_lexer.skipToken(TokenType::close_paren);
} else { /* TODO handle error */ } } else {
throw Exception("syntax error, column type expected");
}
if (m_lexer.tokenType() == TokenType::keyword_not) { if (m_lexer.tokenType() == TokenType::keyword_not) {
m_lexer.nextToken(); m_lexer.nextToken();
@ -169,7 +175,7 @@ std::unique_ptr<Node> Parser::parse_value() {
return std::make_unique<ColNameNode>(name); return std::make_unique<ColNameNode>(name);
} }
throw Exception("Syntax error"); throw Exception("Syntax error, current token: " + m_lexer.currentToken().token_string);
} }
std::unique_ptr<Node> Parser::parse_select_from_table() { std::unique_ptr<Node> Parser::parse_select_from_table() {
@ -326,6 +332,8 @@ std::unique_ptr<Node> Parser::parse_select_from_table() {
return std::make_unique<StringValueNode>(tokenString); return std::make_unique<StringValueNode>(tokenString);
case TokenType::identifier: case TokenType::identifier:
return std::make_unique<DatabaseValueNode>(tokenString); return std::make_unique<DatabaseValueNode>(tokenString);
case TokenType::keyword_null:
return std::make_unique<NullValueNode>();
default:; default:;
throw Exception("Unknown operand node"); throw Exception("Unknown operand node");
} }

View File

@ -1,7 +1,7 @@
#pragma once #pragma once
#include "lexer.h" #include "lexer.h"
#include <stdexcept> #include "exception.h"
#include <string> #include <string>
#include <vector> #include <vector>
@ -16,6 +16,7 @@ namespace usql {
enum class NodeType { enum class NodeType {
true_node, true_node,
null_value,
int_value, int_value,
float_value, float_value,
string_value, string_value,
@ -65,7 +66,7 @@ namespace usql {
int length; int length;
bool null; bool null;
ColDefNode(const std::string col_name, const ColumnType col_type, int col_order, int col_len, bool nullable) : ColDefNode(const std::string col_name, ColumnType col_type, int col_order, int col_len, bool nullable) :
Node(NodeType::column_def), name(col_name), type(col_type), order(col_order), length(col_len), Node(NodeType::column_def), name(col_name), type(col_type), order(col_order), length(col_len),
null(nullable) {} null(nullable) {}
}; };
@ -93,6 +94,17 @@ namespace usql {
virtual ~ValueNode() {}; virtual ~ValueNode() {};
}; };
struct NullValueNode : ValueNode {
NullValueNode() : ValueNode(NodeType::null_value) {}
bool isNull() override { return true; }
long getIntValue() 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"); };
};
struct IntValueNode : ValueNode { struct IntValueNode : ValueNode {
long value; long value;

View File

@ -6,7 +6,7 @@ namespace usql {
Row::Row(int cols_count) { Row::Row(int cols_count) {
m_columns.reserve(cols_count); m_columns.reserve(cols_count);
for (int i = 0; i < cols_count; i++) { for (int i = 0; i < cols_count; i++) {
m_columns.push_back(std::make_unique<ColValue>()); m_columns.push_back(std::make_unique<ColNullValue>());
} }
} }

2
row.h
View File

@ -82,7 +82,7 @@ namespace usql {
return *m_columns[i]; return *m_columns[i];
} }
ColValue * ithColumn(int i) const { ColValue * ith_column(int i) const {
return m_columns[i].get(); return m_columns[i].get();
} }

View File

@ -20,10 +20,34 @@ ColDefNode Table::get_column_def(const std::string &col_name) {
} }
Row Table::createEmptyRow() { Row Table::create_empty_row() {
return Row(columns_count()); return Row(columns_count());
} }
std::string Table::csv_string() {
// header
std::string out_string;
for(int i = 0; i < m_col_defs.size(); i++) {
if (i > 0) out_string += ",";
out_string += m_col_defs[i].name;
}
// rows
for (auto it = m_rows.begin(); it != m_rows.end(); ++it) {
std::string csv_line{"\n"};
for(int i = 0; i < m_col_defs.size(); i++) {
if (i > 0) csv_line += ",";
auto col = it->ith_column(i);
if (!col->isNull()) {
csv_line += col->getStringValue(); // TODO handle enclosing commas etc
}
}
out_string += csv_line;
}
return out_string;
}
void Table::print() { void Table::print() {
std::cout << "** " << m_name << " **" << std::endl; std::cout << "** " << m_name << " **" << std::endl;
@ -36,38 +60,63 @@ Table::Table(const Table &other) {
m_name = other.m_name; m_name = other.m_name;
m_col_defs = other.m_col_defs; m_col_defs = other.m_col_defs;
for(const Row& orig_row : other.m_rows) { for(const Row& orig_row : other.m_rows) {
addCopyOfRow(orig_row); add_copy_of_row(orig_row);
} }
} }
void Table::addRow(const Row &row) { void Table::add_row(const Row &row) {
// TODO validate for not null values validate_row(row);
// todo validate for length etc
m_rows.push_back(row); m_rows.push_back(row);
} }
void Table::addCopyOfRow(const Row &row) { void Table::add_copy_of_row(const Row &row) {
// TODO validate for not null values Row new_row = create_empty_row();
// todo validate for length etc
Row new_row = createEmptyRow();
for(int i = 0; i < m_col_defs.size(); i++) { for(int i = 0; i < m_col_defs.size(); i++) {
ColValue *ct = row.ithColumn(i); ColValue *ct = row.ith_column(i);
if (ct->isNull()) { if (ct->isNull()) {
new_row.setColumnNull(i); new_row.setColumnNull(i);
} else { } else {
if (m_col_defs[i].type == ColumnType::integer_type) { if (m_col_defs[i].type == ColumnType::integer_type) {
new_row.setColumnValue(i, row.ithColumn(i)->getIntValue()); new_row.setColumnValue(i, row.ith_column(i)->getIntValue());
} else if (m_col_defs[i].type == ColumnType::float_type) { } else if (m_col_defs[i].type == ColumnType::float_type) {
new_row.setColumnValue(i, row.ithColumn(i)->getDoubleValue()); new_row.setColumnValue(i, row.ith_column(i)->getDoubleValue());
} else if (m_col_defs[i].type == ColumnType::varchar_type) { } else if (m_col_defs[i].type == ColumnType::varchar_type) {
new_row.setColumnValue(i, row.ithColumn(i)->getStringValue()); new_row.setColumnValue(i, row.ith_column(i)->getStringValue());
} }
} }
} }
m_rows.push_back(row);
validate_row(new_row);
m_rows.push_back(new_row);
}
void Table::validate_column(const ColDefNode *col_def, ValueNode *col_val) {
if (col_def->null == false && col_val->isNull()) {
throw Exception("Column " + col_def->name + " cannot be null");
}
if (col_def->type == ColumnType::varchar_type && !col_val->isNull() && col_val->getStringValue().size() > col_def->length) {
throw Exception("Column value of " + col_def->name + " is too long (" + col_val->getStringValue() + ")");
}
}
void Table::validate_column(const ColDefNode *col_def, ColValue *col_val) {
if (col_def->null == false && col_val->isNull()) {
throw Exception("Column " + col_def->name + " cannot be null");
}
if (col_def->type == ColumnType::varchar_type && !col_val->isNull() && col_val->getStringValue().size() > col_def->length) {
throw Exception("Column value of " + col_def->name + " is too long (" + col_val->getStringValue() + ")");
}
}
void Table::validate_row(const Row &row) {
for(int i = 0; i < m_col_defs.size(); i++) {
ColDefNode col_def = m_col_defs[i];
ColValue *col_val = row.ith_column(i);
validate_column(&col_def, col_val);
}
} }
} // namespace } // namespace

15
table.h
View File

@ -4,6 +4,7 @@
#include "row.h" #include "row.h"
#include <vector> #include <vector>
#include <list>
namespace usql { namespace usql {
@ -16,15 +17,21 @@ namespace usql {
int columns_count() const { return m_col_defs.size(); }; int columns_count() const { return m_col_defs.size(); };
Row createEmptyRow(); // TODO this means unnecessary copying Row create_empty_row(); // TODO this means unnecessary copying
void addRow(const Row &row); void add_row(const Row &row);
void addCopyOfRow(const Row &row); void add_copy_of_row(const Row &row);
void validate_column(const ColDefNode *col_def, ValueNode *col_val);
void validate_column(const ColDefNode *col_def, ColValue *col_val);
void validate_row(const Row &row);
std::string csv_string();
void print(); void print();
std::string m_name; std::string m_name;
std::vector<ColDefNode> m_col_defs; std::vector<ColDefNode> m_col_defs;
std::vector<Row> m_rows; std::list<Row> m_rows;
}; };
} }

103
usql.cpp
View File

@ -9,8 +9,14 @@
namespace usql { namespace usql {
std::unique_ptr<Table> USql::execute(const std::string &command) { std::unique_ptr<Table> USql::execute(const std::string &command) {
std::unique_ptr<Node> node = m_parser.parse(command); try {
return execute(*node); std::unique_ptr<Node> node = m_parser.parse(command);
return execute(*node);
} catch (std::exception &e) {
return create_stmt_result_table(-1, e.what());
}
} }
std::unique_ptr<Table> USql::execute(Node &node) { std::unique_ptr<Table> USql::execute(Node &node) {
@ -39,7 +45,8 @@ std::unique_ptr<Table> USql::execute(Node &node) {
std::unique_ptr<Table> USql::execute_create_table(CreateTableNode &node) { std::unique_ptr<Table> USql::execute_create_table(CreateTableNode &node) {
// TODO check table does not exists check_table_not_exists(node.table_name);
Table table{node.table_name, node.cols_defs}; Table table{node.table_name, node.cols_defs};
m_tables.push_back(table); m_tables.push_back(table);
@ -48,7 +55,7 @@ std::unique_ptr<Table> USql::execute_create_table(CreateTableNode &node) {
std::unique_ptr<Table> USql::execute_create_table_as_table(CreateTableAsSelectNode &node) { std::unique_ptr<Table> USql::execute_create_table_as_table(CreateTableAsSelectNode &node) {
// TODO check table does not exists check_table_not_exists(node.table_name);
auto select = execute_select((SelectFromTableNode &) *node.select_table); auto select = execute_select((SelectFromTableNode &) *node.select_table);
@ -60,7 +67,7 @@ std::unique_ptr<Table> USql::execute_create_table_as_table(CreateTableAsSelectNo
// must be here, if rows are put into new_table, they are lost during m_tables.push_table // 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); Table *table = find_table(node.table_name);
for( Row& orig_row : select->m_rows) { for( Row& orig_row : select->m_rows) {
table->addCopyOfRow(orig_row); table->add_copy_of_row(orig_row);
} }
select.release(); // is it correct? hoping not to release select table here and then when releasing CreateTableAsSelectNode select.release(); // is it correct? hoping not to release select table here and then when releasing CreateTableAsSelectNode
@ -76,7 +83,7 @@ std::unique_ptr<Table> USql::execute_insert_into_table(InsertIntoTableNode &node
Table *table_def = find_table(node.table_name); Table *table_def = find_table(node.table_name);
// prepare empty new_row // prepare empty new_row
Row new_row = table_def->createEmptyRow(); Row new_row = table_def->create_empty_row();
// copy values // copy values
for (size_t i = 0; i < node.cols_names.size(); i++) { for (size_t i = 0; i < node.cols_names.size(); i++) {
@ -87,7 +94,7 @@ std::unique_ptr<Table> USql::execute_insert_into_table(InsertIntoTableNode &node
} }
// append new_row // append new_row
table_def->addRow(new_row); table_def->add_row(new_row);
return create_stmt_result_table(0, "insert succeeded"); return create_stmt_result_table(0, "insert succeeded");
} }
@ -103,7 +110,8 @@ std::unique_ptr<Table> USql::execute_select(SelectFromTableNode &node) {
std::vector<int> source_table_col_index{}; std::vector<int> source_table_col_index{};
for (int i = 0; i < node.cols_names->size(); i++) { for (int i = 0; i < node.cols_names->size(); i++) {
auto [ src_tbl_col_index, rst_tbl_col_def ] = getColumnDefinition(table, &node.cols_names->operator[](i), i); auto [ src_tbl_col_index, rst_tbl_col_def ] = get_column_definition(table,
&node.cols_names->operator[](i), i);
source_table_col_index.push_back(src_tbl_col_index); source_table_col_index.push_back(src_tbl_col_index);
result_tbl_col_defs.push_back(rst_tbl_col_def); result_tbl_col_defs.push_back(rst_tbl_col_def);
@ -116,7 +124,7 @@ std::unique_ptr<Table> USql::execute_select(SelectFromTableNode &node) {
// eval where for row // eval where for row
if (evalWhere(node.where.get(), table, *row)) { if (evalWhere(node.where.get(), table, *row)) {
// prepare empty row // prepare empty row
Row new_row = result->createEmptyRow(); Row new_row = result->create_empty_row();
// copy column values // copy column values
for (auto idx = 0; idx < result->columns_count(); idx++) { for (auto idx = 0; idx < result->columns_count(); idx++) {
@ -128,7 +136,7 @@ std::unique_ptr<Table> USql::execute_select(SelectFromTableNode &node) {
new_row.setColumnValue(&result_tbl_col_defs[idx], col_value); new_row.setColumnValue(&result_tbl_col_defs[idx], col_value);
} else { } else {
ColValue *col_value = row->ithColumn(row_col_index); ColValue *col_value = row->ith_column(row_col_index);
new_row.setColumnValue(&result_tbl_col_defs[idx], col_value); new_row.setColumnValue(&result_tbl_col_defs[idx], col_value);
} }
} }
@ -141,7 +149,7 @@ std::unique_ptr<Table> USql::execute_select(SelectFromTableNode &node) {
return std::move(result); return std::move(result);
} }
std::tuple<int, ColDefNode> USql::getColumnDefinition(Table *table, SelectColNode *select_col_node, int col_order ) { std::tuple<int, ColDefNode> USql::get_column_definition(Table *table, SelectColNode *select_col_node, int col_order ) {
std::string new_col_name = select_col_node->name; std::string new_col_name = select_col_node->name;
if (select_col_node->value->node_type == NodeType::column_name) { if (select_col_node->value->node_type == NodeType::column_name) {
@ -173,7 +181,6 @@ std::unique_ptr<Table> USql::execute_delete(DeleteFromTableNode &node) {
auto it = table->m_rows.begin(); auto it = table->m_rows.begin();
for (; it != table->m_rows.end();) { for (; it != table->m_rows.end();) {
if (evalWhere(node.where.get(), table, *it)) { if (evalWhere(node.where.get(), table, *it)) {
// TODO this can be really expensive operation
it = table->m_rows.erase(it); it = table->m_rows.erase(it);
} else { } else {
++it; ++it;
@ -194,24 +201,15 @@ std::unique_ptr<Table> USql::execute_update(UpdateTableNode &node) {
if (evalWhere(node.where.get(), table, *row)) { if (evalWhere(node.where.get(), table, *row)) {
int i = 0; int i = 0;
for (const auto& col : node.cols_names) { for (const auto& col : node.cols_names) {
// TODO cache it like in select ColDefNode col_def = table->get_column_def(col.name); // TODO cache it like in select
ColDefNode cdef = table->get_column_def(col.name); std::unique_ptr<ValueNode> new_val = evalArithmeticOperator(col_def.type,
static_cast<ArithmeticalOperatorNode &>(*node.values[i]), table, *row);
std::unique_ptr<ValueNode> new_val = evalArithmeticOperator(cdef.type, table->validate_column(&col_def, new_val.get());
static_cast<ArithmeticalOperatorNode &>(*node.values[i]), row->setColumnValue(&col_def, new_val.get());
table, *row);
if (cdef.type == ColumnType::integer_type) {
row->setColumnValue(cdef.order, new_val->getIntValue());
} else if (cdef.type == ColumnType::float_type) {
row->setColumnValue(cdef.order, new_val->getDoubleValue());
} else if (cdef.type == ColumnType::varchar_type) {
row->setColumnValue(cdef.order, new_val->getStringValue());
} else {
throw Exception("Implement me!");
}
i++; i++;
} }
// TODO tady je problem, ze kdyz to zfajluje na jednom radku ostatni by se nemely provest
} }
} }
@ -237,7 +235,7 @@ std::unique_ptr<Table> USql::execute_load(LoadIntoTableNode &node) {
std::vector<std::string> csv_line = *it; std::vector<std::string> csv_line = *it;
// prepare empty new_row // prepare empty new_row
Row new_row = table_def->createEmptyRow(); Row new_row = table_def->create_empty_row();
// copy values // copy values
for (size_t i = 0; i < table_def->columns_count(); i++) { for (size_t i = 0; i < table_def->columns_count(); i++) {
@ -254,7 +252,7 @@ std::unique_ptr<Table> USql::execute_load(LoadIntoTableNode &node) {
} }
// append new_row // append new_row
table_def->addRow(new_row); table_def->add_row(new_row);
} }
return create_stmt_result_table(0, "load succeeded"); return create_stmt_result_table(0, "load succeeded");
@ -265,31 +263,11 @@ std::unique_ptr<Table> USql::execute_save(SaveTableNode &node) {
// find source table // find source table
Table *table_def = find_table(node.table_name); Table *table_def = find_table(node.table_name);
// header std::string csv_string = table_def->csv_string();
std::string out_string;
for(int i = 0; i < table_def->m_col_defs.size(); i++) {
if (i > 0) out_string += ",";
out_string += table_def->m_col_defs[i].name;
}
// rows
for (auto it = table_def->m_rows.begin() + 1; it != table_def->m_rows.end(); ++it) {
std::string csv_line;
for(int i = 0; i < table_def->m_col_defs.size(); i++) {
if (i > 0) csv_line += ",";
auto col = it->ithColumn(i);
if (!col->isNull()) {
csv_line += col->getStringValue(); // TODO handle enclosing commas etc
}
}
out_string += "\n";
out_string += csv_line;
}
// save data // save data
std::ofstream file(node.filename); std::ofstream file(node.filename);
file << out_string; file << csv_string;
file.close(); file.close();
return create_stmt_result_table(0, "save succeeded"); return create_stmt_result_table(0, "save succeeded");
@ -358,6 +336,8 @@ std::unique_ptr<ValueNode> USql::evalValueNode(Table *table, Row &row, Node *nod
} else if (node->node_type == NodeType::function) { } else if (node->node_type == NodeType::function) {
return evalFunctionValueNode(table, row, node); return evalFunctionValueNode(table, row, node);
} else if (node->node_type == NodeType::null_value) {
return std::make_unique<NullValueNode>();
} }
throw Exception("unsupported node type"); throw Exception("unsupported node type");
} }
@ -366,16 +346,16 @@ std::unique_ptr<ValueNode> USql::evalValueNode(Table *table, Row &row, Node *nod
std::unique_ptr<ValueNode> USql::evalDatabaseValueNode(Table *table, Row &row, Node *node) { std::unique_ptr<ValueNode> USql::evalDatabaseValueNode(Table *table, Row &row, Node *node) {
auto *dvl = static_cast<DatabaseValueNode *>(node); auto *dvl = static_cast<DatabaseValueNode *>(node);
ColDefNode col_def = table->get_column_def( dvl->col_name); // TODO optimize it to just get this def once ColDefNode col_def = table->get_column_def( dvl->col_name); // TODO optimize it to just get this def once
auto db_value = row.ithColumn(col_def.order); auto db_value = row.ith_column(col_def.order);
if (col_def.type == ColumnType::integer_type) { if (col_def.type == ColumnType::integer_type) {
return std::__1::make_unique<IntValueNode>(db_value->getIntValue()); return std::make_unique<IntValueNode>(db_value->getIntValue());
} }
if (col_def.type == ColumnType::float_type) { if (col_def.type == ColumnType::float_type) {
return std::__1::make_unique<DoubleValueNode>(db_value->getDoubleValue()); return std::make_unique<DoubleValueNode>(db_value->getDoubleValue());
} }
if (col_def.type == ColumnType::varchar_type) { if (col_def.type == ColumnType::varchar_type) {
return std::__1::make_unique<StringValueNode>(db_value->getStringValue()); return std::make_unique<StringValueNode>(db_value->getStringValue());
} }
throw Exception("unknown database value type"); throw Exception("unknown database value type");
} }
@ -471,6 +451,7 @@ std::unique_ptr<ValueNode> USql::evalArithmeticOperator(ColumnType outType, Arit
default: default:
throw Exception("implement me!!"); throw Exception("implement me!!");
} }
} else if (outType == ColumnType::integer_type) { } else if (outType == ColumnType::integer_type) {
long l = ((ValueNode *) left.get())->getIntValue(); long l = ((ValueNode *) left.get())->getIntValue();
long r = ((ValueNode *) right.get())->getIntValue(); long r = ((ValueNode *) right.get())->getIntValue();
@ -509,10 +490,10 @@ std::unique_ptr<Table> USql::create_stmt_result_table(long code, const std::stri
auto table_def = std::make_unique<Table>("result", result_tbl_col_defs); auto table_def = std::make_unique<Table>("result", result_tbl_col_defs);
Row new_row = table_def->createEmptyRow(); Row new_row = table_def->create_empty_row();
new_row.setColumnValue(0, code); new_row.setColumnValue(0, code);
new_row.setColumnValue(1, text); new_row.setColumnValue(1, text);
table_def->addRow(new_row); table_def->add_row(new_row);
return std::move(table_def); return std::move(table_def);
} }
@ -529,4 +510,12 @@ Table *USql::find_table(const std::string &name) {
} }
} }
void USql::check_table_not_exists(const std::string &name) {
auto name_cmp = [name](const Table& t) { return t.m_name == name; };
auto table_def = std::find_if(begin(m_tables), end(m_tables), name_cmp);
if (table_def != std::end(m_tables)) {
throw Exception("table already exists");
}
}
} // namespace } // namespace

3
usql.h
View File

@ -44,9 +44,10 @@ private:
static std::unique_ptr<Table> create_stmt_result_table(long code, const std::string& text); static std::unique_ptr<Table> create_stmt_result_table(long code, const std::string& text);
static std::tuple<int, ColDefNode> getColumnDefinition(Table *table, SelectColNode *select_col_node, int col_order) ; static std::tuple<int, ColDefNode> get_column_definition(Table *table, SelectColNode *select_col_node, int col_order) ;
Table *find_table(const std::string &name); Table *find_table(const std::string &name);
void check_table_not_exists(const std::string &name);
private: private:
Parser m_parser; Parser m_parser;