usql update
This commit is contained in:
parent
37d0d9b3f5
commit
5c925f2608
|
|
@ -44,6 +44,7 @@ set(SOURCE
|
||||||
usql/usql.cpp
|
usql/usql.cpp
|
||||||
usql/usql_dml.cpp
|
usql/usql_dml.cpp
|
||||||
usql/usql_ddl.cpp
|
usql/usql_ddl.cpp
|
||||||
|
usql/usql_function.cpp
|
||||||
usql/table.cpp
|
usql/table.cpp
|
||||||
usql/table.h
|
usql/table.h
|
||||||
usql/row.cpp
|
usql/row.cpp
|
||||||
|
|
|
||||||
|
|
@ -24,9 +24,9 @@ MlValue uSQL::ivaluize(const usql::Table *table) {
|
||||||
if (c.isNull()) {
|
if (c.isNull()) {
|
||||||
columns.push_back(MlValue::nil());
|
columns.push_back(MlValue::nil());
|
||||||
} else if (type == ColumnType::integer_type || type == ColumnType::date_type) {
|
} else if (type == ColumnType::integer_type || type == ColumnType::date_type) {
|
||||||
columns.push_back(MlValue(c.getIntValue()));
|
columns.push_back(MlValue(c.getIntegerValue()));
|
||||||
} else if (type == ColumnType::bool_type) {
|
} else if (type == ColumnType::bool_type) {
|
||||||
columns.push_back(c.getBoolValue() ? MlValue(c.getIntValue()) : MlValue::nil());
|
columns.push_back(c.getBoolValue() ? MlValue(c.getIntegerValue()) : MlValue::nil());
|
||||||
} else if (type == ColumnType::float_type) {
|
} else if (type == ColumnType::float_type) {
|
||||||
columns.push_back(MlValue(c.getDoubleValue()));
|
columns.push_back(MlValue(c.getDoubleValue()));
|
||||||
} else {
|
} else {
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
#include <errno.h>
|
#include <cerrno>
|
||||||
|
|
||||||
#include "exception.h"
|
#include "exception.h"
|
||||||
#include "csvreader.h"
|
#include "csvreader.h"
|
||||||
|
|
@ -7,15 +7,15 @@
|
||||||
|
|
||||||
namespace usql {
|
namespace usql {
|
||||||
|
|
||||||
CsvReader::CsvReader(bool skip_hdr, char field_sep, char quote_ch, char line_sep, char line_sep2) {
|
CsvReader::CsvReader(bool skip_hdr, char field_sep, char quote_ch, char line_sep, char line_sep2) {
|
||||||
skip_header = skip_hdr;
|
skip_header = skip_hdr;
|
||||||
field_separator = field_sep;
|
field_separator = field_sep;
|
||||||
quote_character = quote_ch;
|
quote_character = quote_ch;
|
||||||
line_separator = line_sep;
|
line_separator = line_sep;
|
||||||
line_separator2 = line_sep2;
|
line_separator2 = line_sep2;
|
||||||
|
|
||||||
header_skiped = !skip_hdr;
|
header_skiped = !skip_hdr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
int CsvReader::parseCSV(const std::string &filename, std::vector<ColDefNode> &cols_def, Table &table) {
|
int CsvReader::parseCSV(const std::string &filename, std::vector<ColDefNode> &cols_def, Table &table) {
|
||||||
|
|
@ -41,7 +41,7 @@ int CsvReader::parseCSV(const std::string &filename, std::vector<ColDefNode> &co
|
||||||
size_t len = 0;
|
size_t len = 0;
|
||||||
|
|
||||||
|
|
||||||
int read_chars;
|
long read_chars;
|
||||||
while ((read_chars = getline(&line_str, &len, fp)) != -1) {
|
while ((read_chars = getline(&line_str, &len, fp)) != -1) {
|
||||||
if (skip_header && !header_skiped) {
|
if (skip_header && !header_skiped) {
|
||||||
header_skiped = true;
|
header_skiped = true;
|
||||||
|
|
@ -59,7 +59,7 @@ int CsvReader::parseCSV(const std::string &filename, std::vector<ColDefNode> &co
|
||||||
if (*aChar == quote_character) {
|
if (*aChar == quote_character) {
|
||||||
inQuote = !inQuote;
|
inQuote = !inQuote;
|
||||||
} else if (*aChar == field_separator) {
|
} else if (*aChar == field_separator) {
|
||||||
if (inQuote == true) {
|
if (inQuote) {
|
||||||
field += *aChar;
|
field += *aChar;
|
||||||
} else {
|
} else {
|
||||||
line.push_back(field);
|
line.push_back(field);
|
||||||
|
|
@ -80,9 +80,6 @@ int CsvReader::parseCSV(const std::string &filename, std::vector<ColDefNode> &co
|
||||||
|
|
||||||
field.clear();
|
field.clear();
|
||||||
line.clear();
|
line.clear();
|
||||||
// DEBUG
|
|
||||||
// if (row_cnt > 50000) break;
|
|
||||||
//
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fclose(fp);
|
fclose(fp);
|
||||||
|
|
@ -93,53 +90,53 @@ int CsvReader::parseCSV(const std::string &filename, std::vector<ColDefNode> &co
|
||||||
return row_cnt;
|
return row_cnt;
|
||||||
}
|
}
|
||||||
|
|
||||||
int CsvReader::parseCSV2(const std::string &csvSource, std::vector<ColDefNode> &cols_def, Table& table) {
|
int CsvReader::parseCSV2(const std::string &csvSource, std::vector<ColDefNode> &cols_def, Table& table) {
|
||||||
int row_cnt = 0;
|
int row_cnt = 0;
|
||||||
bool inQuote(false);
|
bool inQuote(false);
|
||||||
bool newLine(false);
|
bool newLine(false);
|
||||||
std::string field;
|
std::string field;
|
||||||
|
|
||||||
std::vector<std::string> line;
|
std::vector<std::string> line;
|
||||||
line.reserve(32);
|
line.reserve(32);
|
||||||
|
|
||||||
std::string::const_iterator aChar = csvSource.begin();
|
std::string::const_iterator aChar = csvSource.begin();
|
||||||
while (aChar != csvSource.end()) {
|
while (aChar != csvSource.end()) {
|
||||||
if (*aChar == quote_character) {
|
if (*aChar == quote_character) {
|
||||||
newLine = false;
|
newLine = false;
|
||||||
inQuote = !inQuote;
|
inQuote = !inQuote;
|
||||||
} else if (*aChar == field_separator) {
|
} else if (*aChar == field_separator) {
|
||||||
newLine = false;
|
newLine = false;
|
||||||
if (inQuote == true) {
|
if (inQuote) {
|
||||||
field += *aChar;
|
field += *aChar;
|
||||||
} else {
|
} else {
|
||||||
line.push_back(field);
|
line.push_back(field);
|
||||||
field.clear();
|
field.clear();
|
||||||
}
|
}
|
||||||
} else if (*aChar == line_separator || *aChar == line_separator2) {
|
} else if (*aChar == line_separator || *aChar == line_separator2) {
|
||||||
if (inQuote == true) {
|
if (inQuote) {
|
||||||
field += *aChar;
|
field += *aChar;
|
||||||
} else {
|
} else {
|
||||||
if (newLine == false) {
|
if (!newLine) {
|
||||||
line.push_back(field);
|
line.push_back(field);
|
||||||
if (header_skiped) {
|
if (header_skiped) {
|
||||||
table.create_row_from_vector(cols_def, line);
|
table.create_row_from_vector(cols_def, line);
|
||||||
row_cnt++;
|
row_cnt++;
|
||||||
}
|
}
|
||||||
header_skiped = true;
|
header_skiped = true;
|
||||||
field.clear();
|
field.clear();
|
||||||
line.clear();
|
line.clear();
|
||||||
newLine = true;
|
newLine = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
newLine = false;
|
newLine = false;
|
||||||
field.push_back(*aChar);
|
field.push_back(*aChar);
|
||||||
}
|
}
|
||||||
|
|
||||||
aChar++;
|
aChar++;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!field.empty()) line.push_back(field);
|
if (!field.empty()) line.push_back(field);
|
||||||
|
|
||||||
if (header_skiped) {
|
if (header_skiped) {
|
||||||
table.create_row_from_vector(cols_def, line);
|
table.create_row_from_vector(cols_def, line);
|
||||||
|
|
|
||||||
|
|
@ -24,7 +24,7 @@ namespace usql {
|
||||||
bool header_skiped;
|
bool header_skiped;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
CsvReader(bool skip_hdr = true, char field_sep = ',', char quote_ch = '"', char line_sep = '\r', char line_sep2 = '\n');
|
explicit CsvReader(bool skip_hdr = true, char field_sep = ',', char quote_ch = '"', char line_sep = '\r', char line_sep2 = '\n');
|
||||||
|
|
||||||
int parseCSV2(const std::string &csvSource, std::vector<ColDefNode> &cols_def, Table& table);
|
int parseCSV2(const std::string &csvSource, std::vector<ColDefNode> &cols_def, Table& table);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,6 @@
|
||||||
|
|
||||||
namespace usql {
|
namespace usql {
|
||||||
|
|
||||||
Exception::Exception(const std::string msg) : std::runtime_error(msg) {}
|
Exception::Exception(std::string msg) : std::runtime_error(msg) {}
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
|
||||||
|
|
@ -1,14 +1,12 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "lexer.h"
|
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
namespace usql {
|
namespace usql {
|
||||||
|
|
||||||
class Exception : public std::runtime_error {
|
class Exception : public std::runtime_error {
|
||||||
public:
|
public:
|
||||||
Exception(const std::string msg);
|
explicit Exception(std::string msg);
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
@ -0,0 +1,119 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "exception.h"
|
||||||
|
#include "parser.h"
|
||||||
|
#include "row.h"
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
#include <utility>
|
||||||
|
#include <vector>
|
||||||
|
#include <variant>
|
||||||
|
#include <map>
|
||||||
|
|
||||||
|
|
||||||
|
namespace usql {
|
||||||
|
|
||||||
|
using IndexValue = std::variant<long, std::string>;
|
||||||
|
using rowid_t = size_t; // int is now enough but size_t is correct
|
||||||
|
|
||||||
|
static const int k_default_rowids_size = 16;
|
||||||
|
|
||||||
|
|
||||||
|
class Index {
|
||||||
|
public:
|
||||||
|
Index(std::string index_name, std::string col_name, ColumnType type) :
|
||||||
|
m_index_name(std::move(index_name)), m_column_name(std::move(col_name)),
|
||||||
|
m_data_type(type), m_uniq(false) {
|
||||||
|
if (type != ColumnType::integer_type && type != ColumnType::varchar_type)
|
||||||
|
throw Exception("creating index on unsupported type");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
std::vector<rowid_t> search(const ValueNode *key) {
|
||||||
|
return search(to_index_value(key));
|
||||||
|
}
|
||||||
|
|
||||||
|
void insert(const ColValue *key, rowid_t rowid) {
|
||||||
|
return insert(to_index_value(key), rowid);
|
||||||
|
}
|
||||||
|
|
||||||
|
void remove(const ColValue *key, rowid_t rowid) {
|
||||||
|
return remove(to_index_value(key), rowid);
|
||||||
|
}
|
||||||
|
|
||||||
|
void truncate() {
|
||||||
|
m_index.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] const std::string &get_column_name() const {
|
||||||
|
return m_column_name;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] const std::string &get_index_name() const {
|
||||||
|
return m_index_name;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private:
|
||||||
|
IndexValue to_index_value(const ValueNode *key) {
|
||||||
|
if (m_data_type == ColumnType::integer_type)
|
||||||
|
return key->getIntegerValue();
|
||||||
|
else if (m_data_type == ColumnType::varchar_type)
|
||||||
|
return key->getStringValue();
|
||||||
|
else
|
||||||
|
throw Exception("using index on unsupported type");
|
||||||
|
}
|
||||||
|
|
||||||
|
IndexValue to_index_value(const ColValue *key) {
|
||||||
|
if (m_data_type == ColumnType::integer_type)
|
||||||
|
return key->getIntegerValue();
|
||||||
|
else if (m_data_type == ColumnType::varchar_type)
|
||||||
|
return key->getStringValue();
|
||||||
|
else
|
||||||
|
throw Exception("using index on unsupported type");
|
||||||
|
}
|
||||||
|
|
||||||
|
void insert(const IndexValue& key, rowid_t rowid) {
|
||||||
|
auto search = m_index.find(key);
|
||||||
|
if (search != m_index.end()) {
|
||||||
|
if (m_uniq)
|
||||||
|
throw Exception("Inserting duplicate value into unique index");
|
||||||
|
|
||||||
|
search->second.push_back(rowid);
|
||||||
|
} else {
|
||||||
|
std::vector<rowid_t> rowids{rowid};
|
||||||
|
if (!m_uniq)
|
||||||
|
rowids.reserve(k_default_rowids_size);
|
||||||
|
m_index[key] = rowids;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void remove(const IndexValue& key, rowid_t rowid) {
|
||||||
|
auto search = m_index.find(key);
|
||||||
|
if (search != m_index.end()) {
|
||||||
|
search->second.erase(find(search->second.begin(), search->second.end(), rowid));
|
||||||
|
if (search->second.empty())
|
||||||
|
m_index.erase(search);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<rowid_t> search(const IndexValue& key) {
|
||||||
|
auto search = m_index.find(key);
|
||||||
|
if (search != m_index.end()) {
|
||||||
|
return search->second;
|
||||||
|
} else {
|
||||||
|
return std::vector<rowid_t>{};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool m_uniq;
|
||||||
|
std::string m_index_name;
|
||||||
|
std::string m_column_name;
|
||||||
|
ColumnType m_data_type;
|
||||||
|
|
||||||
|
std::map<IndexValue, std::vector<rowid_t> > m_index;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace
|
||||||
561
usql/lexer.cpp
561
usql/lexer.cpp
|
|
@ -5,287 +5,292 @@
|
||||||
|
|
||||||
namespace usql {
|
namespace usql {
|
||||||
|
|
||||||
Token::Token(const std::string &token_str, TokenType typ) {
|
Token::Token(const std::string &token_str, TokenType typ) {
|
||||||
token_string = token_str;
|
token_string = token_str;
|
||||||
type = typ;
|
type = typ;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
Lexer::Lexer() {
|
Lexer::Lexer() {
|
||||||
k_words_regex =
|
k_words_regex =
|
||||||
"[-+]?[0-9]+\\.[0-9]+|[-+]?[0-9]+|[A-Za-z]+[A-Za-z0-9_#]*|[\\(\\)\\[\\]\\{\\}]|[-\\+\\*/"
|
"[-+]?[0-9]+\\.[0-9]+|[-+]?[0-9]+|[A-Za-z]+[A-Za-z0-9_#]*|[\\(\\)\\[\\]\\{\\}]|[-\\+\\*/"
|
||||||
",;:\?]|!=|<>|==|>=|<=|~=|>|<|=|;|~|\\||\n|\r|\r\n|'([^']|'')*'|\".*?\"|%.*?\n";
|
",;:\?]|!=|<>|==|>=|<=|~=|>|<|=|;|~|\\||\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) {
|
|
||||||
if (code.empty())
|
|
||||||
throw Exception("empty code");
|
|
||||||
|
|
||||||
m_tokens.clear();
|
|
||||||
m_tokens.reserve(64);
|
|
||||||
|
|
||||||
m_code_str = code;
|
|
||||||
if (!m_code_str.empty() && m_code_str.back() != '\n') {
|
|
||||||
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);
|
|
||||||
auto words_end = std::sregex_iterator();
|
|
||||||
|
|
||||||
for (std::sregex_iterator i = words_begin; i != words_end; ++i) {
|
|
||||||
std::smatch match = *i;
|
|
||||||
std::string match_str = match.str();
|
|
||||||
TokenType token_type = type(match_str);
|
|
||||||
if (token_type == TokenType::string_literal)
|
|
||||||
match_str = stringLiteral(match_str);
|
|
||||||
|
|
||||||
if (token_type != TokenType::newline)
|
|
||||||
m_tokens.emplace_back(match_str, token_type);
|
|
||||||
}
|
|
||||||
|
|
||||||
// DEBUG IT
|
|
||||||
// debugTokens();
|
|
||||||
|
|
||||||
m_index = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Lexer::debugTokens() {
|
|
||||||
int i = 0;
|
|
||||||
for (auto & m_token : m_tokens) {
|
|
||||||
std::cerr << i << "\t" << m_token.token_string << std::endl;
|
|
||||||
i++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Token Lexer::currentToken() { return m_tokens[m_index]; }
|
|
||||||
|
|
||||||
Token Lexer::consumeToken() {
|
|
||||||
int i = m_index;
|
|
||||||
nextToken();
|
|
||||||
return m_tokens[i];
|
|
||||||
}
|
|
||||||
|
|
||||||
Token Lexer::consumeToken(TokenType type) {
|
|
||||||
int i = m_index;
|
|
||||||
skipToken(type);
|
|
||||||
return m_tokens[i];
|
|
||||||
}
|
|
||||||
|
|
||||||
void Lexer::nextToken() {
|
|
||||||
if (m_index < m_tokens.size()) {
|
|
||||||
m_index++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Lexer::skipToken(TokenType type) {
|
|
||||||
if (tokenType() == type) {
|
|
||||||
nextToken();
|
|
||||||
} else {
|
|
||||||
throw Exception("ERROR unexpected token " + consumeToken().token_string + ", instead of " + typeToString(type));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Lexer::skipTokenOptional(TokenType type) {
|
|
||||||
if (tokenType() == type) {
|
|
||||||
nextToken();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
TokenType Lexer::tokenType() { return m_index < m_tokens.size() ? currentToken().type : TokenType::eof; }
|
|
||||||
|
|
||||||
TokenType Lexer::nextTokenType() {
|
|
||||||
return m_index < m_tokens.size() - 1 ? m_tokens[m_index + 1].type : TokenType::eof;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Lexer::isRelationalOperator(TokenType token_type) {
|
|
||||||
return (token_type == TokenType::equal || token_type == TokenType::not_equal ||
|
|
||||||
token_type == TokenType::greater || token_type == TokenType::greater_equal ||
|
|
||||||
token_type == TokenType::lesser || token_type == TokenType::lesser_equal ||
|
|
||||||
token_type == TokenType::is);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Lexer::isLogicalOperator(TokenType token_type) {
|
|
||||||
return (token_type == TokenType::logical_and || token_type == TokenType::logical_or);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Lexer::isArithmeticalOperator(TokenType token_type) {
|
|
||||||
return (token_type == TokenType::plus || token_type == TokenType::minus ||
|
|
||||||
token_type == TokenType::multiply ||
|
|
||||||
token_type == TokenType::divide);
|
|
||||||
}
|
|
||||||
|
|
||||||
TokenType Lexer::type(const std::string &token) {
|
|
||||||
// FIXME 'one is evaluated as identifier
|
|
||||||
if (token == ";") return TokenType::semicolon;
|
|
||||||
if (token == "+") return TokenType::plus;
|
|
||||||
if (token == "-") return TokenType::minus;
|
|
||||||
if (token == "*") return TokenType::multiply;
|
|
||||||
if (token == "/") return TokenType::divide;
|
|
||||||
if (token == "(") return TokenType::open_paren;
|
|
||||||
if (token == ")") return TokenType::close_paren;
|
|
||||||
if (token == "=") return TokenType::equal;
|
|
||||||
if (token == "!=" || token == "<>") return TokenType::not_equal;
|
|
||||||
if (token == ">") return TokenType::greater;
|
|
||||||
if (token == ">=") return TokenType::greater_equal;
|
|
||||||
if (token == "<") return TokenType::lesser;
|
|
||||||
if (token == "<=") return TokenType::lesser_equal;
|
|
||||||
if (token == "is") return TokenType::is;
|
|
||||||
if (token == "as") return TokenType::keyword_as;
|
|
||||||
if (token == "create") return TokenType::keyword_create;
|
|
||||||
if (token == "drop") return TokenType::keyword_drop;
|
|
||||||
if (token == "where") return TokenType::keyword_where;
|
|
||||||
if (token == "order") return TokenType::keyword_order;
|
|
||||||
if (token == "by") return TokenType::keyword_by;
|
|
||||||
if (token == "offset") return TokenType::keyword_offset;
|
|
||||||
if (token == "limit") return TokenType::keyword_limit;
|
|
||||||
if (token == "asc") return TokenType::keyword_asc;
|
|
||||||
if (token == "desc") return TokenType::keyword_desc;
|
|
||||||
if (token == "from") return TokenType::keyword_from;
|
|
||||||
if (token == "delete") return TokenType::keyword_delete;
|
|
||||||
if (token == "table") return TokenType::keyword_table;
|
|
||||||
if (token == "insert") return TokenType::keyword_insert;
|
|
||||||
if (token == "into") return TokenType::keyword_into;
|
|
||||||
if (token == "values") return TokenType::keyword_values;
|
|
||||||
if (token == "select") return TokenType::keyword_select;
|
|
||||||
if (token == "set") return TokenType::keyword_set;
|
|
||||||
if (token == "copy") return TokenType::keyword_copy;
|
|
||||||
if (token == "update") return TokenType::keyword_update;
|
|
||||||
if (token == "load") return TokenType::keyword_load;
|
|
||||||
if (token == "save") return TokenType::keyword_save;
|
|
||||||
if (token == "not") return TokenType::keyword_not;
|
|
||||||
if (token == "null") return TokenType::keyword_null;
|
|
||||||
if (token == "integer") return TokenType::keyword_integer;
|
|
||||||
if (token == "float") return TokenType::keyword_float;
|
|
||||||
if (token == "varchar") return TokenType::keyword_varchar;
|
|
||||||
if (token == "date") return TokenType::keyword_date;
|
|
||||||
if (token == "boolean") return TokenType::keyword_bool;
|
|
||||||
if (token == "true") return TokenType::keyword_true;
|
|
||||||
if (token == "false") return TokenType::keyword_false;
|
|
||||||
if (token == "distinct") return TokenType::keyword_distinct;
|
|
||||||
if (token == "show") return TokenType::keyword_show;
|
|
||||||
if (token == "or") return TokenType::logical_or;
|
|
||||||
if (token == "and") return TokenType::logical_and;
|
|
||||||
if (token == ",") return TokenType::comma;
|
|
||||||
if (token == "\n" || token == "\r\n" || token == "\r") return TokenType::newline;
|
|
||||||
|
|
||||||
if (token.length() > 1 && token.at(0) == '%' && (token.at(token.length() - 1) == '\n' || token.at(token.length() - 1) == '\r'))
|
|
||||||
return TokenType::comment;
|
|
||||||
|
|
||||||
if (token.length() >= 2 && token.at(0) == '"' && token.at(token.length() - 1) == '"')
|
|
||||||
return TokenType::string_literal;
|
|
||||||
|
|
||||||
if (token.length() >= 2 && token.at(0) == '\'' && token.at(token.length() - 1) == '\'')
|
|
||||||
return TokenType::string_literal;
|
|
||||||
|
|
||||||
if (std::regex_match(token, k_int_regex)) return TokenType::int_number;
|
|
||||||
if (std::regex_match(token, k_int_underscored_regex)) return TokenType::int_number;
|
|
||||||
if (std::regex_match(token, k_double_regex)) return TokenType::double_number;
|
|
||||||
if (std::regex_match(token, k_identifier_regex)) return TokenType::identifier;
|
|
||||||
|
|
||||||
return TokenType::undef;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string Lexer::stringLiteral(std::string token) {
|
|
||||||
// remove ' or " from the literal ends
|
|
||||||
bool replace = token[0] == '\'' && token[token.size() - 1] == '\'';
|
|
||||||
|
|
||||||
std::string str = token.substr(1, token.size() - 2);
|
|
||||||
if (!replace) {
|
|
||||||
return str;
|
|
||||||
}
|
|
||||||
std::string out;
|
|
||||||
out.reserve(str.size());
|
|
||||||
|
|
||||||
|
|
||||||
for (std::string::size_type i = 0; i < str.size(); ++i) {
|
|
||||||
if (str[i] == '\'' && i < str.size() - 1) {
|
|
||||||
if (str[i + 1] == '\'') {
|
|
||||||
out.append(1, '\'');
|
|
||||||
i++;
|
|
||||||
} else {
|
|
||||||
out.append(1, str[i]);
|
|
||||||
}
|
|
||||||
} else if (str[i] == '\\' && i < str.size() - 1) {
|
|
||||||
if (str[i + 1] == 'n') {
|
|
||||||
out.append(1, '\n');
|
|
||||||
i++;
|
|
||||||
} else if (str[i + 1] == 't') {
|
|
||||||
out.append(1, '\t');
|
|
||||||
i++;
|
|
||||||
} else {
|
|
||||||
out.append(1, str[i]);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
out.append(1, str[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return out;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string Lexer::typeToString(TokenType token_type) {
|
|
||||||
switch (token_type) {
|
|
||||||
case TokenType::undef: return "undef";
|
|
||||||
case TokenType::identifier: return "identifier";
|
|
||||||
case TokenType::plus: return "+";
|
|
||||||
case TokenType::minus: return "-";
|
|
||||||
case TokenType::multiply: return "*";
|
|
||||||
case TokenType::divide: return "/";
|
|
||||||
case TokenType::equal: return "==";
|
|
||||||
case TokenType::not_equal: return "!=";
|
|
||||||
case TokenType::greater: return ">";
|
|
||||||
case TokenType::greater_equal: return ">=";
|
|
||||||
case TokenType::lesser: return "<";
|
|
||||||
case TokenType::lesser_equal: return "<=";
|
|
||||||
case TokenType::is: return "is";
|
|
||||||
case TokenType::keyword_as: return "as";
|
|
||||||
case TokenType::keyword_create: return "create";
|
|
||||||
case TokenType::keyword_drop: return "drop";
|
|
||||||
case TokenType::keyword_where: return "where";
|
|
||||||
case TokenType::keyword_order: return "order";
|
|
||||||
case TokenType::keyword_by: return "by";
|
|
||||||
case TokenType::keyword_offset: return "offset";
|
|
||||||
case TokenType::keyword_limit: return "limit";
|
|
||||||
case TokenType::keyword_asc: return "asc";
|
|
||||||
case TokenType::keyword_desc: return "desc";
|
|
||||||
case TokenType::keyword_table: return "table";
|
|
||||||
case TokenType::keyword_into: return "into";
|
|
||||||
case TokenType::keyword_values: return "values";
|
|
||||||
case TokenType::keyword_select: return "select";
|
|
||||||
case TokenType::keyword_set: return "set";
|
|
||||||
case TokenType::keyword_copy: return "copy";
|
|
||||||
case TokenType::keyword_update: return "update";
|
|
||||||
case TokenType::keyword_load: return "load";
|
|
||||||
case TokenType::keyword_save: return "save";
|
|
||||||
case TokenType::keyword_not: return "not";
|
|
||||||
case TokenType::keyword_null: return "null";
|
|
||||||
case TokenType::keyword_integer: return "integer";
|
|
||||||
case TokenType::keyword_float: return "float";
|
|
||||||
case TokenType::keyword_varchar: return "varchar";
|
|
||||||
case TokenType::keyword_date: return "date";
|
|
||||||
case TokenType::keyword_bool: return "boolean";
|
|
||||||
case TokenType::keyword_true: return "true";
|
|
||||||
case TokenType::keyword_false: return "false";
|
|
||||||
case TokenType::keyword_distinct: return "distinct";
|
|
||||||
case TokenType::keyword_show: return "show";
|
|
||||||
case TokenType::int_number: return "int number";
|
|
||||||
case TokenType::double_number: return "double number";
|
|
||||||
case TokenType::string_literal: return "string literal";
|
|
||||||
case TokenType::open_paren: return "(";
|
|
||||||
case TokenType::close_paren: return ")";
|
|
||||||
case TokenType::logical_and: return "and";
|
|
||||||
case TokenType::logical_or: return "or";
|
|
||||||
case TokenType::semicolon: return ";";
|
|
||||||
case TokenType::comma: return ",";
|
|
||||||
case TokenType::newline: return "newline";
|
|
||||||
case TokenType::comment: return "comment";
|
|
||||||
case TokenType::eof: return "eof";
|
|
||||||
default:
|
|
||||||
return "FIXME, unknown token type";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Lexer::parse(const std::string &code) {
|
||||||
|
if (code.empty())
|
||||||
|
throw Exception("Lexer.parse empty code");
|
||||||
|
|
||||||
|
m_tokens.clear();
|
||||||
|
m_tokens.reserve(64);
|
||||||
|
|
||||||
|
m_code_str = code;
|
||||||
|
if (!m_code_str.empty() && m_code_str.back() != '\n') {
|
||||||
|
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);
|
||||||
|
auto words_end = std::sregex_iterator();
|
||||||
|
|
||||||
|
for (std::sregex_iterator i = words_begin; i != words_end; ++i) {
|
||||||
|
std::smatch match = *i;
|
||||||
|
std::string match_str = match.str();
|
||||||
|
TokenType token_type = type(match_str);
|
||||||
|
if (token_type == TokenType::undef)
|
||||||
|
throw Exception("Lexer.parse unknown token type: " + match_str);
|
||||||
|
if (token_type == TokenType::string_literal)
|
||||||
|
match_str = stringLiteral(match_str);
|
||||||
|
|
||||||
|
if (token_type != TokenType::newline)
|
||||||
|
m_tokens.emplace_back(match_str, token_type);
|
||||||
|
}
|
||||||
|
|
||||||
|
// DEBUG IT
|
||||||
|
// debugTokens();
|
||||||
|
|
||||||
|
m_index = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Lexer::debugTokens() {
|
||||||
|
int i = 0;
|
||||||
|
for (auto & m_token : m_tokens) {
|
||||||
|
std::cerr << i << "\t" << m_token.token_string << std::endl;
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Token Lexer::currentToken() { return m_tokens[m_index]; }
|
||||||
|
|
||||||
|
Token Lexer::consumeToken() {
|
||||||
|
int i = m_index;
|
||||||
|
nextToken();
|
||||||
|
return m_tokens[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
Token Lexer::consumeToken(TokenType type) {
|
||||||
|
int i = m_index;
|
||||||
|
skipToken(type);
|
||||||
|
return m_tokens[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
void Lexer::nextToken() {
|
||||||
|
if (m_index < m_tokens.size()) {
|
||||||
|
m_index++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Lexer::skipToken(TokenType type) {
|
||||||
|
if (tokenType() == type) {
|
||||||
|
nextToken();
|
||||||
|
} else {
|
||||||
|
throw Exception("ERROR unexpected token " + consumeToken().token_string + ", instead of " + typeToString(type));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Lexer::skipTokenOptional(TokenType type) {
|
||||||
|
if (tokenType() == type) {
|
||||||
|
nextToken();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TokenType Lexer::tokenType() { return m_index < m_tokens.size() ? currentToken().type : TokenType::eof; }
|
||||||
|
|
||||||
|
TokenType Lexer::nextTokenType() {
|
||||||
|
return m_index < m_tokens.size() - 1 ? m_tokens[m_index + 1].type : TokenType::eof;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Lexer::isRelationalOperator(TokenType token_type) {
|
||||||
|
return (token_type == TokenType::equal || token_type == TokenType::not_equal ||
|
||||||
|
token_type == TokenType::greater || token_type == TokenType::greater_equal ||
|
||||||
|
token_type == TokenType::lesser || token_type == TokenType::lesser_equal ||
|
||||||
|
token_type == TokenType::is);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Lexer::isLogicalOperator(TokenType token_type) {
|
||||||
|
return (token_type == TokenType::logical_and || token_type == TokenType::logical_or);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Lexer::isArithmeticalOperator(TokenType token_type) {
|
||||||
|
return (token_type == TokenType::plus || token_type == TokenType::minus ||
|
||||||
|
token_type == TokenType::multiply ||
|
||||||
|
token_type == TokenType::divide);
|
||||||
|
}
|
||||||
|
|
||||||
|
TokenType Lexer::type(const std::string &token) {
|
||||||
|
if (token == ";") return TokenType::semicolon;
|
||||||
|
if (token == "+") return TokenType::plus;
|
||||||
|
if (token == "-") return TokenType::minus;
|
||||||
|
if (token == "*") return TokenType::multiply;
|
||||||
|
if (token == "/") return TokenType::divide;
|
||||||
|
if (token == "(") return TokenType::open_paren;
|
||||||
|
if (token == ")") return TokenType::close_paren;
|
||||||
|
if (token == "=") return TokenType::equal;
|
||||||
|
if (token == "!=" || token == "<>") return TokenType::not_equal;
|
||||||
|
if (token == ">") return TokenType::greater;
|
||||||
|
if (token == ">=") return TokenType::greater_equal;
|
||||||
|
if (token == "<") return TokenType::lesser;
|
||||||
|
if (token == "<=") return TokenType::lesser_equal;
|
||||||
|
if (token == "is") return TokenType::is;
|
||||||
|
if (token == "as") return TokenType::keyword_as;
|
||||||
|
if (token == "create") return TokenType::keyword_create;
|
||||||
|
if (token == "drop") return TokenType::keyword_drop;
|
||||||
|
if (token == "where") return TokenType::keyword_where;
|
||||||
|
if (token == "order") return TokenType::keyword_order;
|
||||||
|
if (token == "by") return TokenType::keyword_by;
|
||||||
|
if (token == "offset") return TokenType::keyword_offset;
|
||||||
|
if (token == "limit") return TokenType::keyword_limit;
|
||||||
|
if (token == "asc") return TokenType::keyword_asc;
|
||||||
|
if (token == "desc") return TokenType::keyword_desc;
|
||||||
|
if (token == "from") return TokenType::keyword_from;
|
||||||
|
if (token == "delete") return TokenType::keyword_delete;
|
||||||
|
if (token == "table") return TokenType::keyword_table;
|
||||||
|
if (token == "index") return TokenType::keyword_index;
|
||||||
|
if (token == "on") return TokenType::keyword_on;
|
||||||
|
if (token == "insert") return TokenType::keyword_insert;
|
||||||
|
if (token == "into") return TokenType::keyword_into;
|
||||||
|
if (token == "values") return TokenType::keyword_values;
|
||||||
|
if (token == "select") return TokenType::keyword_select;
|
||||||
|
if (token == "set") return TokenType::keyword_set;
|
||||||
|
if (token == "copy") return TokenType::keyword_copy;
|
||||||
|
if (token == "update") return TokenType::keyword_update;
|
||||||
|
if (token == "load") return TokenType::keyword_load;
|
||||||
|
if (token == "save") return TokenType::keyword_save;
|
||||||
|
if (token == "not") return TokenType::keyword_not;
|
||||||
|
if (token == "null") return TokenType::keyword_null;
|
||||||
|
if (token == "integer") return TokenType::keyword_integer;
|
||||||
|
if (token == "float") return TokenType::keyword_float;
|
||||||
|
if (token == "varchar") return TokenType::keyword_varchar;
|
||||||
|
if (token == "date") return TokenType::keyword_date;
|
||||||
|
if (token == "boolean") return TokenType::keyword_bool;
|
||||||
|
if (token == "true") return TokenType::keyword_true;
|
||||||
|
if (token == "false") return TokenType::keyword_false;
|
||||||
|
if (token == "distinct") return TokenType::keyword_distinct;
|
||||||
|
if (token == "show") return TokenType::keyword_show;
|
||||||
|
if (token == "or") return TokenType::logical_or;
|
||||||
|
if (token == "and") return TokenType::logical_and;
|
||||||
|
if (token == ",") return TokenType::comma;
|
||||||
|
if (token == "\n" || token == "\r\n" || token == "\r") return TokenType::newline;
|
||||||
|
|
||||||
|
if (token.length() > 1 && token.at(0) == '%' && (token.at(token.length() - 1) == '\n' || token.at(token.length() - 1) == '\r'))
|
||||||
|
return TokenType::comment;
|
||||||
|
|
||||||
|
if (token.length() >= 2 && token.at(0) == '"')
|
||||||
|
return (token.at(token.length() - 1) == '"') ? TokenType::string_literal : TokenType::undef;
|
||||||
|
|
||||||
|
if (token.length() >= 2 && token.at(0) == '\'')
|
||||||
|
return (token.at(token.length() - 1) == '\'') ? TokenType::string_literal : TokenType::undef;
|
||||||
|
|
||||||
|
if (std::regex_match(token, k_int_regex)) return TokenType::int_number;
|
||||||
|
if (std::regex_match(token, k_int_underscored_regex)) return TokenType::int_number;
|
||||||
|
if (std::regex_match(token, k_double_regex)) return TokenType::double_number;
|
||||||
|
if (std::regex_match(token, k_identifier_regex)) return TokenType::identifier;
|
||||||
|
|
||||||
|
return TokenType::undef;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string Lexer::stringLiteral(std::string token) {
|
||||||
|
// remove ' or " from the literal ends
|
||||||
|
bool replace = token[0] == '\'' && token[token.size() - 1] == '\'';
|
||||||
|
|
||||||
|
std::string str = token.substr(1, token.size() - 2);
|
||||||
|
if (!replace) {
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
std::string out;
|
||||||
|
out.reserve(str.size());
|
||||||
|
|
||||||
|
|
||||||
|
for (std::string::size_type i = 0; i < str.size(); ++i) {
|
||||||
|
if (str[i] == '\'' && i < str.size() - 1) {
|
||||||
|
if (str[i + 1] == '\'') {
|
||||||
|
out.append(1, '\'');
|
||||||
|
i++;
|
||||||
|
} else {
|
||||||
|
out.append(1, str[i]);
|
||||||
|
}
|
||||||
|
} else if (str[i] == '\\' && i < str.size() - 1) {
|
||||||
|
if (str[i + 1] == 'n') {
|
||||||
|
out.append(1, '\n');
|
||||||
|
i++;
|
||||||
|
} else if (str[i + 1] == 't') {
|
||||||
|
out.append(1, '\t');
|
||||||
|
i++;
|
||||||
|
} else {
|
||||||
|
out.append(1, str[i]);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
out.append(1, str[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string Lexer::typeToString(TokenType token_type) {
|
||||||
|
switch (token_type) {
|
||||||
|
case TokenType::undef: return "undef";
|
||||||
|
case TokenType::identifier: return "identifier";
|
||||||
|
case TokenType::plus: return "+";
|
||||||
|
case TokenType::minus: return "-";
|
||||||
|
case TokenType::multiply: return "*";
|
||||||
|
case TokenType::divide: return "/";
|
||||||
|
case TokenType::equal: return "==";
|
||||||
|
case TokenType::not_equal: return "!=";
|
||||||
|
case TokenType::greater: return ">";
|
||||||
|
case TokenType::greater_equal: return ">=";
|
||||||
|
case TokenType::lesser: return "<";
|
||||||
|
case TokenType::lesser_equal: return "<=";
|
||||||
|
case TokenType::is: return "is";
|
||||||
|
case TokenType::keyword_as: return "as";
|
||||||
|
case TokenType::keyword_create: return "create";
|
||||||
|
case TokenType::keyword_drop: return "drop";
|
||||||
|
case TokenType::keyword_where: return "where";
|
||||||
|
case TokenType::keyword_order: return "order";
|
||||||
|
case TokenType::keyword_by: return "by";
|
||||||
|
case TokenType::keyword_offset: return "offset";
|
||||||
|
case TokenType::keyword_limit: return "limit";
|
||||||
|
case TokenType::keyword_asc: return "asc";
|
||||||
|
case TokenType::keyword_desc: return "desc";
|
||||||
|
case TokenType::keyword_table: return "table";
|
||||||
|
case TokenType::keyword_index: return "index";
|
||||||
|
case TokenType::keyword_on: return "on";
|
||||||
|
case TokenType::keyword_into: return "into";
|
||||||
|
case TokenType::keyword_values: return "values";
|
||||||
|
case TokenType::keyword_select: return "select";
|
||||||
|
case TokenType::keyword_set: return "set";
|
||||||
|
case TokenType::keyword_copy: return "copy";
|
||||||
|
case TokenType::keyword_update: return "update";
|
||||||
|
case TokenType::keyword_load: return "load";
|
||||||
|
case TokenType::keyword_save: return "save";
|
||||||
|
case TokenType::keyword_not: return "not";
|
||||||
|
case TokenType::keyword_null: return "null";
|
||||||
|
case TokenType::keyword_integer: return "integer";
|
||||||
|
case TokenType::keyword_float: return "float";
|
||||||
|
case TokenType::keyword_varchar: return "varchar";
|
||||||
|
case TokenType::keyword_date: return "date";
|
||||||
|
case TokenType::keyword_bool: return "boolean";
|
||||||
|
case TokenType::keyword_true: return "true";
|
||||||
|
case TokenType::keyword_false: return "false";
|
||||||
|
case TokenType::keyword_distinct: return "distinct";
|
||||||
|
case TokenType::keyword_show: return "show";
|
||||||
|
case TokenType::int_number: return "int number";
|
||||||
|
case TokenType::double_number: return "double number";
|
||||||
|
case TokenType::string_literal: return "string literal";
|
||||||
|
case TokenType::open_paren: return "(";
|
||||||
|
case TokenType::close_paren: return ")";
|
||||||
|
case TokenType::logical_and: return "and";
|
||||||
|
case TokenType::logical_or: return "or";
|
||||||
|
case TokenType::semicolon: return ";";
|
||||||
|
case TokenType::comma: return ",";
|
||||||
|
case TokenType::newline: return "newline";
|
||||||
|
case TokenType::comment: return "comment";
|
||||||
|
case TokenType::eof: return "eof";
|
||||||
|
default:
|
||||||
|
return "FIXME, unknown token type";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace usql
|
||||||
|
|
@ -25,6 +25,8 @@ namespace usql {
|
||||||
keyword_create,
|
keyword_create,
|
||||||
keyword_drop,
|
keyword_drop,
|
||||||
keyword_table,
|
keyword_table,
|
||||||
|
keyword_index,
|
||||||
|
keyword_on,
|
||||||
keyword_where,
|
keyword_where,
|
||||||
keyword_order,
|
keyword_order,
|
||||||
keyword_by,
|
keyword_by,
|
||||||
|
|
|
||||||
165
usql/main.cpp
165
usql/main.cpp
|
|
@ -1,165 +0,0 @@
|
||||||
#include "parser.h"
|
|
||||||
#include "usql.h"
|
|
||||||
|
|
||||||
#include "linenoise.h"
|
|
||||||
|
|
||||||
// https://dev.to/joaoh82/what-would-sqlite-look-like-if-written-in-rust-part-1-2np4
|
|
||||||
|
|
||||||
using namespace std::chrono;
|
|
||||||
|
|
||||||
const std::vector<std::string> commands {
|
|
||||||
"select", "create", "load", "table"
|
|
||||||
};
|
|
||||||
|
|
||||||
std::string get_history_file_dir() {
|
|
||||||
std::string file{"/.usql_history.txt"};
|
|
||||||
const char *t = std::getenv("HOME");
|
|
||||||
|
|
||||||
if (t == nullptr) return "/tmp/" + file;
|
|
||||||
else return std::string{t} + "/" + file;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
size_t last_token_index( std::string str ) {
|
|
||||||
// remove trailing white space
|
|
||||||
while( !str.empty() && std::isspace( str.back() ) ) str.pop_back() ;
|
|
||||||
|
|
||||||
// locate the last white space
|
|
||||||
return str.find_last_of( "() \t\n" ) ;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void completion(const char *buf, linenoiseCompletions *lc) {
|
|
||||||
if (buf != nullptr) {
|
|
||||||
std::string str{buf};
|
|
||||||
|
|
||||||
const auto pos = last_token_index(str);
|
|
||||||
if (pos == std::string::npos)
|
|
||||||
return; // cannot find what to complete
|
|
||||||
|
|
||||||
std::string token = str.substr(pos + 1);
|
|
||||||
std::string begining = str.substr(0, pos + 1);
|
|
||||||
|
|
||||||
for (const auto & command : commands) {
|
|
||||||
if (command.find(token) == 0) {
|
|
||||||
std::string completion_string = begining + command;
|
|
||||||
linenoiseAddCompletion(lc, completion_string.c_str());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
char *hints(const char *buf, int *color, int *bold) {
|
|
||||||
// if (!strcasecmp(buf,"hello")) {
|
|
||||||
// *color = 35;
|
|
||||||
// *bold = 0;
|
|
||||||
// return " World";
|
|
||||||
// }
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void setup_linenoise() {
|
|
||||||
std::string history_file = get_history_file_dir();
|
|
||||||
|
|
||||||
linenoiseHistorySetMaxLen(500);
|
|
||||||
linenoiseSetCompletionCallback(completion);
|
|
||||||
linenoiseSetHintsCallback(hints);
|
|
||||||
linenoiseSetMultiLine(1);
|
|
||||||
linenoiseHistoryLoad(history_file.c_str());
|
|
||||||
}
|
|
||||||
|
|
||||||
void linenoise_line_read(char *line) {
|
|
||||||
linenoiseHistoryAdd(line);
|
|
||||||
}
|
|
||||||
|
|
||||||
void close_linenoise() {
|
|
||||||
std::string history_file = get_history_file_dir();
|
|
||||||
|
|
||||||
linenoiseHistorySave(history_file.c_str());
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void repl() {
|
|
||||||
std::string code;
|
|
||||||
std::string input;
|
|
||||||
|
|
||||||
setup_linenoise();
|
|
||||||
|
|
||||||
usql::USql uSql{};
|
|
||||||
|
|
||||||
|
|
||||||
while (true) {
|
|
||||||
char *line = linenoise(">>> ");
|
|
||||||
if (line == nullptr) break;
|
|
||||||
|
|
||||||
linenoise_line_read(line);
|
|
||||||
|
|
||||||
input = std::string(line);
|
|
||||||
|
|
||||||
if (input == "!quit" || input == "!q")
|
|
||||||
break;
|
|
||||||
else if (input == "!export" || input == "!x") {
|
|
||||||
std::cout << "File to export to: ";
|
|
||||||
std::getline(std::cin, input);
|
|
||||||
|
|
||||||
//write_file_contents(input, code);
|
|
||||||
} else if (!input.empty()) {
|
|
||||||
try {
|
|
||||||
time_point<high_resolution_clock> start_time = high_resolution_clock::now();
|
|
||||||
auto result = uSql.execute(input);
|
|
||||||
time_point<high_resolution_clock> end_time = high_resolution_clock::now();
|
|
||||||
|
|
||||||
std::cout << input << std::endl;
|
|
||||||
std::cout << "run time: " << duration_cast<milliseconds>(end_time - start_time).count() << " ms " << std::endl <<std::endl;
|
|
||||||
|
|
||||||
result->print();
|
|
||||||
|
|
||||||
code += input + "\n";
|
|
||||||
} catch (std::exception &e) {
|
|
||||||
std::cerr << e.what() << std::endl;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
close_linenoise();
|
|
||||||
}
|
|
||||||
|
|
||||||
void debug() {
|
|
||||||
std::vector<std::string> sql_commands {
|
|
||||||
"set 'DATE_FORMAT' = '%Y-%m-%d' ",
|
|
||||||
"create table history_earnings_dates (datetime date, symbol varchar(8), time varchar(18), title varchar(256))",
|
|
||||||
"insert into history_earnings_dates (symbol,time,datetime,title) values ('BABA', '07:00:00', '2021-11-04', 'Alibaba Group Holding')",
|
|
||||||
"insert into history_earnings_dates (symbol,time,datetime,title) values ('BABA', '07:00:00', '2021-11-04', 'Alibaba Group Holding')",
|
|
||||||
"delete from history_earnings_dates where symbol='BABA' and datetime=to_date('2021-11-04', '%Y-%m-%d')",
|
|
||||||
"select * from history_earnings_dates"
|
|
||||||
};
|
|
||||||
|
|
||||||
usql::USql uSql{};
|
|
||||||
|
|
||||||
for (const auto &command : sql_commands) {
|
|
||||||
time_point<high_resolution_clock> start_time = high_resolution_clock::now();
|
|
||||||
auto result = uSql.execute(command);
|
|
||||||
time_point<high_resolution_clock> end_time = high_resolution_clock::now();
|
|
||||||
|
|
||||||
std::cout << command << std::endl;
|
|
||||||
std::cout << "run time: " << duration_cast<milliseconds>(end_time - start_time).count() << " ms "
|
|
||||||
<< std::endl << std::endl;
|
|
||||||
|
|
||||||
result->print();
|
|
||||||
}
|
|
||||||
|
|
||||||
std::cout << std::endl << std::endl;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
int main(int argc, char *argv[]) {
|
|
||||||
|
|
||||||
#ifdef NDEBUG
|
|
||||||
repl();
|
|
||||||
#else
|
|
||||||
debug();
|
|
||||||
#endif
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
@ -15,6 +15,8 @@ namespace usql {
|
||||||
|
|
||||||
if (m_lexer.tokenType() == TokenType::keyword_create && m_lexer.nextTokenType() == TokenType::keyword_table)
|
if (m_lexer.tokenType() == TokenType::keyword_create && m_lexer.nextTokenType() == TokenType::keyword_table)
|
||||||
return parse_create_table();
|
return parse_create_table();
|
||||||
|
if (m_lexer.tokenType() == TokenType::keyword_create && m_lexer.nextTokenType() == TokenType::keyword_index)
|
||||||
|
return parse_create_index();
|
||||||
if (m_lexer.tokenType() == TokenType::keyword_drop)
|
if (m_lexer.tokenType() == TokenType::keyword_drop)
|
||||||
return parse_drop_table();
|
return parse_drop_table();
|
||||||
|
|
||||||
|
|
@ -302,6 +304,18 @@ namespace usql {
|
||||||
return std::make_unique<UpdateTableNode>(table_name, cols_names, std::move(values), std::move(where_node));
|
return std::make_unique<UpdateTableNode>(table_name, cols_names, std::move(values), std::move(where_node));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::unique_ptr<Node> Parser::parse_create_index() {
|
||||||
|
m_lexer.skipToken(TokenType::keyword_create);
|
||||||
|
m_lexer.skipToken(TokenType::keyword_index);
|
||||||
|
std::string index_name = m_lexer.consumeToken(TokenType::identifier).token_string;
|
||||||
|
m_lexer.skipToken(TokenType::keyword_on);
|
||||||
|
std::string table_name = m_lexer.consumeToken(TokenType::identifier).token_string;
|
||||||
|
m_lexer.skipToken(TokenType::open_paren);
|
||||||
|
std::string column_name = m_lexer.consumeToken(TokenType::identifier).token_string;
|
||||||
|
m_lexer.skipToken(TokenType::close_paren);
|
||||||
|
|
||||||
|
return std::make_unique<CreateIndexNode>(index_name, table_name, column_name);
|
||||||
|
}
|
||||||
|
|
||||||
std::vector<ColOrderNode> Parser::parse_order_by_clause() {
|
std::vector<ColOrderNode> Parser::parse_order_by_clause() {
|
||||||
std::vector<ColOrderNode> order_cols;
|
std::vector<ColOrderNode> order_cols;
|
||||||
|
|
@ -331,7 +345,7 @@ namespace usql {
|
||||||
order_cols.emplace_back(cspec_token, asc);
|
order_cols.emplace_back(cspec_token, asc);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
throw Exception("order by column can be either column index or identifier");
|
throw Exception("order by column can be either column m_index or identifier");
|
||||||
}
|
}
|
||||||
|
|
||||||
m_lexer.skipTokenOptional(TokenType::comma);
|
m_lexer.skipTokenOptional(TokenType::comma);
|
||||||
|
|
|
||||||
439
usql/parser.h
439
usql/parser.h
|
|
@ -6,21 +6,22 @@
|
||||||
#include "settings.h"
|
#include "settings.h"
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <utility>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
static const int FUNCTION_CALL = -1;
|
static const int FUNCTION_CALL = -1;
|
||||||
|
|
||||||
namespace usql {
|
namespace usql {
|
||||||
|
|
||||||
enum class ColumnType {
|
enum class ColumnType {
|
||||||
integer_type,
|
integer_type,
|
||||||
float_type,
|
float_type,
|
||||||
varchar_type,
|
varchar_type,
|
||||||
date_type,
|
date_type,
|
||||||
bool_type
|
bool_type
|
||||||
};
|
};
|
||||||
|
|
||||||
enum class NodeType {
|
enum class NodeType {
|
||||||
true_node,
|
true_node,
|
||||||
null_value,
|
null_value,
|
||||||
int_value,
|
int_value,
|
||||||
|
|
@ -39,173 +40,232 @@ namespace usql {
|
||||||
load_table,
|
load_table,
|
||||||
save_table,
|
save_table,
|
||||||
drop_table,
|
drop_table,
|
||||||
|
create_index,
|
||||||
set,
|
set,
|
||||||
show,
|
show,
|
||||||
database_value,
|
database_value,
|
||||||
offset_limit,
|
offset_limit,
|
||||||
column_order,
|
column_order,
|
||||||
column_value,
|
|
||||||
function,
|
function,
|
||||||
column_def,
|
column_def,
|
||||||
error
|
error
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Node {
|
struct Node {
|
||||||
NodeType node_type;
|
NodeType node_type;
|
||||||
|
|
||||||
explicit Node(const NodeType type) : node_type(type) {}
|
explicit Node(const NodeType type) : node_type(type) {}
|
||||||
virtual ~Node() = default;
|
virtual ~Node() = default;
|
||||||
};
|
|
||||||
|
virtual void dump() const {
|
||||||
|
std::cout << "type: Node" << std::endl;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
struct ColOrderNode : Node {
|
struct ColOrderNode : Node {
|
||||||
std::string col_name;
|
std::string col_name;
|
||||||
int col_index;
|
int col_index;
|
||||||
bool ascending;
|
bool ascending;
|
||||||
|
|
||||||
ColOrderNode(const std::string& name, bool asc) : Node(NodeType::column_order), col_name(name), col_index(-1), ascending(asc) {}
|
ColOrderNode(std::string name, bool asc) : Node(NodeType::column_order), col_name(std::move(name)), col_index(-1), ascending(asc) {}
|
||||||
ColOrderNode(int index, bool asc) : Node(NodeType::database_value), col_name(""), col_index(index), ascending(asc) {}
|
ColOrderNode(int index, bool asc) : Node(NodeType::database_value), col_index(index), ascending(asc) {}
|
||||||
};
|
|
||||||
|
void dump() const override {
|
||||||
|
std::cout << "type: ColOrderNode, col_name: " << col_name << ", col_index: " << col_index << ", asc: " << ascending << std::endl;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
struct OffsetLimitNode : Node {
|
struct OffsetLimitNode : Node {
|
||||||
int offset;
|
int offset;
|
||||||
int limit;
|
int limit;
|
||||||
|
|
||||||
OffsetLimitNode(int off, int lim) : Node(NodeType::offset_limit), offset(off), limit(lim) {}
|
OffsetLimitNode(int off, int lim) : Node(NodeType::offset_limit), offset(off), limit(lim) {}
|
||||||
};
|
|
||||||
|
void dump() const override {
|
||||||
|
std::cout << "type: OffsetLimitNode, offset: " << offset << ", limit: " << limit << std::endl;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
struct SelectColNode : Node {
|
struct SelectColNode : Node {
|
||||||
std::unique_ptr<Node> value;
|
std::unique_ptr<Node> value;
|
||||||
std::string name;
|
std::string name;
|
||||||
|
|
||||||
SelectColNode(std::unique_ptr<Node> column, const std::string &alias) :
|
SelectColNode(std::unique_ptr<Node> column, std::string alias) :
|
||||||
Node(NodeType::database_value), value(std::move(column)), name(alias) {}
|
Node(NodeType::database_value), value(std::move(column)), name(std::move(alias)) {}
|
||||||
};
|
|
||||||
|
|
||||||
struct ColDefNode : Node {
|
void dump() const override {
|
||||||
|
std::cout << "type: SelectColNode, name:" << name << "value:" << std::endl;
|
||||||
|
value->dump();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ColDefNode : Node {
|
||||||
std::string name;
|
std::string name;
|
||||||
ColumnType type;
|
ColumnType type;
|
||||||
int order;
|
int order;
|
||||||
int length;
|
int length;
|
||||||
bool null;
|
bool null;
|
||||||
|
|
||||||
ColDefNode(const std::string& col_name, ColumnType col_type, int col_order, int col_len, bool nullable) :
|
ColDefNode(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(std::move(col_name)), type(col_type), order(col_order), length(col_len),
|
||||||
null(nullable) {}
|
null(nullable) {}
|
||||||
};
|
|
||||||
|
|
||||||
struct FunctionNode : Node {
|
void dump() const override {
|
||||||
std::string function; // TODO use enum
|
std::cout << "type: ColDefNode, name: " << name << ", type: " << (int)type << " TODO add more" << std::endl;
|
||||||
std::vector<std::unique_ptr<Node>> params;
|
}
|
||||||
|
};
|
||||||
|
|
||||||
FunctionNode(const std::string& func_name, std::vector<std::unique_ptr<Node>> pars) :
|
struct FunctionNode : Node {
|
||||||
Node(NodeType::function), function(func_name), params(std::move(pars)) {}
|
std::string function; // TODO use enum
|
||||||
};
|
std::vector<std::unique_ptr<Node>> params;
|
||||||
|
|
||||||
struct TrueNode : Node {
|
FunctionNode(std::string func_name, std::vector<std::unique_ptr<Node>> pars) :
|
||||||
|
Node(NodeType::function), function(std::move(func_name)), params(std::move(pars)) {}
|
||||||
|
|
||||||
|
void dump() const override {
|
||||||
|
std::cout << "type: FunctionNode, function: " << function << " TODO add more" << std::endl;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct TrueNode : Node {
|
||||||
TrueNode() : Node(NodeType::true_node) {}
|
TrueNode() : Node(NodeType::true_node) {}
|
||||||
};
|
|
||||||
|
|
||||||
struct ValueNode : Node {
|
void dump() const override {
|
||||||
|
std::cout << "type: TrueNode," << std::endl;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ValueNode : Node {
|
||||||
explicit ValueNode(NodeType type) : Node(type) {}
|
explicit ValueNode(NodeType type) : Node(type) {}
|
||||||
|
|
||||||
virtual bool isNull() { return false; }
|
virtual bool isNull() const { return false; }
|
||||||
virtual long getIntegerValue() = 0;
|
virtual long getIntegerValue() const = 0;
|
||||||
virtual double getDoubleValue() = 0;
|
virtual double getDoubleValue() const = 0;
|
||||||
virtual std::string getStringValue() = 0;
|
virtual std::string getStringValue() const = 0;
|
||||||
virtual long getDateValue() = 0;
|
virtual long getDateValue() const = 0;
|
||||||
virtual bool getBooleanValue() = 0;
|
virtual bool getBooleanValue() const = 0;
|
||||||
|
|
||||||
virtual ~ValueNode() = default;
|
~ValueNode() override = default;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct NullValueNode : ValueNode {
|
struct NullValueNode : ValueNode {
|
||||||
|
|
||||||
NullValueNode() : ValueNode(NodeType::null_value) {}
|
NullValueNode() : ValueNode(NodeType::null_value) {}
|
||||||
|
|
||||||
bool isNull() override { return true; }
|
bool isNull() const override { return true; }
|
||||||
|
|
||||||
long getIntegerValue() override { throw Exception("getIntegerValue not supported on NullValueNode"); };
|
long getIntegerValue() const override { throw Exception("getIntegerValue not supported on NullValueNode"); };
|
||||||
double getDoubleValue() override { throw Exception("getDoubleValue not supported on NullValueNode"); };
|
double getDoubleValue() const override { throw Exception("getDoubleValue not supported on NullValueNode"); };
|
||||||
std::string getStringValue() override { throw Exception("getStringValue not supported on NullValueNode"); };
|
std::string getStringValue() const override { throw Exception("getStringValue not supported on NullValueNode"); };
|
||||||
long getDateValue() override { throw Exception("getDateValue not supported on NullValueNode"); };
|
long getDateValue() const override { throw Exception("getDateValue not supported on NullValueNode"); };
|
||||||
bool getBooleanValue() override { throw Exception("getBooleanValue not supported on NullValueNode"); };
|
bool getBooleanValue() const override { throw Exception("getBooleanValue not supported on NullValueNode"); };
|
||||||
};
|
|
||||||
|
|
||||||
struct IntValueNode : ValueNode {
|
void dump() const override {
|
||||||
|
std::cout << "type: NullValueNode," << std::endl;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct IntValueNode : ValueNode {
|
||||||
long value;
|
long value;
|
||||||
|
|
||||||
explicit IntValueNode(long value) : ValueNode(NodeType::int_value), value(value) {}
|
explicit IntValueNode(long value) : ValueNode(NodeType::int_value), value(value) {}
|
||||||
|
|
||||||
long getIntegerValue() override { return value; };
|
long getIntegerValue() const override { return value; };
|
||||||
double getDoubleValue() override { return (double) value; };
|
double getDoubleValue() const override { return (double) value; };
|
||||||
std::string getStringValue() override { return Settings::int_to_string(value); }
|
std::string getStringValue() const override { return Settings::long_to_string(value); }
|
||||||
long getDateValue() override { return value; };
|
long getDateValue() const override { return value; };
|
||||||
bool getBooleanValue() override { return value != 0; };
|
bool getBooleanValue() const override { return value != 0; };
|
||||||
};
|
|
||||||
|
|
||||||
struct DoubleValueNode : ValueNode {
|
void dump() const override {
|
||||||
|
std::cout << "type: IntValueNode, value: " << value << std::endl;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct DoubleValueNode : ValueNode {
|
||||||
double value;
|
double value;
|
||||||
|
|
||||||
explicit DoubleValueNode(double value) : ValueNode(NodeType::float_value), value(value) {}
|
explicit DoubleValueNode(double value) : ValueNode(NodeType::float_value), value(value) {}
|
||||||
|
|
||||||
long getIntegerValue() override { return (long) value; };
|
long getIntegerValue() const override { return (long) value; };
|
||||||
double getDoubleValue() override { return value; };
|
double getDoubleValue() const override { return value; };
|
||||||
std::string getStringValue() override { return Settings::double_to_string(value); }
|
std::string getStringValue() const override { return Settings::double_to_string(value); }
|
||||||
long getDateValue() override { return (long) value; };
|
long getDateValue() const override { return (long) value; };
|
||||||
bool getBooleanValue() override { return value != 0.0; };
|
bool getBooleanValue() const override { return value != 0.0; };
|
||||||
};
|
|
||||||
|
|
||||||
struct StringValueNode : ValueNode {
|
void dump() const override {
|
||||||
|
std::cout << "type: DoubleValueNode, value: " << value << std::endl;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct StringValueNode : ValueNode {
|
||||||
std::string value;
|
std::string value;
|
||||||
|
|
||||||
explicit StringValueNode(const std::string &value) : ValueNode(NodeType::string_value), value(value) {}
|
explicit StringValueNode(std::string value) : ValueNode(NodeType::string_value), value(std::move(value)) {}
|
||||||
|
|
||||||
long getIntegerValue() override { return Settings::string_to_int(value); };
|
long getIntegerValue() const override { return Settings::string_to_long(value); };
|
||||||
double getDoubleValue() override { return Settings::string_to_double(value); };
|
double getDoubleValue() const override { return Settings::string_to_double(value); };
|
||||||
std::string getStringValue() override { return value; };
|
std::string getStringValue() const override { return value; };
|
||||||
long getDateValue() override { return Settings::string_to_date(value); };
|
long getDateValue() const override { return Settings::string_to_date(value); };
|
||||||
bool getBooleanValue() override { return Settings::string_to_bool(value); };
|
bool getBooleanValue() const override { return Settings::string_to_bool(value); };
|
||||||
};
|
|
||||||
|
|
||||||
struct BooleanValueNode : ValueNode {
|
void dump() const override {
|
||||||
bool value;
|
std::cout << "type: StringValueNode, value: " << value << std::endl;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
explicit BooleanValueNode(bool value) : ValueNode(NodeType::bool_value), value(value) {}
|
struct BooleanValueNode : ValueNode {
|
||||||
|
bool value;
|
||||||
|
|
||||||
long getIntegerValue() override { return (long) value; };
|
explicit BooleanValueNode(bool value) : ValueNode(NodeType::bool_value), value(value) {}
|
||||||
double getDoubleValue() override { return (double) value; };
|
|
||||||
std::string getStringValue() override { return Settings::bool_to_string(value); }
|
long getIntegerValue() const override { return (long) value; };
|
||||||
long getDateValue() override { return (long) value; };
|
double getDoubleValue() const override { return (double) value; };
|
||||||
bool getBooleanValue() override { return value; };
|
std::string getStringValue() const override { return Settings::bool_to_string(value); }
|
||||||
};
|
long getDateValue() const override { return (long) value; };
|
||||||
|
bool getBooleanValue() const override { return value; };
|
||||||
|
|
||||||
|
void dump() const override {
|
||||||
|
std::cout << "type: BooleanValueNode, value: " << value << std::endl;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
struct DatabaseValueNode : Node {
|
struct DatabaseValueNode : Node {
|
||||||
std::string col_name;
|
std::string col_name;
|
||||||
|
|
||||||
explicit DatabaseValueNode(const std::string &name) : Node(NodeType::database_value), col_name(name) {}
|
explicit DatabaseValueNode(std::string name) : Node(NodeType::database_value), col_name(std::move(name)) {}
|
||||||
};
|
|
||||||
|
|
||||||
enum class LogicalOperatorType {
|
void dump() const override {
|
||||||
|
std::cout << "type: DatabaseValueNode, col_name: " << col_name << std::endl;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class LogicalOperatorType {
|
||||||
and_operator,
|
and_operator,
|
||||||
or_operator,
|
or_operator
|
||||||
not_operator
|
// not_operator
|
||||||
};
|
};
|
||||||
|
|
||||||
struct LogicalOperatorNode : Node {
|
struct LogicalOperatorNode : Node {
|
||||||
LogicalOperatorType op;
|
LogicalOperatorType op;
|
||||||
std::unique_ptr<Node> left;
|
std::unique_ptr<Node> left;
|
||||||
std::unique_ptr<Node> right;
|
std::unique_ptr<Node> right;
|
||||||
|
|
||||||
LogicalOperatorNode(LogicalOperatorType op, std::unique_ptr<Node> left, std::unique_ptr<Node> right) :
|
LogicalOperatorNode(LogicalOperatorType op, std::unique_ptr<Node> left, std::unique_ptr<Node> right) :
|
||||||
Node(NodeType::logical_operator), op(op), left(std::move(left)), right(std::move(right)) {};
|
Node(NodeType::logical_operator), op(op), left(std::move(left)), right(std::move(right)) {};
|
||||||
};
|
|
||||||
|
|
||||||
enum class RelationalOperatorType {
|
void dump() const override {
|
||||||
|
std::cout << "type: LogicalOperatorNode, op: " << (int)op << std::endl;
|
||||||
|
left->dump();
|
||||||
|
right->dump();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class RelationalOperatorType {
|
||||||
equal,
|
equal,
|
||||||
greater,
|
greater,
|
||||||
greater_equal,
|
greater_equal,
|
||||||
|
|
@ -215,54 +275,74 @@ namespace usql {
|
||||||
is,
|
is,
|
||||||
is_not
|
is_not
|
||||||
// like
|
// like
|
||||||
};
|
};
|
||||||
|
|
||||||
struct RelationalOperatorNode : Node {
|
struct RelationalOperatorNode : Node {
|
||||||
RelationalOperatorType op;
|
RelationalOperatorType op;
|
||||||
|
|
||||||
std::unique_ptr<Node> left;
|
std::unique_ptr<Node> left;
|
||||||
std::unique_ptr<Node> right;
|
std::unique_ptr<Node> right;
|
||||||
|
|
||||||
RelationalOperatorNode(RelationalOperatorType op, std::unique_ptr<Node> left, std::unique_ptr<Node> right) :
|
RelationalOperatorNode(RelationalOperatorType op, std::unique_ptr<Node> left, std::unique_ptr<Node> right) :
|
||||||
Node(NodeType::relational_operator), op(op), left(std::move(left)), right(std::move(right)) {};
|
Node(NodeType::relational_operator), op(op), left(std::move(left)), right(std::move(right)) {};
|
||||||
};
|
|
||||||
|
|
||||||
enum class ArithmeticalOperatorType {
|
void dump() const override {
|
||||||
|
std::cout << "type: RelationalOperatorNode, op: " << (int)op << std::endl;
|
||||||
|
left->dump();
|
||||||
|
right->dump();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class ArithmeticalOperatorType {
|
||||||
copy_value, // just copy lef value and do nothing with it
|
copy_value, // just copy lef value and do nothing with it
|
||||||
plus_operator,
|
plus_operator,
|
||||||
minus_operator,
|
minus_operator,
|
||||||
multiply_operator,
|
multiply_operator,
|
||||||
divide_operator
|
divide_operator
|
||||||
};
|
};
|
||||||
|
|
||||||
struct ArithmeticalOperatorNode : Node {
|
struct ArithmeticalOperatorNode : Node {
|
||||||
ArithmeticalOperatorType op;
|
ArithmeticalOperatorType op;
|
||||||
|
|
||||||
std::unique_ptr<Node> left;
|
std::unique_ptr<Node> left;
|
||||||
std::unique_ptr<Node> right;
|
std::unique_ptr<Node> right;
|
||||||
|
|
||||||
ArithmeticalOperatorNode(ArithmeticalOperatorType op, std::unique_ptr<Node> left, std::unique_ptr<Node> right) :
|
ArithmeticalOperatorNode(ArithmeticalOperatorType op, std::unique_ptr<Node> left, std::unique_ptr<Node> right) :
|
||||||
Node(NodeType::arithmetical_operator), op(op), left(std::move(left)), right(std::move(right)) {};
|
Node(NodeType::arithmetical_operator), op(op), left(std::move(left)), right(std::move(right)) {};
|
||||||
};
|
|
||||||
|
|
||||||
struct CreateTableNode : Node {
|
void dump() const override {
|
||||||
|
std::cout << "type: ArithmeticalOperatorNode, op: " << (int)op << std::endl;
|
||||||
|
left->dump();
|
||||||
|
right->dump();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct CreateTableNode : Node {
|
||||||
std::string table_name;
|
std::string table_name;
|
||||||
std::vector<ColDefNode> cols_defs;
|
std::vector<ColDefNode> cols_defs;
|
||||||
|
|
||||||
CreateTableNode(const std::string& name, std::vector<ColDefNode> defs) :
|
CreateTableNode(std::string name, std::vector<ColDefNode> defs) :
|
||||||
Node(NodeType::create_table), table_name(name), cols_defs(std::move(defs)) {}
|
Node(NodeType::create_table), table_name(std::move(name)), cols_defs(std::move(defs)) {}
|
||||||
};
|
|
||||||
|
|
||||||
struct InsertIntoTableNode : Node {
|
void dump() const override {
|
||||||
|
std::cout << "type: CreateTableNode, table_name: " << table_name << "TODO complete me" << std::endl;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct InsertIntoTableNode : Node {
|
||||||
std::string table_name;
|
std::string table_name;
|
||||||
std::vector<DatabaseValueNode> cols_names;
|
std::vector<DatabaseValueNode> cols_names;
|
||||||
std::vector<std::unique_ptr<Node>> cols_values;
|
std::vector<std::unique_ptr<Node>> cols_values;
|
||||||
|
|
||||||
InsertIntoTableNode(const std::string& name, std::vector<DatabaseValueNode> names, std::vector<std::unique_ptr<Node>> values) :
|
InsertIntoTableNode(std::string name, std::vector<DatabaseValueNode> names, std::vector<std::unique_ptr<Node>> values) :
|
||||||
Node(NodeType::insert_into), table_name(name), cols_names(std::move(names)), cols_values(std::move(values)) {}
|
Node(NodeType::insert_into), table_name(std::move(name)), cols_names(std::move(names)), cols_values(std::move(values)) {}
|
||||||
};
|
|
||||||
|
|
||||||
struct SelectFromTableNode : Node {
|
void dump() const override {
|
||||||
|
std::cout << "type: InsertIntoTableNode, table_name: " << table_name << "TODO complete me" << std::endl;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct SelectFromTableNode : Node {
|
||||||
std::string table_name;
|
std::string table_name;
|
||||||
std::unique_ptr<std::vector<SelectColNode>> cols_names;
|
std::unique_ptr<std::vector<SelectColNode>> cols_names;
|
||||||
std::unique_ptr<Node> where;
|
std::unique_ptr<Node> where;
|
||||||
|
|
@ -271,84 +351,134 @@ namespace usql {
|
||||||
bool distinct;
|
bool distinct;
|
||||||
|
|
||||||
SelectFromTableNode(std::string name, std::unique_ptr<std::vector<SelectColNode>> names, std::unique_ptr<Node> where_clause, std::vector<ColOrderNode> orderby, OffsetLimitNode offlim, bool distinct_):
|
SelectFromTableNode(std::string name, std::unique_ptr<std::vector<SelectColNode>> names, std::unique_ptr<Node> where_clause, std::vector<ColOrderNode> orderby, OffsetLimitNode offlim, bool distinct_):
|
||||||
Node(NodeType::select_from), table_name(std::move(name)), cols_names(std::move(names)), where(std::move(where_clause)), order_by(std::move(orderby)), offset_limit(offlim), distinct(distinct_) {}
|
Node(NodeType::select_from), table_name(std::move(name)), cols_names(std::move(names)), where(std::move(where_clause)), order_by(std::move(orderby)), offset_limit(std::move(offlim)), distinct(distinct_) {}
|
||||||
};
|
|
||||||
|
|
||||||
struct CreateTableAsSelectNode : Node {
|
void dump() const override {
|
||||||
std::string table_name;
|
std::cout << "type: SelectFromTableNode, table_name: " << table_name << "TODO complete me" << std::endl;
|
||||||
std::unique_ptr<Node> select_table;
|
where->dump();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
CreateTableAsSelectNode(const std::string& name, std::unique_ptr<Node> table) :
|
struct CreateTableAsSelectNode : Node {
|
||||||
Node(NodeType::create_table_as_select), table_name(name), select_table(std::move(table)) {}
|
std::string table_name;
|
||||||
};
|
std::unique_ptr<Node> select_table;
|
||||||
|
|
||||||
struct UpdateTableNode : Node {
|
CreateTableAsSelectNode(std::string name, std::unique_ptr<Node> table) :
|
||||||
|
Node(NodeType::create_table_as_select), table_name(std::move(name)), select_table(std::move(table)) {}
|
||||||
|
|
||||||
|
void dump() const override {
|
||||||
|
std::cout << "type: CreateTableAsSelectNode, table_name: " << table_name << std::endl;
|
||||||
|
select_table->dump();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct UpdateTableNode : Node {
|
||||||
std::string table_name;
|
std::string table_name;
|
||||||
std::vector<DatabaseValueNode> cols_names;
|
std::vector<DatabaseValueNode> cols_names;
|
||||||
std::vector<std::unique_ptr<Node>> values;
|
std::vector<std::unique_ptr<Node>> values;
|
||||||
std::unique_ptr<Node> where;
|
std::unique_ptr<Node> where;
|
||||||
|
|
||||||
UpdateTableNode(const std::string &name, std::vector<DatabaseValueNode> names, std::vector<std::unique_ptr<Node>> vals,
|
UpdateTableNode(std::string name, std::vector<DatabaseValueNode> names, std::vector<std::unique_ptr<Node>> vals,
|
||||||
std::unique_ptr<Node> where_clause) :
|
std::unique_ptr<Node> where_clause) :
|
||||||
Node(NodeType::update_table), table_name(name), cols_names(names), values(std::move(vals)),
|
Node(NodeType::update_table), table_name(std::move(name)), cols_names(std::move(names)), values(std::move(vals)),
|
||||||
where(std::move(where_clause)) {}
|
where(std::move(where_clause)) {}
|
||||||
};
|
|
||||||
|
|
||||||
struct LoadIntoTableNode : Node {
|
void dump() const override {
|
||||||
|
std::cout << "type: UpdateTableNode, table_name: " << table_name << "TODO complete me" << std::endl;
|
||||||
|
where->dump();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct LoadIntoTableNode : Node {
|
||||||
std::string table_name;
|
std::string table_name;
|
||||||
std::string filename;
|
std::string filename;
|
||||||
|
|
||||||
LoadIntoTableNode(const std::string& name, const std::string &file) :
|
LoadIntoTableNode(std::string name, std::string file) :
|
||||||
Node(NodeType::load_table), table_name(name), filename(file) {}
|
Node(NodeType::load_table), table_name(std::move(name)), filename(std::move(file)) {}
|
||||||
};
|
|
||||||
|
|
||||||
struct SaveTableNode : Node {
|
void dump() const override {
|
||||||
std::string table_name;
|
std::cout << "type: LoadIntoTableNode, table_name: " << table_name << ", filename" << filename << std::endl;
|
||||||
std::string filename;
|
}
|
||||||
|
};
|
||||||
|
|
||||||
SaveTableNode(const std::string& name, const std::string &file) :
|
struct SaveTableNode : Node {
|
||||||
Node(NodeType::save_table), table_name(name), filename(file) {}
|
std::string table_name;
|
||||||
};
|
std::string filename;
|
||||||
|
|
||||||
struct DropTableNode : Node {
|
SaveTableNode(std::string name, std::string file) :
|
||||||
std::string table_name;
|
Node(NodeType::save_table), table_name(std::move(name)), filename(std::move(file)) {}
|
||||||
|
|
||||||
explicit DropTableNode(const std::string& name) : Node(NodeType::drop_table), table_name(name) {}
|
void dump() const override {
|
||||||
};
|
std::cout << "type: SaveTableNode, table_name: " << table_name << ", filename" << filename << std::endl;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
struct DeleteFromTableNode : Node {
|
struct DropTableNode : Node {
|
||||||
|
std::string table_name;
|
||||||
|
|
||||||
|
explicit DropTableNode(std::string name) : Node(NodeType::drop_table), table_name(std::move(name)) {}
|
||||||
|
|
||||||
|
void dump() const override {
|
||||||
|
std::cout << "type: SelectFromTableNode, table_name: " << table_name << std::endl;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct DeleteFromTableNode : Node {
|
||||||
std::string table_name;
|
std::string table_name;
|
||||||
std::unique_ptr<Node> where;
|
std::unique_ptr<Node> where;
|
||||||
|
|
||||||
DeleteFromTableNode(const std::string& name, std::unique_ptr<Node> where_clause) :
|
DeleteFromTableNode(std::string name, std::unique_ptr<Node> where_clause) :
|
||||||
Node(NodeType::delete_from), table_name(name), where(std::move(where_clause)) {}
|
Node(NodeType::delete_from), table_name(std::move(name)), where(std::move(where_clause)) {}
|
||||||
};
|
|
||||||
|
|
||||||
struct SetNode : Node {
|
void dump() const override {
|
||||||
|
std::cout << "type: DeleteFromTableNode, table_name: " << table_name << std::endl;
|
||||||
|
where->dump();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct SetNode : Node {
|
||||||
std::string name;
|
std::string name;
|
||||||
std::string value;
|
std::string value;
|
||||||
|
|
||||||
SetNode(const std::string& name_, const std::string& value_) :
|
SetNode(std::string node_name, std::string node_value) :
|
||||||
Node(NodeType::set), name(name_), value(value_) {}
|
Node(NodeType::set), name(std::move(node_name)), value(std::move(node_value)) {}
|
||||||
};
|
|
||||||
|
|
||||||
struct ShowNode : Node {
|
void dump() const override {
|
||||||
|
std::cout << "type: SetNode, name: " << name << ", value: " << value << std::endl;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ShowNode : Node {
|
||||||
std::string name;
|
std::string name;
|
||||||
|
|
||||||
explicit ShowNode(const std::string& name_) : Node(NodeType::show), name(name_) {}
|
explicit ShowNode(std::string node_name) : Node(NodeType::show), name(std::move(node_name)) {}
|
||||||
};
|
|
||||||
|
|
||||||
|
void dump() const override {
|
||||||
|
std::cout << "type: ShowNode, name: " << name << std::endl;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct CreateIndexNode : Node {
|
||||||
|
std::string index_name;
|
||||||
|
std::string table_name;
|
||||||
|
std::string column_name;
|
||||||
|
|
||||||
class Parser {
|
CreateIndexNode(std::string idx_name, std::string tbl_name, std::string col_name) :
|
||||||
private:
|
Node(NodeType::create_index), index_name(std::move(idx_name)), table_name(std::move(tbl_name)), column_name(std::move(col_name)) {}
|
||||||
|
|
||||||
public:
|
void dump() const override {
|
||||||
|
std::cout << "type: CreateIndexNode, table_name: " << table_name << ", index_name: " << index_name << ", column_name: " << column_name << std::endl;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class Parser {
|
||||||
|
private:
|
||||||
|
public:
|
||||||
Parser();
|
Parser();
|
||||||
|
|
||||||
std::unique_ptr<Node> parse(const std::string &code);
|
std::unique_ptr<Node> parse(const std::string &code);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::unique_ptr<Node> parse_create_table();
|
std::unique_ptr<Node> parse_create_table();
|
||||||
std::unique_ptr<Node> parse_drop_table();
|
std::unique_ptr<Node> parse_drop_table();
|
||||||
std::unique_ptr<Node> parse_load_table();
|
std::unique_ptr<Node> parse_load_table();
|
||||||
|
|
@ -360,6 +490,7 @@ namespace usql {
|
||||||
std::unique_ptr<Node> parse_select_from_table();
|
std::unique_ptr<Node> parse_select_from_table();
|
||||||
std::unique_ptr<Node> parse_delete_from_table();
|
std::unique_ptr<Node> parse_delete_from_table();
|
||||||
std::unique_ptr<Node> parse_update_table();
|
std::unique_ptr<Node> parse_update_table();
|
||||||
|
std::unique_ptr<Node> parse_create_index();
|
||||||
|
|
||||||
std::vector<ColOrderNode> parse_order_by_clause();
|
std::vector<ColOrderNode> parse_order_by_clause();
|
||||||
OffsetLimitNode parse_offset_limit_clause();
|
OffsetLimitNode parse_offset_limit_clause();
|
||||||
|
|
@ -373,8 +504,8 @@ namespace usql {
|
||||||
LogicalOperatorType parse_logical_operator();
|
LogicalOperatorType parse_logical_operator();
|
||||||
ArithmeticalOperatorType parse_arithmetical_operator();
|
ArithmeticalOperatorType parse_arithmetical_operator();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Lexer m_lexer;
|
Lexer m_lexer;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
|
||||||
34
usql/row.cpp
34
usql/row.cpp
|
|
@ -4,16 +4,16 @@
|
||||||
|
|
||||||
namespace usql {
|
namespace usql {
|
||||||
|
|
||||||
int ColNullValue::compare(ColValue &other) {
|
int ColNullValue::compare(ColValue &other) const {
|
||||||
return other.isNull() ? 0 : -1; // null goes to end
|
return other.isNull() ? 0 : -1; // null goes to end
|
||||||
}
|
}
|
||||||
|
|
||||||
int ColIntegerValue::compare(ColValue &other) {
|
int ColIntegerValue::compare(ColValue &other) const {
|
||||||
long r = m_integer - other.getIntValue();
|
long r = m_integer - other.getIntegerValue();
|
||||||
return other.isNull() ? 1 : r > 0 ? 1 : r == 0 ? 0 : -1;
|
return other.isNull() ? 1 : r > 0 ? 1 : r == 0 ? 0 : -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
int ColDoubleValue::compare(ColValue &other) {
|
int ColDoubleValue::compare(ColValue &other) const {
|
||||||
if (other.isNull()) return 1; // null goes to end
|
if (other.isNull()) return 1; // null goes to end
|
||||||
|
|
||||||
double c = m_double - other.getDoubleValue();
|
double c = m_double - other.getDoubleValue();
|
||||||
|
|
@ -25,22 +25,34 @@ ColStringValue & ColStringValue::operator=(ColStringValue other) {
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
int ColStringValue::compare(ColValue &other) {
|
int ColStringValue::compare(ColValue &other) const {
|
||||||
return other.isNull() ? 1 : m_string->compare(other.getStringValue()); // null goes to end
|
return other.isNull() ? 1 : m_string->compare(other.getStringValue()); // null goes to end
|
||||||
}
|
}
|
||||||
|
|
||||||
int ColDateValue::compare(ColValue &other) {
|
std::string ColStringValue::getCsvStringValue() const {
|
||||||
long r = m_date - other.getIntValue();
|
auto src_str = getStringValue();
|
||||||
|
std::string toSearch{"\""}, replaceStr{"\\\""};
|
||||||
|
|
||||||
|
size_t pos = src_str.find(toSearch);
|
||||||
|
while(pos != std::string::npos) {
|
||||||
|
src_str.replace(pos, toSearch.size(), replaceStr);
|
||||||
|
pos =src_str.find(toSearch, pos + replaceStr.size());
|
||||||
|
}
|
||||||
|
return src_str;
|
||||||
|
}
|
||||||
|
|
||||||
|
int ColDateValue::compare(ColValue &other) const {
|
||||||
|
long r = m_date - other.getIntegerValue();
|
||||||
return other.isNull() ? 1 : r > 0 ? 1 : r == 0 ? 0 : -1;
|
return other.isNull() ? 1 : r > 0 ? 1 : r == 0 ? 0 : -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
int ColBooleanValue::compare(ColValue &other) {
|
int ColBooleanValue::compare(ColValue &other) const {
|
||||||
if (other.isNull()) return 1; // null goes to end
|
if (other.isNull()) return 1; // null goes to end
|
||||||
|
|
||||||
return m_bool == other.getBoolValue() ? 0 : m_bool && !other.getBoolValue() ? -1 : 1; // true first
|
return m_bool == other.getBoolValue() ? 0 : m_bool && !other.getBoolValue() ? -1 : 1; // true first
|
||||||
}
|
}
|
||||||
|
|
||||||
Row::Row(const Row &other) : m_columns(other.m_columns.size()) {
|
Row::Row(const Row &other) : m_columns(other.m_columns.size()), m_visible(other.m_visible) {
|
||||||
for (int i = 0; i < other.m_columns.size(); i++) {
|
for (int i = 0; i < other.m_columns.size(); i++) {
|
||||||
if (other[i].isNull())
|
if (other[i].isNull())
|
||||||
continue; // for null NOP
|
continue; // for null NOP
|
||||||
|
|
@ -48,7 +60,7 @@ Row::Row(const Row &other) : m_columns(other.m_columns.size()) {
|
||||||
ColumnType col_type = other[i].getColType();
|
ColumnType col_type = other[i].getColType();
|
||||||
switch (col_type) {
|
switch (col_type) {
|
||||||
case ColumnType::integer_type :
|
case ColumnType::integer_type :
|
||||||
setIntColumnValue(i, other[i].getIntValue());
|
setIntColumnValue(i, other[i].getIntegerValue());
|
||||||
break;
|
break;
|
||||||
case ColumnType::float_type :
|
case ColumnType::float_type :
|
||||||
setFloatColumnValue(i, other[i].getDoubleValue());
|
setFloatColumnValue(i, other[i].getDoubleValue());
|
||||||
|
|
@ -110,7 +122,7 @@ void Row::setBoolColumnValue(int col_index, const std::string &value) {
|
||||||
void Row::setColumnValue(ColDefNode *col_def, ColValue &col_value) {
|
void Row::setColumnValue(ColDefNode *col_def, ColValue &col_value) {
|
||||||
if (!col_value.isNull()) {
|
if (!col_value.isNull()) {
|
||||||
if (col_def->type == ColumnType::integer_type)
|
if (col_def->type == ColumnType::integer_type)
|
||||||
setIntColumnValue(col_def->order, col_value.getIntValue());
|
setIntColumnValue(col_def->order, col_value.getIntegerValue());
|
||||||
else if (col_def->type == ColumnType::float_type)
|
else if (col_def->type == ColumnType::float_type)
|
||||||
setFloatColumnValue(col_def->order, col_value.getDoubleValue());
|
setFloatColumnValue(col_def->order, col_value.getDoubleValue());
|
||||||
else if (col_def->type == ColumnType::varchar_type)
|
else if (col_def->type == ColumnType::varchar_type)
|
||||||
|
|
|
||||||
179
usql/row.h
179
usql/row.h
|
|
@ -9,135 +9,136 @@
|
||||||
|
|
||||||
namespace usql {
|
namespace usql {
|
||||||
|
|
||||||
struct ColValue {
|
struct ColValue {
|
||||||
virtual bool isNull() { return false; };
|
virtual bool isNull() const { return false; };
|
||||||
virtual ColumnType getColType() = 0;
|
virtual ColumnType getColType() const = 0;
|
||||||
virtual long getIntValue() = 0;
|
virtual long getIntegerValue() const = 0;
|
||||||
virtual double getDoubleValue() = 0;
|
virtual double getDoubleValue() const = 0;
|
||||||
virtual std::string getStringValue() = 0;
|
virtual std::string getStringValue() const = 0;
|
||||||
virtual long getDateValue() = 0;
|
virtual std::string getCsvStringValue() const { return getStringValue(); };
|
||||||
virtual bool getBoolValue() = 0;
|
virtual long getDateValue() const = 0;
|
||||||
|
virtual bool getBoolValue() const = 0;
|
||||||
|
|
||||||
virtual int compare(ColValue &other) = 0;
|
virtual int compare(ColValue &other) const = 0;
|
||||||
|
|
||||||
virtual ~ColValue() = default;
|
virtual ~ColValue() = default;
|
||||||
|
};
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
struct ColNullValue : ColValue {
|
struct ColNullValue : ColValue {
|
||||||
bool isNull() override { return true; };
|
bool isNull() const override { return true; };
|
||||||
ColumnType getColType() override { throw Exception("getColType not supported on ColNullValue"); }
|
ColumnType getColType() const override { throw Exception("getColType not supported on ColNullValue"); }
|
||||||
long getIntValue() override { throw Exception("getIntValue not supported on ColNullValue"); };
|
long getIntegerValue() const override { throw Exception("getIntegerValue not supported on ColNullValue"); };
|
||||||
double getDoubleValue() override { throw Exception("getDoubleValue not supported on ColNullValue"); };
|
double getDoubleValue() const override { throw Exception("getDoubleValue not supported on ColNullValue"); };
|
||||||
std::string getStringValue() override { return "null"; };
|
std::string getStringValue() const override { return "null"; };
|
||||||
long getDateValue() override { throw Exception("getDateValue not supported on ColNullValue"); };
|
long getDateValue() const override { throw Exception("getDateValue not supported on ColNullValue"); };
|
||||||
bool getBoolValue() override { throw Exception("getDateValue not supported on ColNullValue"); };
|
bool getBoolValue() const override { throw Exception("getDateValue not supported on ColNullValue"); };
|
||||||
|
|
||||||
int compare(ColValue &other) override;
|
int compare(ColValue &other) const override;
|
||||||
|
|
||||||
virtual ~ColNullValue() = default;
|
~ColNullValue() override = default;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
struct ColIntegerValue : ColValue {
|
struct ColIntegerValue : ColValue {
|
||||||
explicit ColIntegerValue(long value) : m_integer(value) {};
|
explicit ColIntegerValue(long value) : m_integer(value) {};
|
||||||
ColIntegerValue(const ColIntegerValue &other) : m_integer(other.m_integer) {};
|
ColIntegerValue(const ColIntegerValue &other) : m_integer(other.m_integer) {};
|
||||||
|
|
||||||
ColumnType getColType() override { return ColumnType::integer_type; };
|
ColumnType getColType() const override { return ColumnType::integer_type; };
|
||||||
long getIntValue() override { return m_integer; };
|
long getIntegerValue() const override { return m_integer; };
|
||||||
double getDoubleValue() override { return (double) m_integer; };
|
double getDoubleValue() const override { return (double) m_integer; };
|
||||||
std::string getStringValue() override { return std::to_string(m_integer); };
|
std::string getStringValue() const override { return std::to_string(m_integer); };
|
||||||
long getDateValue() override { return m_integer; };
|
long getDateValue() const override { return m_integer; };
|
||||||
bool getBoolValue() override { throw Exception("Not supported on ColIntegerValue"); };
|
bool getBoolValue() const override { throw Exception("Not supported on ColIntegerValue"); };
|
||||||
|
|
||||||
int compare(ColValue &other) override;
|
int compare(ColValue &other) const override;
|
||||||
|
|
||||||
|
~ColIntegerValue() override = default;
|
||||||
|
|
||||||
long m_integer;
|
long m_integer;
|
||||||
|
};
|
||||||
virtual ~ColIntegerValue() = default;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
struct ColDoubleValue : ColValue {
|
struct ColDoubleValue : ColValue {
|
||||||
explicit ColDoubleValue(double value) : m_double(value) {};
|
explicit ColDoubleValue(double value) : m_double(value) {};
|
||||||
ColDoubleValue(const ColDoubleValue &other) : m_double(other.m_double) {}
|
ColDoubleValue(const ColDoubleValue &other) : m_double(other.m_double) {}
|
||||||
|
|
||||||
ColumnType getColType() override { return ColumnType::float_type; };
|
ColumnType getColType() const override { return ColumnType::float_type; };
|
||||||
long getIntValue() override { return (long) m_double; };
|
long getIntegerValue() const override { return (long) m_double; };
|
||||||
double getDoubleValue() override { return m_double; };
|
double getDoubleValue() const override { return m_double; };
|
||||||
std::string getStringValue() override { return Settings::double_to_string(m_double); };
|
std::string getStringValue() const override { return Settings::double_to_string(m_double); };
|
||||||
long getDateValue() override { return (long) m_double; };
|
long getDateValue() const override { return (long) m_double; };
|
||||||
bool getBoolValue() override { throw Exception("Not supported on ColDoubleValue"); };
|
bool getBoolValue() const override { throw Exception("Not supported on ColDoubleValue"); };
|
||||||
|
|
||||||
int compare(ColValue &other) override;
|
int compare(ColValue &other) const override;
|
||||||
|
|
||||||
virtual ~ColDoubleValue() = default;
|
~ColDoubleValue() override = default;
|
||||||
|
|
||||||
double m_double;
|
double m_double;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
struct ColStringValue : ColValue {
|
struct ColStringValue : ColValue {
|
||||||
explicit ColStringValue(const std::string &value) : m_string(std::make_unique<std::string>(value)) {};
|
explicit ColStringValue(const std::string &value) : m_string(std::make_unique<std::string>(value)) {};
|
||||||
ColStringValue(const ColStringValue &other) : m_string(std::make_unique<std::string>(*other.m_string)) {};
|
ColStringValue(const ColStringValue &other) : m_string(std::make_unique<std::string>(*other.m_string)) {};
|
||||||
|
|
||||||
ColStringValue & operator=(ColStringValue other);
|
ColStringValue & operator=(ColStringValue other);
|
||||||
|
|
||||||
ColumnType getColType() override { return ColumnType::varchar_type; };
|
ColumnType getColType() const override { return ColumnType::varchar_type; };
|
||||||
long getIntValue() override { return std::stoi(*m_string); };
|
long getIntegerValue() const override { return std::stoi(*m_string); };
|
||||||
double getDoubleValue() override { return std::stod(*m_string); };
|
double getDoubleValue() const override { return std::stod(*m_string); };
|
||||||
std::string getStringValue() override { return *m_string; };
|
std::string getStringValue() const override { return *m_string; };
|
||||||
long getDateValue() override { return std::stoi(*m_string); };
|
std::string getCsvStringValue() const override;;
|
||||||
bool getBoolValue() override { throw Exception("Not supported on ColStringValue"); };
|
long getDateValue() const override { return std::stoi(*m_string); };
|
||||||
|
bool getBoolValue() const override { throw Exception("Not supported on ColStringValue"); };
|
||||||
|
|
||||||
int compare(ColValue &other) override;
|
int compare(ColValue &other) const override;
|
||||||
|
|
||||||
std::unique_ptr<std::string> m_string;
|
std::unique_ptr<std::string> m_string;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct ColDateValue : ColValue {
|
struct ColDateValue : ColValue {
|
||||||
explicit ColDateValue(long value) : m_date(value) {};
|
explicit ColDateValue(long value) : m_date(value) {};
|
||||||
ColDateValue(const ColDateValue &other) : m_date(other.m_date) {};
|
ColDateValue(const ColDateValue &other) : m_date(other.m_date) {};
|
||||||
|
|
||||||
ColumnType getColType() override { return ColumnType::date_type; };
|
ColumnType getColType() const override { return ColumnType::date_type; };
|
||||||
long getIntValue() override { return m_date; };
|
long getIntegerValue() const override { return m_date; };
|
||||||
double getDoubleValue() override { return (double) m_date; };
|
double getDoubleValue() const override { return (double) m_date; };
|
||||||
std::string getStringValue() override { return Settings::date_to_string(m_date); };
|
std::string getStringValue() const override { return Settings::date_to_string(m_date); };
|
||||||
long getDateValue() override { return m_date; };
|
long getDateValue() const override { return m_date; };
|
||||||
bool getBoolValue() override { throw Exception("Not supported on ColDateValue"); };
|
bool getBoolValue() const override { throw Exception("Not supported on ColDateValue"); };
|
||||||
|
|
||||||
int compare(ColValue &other) override;
|
int compare(ColValue &other) const override;
|
||||||
|
|
||||||
virtual ~ColDateValue() = default;
|
~ColDateValue() override = default;
|
||||||
|
|
||||||
long m_date; // seconds since epoch for now
|
long m_date; // seconds since epoch for now
|
||||||
};
|
};
|
||||||
|
|
||||||
struct ColBooleanValue : ColValue {
|
struct ColBooleanValue : ColValue {
|
||||||
explicit ColBooleanValue(bool value) : m_bool(value) {};
|
explicit ColBooleanValue(bool value) : m_bool(value) {};
|
||||||
ColBooleanValue(const ColBooleanValue &other) : m_bool(other.m_bool) {};
|
ColBooleanValue(const ColBooleanValue &other) : m_bool(other.m_bool) {};
|
||||||
|
|
||||||
ColumnType getColType() override { return ColumnType::bool_type; };
|
ColumnType getColType() const override { return ColumnType::bool_type; };
|
||||||
long getIntValue() override { return (long) m_bool; };
|
long getIntegerValue() const override { return (long) m_bool; };
|
||||||
double getDoubleValue() override { return (double) m_bool; };
|
double getDoubleValue() const override { return (double) m_bool; };
|
||||||
std::string getStringValue() override { return m_bool ? "Y" : "N"; };
|
std::string getStringValue() const override { return m_bool ? "Y" : "N"; };
|
||||||
long getDateValue() override { throw Exception("Not supported on ColBooleanValue"); };
|
long getDateValue() const override { throw Exception("Not supported on ColBooleanValue"); };
|
||||||
bool getBoolValue() override { return m_bool; };
|
bool getBoolValue() const override { return m_bool; };
|
||||||
|
|
||||||
int compare(ColValue &other) override;
|
int compare(ColValue &other) const override;
|
||||||
|
|
||||||
virtual ~ColBooleanValue() = default;
|
~ColBooleanValue() override = default;
|
||||||
|
|
||||||
bool m_bool;
|
bool m_bool;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class Row {
|
class Row {
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit Row(int cols_count) : m_columns(cols_count) {};
|
explicit Row(int cols_count, bool visible) : m_columns(cols_count), m_visible(visible) {};
|
||||||
Row(const Row &other);
|
Row(const Row &other);
|
||||||
|
|
||||||
Row &operator=(Row other);
|
Row &operator=(Row other);
|
||||||
|
|
@ -169,17 +170,23 @@ namespace usql {
|
||||||
return (ColValue &) *std::get_if<ColDateValue>(&m_columns[i]);
|
return (ColValue &) *std::get_if<ColDateValue>(&m_columns[i]);
|
||||||
case 5:
|
case 5:
|
||||||
return (ColValue &) *std::get_if<ColBooleanValue>(&m_columns[i]);
|
return (ColValue &) *std::get_if<ColBooleanValue>(&m_columns[i]);
|
||||||
|
default:
|
||||||
|
throw Exception("should not happen");
|
||||||
}
|
}
|
||||||
throw Exception("should not happen");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int compare(const Row &other) const;
|
[[nodiscard]] int compare(const Row &other) const;
|
||||||
|
|
||||||
void print(const std::vector<ColDefNode> &col_defs);
|
void print(const std::vector<ColDefNode> &col_defs);
|
||||||
static int print_get_column_size(const ColDefNode &col_def);
|
static int print_get_column_size(const ColDefNode &col_def);
|
||||||
private:
|
|
||||||
// xx std::vector<std::unique_ptr<ColValue>> m_columns;
|
[[nodiscard]] bool is_visible() const { return m_visible; };
|
||||||
|
void set_visible() { m_visible = true; };
|
||||||
|
void set_deleted() { m_visible = true; };
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool m_visible;
|
||||||
std::vector<std::variant<ColNullValue, ColIntegerValue, ColDoubleValue, ColStringValue, ColDateValue, ColBooleanValue>> m_columns;
|
std::vector<std::variant<ColNullValue, ColIntegerValue, ColDoubleValue, ColStringValue, ColDateValue, ColBooleanValue>> m_columns;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
@ -1,4 +1,6 @@
|
||||||
|
|
||||||
|
#include "fast_double_parser.h"
|
||||||
|
|
||||||
#include "settings.h"
|
#include "settings.h"
|
||||||
#include "exception.h"
|
#include "exception.h"
|
||||||
#include "ml_date.h"
|
#include "ml_date.h"
|
||||||
|
|
@ -9,21 +11,31 @@ std::vector<std::pair<std::string, std::string>> Settings::m_settings =
|
||||||
{ std::make_pair("DATE_FORMAT", "%Y-%m-%d %H:%M:%S"),
|
{ std::make_pair("DATE_FORMAT", "%Y-%m-%d %H:%M:%S"),
|
||||||
std::make_pair("BOOL_TRUE_LITERAL", "Y"),
|
std::make_pair("BOOL_TRUE_LITERAL", "Y"),
|
||||||
std::make_pair("BOOL_FALSE_LITERAL", "N"),
|
std::make_pair("BOOL_FALSE_LITERAL", "N"),
|
||||||
std::make_pair("DOUBLE_FORMAT", "%.2f") };
|
std::make_pair("DOUBLE_FORMAT", "%.2f"),
|
||||||
|
std::make_pair("USE_INDEXSCAN", "N") };
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
long Settings::string_to_int(const std::string &intstr) {
|
long Settings::string_to_long(const std::string &intstr) {
|
||||||
return std::stoi(intstr);
|
try {
|
||||||
|
return std::stol(intstr);
|
||||||
|
} catch (std::invalid_argument &e) {
|
||||||
|
throw Exception("error parsing as integer: " + intstr);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string Settings::int_to_string(long intval) {
|
std::string Settings::long_to_string(long intval) {
|
||||||
return std::to_string(intval);
|
return std::to_string(intval);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
double Settings::string_to_double(const std::string &doublestr) {
|
double Settings::string_to_double(const std::string &doublestr) {
|
||||||
return std::stod(doublestr); // TODO use fast parsing
|
double result;
|
||||||
|
const char * endptr = fast_double_parser::parse_number(doublestr.c_str(), &result);
|
||||||
|
if (endptr == nullptr) {
|
||||||
|
throw Exception("error parsing as double: " + doublestr);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string Settings::double_to_string(double d) {
|
std::string Settings::double_to_string(double d) {
|
||||||
|
|
@ -47,23 +59,20 @@ std::string Settings::date_to_string(long date) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
bool Settings::string_to_bool(const std::string &boolstr) {
|
bool Settings::string_to_bool(const std::string &value) {
|
||||||
if (boolstr=="true" || boolstr == get_setting("BOOL_TRUE_LITERAL"))
|
if (value == "true" || value == get_setting("BOOL_TRUE_LITERAL"))
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
if (boolstr=="false" || boolstr == get_setting("BOOL_FALSE_LITERAL"))
|
if (value == "false" || value == get_setting("BOOL_FALSE_LITERAL"))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
throw Exception("string_to_bool, unrecognized value: " + boolstr);
|
throw Exception("string_to_bool, unrecognized value: " + value);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string Settings::bool_to_string(bool boolval) {
|
std::string Settings::bool_to_string(bool value) {
|
||||||
return boolval ? "true" : "false";
|
return value ? "true" : "false";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
std::string Settings::get_setting(const std::string &name) {
|
std::string Settings::get_setting(const std::string &name) {
|
||||||
for(const auto& pair : m_settings) {
|
for(const auto& pair : m_settings) {
|
||||||
if (pair.first == name) return pair.second;
|
if (pair.first == name) return pair.second;
|
||||||
|
|
@ -71,6 +80,10 @@ std::string Settings::get_setting(const std::string &name) {
|
||||||
throw Exception("unsupported setting name: " + name);
|
throw Exception("unsupported setting name: " + name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Settings::get_bool_setting(const std::string &name) {
|
||||||
|
return string_to_bool(get_setting(name));
|
||||||
|
}
|
||||||
|
|
||||||
void Settings::set_setting(const std::string &name, const std::string &value) {
|
void Settings::set_setting(const std::string &name, const std::string &value) {
|
||||||
for (auto it = begin(m_settings); it != end(m_settings); ++it) {
|
for (auto it = begin(m_settings); it != end(m_settings); ++it) {
|
||||||
if (it->first == name) {
|
if (it->first == name) {
|
||||||
|
|
|
||||||
|
|
@ -10,9 +10,10 @@ class Settings {
|
||||||
public:
|
public:
|
||||||
static void set_setting(const std::string &name, const std::string &value);
|
static void set_setting(const std::string &name, const std::string &value);
|
||||||
static std::string get_setting(const std::string &name);
|
static std::string get_setting(const std::string &name);
|
||||||
|
static bool get_bool_setting(const std::string &name);
|
||||||
|
|
||||||
static long string_to_int(const std::string &intstr);
|
static long string_to_long(const std::string &intstr);
|
||||||
static std::string int_to_string(long intval);
|
static std::string long_to_string(long intval);
|
||||||
|
|
||||||
static double string_to_double(const std::string &doublestr);
|
static double string_to_double(const std::string &doublestr);
|
||||||
static std::string double_to_string(double doubleval);
|
static std::string double_to_string(double doubleval);
|
||||||
|
|
@ -20,8 +21,8 @@ public:
|
||||||
static long string_to_date(const std::string &datestr);
|
static long string_to_date(const std::string &datestr);
|
||||||
static std::string date_to_string(long dateval);
|
static std::string date_to_string(long dateval);
|
||||||
|
|
||||||
static bool string_to_bool(const std::string &boolstr);
|
static bool string_to_bool(const std::string &value);
|
||||||
static std::string bool_to_string(bool boolval);
|
static std::string bool_to_string(bool value);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static std::vector<std::pair<std::string, std::string>> m_settings;
|
static std::vector<std::pair<std::string, std::string>> m_settings;
|
||||||
|
|
|
||||||
208
usql/table.cpp
208
usql/table.cpp
|
|
@ -1,7 +1,6 @@
|
||||||
#include "table.h"
|
#include "table.h"
|
||||||
#include "csvreader.h"
|
#include "csvreader.h"
|
||||||
#include "ml_string.h"
|
#include "ml_string.h"
|
||||||
#include "fast_double_parser.h"
|
|
||||||
|
|
||||||
#include <charconv>
|
#include <charconv>
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
|
|
@ -19,15 +18,16 @@ 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;
|
||||||
m_rows.reserve(other.m_rows.size());
|
m_rows.reserve(other.m_rows.size());
|
||||||
for(const Row& orig_row : other.m_rows) {
|
|
||||||
commit_copy_of_row(orig_row);
|
for(const Row& orig_row : other.m_rows)
|
||||||
}
|
if (orig_row.is_visible())
|
||||||
|
commit_copy_of_row((Row&)orig_row);
|
||||||
}
|
}
|
||||||
|
|
||||||
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](const ColDefNode& cd) { return cd.name == col_name; };
|
auto name_cmp = [col_name](const ColDefNode& cd) { return cd.name == col_name; };
|
||||||
|
|
||||||
auto col_def = std::find_if(begin(m_col_defs), end(m_col_defs), name_cmp);
|
auto col_def = std::find_if(std::begin(m_col_defs), std::end(m_col_defs), name_cmp);
|
||||||
if (col_def != std::end(m_col_defs)) {
|
if (col_def != std::end(m_col_defs)) {
|
||||||
return *col_def;
|
return *col_def;
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -39,35 +39,43 @@ ColDefNode Table::get_column_def(int col_index) {
|
||||||
if (col_index >= 0 && col_index < columns_count()) {
|
if (col_index >= 0 && col_index < columns_count()) {
|
||||||
return m_col_defs[col_index];
|
return m_col_defs[col_index];
|
||||||
} else {
|
} else {
|
||||||
throw Exception("column with this index does not exists (" + std::to_string(col_index) + ")");
|
throw Exception("column with this m_index does not exists (" + std::to_string(col_index) + ")");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Row& Table::create_empty_row() {
|
Row& Table::create_empty_row() {
|
||||||
m_rows.emplace_back(columns_count());
|
m_rows.emplace_back(columns_count(), false);
|
||||||
return m_rows.back();
|
return m_rows.back();
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string Table::csv_string() {
|
std::string Table::csv_string() {
|
||||||
// header
|
const size_t k_row_size_est = m_col_defs.size() * 16;
|
||||||
|
|
||||||
std::string out_string;
|
std::string out_string;
|
||||||
|
out_string.reserve(m_rows.size() * k_row_size_est);
|
||||||
|
|
||||||
|
// header
|
||||||
for(int i = 0; i < m_col_defs.size(); i++) {
|
for(int i = 0; i < m_col_defs.size(); i++) {
|
||||||
if (i > 0) out_string += ",";
|
if (i > 0) out_string += ',';
|
||||||
out_string += m_col_defs[i].name;
|
out_string += m_col_defs[i].name;
|
||||||
}
|
}
|
||||||
|
|
||||||
// rows
|
// rows
|
||||||
for (auto & m_row : m_rows) {
|
for (auto & row : m_rows) {
|
||||||
std::string csv_line{"\n"};
|
if (row.is_visible()) {
|
||||||
for(int i = 0; i < m_col_defs.size(); i++) {
|
std::string csv_line{"\n"};
|
||||||
if (i > 0) csv_line += ",";
|
csv_line.reserve(k_row_size_est);
|
||||||
|
|
||||||
auto & col = m_row[i];
|
for (int i = 0; i < m_col_defs.size(); i++) {
|
||||||
if (!col.isNull()) {
|
if (i > 0) csv_line += ',';
|
||||||
csv_line += col.getStringValue(); // TODO handle enclosing commas etc
|
|
||||||
|
auto &col = row[i];
|
||||||
|
if (!col.isNull()) {
|
||||||
|
csv_line += col.getCsvStringValue();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
out_string += csv_line;
|
||||||
}
|
}
|
||||||
out_string += csv_line;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return out_string;
|
return out_string;
|
||||||
|
|
@ -95,12 +103,12 @@ int Table::load_csv_file(const std::string &filename) {
|
||||||
if (infile.good()) {
|
if (infile.good()) {
|
||||||
std::string sLine;
|
std::string sLine;
|
||||||
std::getline(infile, sLine);
|
std::getline(infile, sLine);
|
||||||
line_size = (int)sLine.size();
|
line_size = (int)sLine.size() + 1;
|
||||||
}
|
}
|
||||||
infile.close();
|
infile.close();
|
||||||
|
|
||||||
if (file_size > 0) {
|
if (file_size > 0) {
|
||||||
auto new_size = m_rows.size() + int(file_size / line_size * 1.20);
|
auto new_size = m_rows.size() + int((file_size / line_size) * 1.20);
|
||||||
m_rows.reserve(new_size);
|
m_rows.reserve(new_size);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -116,15 +124,15 @@ void Table::create_row_from_vector(const std::vector<ColDefNode> &colDefs, const
|
||||||
Row& new_row = create_empty_row();
|
Row& new_row = create_empty_row();
|
||||||
|
|
||||||
// copy values
|
// copy values
|
||||||
for (int i = 0; i < std::min<int>(columns_count(), csv_line.size()); i++) {
|
for (size_t i = 0; i < std::min<size_t>(columns_count(), csv_line.size()); i++) {
|
||||||
const ColDefNode & col_def = colDefs[i];
|
const ColDefNode & col_def = colDefs[i];
|
||||||
|
|
||||||
if (csv_line[i].empty()) {
|
if (csv_line[i].empty()) {
|
||||||
new_row.setColumnNull(col_def.order);
|
new_row.setColumnNull(col_def.order);
|
||||||
} else if (col_def.type == ColumnType::integer_type) {
|
} else if (col_def.type == ColumnType::integer_type) {
|
||||||
new_row.setIntColumnValue(col_def.order, string_to_long(csv_line[i]));
|
new_row.setIntColumnValue(col_def.order, Settings::string_to_long(csv_line[i]));
|
||||||
} else if (col_def.type == ColumnType::float_type) {
|
} else if (col_def.type == ColumnType::float_type) {
|
||||||
new_row.setFloatColumnValue(col_def.order, string_to_double(csv_line[i]));
|
new_row.setFloatColumnValue(col_def.order, Settings::string_to_double(csv_line[i]));
|
||||||
} else if (col_def.type == ColumnType::varchar_type) {
|
} else if (col_def.type == ColumnType::varchar_type) {
|
||||||
new_row.setStringColumnValue(col_def.order, csv_line[i]);
|
new_row.setStringColumnValue(col_def.order, csv_line[i]);
|
||||||
} else if (col_def.type == ColumnType::date_type) {
|
} else if (col_def.type == ColumnType::date_type) {
|
||||||
|
|
@ -139,23 +147,6 @@ void Table::create_row_from_vector(const std::vector<ColDefNode> &colDefs, const
|
||||||
commit_row(new_row);
|
commit_row(new_row);
|
||||||
}
|
}
|
||||||
|
|
||||||
double Table::string_to_double(const std::string &s) {
|
|
||||||
double result;
|
|
||||||
const char * endptr = fast_double_parser::parse_number(s.c_str(), &result);
|
|
||||||
if (endptr == nullptr) {
|
|
||||||
throw Exception("error parsing as double: " + s);
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
long Table::string_to_long(const std::string &s) {
|
|
||||||
try {
|
|
||||||
return std::stol(s);
|
|
||||||
} catch (std::invalid_argument &e) {
|
|
||||||
throw Exception("error parsing as integer: " + s);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Table::print() {
|
void Table::print() {
|
||||||
std::string out{"| "};
|
std::string out{"| "};
|
||||||
std::string out2{"+-"};
|
std::string out2{"+-"};
|
||||||
|
|
@ -181,16 +172,23 @@ void Table::print() {
|
||||||
std::cout << std::endl;
|
std::cout << std::endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Table::commit_row(const Row &row) {
|
size_t Table::get_rowid(const Row &row) const {
|
||||||
|
const Row* row_addr = (Row*)&row;
|
||||||
|
const Row* begin_addr = &(*m_rows.begin());
|
||||||
|
|
||||||
|
return row_addr - begin_addr;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Table::commit_row(Row &row) {
|
||||||
try {
|
try {
|
||||||
validate_row(row);
|
validate_row(row);
|
||||||
|
index_row(row);
|
||||||
} catch (Exception &e) {
|
} catch (Exception &e) {
|
||||||
m_rows.erase(m_rows.end() - 1);
|
|
||||||
throw e;
|
throw e;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Table::commit_copy_of_row(const Row &row) {
|
void Table::commit_copy_of_row(Row &row) {
|
||||||
Row& new_row = create_empty_row();
|
Row& new_row = create_empty_row();
|
||||||
|
|
||||||
for(int i = 0; i < m_col_defs.size(); i++) {
|
for(int i = 0; i < m_col_defs.size(); i++) {
|
||||||
|
|
@ -200,7 +198,7 @@ void Table::commit_copy_of_row(const Row &row) {
|
||||||
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.setIntColumnValue(i, row[i].getIntValue());
|
new_row.setIntColumnValue(i, row[i].getIntegerValue());
|
||||||
} else if (m_col_defs[i].type == ColumnType::float_type) {
|
} else if (m_col_defs[i].type == ColumnType::float_type) {
|
||||||
new_row.setFloatColumnValue(i, row[i].getDoubleValue());
|
new_row.setFloatColumnValue(i, row[i].getDoubleValue());
|
||||||
} else if (m_col_defs[i].type == ColumnType::varchar_type) {
|
} else if (m_col_defs[i].type == ColumnType::varchar_type) {
|
||||||
|
|
@ -215,6 +213,7 @@ void Table::commit_copy_of_row(const Row &row) {
|
||||||
}
|
}
|
||||||
|
|
||||||
validate_row(new_row);
|
validate_row(new_row);
|
||||||
|
index_row(row);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Table::validate_column(const ColDefNode *col_def, ValueNode *col_val) {
|
void Table::validate_column(const ColDefNode *col_def, ValueNode *col_val) {
|
||||||
|
|
@ -235,13 +234,134 @@ void Table::validate_column(const ColDefNode *col_def, ColValue &col_val) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Table::validate_row(const Row &row) {
|
void Table::validate_row(Row &row) {
|
||||||
for(int i = 0; i < m_col_defs.size(); i++) {
|
for(int i = 0; i < m_col_defs.size(); i++) {
|
||||||
ColDefNode col_def = m_col_defs[i];
|
ColDefNode col_def = m_col_defs[i];
|
||||||
ColValue &col_val = row[i];
|
ColValue &col_val = row[i];
|
||||||
|
|
||||||
validate_column(&col_def, col_val);
|
validate_column(&col_def, col_val);
|
||||||
}
|
}
|
||||||
|
row.set_visible();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Table::create_index(const Index& index) {
|
||||||
|
m_indexes.push_back(index);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Table::drop_index(const std::string &index_name) {
|
||||||
|
auto it = std::find_if(m_indexes.begin(), m_indexes.end(),
|
||||||
|
[&index_name](const Index &idx) {
|
||||||
|
return idx.get_index_name() == index_name;
|
||||||
|
});
|
||||||
|
|
||||||
|
if (it != m_indexes.end()) {
|
||||||
|
m_indexes.erase(it);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Table::index_row(Index &index, const ColDefNode &col_def, const Row &row, const size_t rowid) {
|
||||||
|
index.insert(reinterpret_cast<ColValue *>(&row[col_def.order]), rowid);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Table::unindex_row(Index &index, const ColDefNode &col_def, const Row &row, const size_t rowid) {
|
||||||
|
index.remove(reinterpret_cast<ColValue *>(&row[col_def.order]), rowid);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Table::reindex_row(Index &index, const ColDefNode &col_def, const Row &old_row, const Row &new_row, size_t rowid) {
|
||||||
|
unindex_row(index, col_def, old_row, rowid);
|
||||||
|
index_row(index, col_def, new_row, rowid);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Table::index_row(const Row &row) {
|
||||||
|
if (!m_indexes.empty()) {
|
||||||
|
const size_t rowid = get_rowid(row);
|
||||||
|
for (auto &idx : m_indexes) {
|
||||||
|
ColDefNode cDef = get_column_def(idx.get_column_name());
|
||||||
|
index_row(idx, cDef, row, rowid);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Table::unindex_row(const Row &row) {
|
||||||
|
if (!m_indexes.empty()) {
|
||||||
|
const size_t rowid = get_rowid(row);
|
||||||
|
for (auto &idx : m_indexes) {
|
||||||
|
ColDefNode cDef = get_column_def(idx.get_column_name());
|
||||||
|
unindex_row(idx, cDef, row, rowid);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Table::reindex_row(const Row &old_row, const Row &new_row) {
|
||||||
|
if (!m_indexes.empty()) {
|
||||||
|
const size_t rowid = get_rowid(new_row);
|
||||||
|
for (auto &idx : m_indexes) {
|
||||||
|
ColDefNode cDef = get_column_def(idx.get_column_name());
|
||||||
|
reindex_row(idx, cDef, old_row, new_row, rowid);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Table::index_rows(const std::string &index_name) {
|
||||||
|
auto index = get_index(index_name);
|
||||||
|
|
||||||
|
ColDefNode cDef = get_column_def(index->get_column_name());
|
||||||
|
size_t rowid = 0;
|
||||||
|
for(const Row& r : m_rows) {
|
||||||
|
index_row(*index, cDef, r, rowid);
|
||||||
|
rowid++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Index * Table::get_index(const std::string &index_name) {
|
||||||
|
auto it = std::find_if(m_indexes.begin(), m_indexes.end(),
|
||||||
|
[&index_name](const Index &idx) {
|
||||||
|
return idx.get_index_name() == index_name;
|
||||||
|
});
|
||||||
|
|
||||||
|
return (it != m_indexes.end()) ? &(*it) : nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
Index * Table::get_index_for_column(const std::string &col_name) {
|
||||||
|
auto it = std::find_if(m_indexes.begin(), m_indexes.end(),
|
||||||
|
[&col_name](const Index &idx) {
|
||||||
|
return idx.get_column_name() == col_name;
|
||||||
|
});
|
||||||
|
|
||||||
|
return (it != m_indexes.end()) ? &(*it) : nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Table::empty() {
|
||||||
|
if (m_rows.empty()) return true;
|
||||||
|
for (const auto & r : m_rows)
|
||||||
|
if (r.is_visible()) return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
Row *Table::rows_scanner::next() {
|
||||||
|
if (m_use_rowids) {
|
||||||
|
while (m_rowids_idx < m_rowids.size()) {
|
||||||
|
auto row_ptr = &m_table->m_rows[m_rowids[m_rowids_idx]];
|
||||||
|
if (row_ptr->is_visible()) {
|
||||||
|
m_rowids_idx++;
|
||||||
|
return row_ptr;
|
||||||
|
}
|
||||||
|
m_rowids_idx++;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
while (m_fscan_itr != m_table->m_rows.end()) {
|
||||||
|
if (m_fscan_itr->is_visible()) {
|
||||||
|
auto i = m_fscan_itr;
|
||||||
|
m_fscan_itr++;
|
||||||
|
return &(*i);
|
||||||
|
}
|
||||||
|
m_fscan_itr++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
|
||||||
61
usql/table.h
61
usql/table.h
|
|
@ -1,14 +1,16 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include "index.h"
|
||||||
#include "parser.h"
|
#include "parser.h"
|
||||||
#include "row.h"
|
#include "row.h"
|
||||||
|
|
||||||
|
#include <utility>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
|
|
||||||
namespace usql {
|
namespace usql {
|
||||||
|
|
||||||
struct Table {
|
struct Table {
|
||||||
|
|
||||||
Table(const Table &other);
|
Table(const Table &other);
|
||||||
Table(const std::string& name, const std::vector<ColDefNode>& columns);
|
Table(const std::string& name, const std::vector<ColDefNode>& columns);
|
||||||
|
|
||||||
|
|
@ -18,13 +20,15 @@ namespace usql {
|
||||||
[[nodiscard]] int columns_count() const { return (int) m_col_defs.size(); };
|
[[nodiscard]] int columns_count() const { return (int) m_col_defs.size(); };
|
||||||
[[nodiscard]] size_t rows_count() const { return m_rows.size(); };
|
[[nodiscard]] size_t rows_count() const { return m_rows.size(); };
|
||||||
|
|
||||||
Row& create_empty_row();
|
[[nodiscard]] size_t get_rowid(const Row &row) const;
|
||||||
void commit_row(const Row &row);
|
|
||||||
void commit_copy_of_row(const Row &row);
|
Row &create_empty_row();
|
||||||
|
void commit_row(Row &row);
|
||||||
|
void commit_copy_of_row(Row &row);
|
||||||
|
|
||||||
static void validate_column(const ColDefNode *col_def, ValueNode *col_val);
|
static void validate_column(const ColDefNode *col_def, ValueNode *col_val);
|
||||||
static void validate_column(const ColDefNode *col_def, ColValue &col_val);
|
static void validate_column(const ColDefNode *col_def, ColValue &col_val);
|
||||||
void validate_row(const Row &row);
|
void validate_row(Row &row);
|
||||||
|
|
||||||
std::string csv_string();
|
std::string csv_string();
|
||||||
int load_csv_string(const std::string &content);
|
int load_csv_string(const std::string &content);
|
||||||
|
|
@ -32,14 +36,45 @@ namespace usql {
|
||||||
|
|
||||||
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::vector<Row> m_rows;
|
||||||
|
std::vector<Index> m_indexes;
|
||||||
static long string_to_long(const std::string &s) ;
|
|
||||||
static double string_to_double(const std::string &s) ;
|
|
||||||
|
|
||||||
void create_row_from_vector(const std::vector<ColDefNode> &colDefs, const std::vector<std::string> &csv_line);
|
void create_row_from_vector(const std::vector<ColDefNode> &colDefs, const std::vector<std::string> &csv_line);
|
||||||
};
|
|
||||||
|
|
||||||
}
|
void create_index(const Index& index);
|
||||||
|
bool drop_index(const std::string &index_name);
|
||||||
|
|
||||||
|
static void index_row(Index &index, const ColDefNode &col_def, const Row &row, size_t rowid);
|
||||||
|
static void unindex_row(Index &index, const ColDefNode &col_def, const Row &row, size_t rowid);
|
||||||
|
static void reindex_row(Index &index, const ColDefNode &col_def, const Row &old_row, const Row &new_row, size_t rowid);
|
||||||
|
|
||||||
|
void index_row(const Row &row);
|
||||||
|
void unindex_row(const Row &row);
|
||||||
|
void reindex_row(const Row &old_row, const Row &new_row);
|
||||||
|
|
||||||
|
void index_rows(const std::string &index_name);
|
||||||
|
|
||||||
|
Index * get_index(const std::string &index_name);
|
||||||
|
Index * get_index_for_column(const std::string &col_name);
|
||||||
|
|
||||||
|
bool empty();
|
||||||
|
|
||||||
|
struct rows_scanner {
|
||||||
|
explicit rows_scanner(Table *tbl) : m_use_rowids(false), m_table(tbl), m_fscan_itr(tbl->m_rows.begin()) {}
|
||||||
|
rows_scanner(Table *tbl, std::vector<rowid_t> rowids) : m_use_rowids(true), m_table(tbl), m_rowids(std::move(rowids)), m_rowids_idx(0) {}
|
||||||
|
|
||||||
|
Row *next();
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool m_use_rowids;
|
||||||
|
Table * m_table;
|
||||||
|
std::vector<Row>::iterator m_fscan_itr;
|
||||||
|
std::vector<rowid_t> m_rowids;
|
||||||
|
size_t m_rowids_idx{};
|
||||||
|
};
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
|
||||||
227
usql/usql.cpp
227
usql/usql.cpp
|
|
@ -1,10 +1,8 @@
|
||||||
#include "usql.h"
|
#include "usql.h"
|
||||||
#include "exception.h"
|
#include "exception.h"
|
||||||
#include "ml_date.h"
|
|
||||||
#include "ml_string.h"
|
#include "ml_string.h"
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <fstream>
|
|
||||||
|
|
||||||
namespace usql {
|
namespace usql {
|
||||||
|
|
||||||
|
|
@ -20,10 +18,11 @@ std::unique_ptr<Table> USql::execute(const std::string &command) {
|
||||||
}
|
}
|
||||||
|
|
||||||
std::unique_ptr<Table> USql::execute(Node &node) {
|
std::unique_ptr<Table> USql::execute(Node &node) {
|
||||||
// TODO optimize execution nodes here
|
|
||||||
switch (node.node_type) {
|
switch (node.node_type) {
|
||||||
case NodeType::create_table:
|
case NodeType::create_table:
|
||||||
return execute_create_table(static_cast<CreateTableNode &>(node));
|
return execute_create_table(static_cast<CreateTableNode &>(node));
|
||||||
|
case NodeType::create_index:
|
||||||
|
return execute_create_index(static_cast<CreateIndexNode &>(node));
|
||||||
case NodeType::create_table_as_select:
|
case NodeType::create_table_as_select:
|
||||||
return execute_create_table_as_table(static_cast<CreateTableAsSelectNode &>(node));
|
return execute_create_table_as_table(static_cast<CreateTableAsSelectNode &>(node));
|
||||||
case NodeType::drop_table:
|
case NodeType::drop_table:
|
||||||
|
|
@ -65,7 +64,7 @@ bool USql::eval_relational_operator(const RelationalOperatorNode &filter, Table
|
||||||
return !all_null;
|
return !all_null;
|
||||||
return false;
|
return false;
|
||||||
} else if (left_value->node_type == NodeType::int_value && right_value->node_type == NodeType::int_value) {
|
} else if (left_value->node_type == NodeType::int_value && right_value->node_type == NodeType::int_value) {
|
||||||
comparator = left_value->getIntegerValue() - right_value->getIntegerValue();
|
comparator = (double)(left_value->getIntegerValue() - right_value->getIntegerValue());
|
||||||
} else if ((left_value->node_type == NodeType::int_value && right_value->node_type == NodeType::float_value) ||
|
} else if ((left_value->node_type == NodeType::int_value && right_value->node_type == NodeType::float_value) ||
|
||||||
(left_value->node_type == NodeType::float_value && right_value->node_type == NodeType::int_value) ||
|
(left_value->node_type == NodeType::float_value && right_value->node_type == NodeType::int_value) ||
|
||||||
(left_value->node_type == NodeType::float_value && right_value->node_type == NodeType::float_value)) {
|
(left_value->node_type == NodeType::float_value && right_value->node_type == NodeType::float_value)) {
|
||||||
|
|
@ -108,7 +107,7 @@ std::unique_ptr<ValueNode> USql::eval_value_node(Table *table, Row &row, Node *n
|
||||||
if (node->node_type == NodeType::database_value) {
|
if (node->node_type == NodeType::database_value) {
|
||||||
return eval_database_value_node(table, row, node);
|
return eval_database_value_node(table, row, node);
|
||||||
} else if (node->node_type == NodeType::int_value || node->node_type == NodeType::float_value || node->node_type == NodeType::string_value || node->node_type == NodeType::bool_value) {
|
} else if (node->node_type == NodeType::int_value || node->node_type == NodeType::float_value || node->node_type == NodeType::string_value || node->node_type == NodeType::bool_value) {
|
||||||
return eval_literal_value_node(table, row, node);
|
return eval_literal_value_node(row, node);
|
||||||
} else if (node->node_type == NodeType::function) {
|
} else if (node->node_type == NodeType::function) {
|
||||||
return eval_function_value_node(table, row, node, col_def_node, agg_func_value);
|
return eval_function_value_node(table, row, node, col_def_node, agg_func_value);
|
||||||
} else if (node->node_type == NodeType::null_value) {
|
} else if (node->node_type == NodeType::null_value) {
|
||||||
|
|
@ -129,7 +128,7 @@ std::unique_ptr<ValueNode> USql::eval_database_value_node(Table *table, Row &row
|
||||||
return std::make_unique<NullValueNode>();
|
return std::make_unique<NullValueNode>();
|
||||||
|
|
||||||
if (col_def.type == ColumnType::integer_type)
|
if (col_def.type == ColumnType::integer_type)
|
||||||
return std::make_unique<IntValueNode>(db_value.getIntValue());
|
return std::make_unique<IntValueNode>(db_value.getIntegerValue());
|
||||||
if (col_def.type == ColumnType::float_type)
|
if (col_def.type == ColumnType::float_type)
|
||||||
return std::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)
|
||||||
|
|
@ -137,13 +136,13 @@ std::unique_ptr<ValueNode> USql::eval_database_value_node(Table *table, Row &row
|
||||||
if (col_def.type == ColumnType::bool_type)
|
if (col_def.type == ColumnType::bool_type)
|
||||||
return std::make_unique<BooleanValueNode>(db_value.getBoolValue());
|
return std::make_unique<BooleanValueNode>(db_value.getBoolValue());
|
||||||
if (col_def.type == ColumnType::date_type)
|
if (col_def.type == ColumnType::date_type)
|
||||||
return std::make_unique<IntValueNode>(db_value.getIntValue());
|
return std::make_unique<IntValueNode>(db_value.getIntegerValue());
|
||||||
|
|
||||||
throw Exception("unknown database value type");
|
throw Exception("unknown database value type");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
std::unique_ptr<ValueNode> USql::eval_literal_value_node(Table *table, Row &row, Node *node) {
|
std::unique_ptr<ValueNode> USql::eval_literal_value_node(Row &row, Node *node) {
|
||||||
if (node->node_type == NodeType::int_value) {
|
if (node->node_type == NodeType::int_value) {
|
||||||
auto *ivl = static_cast<IntValueNode *>(node);
|
auto *ivl = static_cast<IntValueNode *>(node);
|
||||||
return std::make_unique<IntValueNode>(ivl->value);
|
return std::make_unique<IntValueNode>(ivl->value);
|
||||||
|
|
@ -166,8 +165,7 @@ std::unique_ptr<ValueNode> USql::eval_literal_value_node(Table *table, Row &row,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
std::unique_ptr<ValueNode>
|
std::unique_ptr<ValueNode> USql::eval_function_value_node(Table *table, Row &row, Node *node, ColDefNode *col_def_node, ColValue *agg_func_value) {
|
||||||
USql::eval_function_value_node(Table *table, Row &row, Node *node, ColDefNode *col_def_node, ColValue *agg_func_value) {
|
|
||||||
auto *fnc = static_cast<FunctionNode *>(node);
|
auto *fnc = static_cast<FunctionNode *>(node);
|
||||||
|
|
||||||
std::vector<std::unique_ptr<ValueNode>> evaluatedPars;
|
std::vector<std::unique_ptr<ValueNode>> evaluatedPars;
|
||||||
|
|
@ -177,7 +175,8 @@ USql::eval_function_value_node(Table *table, Row &row, Node *node, ColDefNode *c
|
||||||
|
|
||||||
// at this moment no functions without parameter(s) or first param can be null
|
// at this moment no functions without parameter(s) or first param can be null
|
||||||
if (evaluatedPars.empty() || evaluatedPars[0]->isNull())
|
if (evaluatedPars.empty() || evaluatedPars[0]->isNull())
|
||||||
return std::make_unique<NullValueNode>();
|
throw Exception("eval_function_value_node, no function parameter or first is null, function: " + fnc->function);
|
||||||
|
// return std::make_unique<NullValueNode>();
|
||||||
|
|
||||||
// TODO use some enum
|
// TODO use some enum
|
||||||
if (fnc->function == "lower") return lower_function(evaluatedPars);
|
if (fnc->function == "lower") return lower_function(evaluatedPars);
|
||||||
|
|
@ -193,23 +192,13 @@ USql::eval_function_value_node(Table *table, Row &row, Node *node, ColDefNode *c
|
||||||
throw Exception("invalid function: " + fnc->function);
|
throw Exception("invalid function: " + fnc->function);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::unique_ptr<ValueNode> USql::count_function(ColValue *agg_func_value, const std::vector<std::unique_ptr<ValueNode>> &evaluatedPars) {
|
|
||||||
long c = 1;
|
|
||||||
if (!agg_func_value->isNull()) {
|
|
||||||
c = agg_func_value->getIntValue() + 1;
|
|
||||||
}
|
|
||||||
return std::make_unique<IntValueNode>(c);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
bool USql::eval_logical_operator(LogicalOperatorNode &node, Table *pTable, Row &row) {
|
bool USql::eval_logical_operator(LogicalOperatorNode &node, Table *pTable, Row &row) {
|
||||||
//bool left = eval_relational_operator(static_cast<const RelationalOperatorNode &>(*node.left), pTable, row);
|
|
||||||
bool left = eval_where(&(*node.left), pTable, row);
|
bool left = eval_where(&(*node.left), pTable, row);
|
||||||
|
|
||||||
if ((node.op == LogicalOperatorType::and_operator && !left) || (node.op == LogicalOperatorType::or_operator && left))
|
if ((node.op == LogicalOperatorType::and_operator && !left) || (node.op == LogicalOperatorType::or_operator && left))
|
||||||
return left;
|
return left;
|
||||||
|
|
||||||
//bool right = eval_relational_operator(static_cast<const RelationalOperatorNode &>(*node.right), pTable, row);
|
|
||||||
bool right = eval_where(&(*node.right), pTable, row);
|
bool right = eval_where(&(*node.right), pTable, row);
|
||||||
return right;
|
return right;
|
||||||
}
|
}
|
||||||
|
|
@ -227,8 +216,8 @@ std::unique_ptr<ValueNode> USql::eval_arithmetic_operator(ColumnType outType, Ar
|
||||||
return std::make_unique<NullValueNode>();
|
return std::make_unique<NullValueNode>();
|
||||||
|
|
||||||
if (outType == ColumnType::float_type) {
|
if (outType == ColumnType::float_type) {
|
||||||
double l = ((ValueNode *) left.get())->getDoubleValue();
|
auto l = left->getDoubleValue();
|
||||||
double r = ((ValueNode *) right.get())->getDoubleValue();
|
auto r = right->getDoubleValue();
|
||||||
switch (node.op) {
|
switch (node.op) {
|
||||||
case ArithmeticalOperatorType::plus_operator:
|
case ArithmeticalOperatorType::plus_operator:
|
||||||
return std::make_unique<DoubleValueNode>(l + r);
|
return std::make_unique<DoubleValueNode>(l + r);
|
||||||
|
|
@ -239,12 +228,12 @@ std::unique_ptr<ValueNode> USql::eval_arithmetic_operator(ColumnType outType, Ar
|
||||||
case ArithmeticalOperatorType::divide_operator:
|
case ArithmeticalOperatorType::divide_operator:
|
||||||
return std::make_unique<DoubleValueNode>(l / r);
|
return std::make_unique<DoubleValueNode>(l / r);
|
||||||
default:
|
default:
|
||||||
throw Exception("implement me!!");
|
throw Exception("eval_arithmetic_operator, float type implement me!!");
|
||||||
}
|
}
|
||||||
|
|
||||||
} else if (outType == ColumnType::integer_type) {
|
} else if (outType == ColumnType::integer_type) {
|
||||||
long l = ((ValueNode *) left.get())->getIntegerValue();
|
auto l = left->getIntegerValue();
|
||||||
long r = ((ValueNode *) right.get())->getIntegerValue();
|
auto r = right->getIntegerValue();
|
||||||
switch (node.op) {
|
switch (node.op) {
|
||||||
case ArithmeticalOperatorType::plus_operator:
|
case ArithmeticalOperatorType::plus_operator:
|
||||||
return std::make_unique<IntValueNode>(l + r);
|
return std::make_unique<IntValueNode>(l + r);
|
||||||
|
|
@ -255,179 +244,57 @@ std::unique_ptr<ValueNode> USql::eval_arithmetic_operator(ColumnType outType, Ar
|
||||||
case ArithmeticalOperatorType::divide_operator:
|
case ArithmeticalOperatorType::divide_operator:
|
||||||
return std::make_unique<IntValueNode>(l / r);
|
return std::make_unique<IntValueNode>(l / r);
|
||||||
default:
|
default:
|
||||||
throw Exception("implement me!!");
|
throw Exception("eval_arithmetic_operator, integer type implement me!!");
|
||||||
}
|
}
|
||||||
|
|
||||||
} else if (outType == ColumnType::varchar_type) {
|
} else if (outType == ColumnType::varchar_type) {
|
||||||
std::string l = ((ValueNode *) left.get())->getStringValue();
|
auto l = left->getStringValue();
|
||||||
std::string r = ((ValueNode *) right.get())->getStringValue();
|
auto r = right->getStringValue();
|
||||||
switch (node.op) {
|
switch (node.op) {
|
||||||
case ArithmeticalOperatorType::plus_operator:
|
case ArithmeticalOperatorType::plus_operator:
|
||||||
return std::make_unique<StringValueNode>(l + r);
|
return std::make_unique<StringValueNode>(l + r);
|
||||||
default:
|
default:
|
||||||
throw Exception("implement me!!");
|
throw Exception("eval_arithmetic_operator, varchar type implement me!!");
|
||||||
|
}
|
||||||
|
|
||||||
|
} else if (outType == ColumnType::date_type) {
|
||||||
|
auto l = left->getDateValue();
|
||||||
|
auto r = right->getDateValue();
|
||||||
|
switch (node.op) {
|
||||||
|
case ArithmeticalOperatorType::plus_operator:
|
||||||
|
return std::make_unique<IntValueNode>(l + r);
|
||||||
|
case ArithmeticalOperatorType::minus_operator:
|
||||||
|
return std::make_unique<IntValueNode>(l - r);
|
||||||
|
default:
|
||||||
|
throw Exception("eval_arithmetic_operator, date_type type implement me!!");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// TODO date node should support addition and subtraction
|
throw Exception("eval_arithmetic_operator, implement me!!");
|
||||||
|
|
||||||
throw Exception("implement me!!");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Table *USql::find_table(const std::string &name) const {
|
||||||
std::unique_ptr<ValueNode> USql::to_string_function(const std::vector<std::unique_ptr<ValueNode>> &evaluatedPars) {
|
|
||||||
long date = evaluatedPars[0]->getDateValue();
|
|
||||||
std::string format = evaluatedPars[1]->getStringValue();
|
|
||||||
std::string formatted_date = date_to_string(date, format);
|
|
||||||
return std::make_unique<StringValueNode>(formatted_date);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::unique_ptr<ValueNode> USql::to_date_function(const std::vector<std::unique_ptr<ValueNode>> &evaluatedPars) {
|
|
||||||
std::string date = evaluatedPars[0]->getStringValue();
|
|
||||||
std::string format = evaluatedPars[1]->getStringValue();
|
|
||||||
long epoch_time = string_to_date(date, format);
|
|
||||||
return std::make_unique<IntValueNode>(epoch_time); // No DateValueNode for now
|
|
||||||
}
|
|
||||||
|
|
||||||
std::unique_ptr<ValueNode> USql::date_add_function(const std::vector<std::unique_ptr<ValueNode>> &evaluatedPars) {
|
|
||||||
long datetime = evaluatedPars[0]->getDateValue();
|
|
||||||
long quantity = evaluatedPars[1]->getIntegerValue();
|
|
||||||
std::string part = evaluatedPars[2]->getStringValue();
|
|
||||||
|
|
||||||
long new_date = add_to_date(datetime, quantity, part);
|
|
||||||
return std::make_unique<IntValueNode>(new_date); // No DateValueNode for now
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
std::unique_ptr<ValueNode> USql::upper_function(const std::vector<std::unique_ptr<ValueNode>> &evaluatedPars) {
|
|
||||||
std::string str = evaluatedPars[0]->getStringValue();
|
|
||||||
std::transform(str.begin(), str.end(), str.begin(), [](unsigned char c) -> unsigned char { return toupper(c); });
|
|
||||||
return std::make_unique<StringValueNode>(str);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::unique_ptr<ValueNode> USql::lower_function(const std::vector<std::unique_ptr<ValueNode>> &evaluatedPars) {
|
|
||||||
std::string str = evaluatedPars[0]->getStringValue();
|
|
||||||
std::transform(str.begin(), str.end(), str.begin(), [](unsigned char c) -> unsigned char { return tolower(c); });
|
|
||||||
return std::make_unique<StringValueNode>(str);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::unique_ptr<ValueNode> USql::pp_function(const std::vector<std::unique_ptr<ValueNode>> &evaluatedPars) {
|
|
||||||
auto &parsed_value = evaluatedPars[0];
|
|
||||||
|
|
||||||
if (parsed_value->node_type == NodeType::int_value || parsed_value->node_type == NodeType::float_value) {
|
|
||||||
std::string format = evaluatedPars.size() > 1 ? evaluatedPars[1]->getStringValue() : "";
|
|
||||||
char buf[20] {0}; // TODO constant here
|
|
||||||
double value = parsed_value->getDoubleValue();
|
|
||||||
|
|
||||||
if (format == "100%")
|
|
||||||
std::snprintf(buf, 20, "%.2f%%", value);
|
|
||||||
else if (format == "%.2f")
|
|
||||||
std::snprintf(buf, 20, "%.2f", value);
|
|
||||||
else if (value >= 1000000000000)
|
|
||||||
std::snprintf(buf, 20, "%7.2fT", value/1000000000000);
|
|
||||||
else if (value >= 1000000000)
|
|
||||||
std::sprintf(buf, "%7.2fB", value/1000000000);
|
|
||||||
else if (value >= 1000000)
|
|
||||||
std::snprintf(buf, 20, "%7.2fM", value/1000000);
|
|
||||||
else if (value >= 100000)
|
|
||||||
std::snprintf(buf, 20, "%7.2fM", value/100000); // 0.12M
|
|
||||||
else if (value <= -1000000000000)
|
|
||||||
std::snprintf(buf, 20, "%7.2fT", value/1000000000000);
|
|
||||||
else if (value <= -1000000000)
|
|
||||||
std::snprintf(buf, 20, "%7.2fB", value/1000000000);
|
|
||||||
else if (value <= -1000000)
|
|
||||||
std::snprintf(buf, 20, "%7.2fM", value/1000000);
|
|
||||||
else if (value <= -100000)
|
|
||||||
std::snprintf(buf, 20, "%7.2fM", value/100000); // 0.12M
|
|
||||||
else if (value == 0)
|
|
||||||
buf[0]='0';
|
|
||||||
else
|
|
||||||
return std::make_unique<StringValueNode>(parsed_value->getStringValue().substr(0, 10));
|
|
||||||
// TODO introduce constant for 10
|
|
||||||
std::string s {buf};
|
|
||||||
return std::make_unique<StringValueNode>(string_padd(s.erase(s.find_last_not_of(" ")+1), 10, ' ', false));
|
|
||||||
}
|
|
||||||
return std::make_unique<StringValueNode>(parsed_value->getStringValue());
|
|
||||||
}
|
|
||||||
|
|
||||||
std::unique_ptr<ValueNode>
|
|
||||||
USql::max_function(const std::vector<std::unique_ptr<ValueNode>> &evaluatedPars, const ColDefNode *col_def_node,
|
|
||||||
ColValue *agg_func_value) {
|
|
||||||
if (col_def_node->type == ColumnType::integer_type || col_def_node->type == ColumnType::date_type) {
|
|
||||||
if (!evaluatedPars[0]->isNull()) {
|
|
||||||
long val = evaluatedPars[0]->getIntegerValue();
|
|
||||||
if (agg_func_value->isNull()) {
|
|
||||||
return std::make_unique<IntValueNode>(val);
|
|
||||||
} else {
|
|
||||||
return std::make_unique<IntValueNode>(std::max(val, agg_func_value->getIntValue()));
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return std::make_unique<IntValueNode>(agg_func_value->getIntValue());
|
|
||||||
}
|
|
||||||
} else if (col_def_node->type == ColumnType::float_type) {
|
|
||||||
if (!evaluatedPars[0]->isNull()) {
|
|
||||||
double val = evaluatedPars[0]->getDoubleValue();
|
|
||||||
if (agg_func_value->isNull()) {
|
|
||||||
return std::make_unique<DoubleValueNode>(val);
|
|
||||||
} else {
|
|
||||||
return std::make_unique<DoubleValueNode>(std::max(val, agg_func_value->getDoubleValue()));
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return std::make_unique<DoubleValueNode>(agg_func_value->getDoubleValue());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO string and boolean
|
|
||||||
throw Exception("unsupported data type for max function");
|
|
||||||
}
|
|
||||||
|
|
||||||
std::unique_ptr<ValueNode>
|
|
||||||
USql::min_function(const std::vector<std::unique_ptr<ValueNode>> &evaluatedPars, const ColDefNode *col_def_node,
|
|
||||||
ColValue *agg_func_value) {
|
|
||||||
if (col_def_node->type == ColumnType::integer_type || col_def_node->type == ColumnType::date_type) {
|
|
||||||
if (!evaluatedPars[0]->isNull()) {
|
|
||||||
long val = evaluatedPars[0]->getIntegerValue();
|
|
||||||
if (agg_func_value->isNull()) {
|
|
||||||
return std::make_unique<IntValueNode>(val);
|
|
||||||
} else {
|
|
||||||
return std::make_unique<IntValueNode>(std::min(val, agg_func_value->getIntValue()));
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return std::make_unique<IntValueNode>(agg_func_value->getIntValue());
|
|
||||||
}
|
|
||||||
} else if (col_def_node->type == ColumnType::float_type) {
|
|
||||||
if (!evaluatedPars[0]->isNull()) {
|
|
||||||
double val = evaluatedPars[0]->getDoubleValue();
|
|
||||||
if (agg_func_value->isNull()) {
|
|
||||||
return std::make_unique<DoubleValueNode>(val);
|
|
||||||
} else {
|
|
||||||
return std::make_unique<DoubleValueNode>(std::min(val, agg_func_value->getDoubleValue()));
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return std::make_unique<DoubleValueNode>(agg_func_value->getDoubleValue());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO string and boolean
|
|
||||||
throw Exception("unsupported data type for min function");
|
|
||||||
}
|
|
||||||
|
|
||||||
Table *USql::find_table(const std::string &name) {
|
|
||||||
auto name_cmp = [name](const Table& t) { return t.m_name == 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);
|
auto table_def = std::find_if(begin(m_tables), end(m_tables), name_cmp);
|
||||||
if (table_def != std::end(m_tables)) {
|
if (table_def != std::end(m_tables))
|
||||||
return table_def.operator->();
|
return const_cast<Table *>(table_def.operator->());
|
||||||
} else {
|
|
||||||
throw Exception("table not found (" + name + ")");
|
throw Exception("table not found (" + name + ")");
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void USql::check_table_not_exists(const std::string &name) {
|
void USql::check_table_not_exists(const std::string &name) const {
|
||||||
auto name_cmp = [name](const Table& t) { return t.m_name == 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);
|
auto table_def = std::find_if(begin(m_tables), end(m_tables), name_cmp);
|
||||||
if (table_def != std::end(m_tables)) {
|
if (table_def != std::end(m_tables))
|
||||||
throw Exception("table already exists");
|
throw Exception("table already exists");
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void USql::check_index_not_exists(const std::string &index_name) {
|
||||||
|
for (auto &table : m_tables)
|
||||||
|
if (table.get_index(index_name) != nullptr)
|
||||||
|
throw Exception("index already exists");
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
60
usql/usql.h
60
usql/usql.h
|
|
@ -1,7 +1,9 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include "settings.h"
|
||||||
#include "parser.h"
|
#include "parser.h"
|
||||||
#include "table.h"
|
#include "table.h"
|
||||||
|
#include "index.h"
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <list>
|
#include <list>
|
||||||
|
|
@ -18,18 +20,19 @@ public:
|
||||||
private:
|
private:
|
||||||
std::unique_ptr<Table> execute(Node &node);
|
std::unique_ptr<Table> execute(Node &node);
|
||||||
|
|
||||||
std::unique_ptr<Table> execute_create_table(CreateTableNode &node);
|
std::unique_ptr<Table> execute_create_table(const CreateTableNode &node);
|
||||||
std::unique_ptr<Table> execute_create_table_as_table(CreateTableAsSelectNode &node);
|
std::unique_ptr<Table> execute_create_index(const CreateIndexNode &node);
|
||||||
std::unique_ptr<Table> execute_load(LoadIntoTableNode &node);
|
std::unique_ptr<Table> execute_create_table_as_table(const CreateTableAsSelectNode &node);
|
||||||
std::unique_ptr<Table> execute_save(SaveTableNode &node);
|
std::unique_ptr<Table> execute_load(const LoadIntoTableNode &node);
|
||||||
std::unique_ptr<Table> execute_drop(DropTableNode &node);
|
std::unique_ptr<Table> execute_save(const SaveTableNode &node);
|
||||||
static std::unique_ptr<Table> execute_set(SetNode &node);
|
std::unique_ptr<Table> execute_drop(const DropTableNode &node);
|
||||||
static std::unique_ptr<Table> execute_show(ShowNode &node);
|
static std::unique_ptr<Table> execute_set(const SetNode &node);
|
||||||
|
static std::unique_ptr<Table> execute_show(const ShowNode &node);
|
||||||
|
|
||||||
std::unique_ptr<Table> execute_insert_into_table(InsertIntoTableNode &node);
|
std::unique_ptr<Table> execute_insert_into_table(const InsertIntoTableNode &node);
|
||||||
std::unique_ptr<Table> execute_select(SelectFromTableNode &node);
|
std::unique_ptr<Table> execute_select(SelectFromTableNode &node) const;
|
||||||
std::unique_ptr<Table> execute_delete(DeleteFromTableNode &node);
|
std::unique_ptr<Table> execute_delete(const DeleteFromTableNode &node);
|
||||||
std::unique_ptr<Table> execute_update(UpdateTableNode &node);
|
std::unique_ptr<Table> execute_update(const UpdateTableNode &node);
|
||||||
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
@ -37,7 +40,7 @@ private:
|
||||||
|
|
||||||
static std::unique_ptr<ValueNode> eval_value_node(Table *table, Row &row, Node *node, ColDefNode *col_def_node, ColValue *agg_func_value);
|
static std::unique_ptr<ValueNode> eval_value_node(Table *table, Row &row, Node *node, ColDefNode *col_def_node, ColValue *agg_func_value);
|
||||||
static std::unique_ptr<ValueNode> eval_database_value_node(Table *table, Row &row, Node *node);
|
static std::unique_ptr<ValueNode> eval_database_value_node(Table *table, Row &row, Node *node);
|
||||||
static std::unique_ptr<ValueNode> eval_literal_value_node(Table *table, Row &row, Node *node);
|
static std::unique_ptr<ValueNode> eval_literal_value_node(Row &row, Node *node);
|
||||||
static std::unique_ptr<ValueNode> eval_function_value_node(Table *table, Row &row, Node *node, ColDefNode *col_def_node, ColValue *agg_func_value);
|
static std::unique_ptr<ValueNode> eval_function_value_node(Table *table, Row &row, Node *node, ColDefNode *col_def_node, ColValue *agg_func_value);
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -50,22 +53,23 @@ private:
|
||||||
static std::tuple<int, ColDefNode> get_column_definition(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);
|
||||||
static ColDefNode get_db_column_definition(Table *table, Node *node);
|
static ColDefNode get_db_column_definition(Table *table, Node *node);
|
||||||
static std::tuple<int, ColDefNode> get_node_definition(Table *table, Node *select_col_node, const std::string & col_name, int col_order);
|
static std::tuple<int, ColDefNode> get_node_definition(Table *table, Node *select_col_node, const std::string & col_name, int col_order);
|
||||||
Table *find_table(const std::string &name);
|
[[nodiscard]] Table *find_table(const std::string &name) const;
|
||||||
|
|
||||||
void check_table_not_exists(const std::string &name);
|
void check_table_not_exists(const std::string &name) const;
|
||||||
|
void check_index_not_exists(const std::string &index_name);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Parser m_parser;
|
Parser m_parser;
|
||||||
std::list<Table> m_tables;
|
std::list<Table> m_tables;
|
||||||
|
|
||||||
static void execute_distinct(SelectFromTableNode &node, Table *result) ;
|
static void execute_distinct(SelectFromTableNode &node, Table *result);
|
||||||
static void execute_order_by(SelectFromTableNode &node, Table *table, Table *result) ;
|
static void execute_order_by(SelectFromTableNode &node, Table *result);
|
||||||
static void execute_offset_limit(OffsetLimitNode &node, Table *result) ;
|
static void execute_offset_limit(OffsetLimitNode &node, Table *result);
|
||||||
|
|
||||||
void expand_asterix_char(SelectFromTableNode &node, Table *table) const;
|
static void expand_asterix_char(SelectFromTableNode &node, Table *table) ;
|
||||||
void setup_order_columns(std::vector<ColOrderNode> &node, Table *table) const;
|
static void setup_order_columns(std::vector<ColOrderNode> &node, Table *table) ;
|
||||||
|
|
||||||
bool check_for_aggregate_only_functions(SelectFromTableNode &node, int result_cols_cnt) const;
|
static bool check_for_aggregate_only_functions(SelectFromTableNode &node, size_t result_cols_cnt) ;
|
||||||
|
|
||||||
static std::unique_ptr<ValueNode> lower_function(const std::vector<std::unique_ptr<ValueNode>> &evaluatedPars);
|
static std::unique_ptr<ValueNode> lower_function(const std::vector<std::unique_ptr<ValueNode>> &evaluatedPars);
|
||||||
static std::unique_ptr<ValueNode> upper_function(const std::vector<std::unique_ptr<ValueNode>> &evaluatedPars);
|
static std::unique_ptr<ValueNode> upper_function(const std::vector<std::unique_ptr<ValueNode>> &evaluatedPars);
|
||||||
|
|
@ -77,8 +81,20 @@ private:
|
||||||
static std::unique_ptr<ValueNode> max_function(const std::vector<std::unique_ptr<ValueNode>> &evaluatedPars, const ColDefNode *col_def_node, ColValue *agg_func_value);
|
static std::unique_ptr<ValueNode> max_function(const std::vector<std::unique_ptr<ValueNode>> &evaluatedPars, const ColDefNode *col_def_node, ColValue *agg_func_value);
|
||||||
static std::unique_ptr<ValueNode> min_function(const std::vector<std::unique_ptr<ValueNode>> &evaluatedPars, const ColDefNode *col_def_node, ColValue *agg_func_value);
|
static std::unique_ptr<ValueNode> min_function(const std::vector<std::unique_ptr<ValueNode>> &evaluatedPars, const ColDefNode *col_def_node, ColValue *agg_func_value);
|
||||||
|
|
||||||
static std::unique_ptr<ValueNode>
|
static std::unique_ptr<ValueNode> count_function(ColValue *agg_func_value, const std::vector<std::unique_ptr<ValueNode>> &evaluatedPars);
|
||||||
count_function(ColValue *agg_func_value, const std::vector<std::unique_ptr<ValueNode>> &evaluatedPars);
|
|
||||||
|
static void select_row(SelectFromTableNode &where_node,
|
||||||
|
Table *src_table, Row *src_row,
|
||||||
|
Table *rslt_table,
|
||||||
|
const std::vector<ColDefNode> &rslt_tbl_col_defs, const std::vector<int> &src_table_col_index,
|
||||||
|
bool is_aggregated) ;
|
||||||
|
|
||||||
|
std::pair<bool, std::vector<rowid_t>> probe_index_scan(const Node *where, Table *table) const;
|
||||||
|
std::pair<bool, std::vector<rowid_t>> look_for_usable_index(const Node *where, Table *table) const;
|
||||||
|
bool normalize_where(const Node *node) const;
|
||||||
|
|
||||||
|
Table::rows_scanner get_iterator(Table *table, const Node *where) const;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
@ -1,6 +1,5 @@
|
||||||
#include "usql.h"
|
#include "usql.h"
|
||||||
#include "exception.h"
|
#include "exception.h"
|
||||||
#include "ml_date.h"
|
|
||||||
#include "ml_string.h"
|
#include "ml_string.h"
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
|
@ -10,7 +9,7 @@ namespace usql {
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
std::unique_ptr<Table> USql::execute_create_table(CreateTableNode &node) {
|
std::unique_ptr<Table> USql::execute_create_table(const CreateTableNode &node) {
|
||||||
check_table_not_exists(node.table_name);
|
check_table_not_exists(node.table_name);
|
||||||
|
|
||||||
Table table{node.table_name, node.cols_defs};
|
Table table{node.table_name, node.cols_defs};
|
||||||
|
|
@ -20,7 +19,23 @@ 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_index(const CreateIndexNode &node) {
|
||||||
|
Table *table_def = find_table(node.table_name); // throws exception if not found
|
||||||
|
ColDefNode col_def = table_def->get_column_def(node.column_name); // throws exception if not found
|
||||||
|
|
||||||
|
check_index_not_exists(node.index_name);
|
||||||
|
if (col_def.null) throw Exception("index on not null supported only");
|
||||||
|
if (table_def->get_index_for_column(node.column_name) != nullptr) throw Exception("column is already indexed");
|
||||||
|
|
||||||
|
table_def->create_index({node.index_name, node.column_name, col_def.type});
|
||||||
|
|
||||||
|
table_def->index_rows(node.index_name);
|
||||||
|
|
||||||
|
return create_stmt_result_table(0, "index created", 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
std::unique_ptr<Table> USql::execute_create_table_as_table(const CreateTableAsSelectNode &node) {
|
||||||
check_table_not_exists(node.table_name);
|
check_table_not_exists(node.table_name);
|
||||||
|
|
||||||
auto select = execute_select((SelectFromTableNode &) *node.select_table);
|
auto select = execute_select((SelectFromTableNode &) *node.select_table);
|
||||||
|
|
@ -43,7 +58,7 @@ std::unique_ptr<Table> USql::execute_create_table_as_table(CreateTableAsSelectNo
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
std::unique_ptr<Table> USql::execute_drop(DropTableNode &node) {
|
std::unique_ptr<Table> USql::execute_drop(const DropTableNode &node) {
|
||||||
auto name_cmp = [node](const Table& t) { return t.m_name == node.table_name; };
|
auto name_cmp = [node](const Table& t) { return t.m_name == node.table_name; };
|
||||||
|
|
||||||
auto table_def = std::find_if(begin(m_tables), end(m_tables), name_cmp);
|
auto table_def = std::find_if(begin(m_tables), end(m_tables), name_cmp);
|
||||||
|
|
@ -55,12 +70,12 @@ std::unique_ptr<Table> USql::execute_drop(DropTableNode &node) {
|
||||||
throw Exception("table not found (" + node.table_name + ")");
|
throw Exception("table not found (" + node.table_name + ")");
|
||||||
}
|
}
|
||||||
|
|
||||||
std::unique_ptr<Table> USql::execute_set(SetNode &node) {
|
std::unique_ptr<Table> USql::execute_set(const SetNode &node) {
|
||||||
Settings::set_setting(node.name, node.value);
|
Settings::set_setting(node.name, node.value);
|
||||||
return create_stmt_result_table(0, "set succeeded", 1);
|
return create_stmt_result_table(0, "set succeeded", 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::unique_ptr<Table> USql::execute_show(ShowNode &node) {
|
std::unique_ptr<Table> USql::execute_show(const ShowNode &node) {
|
||||||
std::string value = Settings::get_setting(node.name);
|
std::string value = Settings::get_setting(node.name);
|
||||||
return create_stmt_result_table(0, "show succeeded: " + value, 1);
|
return create_stmt_result_table(0, "show succeeded: " + value, 1);
|
||||||
}
|
}
|
||||||
|
|
@ -70,7 +85,7 @@ std::unique_ptr<Table> USql::create_stmt_result_table(long code, const std::stri
|
||||||
std::vector<ColDefNode> result_tbl_col_defs{};
|
std::vector<ColDefNode> result_tbl_col_defs{};
|
||||||
result_tbl_col_defs.emplace_back("code", ColumnType::integer_type, 0, 1, false);
|
result_tbl_col_defs.emplace_back("code", ColumnType::integer_type, 0, 1, false);
|
||||||
result_tbl_col_defs.emplace_back("desc", ColumnType::varchar_type, 1, 48, false);
|
result_tbl_col_defs.emplace_back("desc", ColumnType::varchar_type, 1, 48, false);
|
||||||
result_tbl_col_defs.emplace_back("affected_rows", ColumnType::integer_type, 0, 1, true);
|
result_tbl_col_defs.emplace_back("aff_rows", ColumnType::integer_type, 0, 1, true);
|
||||||
|
|
||||||
auto table_def = std::make_unique<Table>("result", result_tbl_col_defs);
|
auto table_def = std::make_unique<Table>("result", result_tbl_col_defs);
|
||||||
|
|
||||||
|
|
@ -85,7 +100,7 @@ std::unique_ptr<Table> USql::create_stmt_result_table(long code, const std::stri
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
std::unique_ptr<Table> USql::execute_load(LoadIntoTableNode &node) {
|
std::unique_ptr<Table> USql::execute_load(const LoadIntoTableNode &node) {
|
||||||
// find source table
|
// find source table
|
||||||
Table *table_def = find_table(node.table_name);
|
Table *table_def = find_table(node.table_name);
|
||||||
|
|
||||||
|
|
@ -101,7 +116,7 @@ std::unique_ptr<Table> USql::execute_load(LoadIntoTableNode &node) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
std::unique_ptr<Table> USql::execute_save(SaveTableNode &node) {
|
std::unique_ptr<Table> USql::execute_save(const SaveTableNode &node) {
|
||||||
// find source table
|
// find source table
|
||||||
Table *table_def = find_table(node.table_name);
|
Table *table_def = find_table(node.table_name);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,87 +1,118 @@
|
||||||
#include "usql.h"
|
#include "usql.h"
|
||||||
#include "exception.h"
|
#include "exception.h"
|
||||||
#include "ml_date.h"
|
|
||||||
#include "ml_string.h"
|
#include "ml_string.h"
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <fstream>
|
|
||||||
|
|
||||||
namespace usql {
|
namespace usql {
|
||||||
|
|
||||||
|
|
||||||
std::unique_ptr<Table> USql::execute_select(SelectFromTableNode &node) {
|
std::pair<bool, std::vector<rowid_t>> USql::probe_index_scan(const Node *where, Table *table) const {
|
||||||
// find source table
|
bool indexscan_possible = normalize_where(where);
|
||||||
Table *table = find_table(node.table_name);
|
|
||||||
|
|
||||||
// expand *
|
if (indexscan_possible && Settings::get_bool_setting("USE_INDEXSCAN")) {
|
||||||
expand_asterix_char(node, table);
|
// where->dump();
|
||||||
|
return look_for_usable_index(where, table);
|
||||||
// create result table
|
|
||||||
std::vector<ColDefNode> result_tbl_col_defs{};
|
|
||||||
std::vector<int> source_table_col_index{};
|
|
||||||
for (int i = 0; i < node.cols_names->size(); i++) {
|
|
||||||
SelectColNode * col_node = &node.cols_names->operator[](i);
|
|
||||||
auto [src_tbl_col_index, rst_tbl_col_def] = get_column_definition(table, col_node, i);
|
|
||||||
|
|
||||||
source_table_col_index.push_back(src_tbl_col_index);
|
|
||||||
result_tbl_col_defs.push_back(rst_tbl_col_def);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// check for aggregate function
|
// no index scan
|
||||||
bool aggregate_funcs = check_for_aggregate_only_functions(node, result_tbl_col_defs.size());
|
return std::make_pair(false, std::vector<rowid_t>{});
|
||||||
|
|
||||||
// prepare result table structure
|
|
||||||
auto result = std::make_unique<Table>("result", result_tbl_col_defs);
|
|
||||||
|
|
||||||
// replace possible order by col names to col indexes and validate
|
|
||||||
setup_order_columns(node.order_by, result.get());
|
|
||||||
|
|
||||||
// execute access plan
|
|
||||||
Row* new_row = nullptr;
|
|
||||||
for (auto row = begin(table->m_rows); row != end(table->m_rows); ++row) {
|
|
||||||
// eval where for row
|
|
||||||
if (eval_where(node.where.get(), table, *row)) {
|
|
||||||
// prepare empty row and copy column values
|
|
||||||
// when agregate functions in result only one row for table
|
|
||||||
if (!aggregate_funcs || result->rows_count()==0) {
|
|
||||||
new_row = &result->create_empty_row();
|
|
||||||
}
|
|
||||||
|
|
||||||
for (auto idx = 0; idx < result->columns_count(); idx++) {
|
|
||||||
auto src_table_col_idx = source_table_col_index[idx];
|
|
||||||
|
|
||||||
if (src_table_col_idx == FUNCTION_CALL) {
|
|
||||||
auto evaluated_value = eval_value_node(table, *row, node.cols_names->operator[](idx).value.get(), &result_tbl_col_defs[idx], &new_row->operator[](idx));
|
|
||||||
ValueNode *col_value = evaluated_value.get();
|
|
||||||
|
|
||||||
new_row->setColumnValue(&result_tbl_col_defs[idx], col_value);
|
|
||||||
} else {
|
|
||||||
ColValue &col_value = row->operator[](src_table_col_idx);
|
|
||||||
new_row->setColumnValue(&result_tbl_col_defs[idx], col_value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// add row to result
|
|
||||||
if (aggregate_funcs == 0) {
|
|
||||||
result->commit_row(*new_row);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// when aggregates commit this one row
|
|
||||||
if (aggregate_funcs && new_row != nullptr) {
|
|
||||||
result->commit_row(*new_row);
|
|
||||||
}
|
|
||||||
|
|
||||||
execute_distinct(node, result.get());
|
|
||||||
|
|
||||||
execute_order_by(node, table, result.get());
|
|
||||||
|
|
||||||
execute_offset_limit(node.offset_limit, result.get());
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool USql::check_for_aggregate_only_functions(SelectFromTableNode &node, int result_cols_cnt) const {
|
std::pair<bool, std::vector<rowid_t>> USql::look_for_usable_index(const Node *where, Table *table) const {
|
||||||
|
if (where->node_type == NodeType::relational_operator) {
|
||||||
|
auto * ron = (RelationalOperatorNode *)where;
|
||||||
|
// TODO implement >, >=, <=, <
|
||||||
|
// https://en.cppreference.com/w/cpp/container/map/upper_bound
|
||||||
|
if (ron->op == RelationalOperatorType::equal) {
|
||||||
|
if (ron->left->node_type == NodeType::database_value &&
|
||||||
|
((ron->right->node_type == NodeType::int_value) || (ron->right->node_type == NodeType::string_value))
|
||||||
|
) {
|
||||||
|
auto col_name = ((DatabaseValueNode *)ron->left.get())->col_name;
|
||||||
|
|
||||||
|
Index * used_index = table->get_index_for_column(col_name);
|
||||||
|
if (used_index != nullptr) {
|
||||||
|
std::vector<rowid_t> rowids = used_index->search((ValueNode *)ron->right.get());
|
||||||
|
#ifndef NDEBUG
|
||||||
|
std::cout << "using index " << table->m_name << "(" << used_index->get_column_name() << "), " << rowids.size() << "/" << table->rows_count() << std::endl;
|
||||||
|
#endif
|
||||||
|
return std::make_pair(true, rowids);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (where->node_type == NodeType::logical_operator) {
|
||||||
|
auto * operatorNode = (LogicalOperatorNode *)where;
|
||||||
|
if (operatorNode->op == LogicalOperatorType::and_operator) {
|
||||||
|
auto [use_index, rowids] = look_for_usable_index(operatorNode->left.get(), table);
|
||||||
|
if (use_index) {
|
||||||
|
return std::make_pair(true, rowids);
|
||||||
|
}
|
||||||
|
return look_for_usable_index(operatorNode->right.get(), table);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// no index available
|
||||||
|
return std::make_pair(false, std::vector<rowid_t>{});
|
||||||
|
}
|
||||||
|
|
||||||
|
bool USql::normalize_where(const Node *node) const {
|
||||||
|
// normalize relational operators "layout" and check whether index scan even possible
|
||||||
|
|
||||||
|
// unify relational operators tha left node is always database value
|
||||||
|
if (node->node_type == NodeType::relational_operator) {
|
||||||
|
// TODO more optimizations here, for example node 1 = 2 etc
|
||||||
|
auto * ron = (RelationalOperatorNode *)node;
|
||||||
|
if (ron->right->node_type == NodeType::database_value && ((ron->left->node_type == NodeType::int_value) || (ron->left->node_type == NodeType::string_value)) ) {
|
||||||
|
std::swap(ron->left, ron->right);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
} else if (node->node_type == NodeType::logical_operator) {
|
||||||
|
auto * operatorNode = (LogicalOperatorNode *)node;
|
||||||
|
if (operatorNode->op == LogicalOperatorType::or_operator) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
bool left_subnode = normalize_where(operatorNode->left.get());
|
||||||
|
bool right_subnode = normalize_where(operatorNode->left.get());
|
||||||
|
return left_subnode && right_subnode;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void USql::select_row(SelectFromTableNode &where_node,
|
||||||
|
Table *src_table, Row *src_row,
|
||||||
|
Table *rslt_table,
|
||||||
|
const std::vector<ColDefNode> &rslt_tbl_col_defs,
|
||||||
|
const std::vector<int> &src_table_col_index,
|
||||||
|
bool is_aggregated) {
|
||||||
|
|
||||||
|
Row *rslt_row = nullptr;
|
||||||
|
|
||||||
|
// when aggregate functions in rslt_table only one row exists
|
||||||
|
if (is_aggregated && !rslt_table->empty())
|
||||||
|
rslt_row = &rslt_table->m_rows[0];
|
||||||
|
else
|
||||||
|
rslt_row = &rslt_table->create_empty_row();
|
||||||
|
|
||||||
|
for (auto idx = 0; idx < rslt_table->columns_count(); idx++) {
|
||||||
|
auto src_table_col_idx = src_table_col_index[idx];
|
||||||
|
|
||||||
|
if (src_table_col_idx == FUNCTION_CALL) {
|
||||||
|
auto evaluated_value = eval_value_node(src_table, *src_row, where_node.cols_names->operator[](idx).value.get(),
|
||||||
|
const_cast<ColDefNode *>(&rslt_tbl_col_defs[idx]), &rslt_row->operator[](idx));
|
||||||
|
ValueNode *col_value = evaluated_value.get();
|
||||||
|
|
||||||
|
rslt_row->setColumnValue((ColDefNode *) &rslt_tbl_col_defs[idx], col_value);
|
||||||
|
} else {
|
||||||
|
ColValue &col_value = src_row->operator[](src_table_col_idx);
|
||||||
|
rslt_row->setColumnValue((ColDefNode *) &rslt_tbl_col_defs[idx], col_value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// for aggregate is validated more than needed
|
||||||
|
rslt_table->commit_row(*rslt_row);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool USql::check_for_aggregate_only_functions(SelectFromTableNode &node, size_t result_cols_cnt) {
|
||||||
int aggregate_funcs = 0;
|
int aggregate_funcs = 0;
|
||||||
for (int i = 0; i < node.cols_names->size(); i++) {
|
for (int i = 0; i < node.cols_names->size(); i++) {
|
||||||
SelectColNode * col_node = &node.cols_names->operator[](i);
|
SelectColNode * col_node = &node.cols_names->operator[](i);
|
||||||
|
|
@ -99,7 +130,7 @@ bool USql::check_for_aggregate_only_functions(SelectFromTableNode &node, int res
|
||||||
return aggregate_funcs > 0;
|
return aggregate_funcs > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void USql::expand_asterix_char(SelectFromTableNode &node, Table *table) const {
|
void USql::expand_asterix_char(SelectFromTableNode &node, Table *table) {
|
||||||
if (node.cols_names->size() == 1 && node.cols_names->operator[](0).name == "*") {
|
if (node.cols_names->size() == 1 && node.cols_names->operator[](0).name == "*") {
|
||||||
node.cols_names->clear();
|
node.cols_names->clear();
|
||||||
node.cols_names->reserve(table->columns_count());
|
node.cols_names->reserve(table->columns_count());
|
||||||
|
|
@ -109,7 +140,7 @@ void USql::expand_asterix_char(SelectFromTableNode &node, Table *table) const {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void USql::setup_order_columns(std::vector<ColOrderNode> &node, Table *table) const {
|
void USql::setup_order_columns(std::vector<ColOrderNode> &node, Table *table) {
|
||||||
for (auto& order_node : node) {
|
for (auto& order_node : node) {
|
||||||
if (!order_node.col_name.empty()) {
|
if (!order_node.col_name.empty()) {
|
||||||
ColDefNode col_def = table->get_column_def(order_node.col_name);
|
ColDefNode col_def = table->get_column_def(order_node.col_name);
|
||||||
|
|
@ -132,7 +163,7 @@ void USql::execute_distinct(SelectFromTableNode &node, Table *result) {
|
||||||
result->m_rows.erase(std::unique(result->m_rows.begin(), result->m_rows.end()), result->m_rows.end());
|
result->m_rows.erase(std::unique(result->m_rows.begin(), result->m_rows.end()), result->m_rows.end());
|
||||||
}
|
}
|
||||||
|
|
||||||
void USql::execute_order_by(SelectFromTableNode &node, Table *table, Table *result) {
|
void USql::execute_order_by(SelectFromTableNode &node, Table *result) {
|
||||||
if (node.order_by.empty()) return;
|
if (node.order_by.empty()) return;
|
||||||
|
|
||||||
auto compare_rows = [&node, &result](const Row &a, const Row &b) {
|
auto compare_rows = [&node, &result](const Row &a, const Row &b) {
|
||||||
|
|
@ -160,6 +191,21 @@ void USql::execute_offset_limit(OffsetLimitNode &node, Table *result) {
|
||||||
result->m_rows.erase(result->m_rows.begin() + node.limit, result->m_rows.end());
|
result->m_rows.erase(result->m_rows.begin() + node.limit, result->m_rows.end());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool USql::eval_where(Node *where, Table *table, Row &row)
|
||||||
|
{
|
||||||
|
switch (where->node_type)
|
||||||
|
{
|
||||||
|
case NodeType::true_node:
|
||||||
|
return true;
|
||||||
|
case NodeType::relational_operator: // just one condition
|
||||||
|
return eval_relational_operator(*((RelationalOperatorNode *)where), table, row);
|
||||||
|
case NodeType::logical_operator:
|
||||||
|
return eval_logical_operator(*((LogicalOperatorNode *)where), table, row);
|
||||||
|
default:
|
||||||
|
throw Exception("Wrong node type");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
std::tuple<int, ColDefNode> USql::get_column_definition(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 ) {
|
||||||
return get_node_definition(table, select_col_node->value.get(), select_col_node->name, col_order );
|
return get_node_definition(table, select_col_node->value.get(), select_col_node->name, col_order );
|
||||||
}
|
}
|
||||||
|
|
@ -218,7 +264,7 @@ std::tuple<int, ColDefNode> USql::get_node_definition(Table *table, Node * node,
|
||||||
auto [left_col_index, left_tbl_col_def] = get_node_definition(table, ari_node->left.get(), col_name, col_order );
|
auto [left_col_index, left_tbl_col_def] = get_node_definition(table, ari_node->left.get(), col_name, col_order );
|
||||||
auto [right_col_index, right_tbl_col_def] = get_node_definition(table, ari_node->right.get(), col_name, col_order );
|
auto [right_col_index, right_tbl_col_def] = get_node_definition(table, ari_node->right.get(), col_name, col_order );
|
||||||
|
|
||||||
ColumnType col_type; // TODO handle varchar and it len
|
ColumnType col_type; // TODO handle varchar and its len
|
||||||
if (left_tbl_col_def.type==ColumnType::float_type || right_tbl_col_def.type==ColumnType::float_type)
|
if (left_tbl_col_def.type==ColumnType::float_type || right_tbl_col_def.type==ColumnType::float_type)
|
||||||
col_type = ColumnType::float_type;
|
col_type = ColumnType::float_type;
|
||||||
else
|
else
|
||||||
|
|
@ -249,8 +295,7 @@ std::tuple<int, ColDefNode> USql::get_node_definition(Table *table, Node * node,
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
std::unique_ptr<Table> USql::execute_insert_into_table(const InsertIntoTableNode &node) {
|
||||||
std::unique_ptr<Table> USql::execute_insert_into_table(InsertIntoTableNode &node) {
|
|
||||||
// find table
|
// find table
|
||||||
Table *table_def = find_table(node.table_name);
|
Table *table_def = find_table(node.table_name);
|
||||||
|
|
||||||
|
|
@ -276,45 +321,52 @@ std::unique_ptr<Table> USql::execute_insert_into_table(InsertIntoTableNode &node
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
std::unique_ptr<Table> USql::execute_delete(DeleteFromTableNode &node) {
|
std::unique_ptr<Table> USql::execute_delete(const DeleteFromTableNode &node) {
|
||||||
|
size_t affected_rows = 0;
|
||||||
|
|
||||||
// find source table
|
// find source table
|
||||||
Table *table = find_table(node.table_name);
|
Table *table = find_table(node.table_name);
|
||||||
|
|
||||||
// execute access plan
|
// execute access plan
|
||||||
auto affected_rows = table->rows_count();
|
Table::rows_scanner i = get_iterator(table, node.where.get());
|
||||||
|
while(Row *row = i.next()) {
|
||||||
|
if (eval_where(node.where.get(), table, *row)) {
|
||||||
|
row->set_deleted();
|
||||||
|
table->unindex_row(*row);
|
||||||
|
|
||||||
table->m_rows.erase(
|
affected_rows++;
|
||||||
std::remove_if(table->m_rows.begin(), table->m_rows.end(),
|
}
|
||||||
[&node, table](Row &row){return eval_where(node.where.get(), table, row);}),
|
}
|
||||||
table->m_rows.end());
|
|
||||||
|
|
||||||
affected_rows -= table->rows_count();
|
|
||||||
|
|
||||||
return create_stmt_result_table(0, "delete succeeded", affected_rows);
|
return create_stmt_result_table(0, "delete succeeded", affected_rows);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
std::unique_ptr<Table> USql::execute_update(UpdateTableNode &node) {
|
std::unique_ptr<Table> USql::execute_update(const UpdateTableNode &node) {
|
||||||
|
size_t affected_rows = 0;
|
||||||
|
|
||||||
// find source table
|
// find source table
|
||||||
Table *table = find_table(node.table_name);
|
Table *table = find_table(node.table_name);
|
||||||
|
|
||||||
// execute access plan
|
// execute access plan
|
||||||
int affected_rows = 0;
|
Table::rows_scanner i = get_iterator(table, node.where.get());
|
||||||
for (auto row = begin(table->m_rows); row != end(table->m_rows); ++row) {
|
while(Row *row = i.next()) {
|
||||||
// eval where for row
|
|
||||||
if (eval_where(node.where.get(), table, *row)) {
|
if (eval_where(node.where.get(), table, *row)) {
|
||||||
int i = 0;
|
Row old_row = * row;
|
||||||
|
|
||||||
|
int col_idx = 0;
|
||||||
for (const auto& col : node.cols_names) {
|
for (const auto& col : node.cols_names) {
|
||||||
// TODO cache it like in select
|
// PERF cache it like in select
|
||||||
ColDefNode col_def = table->get_column_def(col.col_name);
|
ColDefNode col_def = table->get_column_def(col.col_name);
|
||||||
std::unique_ptr<ValueNode> new_val = eval_arithmetic_operator(col_def.type,
|
std::unique_ptr<ValueNode> new_val = eval_arithmetic_operator(col_def.type,
|
||||||
static_cast<ArithmeticalOperatorNode &>(*node.values[i]),
|
static_cast<ArithmeticalOperatorNode &>(*node.values[col_idx]), table, *row);
|
||||||
table, *row);
|
|
||||||
|
|
||||||
usql::Table::validate_column(&col_def, new_val.get());
|
usql::Table::validate_column(&col_def, new_val.get());
|
||||||
row->setColumnValue(&col_def, new_val.get());
|
row->setColumnValue(&col_def, new_val.get());
|
||||||
i++;
|
col_idx++;
|
||||||
}
|
}
|
||||||
|
table->reindex_row(old_row, *row);
|
||||||
|
|
||||||
affected_rows++;
|
affected_rows++;
|
||||||
// TODO tady je problem, ze kdyz to zfajluje na jednom radku ostatni by se nemely provest
|
// TODO tady je problem, ze kdyz to zfajluje na jednom radku ostatni by se nemely provest
|
||||||
}
|
}
|
||||||
|
|
@ -324,20 +376,58 @@ std::unique_ptr<Table> USql::execute_update(UpdateTableNode &node) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
bool USql::eval_where(Node *where, Table *table, Row &row) {
|
std::unique_ptr<Table> USql::execute_select(SelectFromTableNode &node) const {
|
||||||
switch (where->node_type) {
|
// find source table
|
||||||
case NodeType::true_node:
|
Table *table = find_table(node.table_name);
|
||||||
return true;
|
|
||||||
case NodeType::relational_operator: // just one condition
|
// expand *
|
||||||
return eval_relational_operator(*((RelationalOperatorNode *) where), table, row);
|
expand_asterix_char(node, table);
|
||||||
case NodeType::logical_operator:
|
|
||||||
return eval_logical_operator(*((LogicalOperatorNode *) where), table, row);
|
// create result table
|
||||||
default:
|
std::vector<ColDefNode> result_tbl_col_defs{};
|
||||||
throw Exception("Wrong node type");
|
std::vector<int> source_table_col_index{};
|
||||||
|
for (int i = 0; i < node.cols_names->size(); i++) {
|
||||||
|
SelectColNode *col_node = &node.cols_names->operator[](i);
|
||||||
|
auto [src_tbl_col_index, rst_tbl_col_def] = get_column_definition(table, col_node, i);
|
||||||
|
|
||||||
|
source_table_col_index.push_back(src_tbl_col_index);
|
||||||
|
result_tbl_col_defs.push_back(rst_tbl_col_def);
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
// check for aggregate function
|
||||||
|
bool is_aggregated = check_for_aggregate_only_functions(node, result_tbl_col_defs.size());
|
||||||
|
|
||||||
|
// prepare result table structure
|
||||||
|
auto result = std::make_unique<Table>("result", result_tbl_col_defs);
|
||||||
|
|
||||||
|
// replace possible order by col names to col indexes and validate
|
||||||
|
setup_order_columns(node.order_by, result.get());
|
||||||
|
|
||||||
|
// execute access plan
|
||||||
|
Table::rows_scanner i = get_iterator(table, node.where.get());
|
||||||
|
while(Row *row = i.next()) {
|
||||||
|
if (eval_where(node.where.get(), table, *row)) { // put it into row_scanner.next
|
||||||
|
select_row(node, table, row, result.get(), result_tbl_col_defs, source_table_col_index, is_aggregated);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
execute_distinct(node, result.get());
|
||||||
|
|
||||||
|
execute_order_by(node, result.get());
|
||||||
|
|
||||||
|
execute_offset_limit(node.offset_limit, result.get());
|
||||||
|
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Table::rows_scanner USql::get_iterator(Table *table, const Node *where) const {
|
||||||
|
auto[use_index, rowids] = probe_index_scan(where, table);
|
||||||
|
|
||||||
|
if (use_index)
|
||||||
|
return Table::rows_scanner(table, rowids);
|
||||||
|
else
|
||||||
|
return Table::rows_scanner(table);
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,168 @@
|
||||||
|
#include "usql.h"
|
||||||
|
#include "exception.h"
|
||||||
|
#include "ml_date.h"
|
||||||
|
#include "ml_string.h"
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
|
namespace usql {
|
||||||
|
|
||||||
|
std::unique_ptr<ValueNode> USql::to_string_function(const std::vector<std::unique_ptr<ValueNode>> &evaluatedPars) {
|
||||||
|
long date = evaluatedPars[0]->getDateValue();
|
||||||
|
std::string format = evaluatedPars[1]->getStringValue();
|
||||||
|
std::string formatted_date = date_to_string(date, format);
|
||||||
|
return std::make_unique<StringValueNode>(formatted_date);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::unique_ptr<ValueNode> USql::to_date_function(const std::vector<std::unique_ptr<ValueNode>> &evaluatedPars) {
|
||||||
|
std::string date = evaluatedPars[0]->getStringValue();
|
||||||
|
std::string format = evaluatedPars[1]->getStringValue();
|
||||||
|
long epoch_time = string_to_date(date, format);
|
||||||
|
return std::make_unique<IntValueNode>(epoch_time); // No DateValueNode for now
|
||||||
|
}
|
||||||
|
|
||||||
|
std::unique_ptr<ValueNode> USql::date_add_function(const std::vector<std::unique_ptr<ValueNode>> &evaluatedPars) {
|
||||||
|
long datetime = evaluatedPars[0]->getDateValue();
|
||||||
|
long quantity = evaluatedPars[1]->getIntegerValue();
|
||||||
|
std::string part = evaluatedPars[2]->getStringValue();
|
||||||
|
|
||||||
|
long new_date = add_to_date(datetime, quantity, part);
|
||||||
|
return std::make_unique<IntValueNode>(new_date); // No DateValueNode for now
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
std::unique_ptr<ValueNode> USql::upper_function(const std::vector<std::unique_ptr<ValueNode>> &evaluatedPars) {
|
||||||
|
std::string str = evaluatedPars[0]->getStringValue();
|
||||||
|
std::transform(str.begin(), str.end(), str.begin(), [](unsigned char c) -> unsigned char { return toupper(c); });
|
||||||
|
return std::make_unique<StringValueNode>(str);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::unique_ptr<ValueNode> USql::lower_function(const std::vector<std::unique_ptr<ValueNode>> &evaluatedPars) {
|
||||||
|
std::string str = evaluatedPars[0]->getStringValue();
|
||||||
|
std::transform(str.begin(), str.end(), str.begin(), [](unsigned char c) -> unsigned char { return tolower(c); });
|
||||||
|
return std::make_unique<StringValueNode>(str);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::unique_ptr<ValueNode> USql::pp_function(const std::vector<std::unique_ptr<ValueNode>> &evaluatedPars) {
|
||||||
|
auto &parsed_value = evaluatedPars[0];
|
||||||
|
|
||||||
|
if (parsed_value->node_type == NodeType::int_value || parsed_value->node_type == NodeType::float_value) {
|
||||||
|
std::string format = evaluatedPars.size() > 1 ? evaluatedPars[1]->getStringValue() : "";
|
||||||
|
char buf[20] {0}; // TODO constant here
|
||||||
|
double value = parsed_value->getDoubleValue();
|
||||||
|
|
||||||
|
if (format == "100%")
|
||||||
|
std::snprintf(buf, 20, "%.2f%%", value);
|
||||||
|
else if (format == "%.2f")
|
||||||
|
std::snprintf(buf, 20, "%.2f", value);
|
||||||
|
else if (value >= 1000000000000)
|
||||||
|
std::snprintf(buf, 20, "%7.2fT", value/1000000000000);
|
||||||
|
else if (value >= 1000000000)
|
||||||
|
std::sprintf(buf, "%7.2fB", value/1000000000);
|
||||||
|
else if (value >= 1000000)
|
||||||
|
std::snprintf(buf, 20, "%7.2fM", value/1000000);
|
||||||
|
else if (value >= 100000)
|
||||||
|
std::snprintf(buf, 20, "%7.2fM", value/100000); // 0.12M
|
||||||
|
else if (value <= -1000000000000)
|
||||||
|
std::snprintf(buf, 20, "%7.2fT", value/1000000000000);
|
||||||
|
else if (value <= -1000000000)
|
||||||
|
std::snprintf(buf, 20, "%7.2fB", value/1000000000);
|
||||||
|
else if (value <= -1000000)
|
||||||
|
std::snprintf(buf, 20, "%7.2fM", value/1000000);
|
||||||
|
else if (value <= -100000)
|
||||||
|
std::snprintf(buf, 20, "%7.2fM", value/100000); // 0.12M
|
||||||
|
else if (value == 0)
|
||||||
|
buf[0]='0';
|
||||||
|
else
|
||||||
|
return std::make_unique<StringValueNode>(parsed_value->getStringValue().substr(0, 10));
|
||||||
|
// TODO introduce constant for 10
|
||||||
|
std::string s {buf};
|
||||||
|
return std::make_unique<StringValueNode>(string_padd(s.erase(s.find_last_not_of(' ')+1), 10, ' ', false));
|
||||||
|
}
|
||||||
|
return std::make_unique<StringValueNode>(parsed_value->getStringValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
std::unique_ptr<ValueNode>
|
||||||
|
USql::max_function(const std::vector<std::unique_ptr<ValueNode>> &evaluatedPars, const ColDefNode *col_def_node, ColValue *agg_func_value) {
|
||||||
|
if (col_def_node->type == ColumnType::integer_type || col_def_node->type == ColumnType::date_type) {
|
||||||
|
if (!evaluatedPars[0]->isNull()) {
|
||||||
|
auto val = evaluatedPars[0]->getIntegerValue();
|
||||||
|
if (agg_func_value->isNull())
|
||||||
|
return std::make_unique<IntValueNode>(val);
|
||||||
|
else
|
||||||
|
return std::make_unique<IntValueNode>(std::max(val, agg_func_value->getIntegerValue()));
|
||||||
|
} else {
|
||||||
|
return std::make_unique<IntValueNode>(agg_func_value->getIntegerValue());
|
||||||
|
}
|
||||||
|
} else if (col_def_node->type == ColumnType::float_type) {
|
||||||
|
if (!evaluatedPars[0]->isNull()) {
|
||||||
|
auto val = evaluatedPars[0]->getDoubleValue();
|
||||||
|
if (agg_func_value->isNull())
|
||||||
|
return std::make_unique<DoubleValueNode>(val);
|
||||||
|
else
|
||||||
|
return std::make_unique<DoubleValueNode>(std::max(val, agg_func_value->getDoubleValue()));
|
||||||
|
} else {
|
||||||
|
return std::make_unique<DoubleValueNode>(agg_func_value->getDoubleValue());
|
||||||
|
}
|
||||||
|
} else if (col_def_node->type == ColumnType::varchar_type) {
|
||||||
|
if (!evaluatedPars[0]->isNull()) {
|
||||||
|
auto val = evaluatedPars[0]->getStringValue();
|
||||||
|
if (agg_func_value->isNull())
|
||||||
|
return std::make_unique<StringValueNode>(val);
|
||||||
|
else
|
||||||
|
return std::make_unique<StringValueNode>(std::max(val, agg_func_value->getStringValue()));
|
||||||
|
} else {
|
||||||
|
return std::make_unique<StringValueNode>(agg_func_value->getStringValue());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
throw Exception("unsupported data type for max function");
|
||||||
|
}
|
||||||
|
|
||||||
|
std::unique_ptr<ValueNode>
|
||||||
|
USql::min_function(const std::vector<std::unique_ptr<ValueNode>> &evaluatedPars, const ColDefNode *col_def_node,
|
||||||
|
ColValue *agg_func_value) {
|
||||||
|
if (col_def_node->type == ColumnType::integer_type || col_def_node->type == ColumnType::date_type) {
|
||||||
|
if (!evaluatedPars[0]->isNull()) {
|
||||||
|
long val = evaluatedPars[0]->getIntegerValue();
|
||||||
|
if (agg_func_value->isNull())
|
||||||
|
return std::make_unique<IntValueNode>(val);
|
||||||
|
else
|
||||||
|
return std::make_unique<IntValueNode>(std::min(val, agg_func_value->getIntegerValue()));
|
||||||
|
} else {
|
||||||
|
return std::make_unique<IntValueNode>(agg_func_value->getIntegerValue());
|
||||||
|
}
|
||||||
|
} else if (col_def_node->type == ColumnType::float_type) {
|
||||||
|
if (!evaluatedPars[0]->isNull()) {
|
||||||
|
double val = evaluatedPars[0]->getDoubleValue();
|
||||||
|
if (agg_func_value->isNull())
|
||||||
|
return std::make_unique<DoubleValueNode>(val);
|
||||||
|
else
|
||||||
|
return std::make_unique<DoubleValueNode>(std::min(val, agg_func_value->getDoubleValue()));
|
||||||
|
} else {
|
||||||
|
return std::make_unique<DoubleValueNode>(agg_func_value->getDoubleValue());
|
||||||
|
}
|
||||||
|
} else if (col_def_node->type == ColumnType::varchar_type) {
|
||||||
|
if (!evaluatedPars[0]->isNull()) {
|
||||||
|
auto val = evaluatedPars[0]->getStringValue();
|
||||||
|
if (agg_func_value->isNull())
|
||||||
|
return std::make_unique<StringValueNode>(val);
|
||||||
|
else
|
||||||
|
return std::make_unique<StringValueNode>(std::min(val, agg_func_value->getStringValue()));
|
||||||
|
} else {
|
||||||
|
return std::make_unique<StringValueNode>(agg_func_value->getStringValue());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
throw Exception("unsupported data type for min function");
|
||||||
|
}
|
||||||
|
|
||||||
|
std::unique_ptr<ValueNode> USql::count_function(ColValue *agg_func_value, const std::vector<std::unique_ptr<ValueNode>> &evaluatedPars) {
|
||||||
|
long c = 1;
|
||||||
|
if (!agg_func_value->isNull()) {
|
||||||
|
c = agg_func_value->getIntegerValue() + 1;
|
||||||
|
}
|
||||||
|
return std::make_unique<IntValueNode>(c);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
Loading…
Reference in New Issue