usql update

This commit is contained in:
VaclavT 2021-08-23 18:14:05 +02:00
parent 8d220356f2
commit 0e90d6047c
28 changed files with 1623 additions and 3774 deletions

View File

@ -16,7 +16,7 @@ set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,-stack_size -Wl,0x1000
# set(CMAKE_CXX_FLAGS "-Wall -Wextra")
# set(CMAKE_CXX_FLAGS_RELEASE "-O3")
include_directories(/usr/local/opt/openssl/include ${CMAKE_SOURCE_DIR}/clib ${CMAKE_SOURCE_DIR} )
include_directories(/usr/local/opt/openssl/include ${CMAKE_SOURCE_DIR}/clib ${CMAKE_SOURCE_DIR})
link_directories(/usr/local/lib /usr/local/opt/openssl/lib)
@ -45,6 +45,7 @@ set(SOURCE
usql/table.h
usql/row.cpp
usql/csvreader.cpp
usql/settings.cpp
usql/usql.cpp)
add_executable(${PROJECT_NAME} ${SOURCE})

View File

@ -1,8 +1,6 @@
#pragma once
#include "ml.h"
#include <string>
#include <regex>

View File

@ -13,20 +13,25 @@ MlValue uSQL::ivaluize(const usql::Table *table) {
std::vector<MlValue> rows;
std::vector<MlValue> columns;
columns.reserve(table->columns_count());
rows.reserve(table->rows_count());
for (auto row : table->m_rows) {
columns.clear();
for (int i = 0; i < table->columns_count(); i++) {
auto c = row.ith_column(i);
auto & c = row[i];
auto type = table->m_col_defs[i].type;
if (type == ColumnType::integer_type) {
columns.push_back(MlValue(c->getIntValue()));
if (type == ColumnType::integer_type || type == ColumnType::date_type) {
columns.push_back(MlValue(c.getIntValue()));
} else if (type == ColumnType::bool_type) {
columns.push_back(c.getBoolValue() ? MlValue(c.getIntValue()) : MlValue::nil());
} else if (type == ColumnType::float_type) {
columns.push_back(MlValue(c->getDoubleValue()));
columns.push_back(MlValue(c.getDoubleValue()));
} else {
columns.push_back(MlValue::string(c->getStringValue()));
columns.push_back(MlValue::string(c.getStringValue()));
}
}
rows.push_back(columns);
rows.emplace_back(columns);
}
return rows;
}

View File

@ -4,7 +4,7 @@
#include <string>
#include <vector>
const std::vector<std::string> commands {
const std::vector<std::string> commands {
"eval", "type", "parse", "do", "if", "for", "while", "scope", "quote", "defun",
"define", "lambda", "benchmark", "=", "!=", ">", "<", ">=", "<=", "+", "-", "*", "/", "%",
"list", "insert", "index", "remove", "len", "push", "pop", "head", "tail", "first", "last",

View File

@ -1,10 +1,14 @@
### TODO
- support for *
- support for order by, offset, limit
- add pipe | token
- coalesce, date functions now, add_date; string functions rtrim, ltrim, rpad, lpad; math function round
- add pipe | concatenation
- support for order by, offset, limit (allow column name in order by, validate)
- support for uniqueue indexes (primary key)
- support for btree indexes
- support for joining
- add count min and max functions, eg aggregate functions
- maybe to create iterator on table
- add exceptions and rename it to UsqlException
- class members should have prefix m_
- add const wherever should be
- PERF in Row::Row(const Row &other), could be more efficient (memory and cpu)
- use references where pointer cannot be nullptr

View File

@ -1 +0,0 @@
../../clib/date.h

View File

@ -1,5 +1,7 @@
#include "csvreader.h"
#include "parser.h"
namespace usql {
@ -10,18 +12,90 @@ namespace usql {
line_separator = line_sep;
line_separator2 = line_sep2;
header_skiped = false;
header_skiped = !skip_hdr;
}
std::vector<std::vector<std::string>> CsvReader::parseCSV(const std::string &csvSource) {
int linesRead = 0;
int CsvReader::parseCSV(const std::string &filename, std::vector<ColDefNode> &cols_def, Table &table) {
std::vector<ColDefNode> cdefs;
cdefs.reserve(cols_def.size());
for (auto &cd : cols_def) {
cdefs.emplace_back(table.get_column_def(cd.name));
}
int row_cnt = 0;
bool inQuote(false);
std::string field;
std::vector<std::string> line;
line.reserve(32);
FILE* fp = fopen(filename.c_str(), "r");
if (fp == NULL)
exit(EXIT_FAILURE);
char* line_str = NULL;
size_t len = 0;
int read_chars;
while ((read_chars = getline(&line_str, &len, fp)) != -1) {
if (skip_header && !header_skiped) {
header_skiped = true;
continue;
}
if (read_chars > 0 && line_str[read_chars - 1] == '\n') {
line_str[read_chars - 1] = '\0';
--read_chars;
}
std::string csvSource{line_str};
std::string::const_iterator aChar = csvSource.begin();
std::string::const_iterator strEnd = csvSource.end();
while (aChar != strEnd) {
if (*aChar == quote_character) {
inQuote = !inQuote;
} else if (*aChar == field_separator) {
if (inQuote == true) {
field += *aChar;
} else {
line.push_back(field);
field.clear();
}
} else {
field.push_back(*aChar);
}
++aChar;
}
if (!field.empty())
line.push_back(field);
table.create_row_from_vector(cols_def, line);
row_cnt++;
field.clear();
line.clear();
// DEBUG
// if (row_cnt > 50000) break;
//
}
fclose(fp);
if (line_str)
free(line_str);
return row_cnt;
}
int CsvReader::parseCSV2(const std::string &csvSource, std::vector<ColDefNode> &cols_def, Table& table) {
int row_cnt = 0;
bool inQuote(false);
bool newLine(false);
std::string field;
std::vector<std::vector<std::string>> parsed_data;
parsed_data.reserve(128);
std::vector<std::string> line;
line.reserve(32);
@ -44,17 +118,13 @@ namespace usql {
} else {
if (newLine == false) {
line.push_back(field);
add_line(line, parsed_data);
if (header_skiped) {
table.create_row_from_vector(cols_def, line);
row_cnt++;
}
header_skiped = true;
field.clear();
line.clear();
linesRead++;
if (linesRead == 16) {
int linesEstimation =
csvSource.size() /
(std::distance(csvSource.begin(), aChar) / linesRead);
if (linesEstimation > parsed_data.capacity())
parsed_data.reserve(linesEstimation);
}
newLine = true;
}
}
@ -66,22 +136,15 @@ namespace usql {
aChar++;
}
if (field.size())
line.push_back(field);
if (!field.empty()) line.push_back(field);
add_line(line, parsed_data);
return parsed_data;
}
void CsvReader::add_line(const std::vector<std::string> &line, std::vector<std::vector<std::string>> &lines) {
if (skip_header && !header_skiped) {
if (header_skiped) {
table.create_row_from_vector(cols_def, line);
row_cnt++;
header_skiped = true;
} else {
if (line.size())
lines.push_back(line);
}
}
return row_cnt;
}
} // namespace

View File

@ -5,6 +5,10 @@
#include <string>
#include <vector>
#include <regex>
#include <functional>
#include "parser.h"
#include "table.h"
namespace usql {
@ -20,12 +24,12 @@ namespace usql {
bool header_skiped;
public:
CsvReader(bool skip_hdr = false, char field_sep = ',', char quote_ch = '"', char line_sep = '\r',
char line_sep2 = '\n');
CsvReader(bool skip_hdr = true, char field_sep = ',', char quote_ch = '"', char line_sep = '\r', char line_sep2 = '\n');
std::vector<std::vector<std::string>> parseCSV(const std::string &csvSource);
int parseCSV2(const std::string &csvSource, std::vector<ColDefNode> &cols_def, Table& table);
int parseCSV(const std::string &filename, std::vector<ColDefNode> &cols_def, Table& table);
private:
void add_line(const std::vector<std::string> &line, std::vector<std::vector<std::string>> &lines);
};
}
} // namespace

View File

@ -1,3 +0,0 @@
Ticker,Price
FDX,257.3
C,59.85
1 Ticker Price
2 FDX 257.3
3 C 59.85

View File

@ -14,7 +14,7 @@ namespace usql {
Lexer::Lexer() {
k_words_regex =
"[-+]?[0-9]+\\.[0-9]+|[-+]?[0-9][0-9_]+[0-9]|[0-9]+|[A-Za-z]+[A-Za-z0-9_#]*|[\\(\\)\\[\\]\\{\\}]|[-\\+\\*/"
",;:\?]|==|>=|<=|~=|>|<|=|;|~|\\||or|and|\n|\r|\r\n|'([^']|'')*'|\".*?\"|%.*?\n";
",;:\?]|!=|<>|==|>=|<=|~=|>|<|=|;|~|\\||or|and|\n|\r|\r\n|'([^']|'')*'|\".*?\"|%.*?\n";
k_int_regex = "[-+]?[0-9]+";
k_int_underscored_regex = "[-+]?[0-9][0-9_]+[0-9]";
k_double_regex = "[-+]?[0-9]+\\.[0-9]+";
@ -22,16 +22,15 @@ namespace usql {
}
void Lexer::parse(const std::string &code) {
// TODO handle empty code
m_tokens.clear();
if (code.empty())
throw Exception("empty code");
m_tokens.clear();
m_tokens.reserve(64);
// PERF something like this to preallocate ??
if (code.size() > 100) {
m_tokens.reserve(code.size() / 10);
}
m_code_str = code;
if (!m_code_str.empty() && m_code_str.back() != '\n') {
m_code_str.append("\n"); // TODO temp solution to prevent possible situation when last line is a comment
m_code_str.append("\n"); // temp solution to prevent possible situation when last line is a comment
}
auto words_begin = std::sregex_iterator(m_code_str.begin(), m_code_str.end(), k_words_regex);
@ -45,7 +44,7 @@ namespace usql {
match_str = stringLiteral(match_str);
if (token_type != TokenType::newline)
m_tokens.push_back(Token{match_str, token_type});
m_tokens.emplace_back(match_str, token_type);
}
// DEBUG IT
@ -56,8 +55,8 @@ namespace usql {
void Lexer::debugTokens() {
int i = 0;
for (std::vector<Token>::iterator it = m_tokens.begin(); it != m_tokens.end(); ++it) {
std::cerr << i << "\t" << it->token_string << std::endl;
for (auto & m_token : m_tokens) {
std::cerr << i << "\t" << m_token.token_string << std::endl;
i++;
}
}
@ -97,8 +96,6 @@ namespace usql {
return m_index < m_tokens.size() - 1 ? m_tokens[m_index + 1].type : TokenType::eof;
}
TokenType Lexer::prevTokenType() { return m_index > 0 ? m_tokens[m_index - 1].type : TokenType::undef; }
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 ||
@ -116,118 +113,101 @@ namespace usql {
}
TokenType Lexer::type(const std::string &token) {
// TODO, FIXME 'one is evaluated as identifier
// FIXME 'one is evaluated as identifier
if (token == ";")
return TokenType::semicolon;
if (token == "+")
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 == "!=")
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 == "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 == "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;
@ -267,7 +247,7 @@ namespace usql {
if (!replace) {
return str;
}
std::string out = "";
std::string out;
out.reserve(str.size());
@ -347,6 +327,24 @@ namespace usql {
case TokenType::keyword_where:
txt = "where";
break;
case TokenType::keyword_order:
txt = "order";
break;
case TokenType::keyword_by:
txt = "by";
break;
case TokenType::keyword_offset:
txt = "offset";
break;
case TokenType::keyword_limit:
txt = "limit";
break;
case TokenType::keyword_asc:
txt = "asc";
break;
case TokenType::keyword_desc:
txt = "desc";
break;
case TokenType::keyword_table:
txt = "table";
break;
@ -389,6 +387,18 @@ namespace usql {
case TokenType::keyword_varchar:
txt = "varchar";
break;
case TokenType::keyword_date:
txt = "date";
break;
case TokenType::keyword_bool:
txt = "boolean";
break;
case TokenType::keyword_distinct:
txt = "distinct";
break;
case TokenType::keyword_show:
txt = "show";
break;
case TokenType::int_number:
txt = "int number";
break;

View File

@ -25,6 +25,12 @@ namespace usql {
keyword_drop,
keyword_table,
keyword_where,
keyword_order,
keyword_by,
keyword_offset,
keyword_limit,
keyword_asc,
keyword_desc,
keyword_delete,
keyword_update,
keyword_load,
@ -41,6 +47,10 @@ namespace usql {
keyword_integer,
keyword_float,
keyword_varchar,
keyword_date,
keyword_bool,
keyword_distinct,
keyword_show,
int_number,
double_number,
string_literal,
@ -85,8 +95,6 @@ namespace usql {
TokenType nextTokenType();
TokenType prevTokenType();
static bool isRelationalOperator(TokenType token_type);
static bool isLogicalOperator(TokenType token_type);
@ -96,7 +104,7 @@ namespace usql {
private:
TokenType type(const std::string &token);
std::string stringLiteral(std::string token);
static std::string stringLiteral(std::string token);
static std::string typeToString(TokenType token_type);

View File

@ -1,61 +1,206 @@
#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;
int main(int argc, char *argv[]) {
std::vector<std::string> sql_commands{
"create table a (i integer not null, s varchar(64), f float null)",
"insert into a (i, s) values(1, upper('one'))",
"update table a set s = 'null string aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'",
// "update table a set i = null",
"insert into a (i, s) values(2, 'two')",
"insert into a (i, s) values(3, 'two')",
"insert into a (i, s) values(4, lower('FOUR'))",
"insert into a (i, s) values(5, 'five')",
"insert into a (i, s) values(to_date('20.12.1973', '%d.%m.%Y'), 'six')",
"save table a into '/tmp/a.csv'",
"select i, s from a where i > 2",
"select i, s from a where i = 1",
"select i, s from a where s = 'two'",
"select i, s from a where i <= 3 and s = 'one'",
"select i, s from a where i > 0",
"delete from a where i = 4",
"select i, s from a where i > 0",
"update a set f = 9.99 where i = 3",
"select i, s, f from a where i = 3",
"update a set s = 'three', f = f + 0.01 where i = 3",
"select i, s, f from a where i = 3",
"create table data (ticker varchar(8), price float null)",
"load data from '/Users/vaclavt/Library/Mobile Documents/com~apple~CloudDocs/Development/usql/data.csv')",
"select ticker, price from data",
"select i, s, f from a where i < 300",
"create table x as select i, s, f from a where i < 300",
"select i, s, f from x where i < 300",
"drop table x",
"select i, s, f from a where i > 300",
"select i, to_string(i, '%d.%m.%Y'), s, f from a where i > 300",
"create table prices (datetime integer, symbol varchar(8), prev_close float, open float, price float, change float, change_prct varchar(16))",
"load prices from '/Users/vaclavt/Library/Mobile Documents/com~apple~CloudDocs/Development/usql/prices.csv'",
"insert into prices (datetime, symbol, prev_close, open, price, change, change_prct) values (1626979443, 'MPC', 54.08, 53.82, 53.63, -0.832101, '-0.83 %')",
"select to_string(datetime, '%d.%m.%Y %H:%M:%S'), symbol, prev_close, open, price, change, change_prct from prices where symbol = 'SYF'"
};
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{};
for (auto command : sql_commands) {
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{
// "create table ticker ( tablee varchar(5) not null, permaticker integer, ticker varchar(10) not null, name varchar(256) not null, exchange varchar(32), isdelisted boolean, category varchar(32), cusips varchar(256), siccode integer, sicsector varchar(256), sicindustry varchar(256), famasector varchar(256), famaindustry varchar(256), sector varchar(128), industry varchar(128), scalemarketcap varchar(64), scalerevenue varchar(64), relatedtickers varchar(128), currency varchar(3), location varchar(64), lastupdated date, firstadded date, firstpricedate date, lastpricedate date, firstquarter date, lastquarter date, secfilings varchar(256), companysite varchar(256))",
// "load ticker from '/Users/vaclavt/Library/Mobile Documents/com~apple~CloudDocs/Development/usql/tickers.csv')",
// "select * from ticker where ticker = 'WFC' and tablee = 'SF1'",
// "set 'DATE_FORMAT' = '%Y-%m-%d'",
// "show 'DATE_FORMAT'",
"create table sf1 ( ticker varchar(8), dimension varchar(3), calendar_date date, date_key date, report_period date, last_updated date, accoci float, assets float, assetsavg float, assetsc float, assetsnc float, assetturnover float, bvps float, capex float, cashneq float, cashnequsd float, cor float, consolinc float, currentratio float, de float, debt float, debtc float, debtnc float, debtusd float, deferredrev float, depamor float, deposits float, divyield float, dps float, ebit float, ebitda float, ebitdamargin float, ebitdausd float, ebitusd float, ebt float, eps float, epsdil float, epsusd float, equity float, equityavg float, equityusd float, ev float, evebit float, evebitda float, fcf float, fcfps float, fxusd float, gp float, grossmargin float, intangibles float, intexp float, invcap float, invcapavg float, inventory float, investments float, investmentsc float, investmentsnc float, liabilities float, liabilitiesc float, liabilitiesnc float, marketcap float, ncf float, ncfbus float, ncfcommon float, ncfdebt float, ncfdiv float, ncff float, ncfi float, ncfinv float, ncfo float, ncfx float, netinc float, netinccmn float, netinccmnusd float, netincdis float, netincnci float, netmargin float, opex float, opinc float, payables float, payoutratio float, pb float, pe float, pe1 float, ppnenet float, prefdivis float, price float, ps float, ps1 float, receivables float, retearn float, revenue float, revenueusd float, rnd float, roa float, roe float, roic float, ros float, sbcomp float, sgna float, sharefactor float, sharesbas float, shareswa float, shareswadil float, sps float, tangibles float, taxassets float, taxexp float, taxliabilities float, tbvps float, workingcapital float)",
"load sf1 from '/tmp/sf1.csv'",
// "select ticker, calendar_date from sf1 where calendar_date > to_date('2019-01-01', '%Y-%m-%d') limit 1",
// "select ticker, dimension, calendar_date, eps, dps, roa*100 as roa, roe*100 as roe, pp(revenue), netinc from sf1 where (ticker = 'AIG' or ticker = 'WFC') and dimension = 'MRY' and calendar_date > to_date('2019-01-01', '%Y-%m-%d') order by 3 desc
"select ticker, dimension, calendar_date, eps, dps, roa*100 as roa, roe*100 as roe, pp(revenue) as revenue, pp(netinc) as netinc from sf1 where (ticker = 'AIG' or ticker = 'WFC') and dimension = 'MRY' and calendar_date > to_date('2019-01-01', '%Y-%m-%d') order by 3 desc",
// "create table a (i integer not null, s varchar(64), f float null, d date null, b boolean)",
// "insert into a (i, s, b) values(1, upper('one'), 'Y')",
// "insert into a (i, s, b, f) values(1 + 10000, upper('one'), 'Y', 3.1415)",
// "update a set i = i * 100, f = f + 0.01 where i > 1",
// "select to_string(i, '%d.%m.%Y %H:%M:%S'), i, s from a where i < to_date('20.12.2019', '%d.%m.%Y')",
// "select i + 2 as first, i, s, b, f from a where i >=1 order by 1 desc offset 0 limit 1",
// "update table a set s = 'null string aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'",
// "update table a set i = null",
// "insert into a (i, s) values(2, 'two')",
// "insert into a (i, s) values(3, 'two')",
// "insert into a (i, s) values(4, lower('FOUR'))",
// "insert into a (i, s) values(5, 'five')",
// "insert into a (i, s) values(to_date('20.12.1973', '%d.%m.%Y'), 'six')",
// tohle zpusobi kresh "insert into a (i, d) values(6', '2006-10-04')",
// "insert into a (i, d) values(6, '2006-10-04')",
// "save table a into '/tmp/a.csv'",
// "select i, s from a where i > 2 order by 1 desc offset 1 limit 1",
// "select distinct s, d from a",
// "select i, s from a where i = 1",
// "select i, s from a where s = 'two'",
// "select i, s from a where i <= 3 and s = 'one'",
// "select i, s from a where i > 0",
// "delete from a where i = 4",
// "select i, s from a where i > 0",
// "update a set f = 9.99 where i = 3",
// "select i, s, f from a where i = 3",
// "update a set s = 'three', f = f + 0.01 where i = 3",
// "select i, s, f from a where i = 3",
// "create table data (ticker varchar(8), price float null)",
// "load data from '/Users/vaclavt/Library/Mobile Documents/com~apple~CloudDocs/Development/usql/data.csv')",
// "select ticker, price from data",
// "select i, s, f from a where i < 300",
// "create table x as select i, s, f from a where i < 300",
// "select i, s, f from x where i < 300",
// "drop table x",
// "select i, s, f from a where i > 300",
// "select i, to_string(i, '%d.%m.%Y'), s, f from a where i > 300",
// "create table prices (datetime integer, symbol varchar(8), prev_close float, open float, price float, change float, change_prct varchar(16))",
// "load prices from '/Users/vaclavt/Library/Mobile Documents/com~apple~CloudDocs/Development/usql/prices.csv'",
// "insert into prices (datetime, symbol, prev_close, open, price, change, change_prct) values (1626979443, 'MPC', 54.08, 53.82, 53.63, -0.832101, '-0.83 %')",
// "select to_string(datetime, '%d.%m.%Y %H:%M:%S'), symbol, prev_close, open, price, change, change_prct, datetime from prices where symbol = 'SYF' order by 8 desc limit 10"
};
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::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[]) {
// debug();
repl();
return 0;
}

View File

@ -1,59 +0,0 @@
#include "ml_date.h"
long now() {
// get-universal-time
time_t t = std::time(0);
long int now = static_cast<long int>(t);
return now;
}
std::string date_to_string(const long datetime, const std::string format) {
// std::locale::global(std::locale("en-US.UTF8"));
time_t timestamp = datetime;
char mbstr[128];
if (std::strftime(mbstr, sizeof(mbstr), format.c_str(), std::localtime(&timestamp))) {
std::string result = {mbstr};
return result;
}
// TODO exception here
return "invalid argument";
}
long string_to_date(const std::string &datestr, const std::string &format) {
// format for example "%d.%m.%Y";
std::istringstream in{datestr.c_str()};
date::sys_seconds tp;
in >> date::parse(format, tp);
return tp.time_since_epoch().count();
}
long add_to_date(const long datetime, const long quantity, const std::string &part) {
// part is one of 'year', 'month', 'day', 'hour', 'minute' or 'second'
// very basic implementation, just for now - no timezones DST etc
time_t base = datetime;
struct tm *tm = localtime(&base);
if (part == "year") {
tm->tm_year += quantity;
} else if (part == "month") {
tm->tm_mon += quantity;
} else if (part == "day") {
tm->tm_mday += quantity;
} else if (part == "hour") {
tm->tm_hour += quantity;
} else if (part == "minute") {
tm->tm_min += quantity;
} else if (part == "second") {
tm->tm_sec += quantity;
} else {
// TODO exception here
}
return mktime(tm);
}

View File

@ -1,17 +0,0 @@
#pragma once
#include "clib/date.h"
#include <string>
#include <vector>
long now();
std::string date_to_string(const long datetime, const std::string format);
long string_to_date(const std::string &datestr, const std::string &format);
long add_to_date(const long datetime, const long quantity, const std::string &part);

View File

@ -15,6 +15,9 @@ namespace usql {
if (m_lexer.tokenType() == TokenType::keyword_create && m_lexer.nextTokenType() == TokenType::keyword_table)
return parse_create_table();
if (m_lexer.tokenType() == TokenType::keyword_drop)
return parse_drop_table();
if (m_lexer.tokenType() == TokenType::keyword_insert)
return parse_insert_into_table();
if (m_lexer.tokenType() == TokenType::keyword_select)
@ -23,12 +26,16 @@ namespace usql {
return parse_delete_from_table();
if (m_lexer.tokenType() == TokenType::keyword_update)
return parse_update_table();
if (m_lexer.tokenType() == TokenType::keyword_load)
return parse_load_table();
if (m_lexer.tokenType() == TokenType::keyword_save)
return parse_save_table();
if (m_lexer.tokenType() == TokenType::keyword_drop)
return parse_drop_table();
if (m_lexer.tokenType() == TokenType::keyword_set)
return parse_set();
if (m_lexer.tokenType() == TokenType::keyword_show)
return parse_show();
std::cout << "ERROR, token:" << m_lexer.currentToken().token_string << std::endl;
return std::make_unique<Node>(NodeType::error);
@ -40,7 +47,9 @@ namespace usql {
m_lexer.skipToken(TokenType::keyword_create);
m_lexer.skipToken(TokenType::keyword_table);
if (m_lexer.tokenType() != TokenType::identifier) { /* TODO handle error */ }
if (m_lexer.tokenType() != TokenType::identifier)
throw Exception("syntax error, expecting identifier but found " + m_lexer.currentToken().token_string);
std::string table_name = m_lexer.consumeCurrentToken().token_string;
// create as select
@ -54,16 +63,16 @@ namespace usql {
m_lexer.skipToken(TokenType::open_paren);
int column_order = 0;
do {
std::string column_name;
std::string database_value;
ColumnType column_type;
int column_len{1};
bool column_nullable{true};
int column_len = 1;
bool column_nullable = true;
// column name
if (m_lexer.tokenType() != TokenType::identifier) {
throw Exception("syntax error, expected identifier");
}
column_name = m_lexer.consumeCurrentToken().token_string;
database_value = m_lexer.consumeCurrentToken().token_string;
// column type and optionally len
if (m_lexer.tokenType() == TokenType::keyword_integer) {
@ -82,8 +91,14 @@ namespace usql {
throw Exception("syntax error, expected int number");
}
m_lexer.skipToken(TokenType::close_paren);
} else if (m_lexer.tokenType() == TokenType::keyword_date) {
column_type = ColumnType::date_type;
m_lexer.nextToken();
} else if (m_lexer.tokenType() == TokenType::keyword_bool) {
column_type = ColumnType::bool_type;
m_lexer.nextToken();
} else {
throw Exception("syntax error, column type expected");
throw Exception("syntax error, column type expected, found " + m_lexer.currentToken().token_string);
}
if (m_lexer.tokenType() == TokenType::keyword_not) {
@ -94,11 +109,12 @@ namespace usql {
m_lexer.nextToken();
}
cols_def.push_back( ColDefNode(column_name, column_type, column_order++, column_len, column_nullable));
cols_def.emplace_back(database_value, column_type, column_order++, column_len, column_nullable);
m_lexer.skipTokenOptional(TokenType::comma);
// TODO in future constraints
//constraints
//defaults
} while (m_lexer.tokenType() != TokenType::close_paren);
return std::make_unique<CreateTableNode>(table_name, cols_def);
@ -140,22 +156,49 @@ namespace usql {
return std::make_unique<DropTableNode>(table_name);
}
std::unique_ptr<Node> Parser::parse_set() {
m_lexer.skipToken(TokenType::keyword_set);
if (m_lexer.currentToken().type!=TokenType::string_literal) throw Exception("Expecting literal in set name");
std::string name = m_lexer.consumeCurrentToken().token_string;
m_lexer.skipTokenOptional(TokenType::equal);
if (m_lexer.currentToken().type!=TokenType::string_literal) throw Exception("Expecting literal in set value");
std::string value = m_lexer.consumeCurrentToken().token_string;
return std::make_unique<SetNode>(name, value);
}
std::unique_ptr<Node> Parser::parse_show() {
m_lexer.skipToken(TokenType::keyword_show);
if (m_lexer.currentToken().type!=TokenType::string_literal) throw Exception("Expecting literal on show parameter name");
std::string name = m_lexer.consumeCurrentToken().token_string;
return std::make_unique<ShowNode>(name);
}
std::unique_ptr<Node> Parser::parse_insert_into_table() {
std::vector<ColNameNode> column_names{};
std::vector<DatabaseValueNode> database_values{};
std::vector<std::unique_ptr<Node>> column_values{};
m_lexer.skipToken(TokenType::keyword_insert);
m_lexer.skipToken(TokenType::keyword_into);
// table name
if (m_lexer.tokenType() != TokenType::identifier) { /* TODO handle error */ }
if (m_lexer.tokenType() != TokenType::identifier)
throw Exception("syntax error, expecting identifier but found " + m_lexer.currentToken().token_string);
std::string table_name = m_lexer.consumeCurrentToken().token_string;
// column names
m_lexer.skipToken(TokenType::open_paren);
do {
if (m_lexer.tokenType() != TokenType::identifier) { /* TODO handle error */ }
column_names.push_back(m_lexer.consumeCurrentToken().token_string);
if (m_lexer.tokenType() != TokenType::identifier)
throw Exception("syntax error, expecting identifier but found " + m_lexer.currentToken().token_string);
database_values.emplace_back(m_lexer.consumeCurrentToken().token_string);
m_lexer.skipTokenOptional(TokenType::comma);
} while (m_lexer.tokenType() != TokenType::close_paren);
@ -166,65 +209,53 @@ namespace usql {
// column values
m_lexer.skipToken(TokenType::open_paren);
do {
auto col_value = parse_value();
auto col_value = parse_expression();
column_values.push_back(std::move(col_value));
m_lexer.skipTokenOptional(TokenType::comma);
} while (m_lexer.tokenType() != TokenType::close_paren);
m_lexer.skipToken(TokenType::close_paren);
return std::make_unique<InsertIntoTableNode>(table_name, column_names, std::move(column_values));
return std::make_unique<InsertIntoTableNode>(table_name, database_values, std::move(column_values));
}
std::unique_ptr<Node> Parser::parse_value() {
if (m_lexer.tokenType() == TokenType::int_number) {
return std::make_unique<IntValueNode>(std::stoi(m_lexer.consumeCurrentToken().token_string));
}
if (m_lexer.tokenType() == TokenType::double_number) {
return std::make_unique<DoubleValueNode>(std::stof(m_lexer.consumeCurrentToken().token_string));
}
if (m_lexer.tokenType() == TokenType::string_literal) {
return std::make_unique<StringValueNode>(m_lexer.consumeCurrentToken().token_string);
}
if (m_lexer.tokenType() == TokenType::identifier && m_lexer.nextTokenType() == TokenType::open_paren) {
// function
std::string function_name = m_lexer.consumeCurrentToken().token_string;
std::vector<std::unique_ptr<Node>> pars;
m_lexer.skipToken(TokenType::open_paren);
while (m_lexer.tokenType() != TokenType::close_paren) { // TODO handle errors
pars.push_back(parse_value());
m_lexer.skipTokenOptional(TokenType::comma);
}
m_lexer.skipToken(TokenType::close_paren);
return std::make_unique<FunctionNode>(function_name, std::move(pars));
}
if (m_lexer.tokenType() == TokenType::identifier) {
std::string name = m_lexer.consumeCurrentToken().token_string;
return std::make_unique<ColNameNode>(name);
}
throw Exception("Syntax error, current token: " + m_lexer.currentToken().token_string);
}
std::unique_ptr<Node> Parser::parse_select_from_table() {
std::unique_ptr<Node> Parser::parse_select_from_table() {
bool distinct = false;
auto cols = std::make_unique<std::vector<SelectColNode>>();
m_lexer.skipToken(TokenType::keyword_select);
if (m_lexer.tokenType() == TokenType::keyword_distinct) {
distinct = true;
m_lexer.skipToken(TokenType::keyword_distinct);
}
int i = 1;
while (m_lexer.tokenType() != TokenType::keyword_from) {
auto column_value = parse_value();
if (m_lexer.tokenType()==TokenType::multiply) {
std::string name = m_lexer.consumeCurrentToken().token_string;
auto multiply_char = std::make_unique<DatabaseValueNode>(name);
cols->push_back(SelectColNode{std::move(multiply_char), "*"});
} else {
auto column_value = parse_expression();
std::string column_alias;
if (column_value->node_type == NodeType::column_name) {
column_alias = ((ColNameNode*) column_value.get())->name;
if (m_lexer.tokenType() == TokenType::keyword_as) {
m_lexer.skipToken(TokenType::keyword_as);
column_alias = m_lexer.consumeCurrentToken().token_string;
} else {
if (column_value->node_type == NodeType::database_value) {
column_alias = ((DatabaseValueNode*) column_value.get())->col_name;
} else {
column_alias = "c" + std::to_string(i);
i++;
}
}
cols->push_back(SelectColNode{std::move(column_value), column_alias});
}
m_lexer.skipTokenOptional(TokenType::comma);
}
@ -235,12 +266,13 @@ std::unique_ptr<Node> Parser::parse_select_from_table() {
std::unique_ptr<Node> where_node = parse_where_clause();
// if (m_lexer.tokenType() == TokenType::keyword_order_by) {}
// if (m_lexer.tokenType() == TokenType::keyword_offset) {}
// if (m_lexer.tokenType() == TokenType::keyword_limit) {}
std::vector<ColOrderNode> orderby_node = parse_order_by_clause();
return std::make_unique<SelectFromTableNode>(table_name, std::move(cols), std::move(where_node));
}
OffsetLimitNode offsetlimit_node = parse_offset_limit_clause();
return std::make_unique<SelectFromTableNode>(table_name, std::move(cols), std::move(where_node), orderby_node, offsetlimit_node, distinct);
}
std::unique_ptr<Node> Parser::parse_delete_from_table() {
m_lexer.skipToken(TokenType::keyword_delete);
@ -261,24 +293,22 @@ std::unique_ptr<Node> Parser::parse_select_from_table() {
m_lexer.skipToken(TokenType::keyword_set);
std::vector<ColNameNode> cols_names;
std::vector<DatabaseValueNode> cols_names;
std::vector<std::unique_ptr<Node>> values;
do {
cols_names.push_back(m_lexer.consumeCurrentToken().token_string);
cols_names.emplace_back(m_lexer.consumeCurrentToken().token_string);
m_lexer.skipToken(TokenType::equal);
std::unique_ptr<Node> left = Parser::parse_operand_node();
std::unique_ptr<Node> left = Parser::parse_value();
if (Lexer::isArithmeticalOperator(m_lexer.tokenType())) {
ArithmeticalOperatorType op = parse_arithmetical_operator();
std::unique_ptr<Node> right = Parser::parse_operand_node();
std::unique_ptr<Node> right = Parser::parse_value();
values.push_back(std::make_unique<ArithmeticalOperatorNode>(op, std::move(left),
std::move(right)));
values.push_back(std::make_unique<ArithmeticalOperatorNode>(op, std::move(left), std::move(right)));
} else {
std::unique_ptr<Node> right = std::make_unique<IntValueNode>(0);
values.push_back(
std::make_unique<ArithmeticalOperatorNode>(ArithmeticalOperatorType::copy_value,
values.push_back(std::make_unique<ArithmeticalOperatorNode>(ArithmeticalOperatorType::copy_value,
std::move(left), std::move(right)));
}
m_lexer.skipTokenOptional(TokenType::comma);
@ -290,55 +320,163 @@ std::unique_ptr<Node> Parser::parse_select_from_table() {
return std::make_unique<UpdateTableNode>(table_name, cols_names, std::move(values), std::move(where_node));
}
std::unique_ptr<Node> Parser::parse_where_clause() {
// TODO add support for multiple filters
// TODO add support for parenthesis
if (m_lexer.tokenType() != TokenType::keyword_where) {
return std::make_unique<TrueNode>();
}
std::vector<ColOrderNode> Parser::parse_order_by_clause() {
std::vector<ColOrderNode> order_cols;
if (m_lexer.tokenType() == TokenType::keyword_order) {
m_lexer.skipToken(TokenType::keyword_order);
m_lexer.skipToken(TokenType::keyword_by);
std::unique_ptr<Node> node;
m_lexer.skipToken(TokenType::keyword_where);
do {
node = parse_relational_expression();
int col_index = FUNCTION_CALL;
bool asc = true;
if (Lexer::isLogicalOperator(m_lexer.tokenType())) {
auto operation = parse_logical_operator();
std::unique_ptr<Node> node2 = parse_relational_expression();
node = std::make_unique<LogicalOperatorNode>(operation, std::move(node), std::move(node2));
}
} while (m_lexer.tokenType() != TokenType::eof); // until whole where clause parsed
return node;
}
std::unique_ptr<Node> Parser::parse_relational_expression() {
auto left = parse_operand_node();
auto operation = parse_relational_operator();
auto right = parse_operand_node();
return std::make_unique<RelationalOperatorNode>(operation, std::move(left), std::move(right));
}
std::unique_ptr<Node> Parser::parse_operand_node() {
// while not end or order or limit
auto token_type = m_lexer.tokenType();
std::string tokenString = m_lexer.consumeCurrentToken().token_string;
switch (token_type) {
case TokenType::int_number:
return std::make_unique<IntValueNode>(std::stoi(tokenString));
case TokenType::double_number:
return std::make_unique<DoubleValueNode>(std::stod(tokenString));
case TokenType::string_literal:
return std::make_unique<StringValueNode>(tokenString);
case TokenType::identifier:
return std::make_unique<DatabaseValueNode>(tokenString);
case TokenType::keyword_null:
return std::make_unique<NullValueNode>();
default:;
throw Exception("Unknown operand node");
col_index = std::stoi(tokenString);
break;
default:
throw Exception("column index allowed in order by clause at this moment");
}
if (m_lexer.tokenType() == TokenType::keyword_asc) {
m_lexer.skipToken(TokenType::keyword_asc);
} else if (m_lexer.tokenType() == TokenType::keyword_desc) {
m_lexer.skipToken(TokenType::keyword_desc);
asc = false;
}
order_cols.emplace_back(col_index, asc);
m_lexer.skipTokenOptional(TokenType::comma);
} while (m_lexer.tokenType() != TokenType::eof && m_lexer.tokenType() != TokenType::keyword_offset && m_lexer.tokenType() != TokenType::keyword_limit);
}
return order_cols;
}
OffsetLimitNode Parser::parse_offset_limit_clause() {
int offset = 0;
int limit = 999999999;
if (m_lexer.tokenType() == TokenType::keyword_offset) {
m_lexer.skipToken(TokenType::keyword_offset);
if (m_lexer.tokenType() != TokenType::int_number)
throw Exception("expecting integer in offset clause");
offset = std::stoi(m_lexer.consumeCurrentToken().token_string);
}
if (m_lexer.tokenType() == TokenType::keyword_limit) {
m_lexer.skipToken(TokenType::keyword_limit);
if (m_lexer.tokenType() != TokenType::int_number)
throw Exception("expecting integer in limit clause");
limit = std::stoi(m_lexer.consumeCurrentToken().token_string);
}
return OffsetLimitNode{offset, limit};
}
std::unique_ptr<Node> Parser::parse_where_clause() {
if (m_lexer.tokenType() != TokenType::keyword_where) {
return std::make_unique<TrueNode>();
}
m_lexer.skipToken(TokenType::keyword_where);
std::unique_ptr<Node> left = parse_expression();
do {
left = parse_expression(std::move(left));
} while (m_lexer.tokenType() != TokenType::eof && m_lexer.tokenType() != TokenType::keyword_order && m_lexer.tokenType() != TokenType::keyword_offset && m_lexer.tokenType() != TokenType::keyword_limit);
return left;
}
std::unique_ptr<Node> Parser::parse_expression() {
std::unique_ptr<Node> left = parse_value();
return parse_expression(std::move(left));
}
std::unique_ptr<Node> Parser::parse_expression(std::unique_ptr<Node> left) {
if (Lexer::isRelationalOperator(m_lexer.tokenType())) {
auto operation = parse_relational_operator();
auto right = parse_value();
return std::make_unique<RelationalOperatorNode>(operation, std::move(left), std::move(right));
} else if (Lexer::isLogicalOperator(m_lexer.tokenType())) {
auto operation = parse_logical_operator();
auto right = parse_expression();
return std::make_unique<LogicalOperatorNode>(operation, std::move(left), std::move(right));
} else if (Lexer::isArithmeticalOperator(m_lexer.tokenType())) {
auto operation = parse_arithmetical_operator();
auto right = parse_value();
return std::make_unique<ArithmeticalOperatorNode>(operation, std::move(left), std::move(right));
} else if (m_lexer.tokenType() == TokenType::int_number || m_lexer.tokenType() == TokenType::double_number ||m_lexer.tokenType() == TokenType::string_literal ||m_lexer.tokenType() == TokenType::identifier || m_lexer.tokenType() == TokenType::keyword_null || m_lexer.tokenType() == TokenType::open_paren) {
return parse_value();
}
return left;
}
std::unique_ptr<Node> Parser::parse_value() {
auto token_type = m_lexer.tokenType();
// parenthesised expression
if (token_type == TokenType::open_paren) {
m_lexer.skipToken(TokenType::open_paren);
auto left = parse_expression();
do {
left = parse_expression(std::move(left));
} while (m_lexer.tokenType() != TokenType::close_paren && m_lexer.tokenType() != TokenType::eof);
m_lexer.skipToken(TokenType::close_paren);
return left;
}
// function call
if (token_type == TokenType::identifier && m_lexer.nextTokenType() == TokenType::open_paren) {
std::string function_name = m_lexer.consumeCurrentToken().token_string;
std::vector<std::unique_ptr<Node>> pars;
m_lexer.skipToken(TokenType::open_paren);
while (m_lexer.tokenType() != TokenType::close_paren && m_lexer.tokenType() != TokenType::eof) {
pars.push_back(parse_value());
m_lexer.skipTokenOptional(TokenType::comma);
}
m_lexer.skipToken(TokenType::close_paren);
return std::make_unique<FunctionNode>(function_name, std::move(pars));
}
// numbers and strings
std::string tokenString = m_lexer.consumeCurrentToken().token_string;
if (token_type == TokenType::int_number)
return std::make_unique<IntValueNode>(std::stoi(tokenString));
if (token_type == TokenType::double_number)
return std::make_unique<DoubleValueNode>(std::stod(tokenString));
if (token_type == TokenType::string_literal)
return std::make_unique<StringValueNode>(tokenString);
// db column
if (token_type == TokenType::identifier)
return std::make_unique<DatabaseValueNode>(tokenString);
// null
if (token_type == TokenType::keyword_null)
return std::make_unique<NullValueNode>();
throw Exception("Unknown operand node " + tokenString);
}
RelationalOperatorType Parser::parse_relational_operator() {
@ -357,7 +495,7 @@ std::unique_ptr<Node> Parser::parse_select_from_table() {
case TokenType::lesser_equal:
return RelationalOperatorType::lesser_equal;
default:
throw Exception("Unknown relational operator");
throw Exception("Unknown relational operator " + op.token_string);
}
}
@ -389,4 +527,5 @@ std::unique_ptr<Node> Parser::parse_select_from_table() {
}
}
}
} // namespace

View File

@ -2,16 +2,22 @@
#include "lexer.h"
#include "exception.h"
#include <string>
#include "ml_date.h"
#include "settings.h"
#include <string>
#include <vector>
static const int FUNCTION_CALL = -1;
namespace usql {
enum class ColumnType {
integer_type,
float_type,
varchar_type
varchar_type,
date_type,
bool_type
};
enum class NodeType {
@ -20,7 +26,7 @@ namespace usql {
int_value,
float_value,
string_value,
database_value,
bool_value,
logical_operator,
relational_operator,
arithmetical_operator,
@ -33,7 +39,11 @@ namespace usql {
load_table,
save_table,
drop_table,
column_name,
set,
show,
database_value,
offset_limit,
column_order,
column_value,
function,
column_def,
@ -43,21 +53,35 @@ namespace usql {
struct Node {
NodeType node_type;
Node(const NodeType type) : node_type(type) {}
explicit Node(const NodeType type) : node_type(type) {}
};
struct ColNameNode : Node {
std::string name;
ColNameNode(const std::string col_name) : Node(NodeType::column_name), name(col_name) {}
struct ColOrderNode : Node {
std::string col_name;
int col_index;
bool ascending;
ColOrderNode(const std::string& name, bool asc) : Node(NodeType::column_order), col_name(name), col_index(-1), ascending(asc) {}
ColOrderNode(int index, bool asc) : Node(NodeType::database_value), col_name(""), col_index(index), ascending(asc) {}
};
struct OffsetLimitNode : Node {
int offset;
int limit;
OffsetLimitNode(int off, int lim) : Node(NodeType::offset_limit), offset(off), limit(lim) {}
};
struct SelectColNode : Node {
std::unique_ptr<Node> value;
std::string name;
SelectColNode(std::unique_ptr<Node> column, const std::string& alias) :
Node(NodeType::column_name), value(std::move(column)), name(alias) {}
SelectColNode(std::unique_ptr<Node> column, const std::string &alias) :
Node(NodeType::database_value), value(std::move(column)), name(alias) {}
};
struct ColDefNode : Node {
@ -67,16 +91,16 @@ namespace usql {
int length;
bool null;
ColDefNode(const std::string col_name, ColumnType col_type, int col_order, int col_len, bool nullable) :
ColDefNode(const std::string& col_name, ColumnType col_type, int col_order, int col_len, bool nullable) :
Node(NodeType::column_def), name(col_name), type(col_type), order(col_order), length(col_len),
null(nullable) {}
};
struct FunctionNode : Node {
std::string function;
std::string function; // TODO use enum
std::vector<std::unique_ptr<Node>> params;
FunctionNode(const std::string func_name, std::vector<std::unique_ptr<Node>> pars) :
FunctionNode(const std::string& func_name, std::vector<std::unique_ptr<Node>> pars) :
Node(NodeType::function), function(func_name), params(std::move(pars)) {}
};
@ -85,14 +109,16 @@ namespace usql {
};
struct ValueNode : Node {
ValueNode(NodeType type) : Node(type) {}
explicit ValueNode(NodeType type) : Node(type) {}
virtual bool isNull() { return false; }
virtual long getIntValue() = 0;
virtual long getIntegerValue() = 0;
virtual double getDoubleValue() = 0;
virtual std::string getStringValue() = 0;
virtual long getDateValue() = 0;
virtual bool getBooleanValue() = 0;
virtual ~ValueNode() {};
virtual ~ValueNode() = default;
};
struct NullValueNode : ValueNode {
@ -101,45 +127,66 @@ namespace usql {
bool isNull() override { return true; }
long getIntValue() override { throw Exception("not supported on null value"); };
double getDoubleValue() override { throw Exception("not supported on null value"); };
std::string getStringValue() override { throw Exception("not supported on null value"); };
long getIntegerValue() override { throw Exception("getIntegerValue not supported on NullValueNode"); };
double getDoubleValue() override { throw Exception("getDoubleValue not supported on NullValueNode"); };
std::string getStringValue() override { throw Exception("getStringValue not supported on NullValueNode"); };
long getDateValue() override { throw Exception("getDateValue not supported on NullValueNode"); };
bool getBooleanValue() override { throw Exception("getBooleanValue not supported on NullValueNode"); };
};
struct IntValueNode : ValueNode {
long value;
IntValueNode(long value) : ValueNode(NodeType::int_value), value(value) {}
explicit IntValueNode(long value) : ValueNode(NodeType::int_value), value(value) {}
long getIntValue() override { return value; };
long getIntegerValue() override { return value; };
double getDoubleValue() override { return (double) value; };
std::string getStringValue() override { return std::to_string(value); }
long getDateValue() override { return value; };
bool getBooleanValue() override { return value != 0; };
};
struct DoubleValueNode : ValueNode {
double value;
DoubleValueNode(double value) : ValueNode(NodeType::float_value), value(value) {}
explicit DoubleValueNode(double value) : ValueNode(NodeType::float_value), value(value) {}
long getIntValue() override { return (long) value; };
long getIntegerValue() override { return (long) value; };
double getDoubleValue() override { return value; };
std::string getStringValue() override { return std::to_string(value); }
long getDateValue() override { return (long) value; };
bool getBooleanValue() override { return value != 0.0; };
};
struct StringValueNode : ValueNode {
std::string value;
StringValueNode(std::string value) : ValueNode(NodeType::string_value), value(value) {}
explicit StringValueNode(const std::string &value) : ValueNode(NodeType::string_value), value(value) {}
long getIntValue() override { return std::stoi(value); };
long getIntegerValue() override { return std::stoi(value); };
double getDoubleValue() override { return std::stod(value); };
std::string getStringValue() override { return value; };
long getDateValue() override { return Settings::string_to_date(value); };
bool getBooleanValue() override { return value == "true"; };
};
struct BooleanValueNode : ValueNode {
bool value;
explicit BooleanValueNode(bool value) : ValueNode(NodeType::bool_value), value(value) {}
long getIntegerValue() override { return (long) value; };
double getDoubleValue() override { return (double) value; };
std::string getStringValue() override { return value ? "true" : "false"; }
long getDateValue() override { return (long) value; };
bool getBooleanValue() override { return value; };
};
struct DatabaseValueNode : Node {
std::string col_name;
DatabaseValueNode(std::string name) : Node(NodeType::database_value), col_name(name) {}
explicit DatabaseValueNode(const std::string &name) : Node(NodeType::database_value), col_name(name) {}
};
enum class LogicalOperatorType {
@ -200,42 +247,45 @@ namespace usql {
std::vector<ColDefNode> cols_defs;
CreateTableNode(const std::string& name, std::vector<ColDefNode> defs) :
Node(NodeType::create_table), table_name(name), cols_defs(defs) {}
Node(NodeType::create_table), table_name(name), cols_defs(std::move(defs)) {}
};
struct InsertIntoTableNode : Node {
std::string table_name;
std::vector<ColNameNode> cols_names;
std::vector<DatabaseValueNode> cols_names;
std::vector<std::unique_ptr<Node>> cols_values;
InsertIntoTableNode(const std::string name, std::vector<ColNameNode> names, std::vector<std::unique_ptr<Node>> values) :
Node(NodeType::insert_into), table_name(name), cols_names(names), cols_values(std::move(values)) {}
InsertIntoTableNode(const 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)) {}
};
struct SelectFromTableNode : Node {
std::string table_name;
std::unique_ptr<std::vector<SelectColNode>> cols_names;
std::unique_ptr<Node> where;
std::vector<ColOrderNode> order_by;
OffsetLimitNode offset_limit;
bool distinct;
SelectFromTableNode(std::string name, std::unique_ptr<std::vector<SelectColNode>> names, std::unique_ptr<Node> where_clause) :
Node(NodeType::select_from), table_name(name), cols_names(std::move(names)), where(std::move(where_clause)) {}
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_) {}
};
struct CreateTableAsSelectNode : Node {
std::string table_name;
std::unique_ptr<Node> select_table;
CreateTableAsSelectNode(const std::string name, std::unique_ptr<Node> table) :
CreateTableAsSelectNode(const std::string& name, std::unique_ptr<Node> table) :
Node(NodeType::create_table_as_select), table_name(name), select_table(std::move(table)) {}
};
struct UpdateTableNode : Node {
std::string table_name;
std::vector<ColNameNode> cols_names;
std::vector<DatabaseValueNode> cols_names;
std::vector<std::unique_ptr<Node>> values;
std::unique_ptr<Node> where;
UpdateTableNode(std::string name, std::vector<ColNameNode> names, std::vector<std::unique_ptr<Node>> vals,
UpdateTableNode(const std::string &name, std::vector<DatabaseValueNode> names, std::vector<std::unique_ptr<Node>> vals,
std::unique_ptr<Node> where_clause) :
Node(NodeType::update_table), table_name(name), cols_names(names), values(std::move(vals)),
where(std::move(where_clause)) {}
@ -245,7 +295,7 @@ namespace usql {
std::string table_name;
std::string filename;
LoadIntoTableNode(const std::string name, std::string file) :
LoadIntoTableNode(const std::string& name, const std::string &file) :
Node(NodeType::load_table), table_name(name), filename(file) {}
};
@ -253,14 +303,14 @@ namespace usql {
std::string table_name;
std::string filename;
SaveTableNode(const std::string& name, std::string file) :
SaveTableNode(const std::string& name, const std::string &file) :
Node(NodeType::save_table), table_name(name), filename(file) {}
};
struct DropTableNode : Node {
std::string table_name;
DropTableNode(const std::string& name) : Node(NodeType::drop_table), table_name(name) {}
explicit DropTableNode(const std::string& name) : Node(NodeType::drop_table), table_name(name) {}
};
struct DeleteFromTableNode : Node {
@ -271,6 +321,21 @@ namespace usql {
Node(NodeType::delete_from), table_name(name), where(std::move(where_clause)) {}
};
struct SetNode : Node {
std::string name;
std::string value;
SetNode(const std::string& name_, const std::string& value_) :
Node(NodeType::set), name(name_), value(value_) {}
};
struct ShowNode : Node {
std::string name;
explicit ShowNode(const std::string& name_) : Node(NodeType::show), name(name_) {}
};
class Parser {
private:
@ -282,17 +347,24 @@ namespace usql {
private:
std::unique_ptr<Node> parse_create_table();
std::unique_ptr<Node> parse_drop_table();
std::unique_ptr<Node> parse_load_table();
std::unique_ptr<Node> parse_save_table();
std::unique_ptr<Node> parse_drop_table();
std::unique_ptr<Node> parse_set();
std::unique_ptr<Node> parse_show();
std::unique_ptr<Node> parse_insert_into_table();
std::unique_ptr<Node> parse_select_from_table();
std::unique_ptr<Node> parse_delete_from_table();
std::unique_ptr<Node> parse_update_table();
std::vector<ColOrderNode> parse_order_by_clause();
OffsetLimitNode parse_offset_limit_clause();
std::unique_ptr<Node> parse_where_clause();
std::unique_ptr<Node> parse_operand_node();
std::unique_ptr<Node> parse_expression();
std::unique_ptr<Node> parse_expression(std::unique_ptr<Node> left);
std::unique_ptr<Node> parse_value();
RelationalOperatorType parse_relational_operator();
LogicalOperatorType parse_logical_operator();
@ -300,8 +372,6 @@ namespace usql {
private:
Lexer m_lexer;
std::unique_ptr<Node> parse_relational_expression();
};
}
} // namespace

File diff suppressed because it is too large Load Diff

View File

@ -1,89 +1,177 @@
#include <ml_string.h>
#include "row.h"
namespace usql {
Row::Row(int cols_count) {
m_columns.reserve(cols_count);
for (int i = 0; i < cols_count; i++) {
m_columns.push_back(std::make_unique<ColNullValue>());
}
}
int ColNullValue::compare(ColValue &other) {
return other.isNull() ? 0 : -1; // null goes to end
}
Row::Row(const Row &other) {
m_columns.reserve(other.m_columns.size());
// TODO fixme this is nonsense
int ColIntegerValue::compare(ColValue &other) {
long r = m_integer - other.getIntValue();
return other.isNull() ? 1 : r > 0 ? 1 : r == 0 ? 0 : -1;
}
int ColDoubleValue::compare(ColValue &other) {
if (other.isNull()) return 1; // null goes to end
double c = m_double - other.getDoubleValue();
return c < 0 ? -1 : c == 0.0 ? 0 : 1;
}
ColStringValue & ColStringValue::operator=(ColStringValue other) {
std::swap(m_string, other.m_string);
return *this;
}
int ColStringValue::compare(ColValue &other) {
return other.isNull() ? 1 : m_string->compare(other.getStringValue()); // null goes to end
}
int ColDateValue::compare(ColValue &other) {
long r = m_date - other.getIntValue();
return other.isNull() ? 1 : r > 0 ? 1 : r == 0 ? 0 : -1;
}
int ColBooleanValue::compare(ColValue &other) {
if (other.isNull()) return 1; // null goes to end
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()) {
for (int i = 0; i < other.m_columns.size(); i++) {
m_columns.push_back(std::make_unique<ColNullValue>());
}
if (other[i].isNull())
continue; // for null NOP
for (int i = 0; i < other.m_columns.size(); i++) {
if (ColIntegerValue *other_v = dynamic_cast<ColIntegerValue *>(other.m_columns[i].get())) {
setColumnValue(i, other_v->getIntValue());
}
if (ColDoubleValue *other_v = dynamic_cast<ColDoubleValue *>(other.m_columns[i].get())) {
setColumnValue(i, other_v->getDoubleValue());
}
if (ColStringValue *other_v = dynamic_cast<ColStringValue *>(other.m_columns[i].get())) {
setColumnValue(i, other_v->getStringValue());
}
ColumnType col_type = other[i].getColType();
switch (col_type) {
case ColumnType::integer_type :
setIntColumnValue(i, other[i].getIntValue());
break;
case ColumnType::float_type :
setFloatColumnValue(i, other[i].getDoubleValue());
break;
case ColumnType::varchar_type :
setStringColumnValue(i, other[i].getStringValue());
break;
case ColumnType::date_type :
setDateColumnValue(i, other[i].getDateValue());
break;
case ColumnType::bool_type :
setBoolColumnValue(i, other[i].getBoolValue());
break;
default:
throw Exception("unsupported column type");
}
}
}
Row &Row::operator=(Row other) {
Row &Row::operator=(Row other) {
std::swap(m_columns, other.m_columns);
return *this;
}
void Row::setColumnNull(int col_index) {
m_columns[col_index] = std::make_unique<ColNullValue>();
}
void Row::setColumnValue(int col_index, long value) {
m_columns[col_index] = std::make_unique<ColIntegerValue>(value);
}
void Row::setColumnValue(int col_index, double value) {
m_columns[col_index] = std::make_unique<ColDoubleValue>(value);
}
void Row::setColumnValue(int col_index, const std::string &value) {
m_columns[col_index] = std::make_unique<ColStringValue>(value);
};
void Row::setColumnValue(ColDefNode *col_def, ColValue *col_value) {
if (!col_value->isNull()) {
if (col_def->type == ColumnType::integer_type)
setColumnValue(col_def->order, col_value->getIntValue());
else if (col_def->type == ColumnType::float_type)
setColumnValue(col_def->order, col_value->getDoubleValue());
else if (col_def->type == ColumnType::varchar_type)
setColumnValue(col_def->order, col_value->getStringValue());
} else {
setColumnNull(col_def->order);
}
}
void Row::setColumnValue(ColDefNode *col_def, ValueNode *col_value) {
if (!col_value->isNull()) {
if (col_def->type == ColumnType::integer_type)
setColumnValue(col_def->order, col_value->getIntValue());
else if (col_def->type == ColumnType::float_type)
setColumnValue(col_def->order, col_value->getDoubleValue());
else if (col_def->type == ColumnType::varchar_type)
setColumnValue(col_def->order, col_value->getStringValue());
} else {
setColumnNull(col_def->order);
}
}
void Row::print() {
for (int ci = 0; ci < m_columns.size(); ci++) {
if (ci > 0) std::cout << ",";
auto v = m_columns[ci]->getStringValue();
std::cout << v;
}
std::cout << std::endl;
}
}
void Row::setColumnNull(int col_index) {
m_columns[col_index] = ColNullValue();
}
void Row::setIntColumnValue(int col_index, long value) {
m_columns[col_index] = ColIntegerValue(value);
}
void Row::setFloatColumnValue(int col_index, double value) {
m_columns[col_index] = ColDoubleValue(value);
}
void Row::setStringColumnValue(int col_index, const std::string &value) {
m_columns[col_index] = ColStringValue(value);
}
void Row::setDateColumnValue(int col_index, long value) {
m_columns[col_index] = ColDateValue(value);
}
void Row::setDateColumnValue(int col_index, const std::string &value) {
m_columns[col_index] = ColDateValue(Settings::string_to_date(value));
}
void Row::setBoolColumnValue(int col_index, bool value) {
m_columns[col_index] = ColBooleanValue(value);
}
void Row::setBoolColumnValue(int col_index, const std::string &value) {
bool v = (value == "Y" || value == "1");
m_columns[col_index] = ColBooleanValue(v);
}
void Row::setColumnValue(ColDefNode *col_def, ColValue &col_value) {
if (!col_value.isNull()) {
if (col_def->type == ColumnType::integer_type)
setIntColumnValue(col_def->order, col_value.getIntValue());
else if (col_def->type == ColumnType::float_type)
setFloatColumnValue(col_def->order, col_value.getDoubleValue());
else if (col_def->type == ColumnType::varchar_type)
setStringColumnValue(col_def->order, col_value.getStringValue());
else if (col_def->type == ColumnType::date_type)
setDateColumnValue(col_def->order, col_value.getDateValue());
else if (col_def->type == ColumnType::bool_type)
setBoolColumnValue(col_def->order, col_value.getBoolValue());
} else {
setColumnNull(col_def->order);
}
}
void Row::setColumnValue(ColDefNode *col_def, ValueNode *col_value) {
if (!col_value->isNull()) {
if (col_def->type == ColumnType::integer_type)
setIntColumnValue(col_def->order, col_value->getIntegerValue());
else if (col_def->type == ColumnType::float_type)
setFloatColumnValue(col_def->order, col_value->getDoubleValue());
else if (col_def->type == ColumnType::varchar_type)
setStringColumnValue(col_def->order, col_value->getStringValue());
else if (col_def->type == ColumnType::date_type)
setIntColumnValue(col_def->order, col_value->getDateValue());
else if (col_def->type == ColumnType::bool_type)
setBoolColumnValue(col_def->order, col_value->getBooleanValue());
else
throw Exception("unsupported data type");
} else {
setColumnNull(col_def->order);
}
}
int Row::compare(const Row &other) const {
for (int ci = 0; ci < m_columns.size(); ci++) {
int cmp = this->operator[](ci).compare(other[ci]);
if (cmp != 0) return cmp;
}
return 0;
}
void Row::print(const std::vector<ColDefNode> &col_defs) {
std::string out{"| "};
for (int ci = 0; ci < m_columns.size(); ci++) {
auto & col_def = col_defs[ci];
int col_size = print_get_column_size(col_def);
if (col_def.type==ColumnType::integer_type || col_def.type==ColumnType::float_type || col_def.type==ColumnType::bool_type)
out.append(string_padd(this->operator[](ci).getStringValue(), col_size, ' ', false) + " | ");
else
out.append(string_padd(this->operator[](ci).getStringValue(), col_size, ' ', true) + " | ");
}
std::cout << out << std::endl;
}
int Row::print_get_column_size(const ColDefNode &col_def) {
int col_size = col_def.type == ColumnType::varchar_type ? col_def.length :
col_def.type == ColumnType::float_type ? 16 : 10;
return col_size;
}
} // namespace

View File

@ -2,94 +2,174 @@
#include "exception.h"
#include "parser.h"
#include "settings.h"
#include <variant>
#include <vector>
namespace usql {
struct ColValue {
virtual bool isNull() { return false; };
virtual long getIntValue() { throw Exception("Not supported"); };
virtual double getDoubleValue() { throw Exception("Not supported"); };
virtual std::string getStringValue() { throw Exception("Not supported"); };
virtual ColumnType getColType() = 0;
virtual long getIntValue() = 0;
virtual double getDoubleValue() = 0;
virtual std::string getStringValue() = 0;
virtual long getDateValue() = 0;
virtual bool getBoolValue() = 0;
virtual int compare(ColValue &other) = 0;
virtual ~ColValue() = default;
};
struct ColNullValue : ColValue {
bool isNull() override { return true; };
ColumnType getColType() override { throw Exception("getColType not supported on ColNullValue"); }
long getIntValue() override { throw Exception("getIntValue not supported on ColNullValue"); };
double getDoubleValue() override { throw Exception("getDoubleValue not supported on ColNullValue"); };
std::string getStringValue() override { return "null"; };
long getDateValue() override { throw Exception("getDateValue not supported on ColNullValue"); };
bool getBoolValue() override { throw Exception("getDateValue not supported on ColNullValue"); };
virtual bool isNull() { return true; };
virtual std::string getStringValue() { return "null"; };
int compare(ColValue &other) override;
};
struct ColIntegerValue : ColValue {
ColIntegerValue(long value) : m_integer(value) {};
explicit ColIntegerValue(long value) : m_integer(value) {};
ColIntegerValue(const ColIntegerValue &other) : m_integer(other.m_integer) {};
virtual long getIntValue() { return m_integer; };
virtual double getDoubleValue() { return (double) m_integer; };
virtual std::string getStringValue() { return std::to_string(m_integer); };
ColumnType getColType() override { return ColumnType::integer_type; };
long getIntValue() override { return m_integer; };
double getDoubleValue() override { return (double) m_integer; };
std::string getStringValue() override { return std::to_string(m_integer); };
long getDateValue() override { return m_integer; };
bool getBoolValue() override { throw Exception("Not supported on ColIntegerValue"); };
int m_integer;
int compare(ColValue &other) override;
long m_integer;
};
struct ColDoubleValue : ColValue {
ColDoubleValue(double value) : m_double(value) {};
explicit ColDoubleValue(double value) : m_double(value) {};
ColDoubleValue(const ColDoubleValue &other) : m_double(other.m_double) {}
virtual long getIntValue() { return (long) m_double; };
virtual double getDoubleValue() { return m_double; };
virtual std::string getStringValue() { return std::to_string(m_double); };
ColumnType getColType() override { return ColumnType::float_type; };
long getIntValue() override { return (long) m_double; };
double getDoubleValue() override { return m_double; };
std::string getStringValue() override { return Settings::double_to_string(m_double); };
long getDateValue() override { return (long) m_double; };
bool getBoolValue() override { throw Exception("Not supported on ColDoubleValue"); };
int compare(ColValue &other) override;
double m_double;
};
struct ColStringValue : ColValue {
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 std::string value) : m_string(value) {};
ColStringValue(const ColStringValue &other) : m_string(other.m_string) {};
ColStringValue & operator=(ColStringValue other);
virtual long getIntValue() { return std::stoi(m_string); };
virtual double getDoubleValue() { return std::stod(m_string); };
virtual std::string getStringValue() { return m_string; };
ColumnType getColType() override { return ColumnType::varchar_type; };
long getIntValue() override { return std::stoi(*m_string); };
double getDoubleValue() override { return std::stod(*m_string); };
std::string getStringValue() override { return *m_string; };
long getDateValue() override { return std::stoi(*m_string); };
bool getBoolValue() override { throw Exception("Not supported on ColStringValue"); };
std::string m_string;
int compare(ColValue &other) override;
std::unique_ptr<std::string> m_string;
};
struct ColDateValue : ColValue {
explicit ColDateValue(long value) : m_date(value) {};
ColDateValue(const ColDateValue &other) : m_date(other.m_date) {};
ColumnType getColType() override { return ColumnType::date_type; };
long getIntValue() override { return m_date; };
double getDoubleValue() override { return (double) m_date; };
std::string getStringValue() override { return Settings::date_to_string(m_date); };
long getDateValue() override { return m_date; };
bool getBoolValue() override { throw Exception("Not supported on ColDateValue"); };
int compare(ColValue &other) override;
long m_date; // seconds since epoch for now
};
struct ColBooleanValue : ColValue {
explicit ColBooleanValue(bool value) : m_bool(value) {};
ColBooleanValue(const ColBooleanValue &other) : m_bool(other.m_bool) {};
ColumnType getColType() override { return ColumnType::bool_type; };
long getIntValue() override { return (long) m_bool; };
double getDoubleValue() override { return (double) m_bool; };
std::string getStringValue() override { return m_bool ? "Y" : "N"; };
long getDateValue() override { throw Exception("Not supported on ColBooleanValue"); };
bool getBoolValue() override { return m_bool; };
int compare(ColValue &other) override;
bool m_bool;
};
class Row {
public:
Row(int cols_count);
explicit Row(int cols_count) : m_columns(cols_count) {};
Row(const Row &other);
Row &operator=(Row other);
bool operator==(const Row &other) const {return this->compare(other) == 0; };
void setColumnNull(int col_index);
void setColumnValue(int col_index, long value);
void setColumnValue(int col_index, double value);
void setColumnValue(int col_index, const std::string &value);
void setColumnValue(ColDefNode *col_def, ColValue *col_value);
void setIntColumnValue(int col_index, long value);
void setFloatColumnValue(int col_index, double value);
void setStringColumnValue(int col_index, const std::string &value);
void setDateColumnValue(int col_index, long value);
void setDateColumnValue(int col_index, const std::string &value);
void setBoolColumnValue(int col_index, bool value);
void setBoolColumnValue(int col_index, const std::string &value);
void setColumnValue(ColDefNode *col_def, ColValue &col_value);
void setColumnValue(ColDefNode *col_def, ValueNode *col_value);
ColValue &operator[](int i) {
return *m_columns[i];
ColValue &operator[](int i) const {
auto type_index = m_columns[i].index();
switch (type_index) {
case 0:
return (ColValue &) *std::get_if<ColNullValue>(&m_columns[i]);
case 1:
return (ColValue &) *std::get_if<ColIntegerValue>(&m_columns[i]);
case 2:
return (ColValue &) *std::get_if<ColDoubleValue>(&m_columns[i]);
case 3:
return (ColValue &) *std::get_if<ColStringValue>(&m_columns[i]);
case 4:
return (ColValue &) *std::get_if<ColDateValue>(&m_columns[i]);
case 5:
return (ColValue &) *std::get_if<ColBooleanValue>(&m_columns[i]);
}
throw Exception("should not happen");
}
ColValue * ith_column(int i) const {
return m_columns[i].get();
}
void print();
int compare(const Row &other) const;
void print(const std::vector<ColDefNode> &col_defs);
static int print_get_column_size(const ColDefNode &col_def);
private:
std::vector<std::unique_ptr<ColValue>> m_columns;
// xx std::vector<std::unique_ptr<ColValue>> m_columns;
std::vector<std::variant<ColNullValue, ColIntegerValue, ColDoubleValue, ColStringValue, ColDateValue, ColBooleanValue>> m_columns;
};
} // namespace

51
usql/settings.cpp Normal file
View File

@ -0,0 +1,51 @@
#include "settings.h"
#include "exception.h"
#include "ml_date.h"
namespace usql {
std::vector<std::pair<std::string, std::string>> Settings::m_settings =
{ std::make_pair("DATE_FORMAT", "%Y-%m-%d"),
std::make_pair("BOOL_TRUE_LITERAL", "Y"),
std::make_pair("BOOL_FALSE_LITERAL", "N"),
std::make_pair("DOUBLE_FORMAT", "%.2f") };
long Settings::string_to_date(const std::string &datestr) {
return ::string_to_date(datestr, get_setting("DATE_FORMAT"));
}
std::string Settings::date_to_string(long date) {
return ::date_to_string(date, get_setting("DATE_FORMAT"));
}
std::string Settings::double_to_string(double d) {
char buffer[32];
int r, buf_size = 32;
r = snprintf(buffer, buf_size, get_setting("DOUBLE_FORMAT").c_str(), d);
if (r > 0 && r < buf_size) return std::string(buffer);
return "ERROR";
}
std::string Settings::get_setting(const std::string &name) {
for(const auto& pair : m_settings) {
if (pair.first == name) return pair.second;
}
throw Exception("unsupported setting name: " + name);
}
void Settings::set_setting(const std::string &name, const std::string &value) {
for (auto it = begin(m_settings); it != end(m_settings); ++it) {
if (it->first == name) {
*it = std::make_pair(name, value);
return;
}
}
throw Exception("unsupported setting name: " + name);
}
} // namespace

23
usql/settings.h Normal file
View File

@ -0,0 +1,23 @@
#pragma once
#include <string>
#include <map>
namespace usql {
class Settings {
public:
static void set_setting(const std::string &name, const std::string &value);
static std::string get_setting(const std::string &name);
static long string_to_date(const std::string &datestr);
static std::string date_to_string(long date);
static std::string double_to_string(double d);
// TODO add bool_to_string and use it in ColBooleanValue
private:
static std::vector<std::pair<std::string, std::string>> m_settings;
};
} // namespace

View File

@ -1,29 +1,49 @@
#include "table.h"
#include "csvreader.h"
#include "ml_string.h"
#include <fstream>
#include <algorithm>
namespace usql {
Table::Table(const std::string name, const std::vector<ColDefNode> columns) {
Table::Table(const std::string& name, const std::vector<ColDefNode>& columns) {
m_name = name;
m_col_defs = columns;
m_rows.clear();
m_rows.reserve(256);
}
Table::Table(const Table &other) {
m_name = other.m_name;
m_col_defs = other.m_col_defs;
m_rows.reserve(other.m_rows.size());
for(const Row& orig_row : other.m_rows) {
commit_copy_of_row(orig_row);
}
}
ColDefNode Table::get_column_def(const std::string &col_name) {
auto name_cmp = [col_name](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);
if (col_def != std::end(m_col_defs)) {
return *col_def;
} else {
throw Exception("column not exists (" + col_name + ")");
throw Exception("column does not exist (" + col_name + ")");
}
}
ColDefNode Table::get_column_def(int col_index) {
if (col_index >= 0 && col_index < columns_count()) {
return m_col_defs[col_index];
} else {
throw Exception("column with this index does not exists (" + std::to_string(col_index) + ")");
}
}
Row Table::create_empty_row() {
return Row(columns_count());
Row& Table::create_empty_row() {
m_rows.emplace_back(columns_count());
return m_rows.back();
}
std::string Table::csv_string() {
@ -35,14 +55,14 @@ std::string Table::csv_string() {
}
// rows
for (auto it = m_rows.begin(); it != m_rows.end(); ++it) {
for (auto & m_row : m_rows) {
std::string csv_line{"\n"};
for(int i = 0; i < m_col_defs.size(); i++) {
if (i > 0) csv_line += ",";
auto col = it->ith_column(i);
if (!col->isNull()) {
csv_line += col->getStringValue(); // TODO handle enclosing commas etc
auto & col = m_row[i];
if (!col.isNull()) {
csv_line += col.getStringValue(); // TODO handle enclosing commas etc
}
}
out_string += csv_line;
@ -52,87 +72,150 @@ std::string Table::csv_string() {
}
int Table::load_csv_string(const std::string &content) {
int row_cnt = 0;
CsvReader csvparser{};
auto csv = csvparser.parseCSV(content);
std::vector<ColDefNode> &colDefs = m_col_defs;
for (auto it = csv.begin() + 1; it != csv.end(); ++it) {
std::vector<std::string> csv_line = *it;
// prepare empty new_row
Row new_row = create_empty_row();
// copy values
for (size_t i = 0; i < columns_count(); i++) {
ColDefNode col_def = get_column_def(colDefs[i].name);
// TODO validate value
if (col_def.type == ColumnType::integer_type) {
new_row.setColumnValue(col_def.order, std::stol(csv_line[i]));
} else if (col_def.type == ColumnType::float_type) {
new_row.setColumnValue(col_def.order, std::stof(csv_line[i]));
} else {
new_row.setColumnValue(col_def.order, csv_line[i]);
}
}
// append new_row
add_row(new_row);
row_cnt++;
}
CsvReader csvparser{};
int row_cnt = csvparser.parseCSV2(content, colDefs, *this);
return row_cnt;
}
int Table::load_csv_file(const std::string &filename) {
std::vector<ColDefNode> &colDefs = m_col_defs;
// allocate enough space
int line_size = 128;
std::ifstream in(filename, std::ifstream::ate | std::ifstream::binary);
auto file_size = in.tellg();
std::ifstream infile(filename);
if (infile.good()) {
std::string sLine;
std::getline(infile, sLine);
line_size = (int)sLine.size();
}
infile.close();
if (file_size > 0) {
auto new_size = m_rows.size() + int(file_size / line_size * 1.20);
m_rows.reserve(new_size);
}
// load rows
CsvReader csvparser{};
int row_cnt = csvparser.parseCSV(filename, colDefs, *this);
return row_cnt;
}
void Table::create_row_from_vector(const std::vector<ColDefNode> &colDefs, const std::vector<std::string> &csv_line) {
// prepare empty new_row
Row& new_row = create_empty_row();
// copy values
for (int i = 0; i < std::min<int>(columns_count(), csv_line.size()); i++) {
const ColDefNode & col_def = colDefs[i];
if (csv_line[i].empty()) {
new_row.setColumnNull(col_def.order);
} else if (col_def.type == ColumnType::integer_type) {
new_row.setIntColumnValue(col_def.order, string_to_long(csv_line[i]));
} else if (col_def.type == ColumnType::float_type) {
new_row.setFloatColumnValue(col_def.order, string_to_double(csv_line[i]));
} else if (col_def.type == ColumnType::varchar_type) {
new_row.setStringColumnValue(col_def.order, csv_line[i]);
} else if (col_def.type == ColumnType::date_type) {
new_row.setDateColumnValue(col_def.order, csv_line[i]);
} else if (col_def.type == ColumnType::bool_type) {
new_row.setBoolColumnValue(col_def.order, csv_line[i]);
} else
throw Exception("unsupported column type");
}
// append new_row
commit_row(new_row);
}
double Table::string_to_double(const std::string &s) {
try {
return std::stod(s);
} catch (std::invalid_argument &e) {
throw Exception("error parsing as double: " + s);
}
}
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() {
std::cout << "** " << m_name << " **" << std::endl;
for (auto row : m_rows) {
row.print();
std::string out{"| "};
std::string out2{"+-"};
for(const auto& col_def : m_col_defs) {
int col_size = Row::print_get_column_size(col_def);
if (col_def.type==ColumnType::integer_type || col_def.type==ColumnType::float_type || col_def.type==ColumnType::bool_type)
out.append(string_padd(col_def.name, col_size, ' ', false) + " | ");
else
out.append(string_padd(col_def.name, col_size, ' ', true) + " | ");
out2.append(string_padd("-", col_size, '-', true) + "-+ ");
}
// std::cout << "** " << m_name << " **" << std::endl;
std::cout << out << std::endl;
std::cout << out2 << std::endl;
for(auto& row : m_rows) {
row.print(m_col_defs);
}
std::cout << std::endl;
}
Table::Table(const Table &other) {
m_name = other.m_name;
m_col_defs = other.m_col_defs;
for(const Row& orig_row : other.m_rows) {
add_copy_of_row(orig_row);
}
}
void Table::add_row(const Row &row) {
void Table::commit_row(const Row &row) {
try {
validate_row(row);
m_rows.push_back(row);
} catch (Exception &e) {
m_rows.erase(m_rows.end() - 1);
throw e;
}
}
void Table::add_copy_of_row(const Row &row) {
Row new_row = create_empty_row();
void Table::commit_copy_of_row(const Row &row) {
Row& new_row = create_empty_row();
for(int i = 0; i < m_col_defs.size(); i++) {
ColValue *ct = row.ith_column(i);
ColValue &ct = row[i];
if (ct->isNull()) {
if (ct.isNull()) {
new_row.setColumnNull(i);
} else {
if (m_col_defs[i].type == ColumnType::integer_type) {
new_row.setColumnValue(i, row.ith_column(i)->getIntValue());
new_row.setIntColumnValue(i, row[i].getIntValue());
} else if (m_col_defs[i].type == ColumnType::float_type) {
new_row.setColumnValue(i, row.ith_column(i)->getDoubleValue());
new_row.setFloatColumnValue(i, row[i].getDoubleValue());
} else if (m_col_defs[i].type == ColumnType::varchar_type) {
new_row.setColumnValue(i, row.ith_column(i)->getStringValue());
}
new_row.setStringColumnValue(i, row[i].getStringValue());
} else if (m_col_defs[i].type == ColumnType::date_type) {
new_row.setDateColumnValue(i, row[i].getDateValue());
} else if (m_col_defs[i].type == ColumnType::bool_type) {
new_row.setBoolColumnValue(i, row[i].getBoolValue());
} else
throw Exception("unsupported column type");
}
}
validate_row(new_row);
m_rows.push_back(new_row);
}
void Table::validate_column(const ColDefNode *col_def, ValueNode *col_val) {
if (col_def->null == false && col_val->isNull()) {
if (!col_def->null && col_val->isNull()) {
throw Exception("Column " + col_def->name + " cannot be null");
}
if (col_def->type == ColumnType::varchar_type && !col_val->isNull() && col_val->getStringValue().size() > col_def->length) {
@ -140,19 +223,19 @@ void Table::validate_column(const ColDefNode *col_def, ValueNode *col_val) {
}
}
void Table::validate_column(const ColDefNode *col_def, ColValue *col_val) {
if (col_def->null == false && col_val->isNull()) {
void Table::validate_column(const ColDefNode *col_def, ColValue &col_val) {
if (!col_def->null && col_val.isNull()) {
throw Exception("Column " + col_def->name + " cannot be null");
}
if (col_def->type == ColumnType::varchar_type && !col_val->isNull() && col_val->getStringValue().size() > col_def->length) {
throw Exception("Column value of " + col_def->name + " is too long (" + col_val->getStringValue() + ")");
if (col_def->type == ColumnType::varchar_type && !col_val.isNull() && col_val.getStringValue().size() > col_def->length) {
throw Exception("Column value of " + col_def->name + " is too long (" + col_val.getStringValue() + ")");
}
}
void Table::validate_row(const Row &row) {
for(int i = 0; i < m_col_defs.size(); i++) {
ColDefNode col_def = m_col_defs[i];
ColValue *col_val = row.ith_column(i);
ColValue &col_val = row[i];
validate_column(&col_def, col_val);
}

View File

@ -4,35 +4,42 @@
#include "row.h"
#include <vector>
#include <list>
namespace usql {
struct Table {
Table(const Table &other);
Table(const std::string name, const std::vector<ColDefNode> columns);
Table(const std::string& name, const std::vector<ColDefNode>& columns);
ColDefNode get_column_def(const std::string &col_name);
ColDefNode get_column_def(int col_index);
int columns_count() const { return 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(); };
Row create_empty_row(); // TODO this means unnecessary copying
void add_row(const Row &row);
void add_copy_of_row(const Row &row);
Row& create_empty_row();
void commit_row(const Row &row);
void commit_copy_of_row(const Row &row);
void validate_column(const ColDefNode *col_def, ValueNode *col_val);
void validate_column(const ColDefNode *col_def, ColValue *col_val);
static void validate_column(const ColDefNode *col_def, ValueNode *col_val);
static void validate_column(const ColDefNode *col_def, ColValue &col_val);
void validate_row(const Row &row);
std::string csv_string();
int load_csv_string(const std::string &content);
int load_csv_file(const std::string &filename);
void print();
std::string m_name;
std::vector<ColDefNode> m_col_defs;
std::list<Row> m_rows;
std::vector<Row> m_rows;
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);
};
}

View File

@ -1,6 +1,7 @@
#include "usql.h"
#include "exception.h"
#include "ml_date.h"
#include "ml_string.h"
#include <algorithm>
#include <fstream>
@ -25,6 +26,8 @@ std::unique_ptr<Table> USql::execute(Node &node) {
return execute_create_table(static_cast<CreateTableNode &>(node));
case NodeType::create_table_as_select:
return execute_create_table_as_table(static_cast<CreateTableAsSelectNode &>(node));
case NodeType::drop_table:
return execute_drop(static_cast<DropTableNode &>(node));
case NodeType::insert_into:
return execute_insert_into_table(static_cast<InsertIntoTableNode &>(node));
case NodeType::select_from:
@ -37,8 +40,10 @@ std::unique_ptr<Table> USql::execute(Node &node) {
return execute_load(static_cast<LoadIntoTableNode &>(node));
case NodeType::save_table:
return execute_save(static_cast<SaveTableNode &>(node));
case NodeType::drop_table:
return execute_drop(static_cast<DropTableNode &>(node));
case NodeType::set:
return execute_set(static_cast<SetNode &>(node));
case NodeType::show:
return execute_show(static_cast<ShowNode &>(node));
default:
return create_stmt_result_table(-1, "unknown statement", 0);
}
@ -68,7 +73,7 @@ std::unique_ptr<Table> USql::execute_create_table_as_table(CreateTableAsSelectNo
// must be here, if rows are put into new_table, they are lost during m_tables.push_table
Table *table = find_table(node.table_name);
for( Row& orig_row : select->m_rows) {
table->add_copy_of_row(orig_row);
table->commit_copy_of_row(orig_row);
}
select.release(); // is it correct? hoping not to release select table here and then when releasing CreateTableAsSelectNode
@ -83,12 +88,14 @@ std::unique_ptr<Table> USql::execute_load(LoadIntoTableNode &node) {
Table *table_def = find_table(node.table_name);
// read data
std::ifstream ifs(node.filename);
std::string content((std::istreambuf_iterator<char>(ifs)),
(std::istreambuf_iterator<char>()));
// std::ifstream ifs(node.filename);
// std::string content((std::istreambuf_iterator<char>(ifs)),
// (std::istreambuf_iterator<char>()));
// load rows
auto rows_cnt = table_def->load_csv_string(content);
// auto rows_cnt = table_def->load_csv_string(content);
auto rows_cnt = table_def->load_csv_file(node.filename);
return create_stmt_result_table(0, "load succeeded", rows_cnt);
}
@ -106,7 +113,7 @@ std::unique_ptr<Table> USql::execute_save(SaveTableNode &node) {
file << csv_string;
file.close();
return create_stmt_result_table(0, "save succeeded", 0);
return create_stmt_result_table(0, "save succeeded", table_def->rows_count());
}
std::unique_ptr<Table> USql::execute_drop(DropTableNode &node) {
@ -121,25 +128,36 @@ std::unique_ptr<Table> USql::execute_drop(DropTableNode &node) {
throw Exception("table not found (" + node.table_name + ")");
}
std::unique_ptr<Table> USql::execute_insert_into_table(InsertIntoTableNode &node) {
// TODO check column names.size = values.size
std::unique_ptr<Table> USql::execute_set(SetNode &node) {
Settings::set_setting(node.name, node.value);
return create_stmt_result_table(0, "set succeeded", 1);
}
std::unique_ptr<Table> USql::execute_show(ShowNode &node) {
std::string value = Settings::get_setting(node.name);
return create_stmt_result_table(0, "show succeeded: " + value, 1);
}
std::unique_ptr<Table> USql::execute_insert_into_table(InsertIntoTableNode &node) {
// find table
Table *table_def = find_table(node.table_name);
if (node.cols_names.size() != node.cols_values.size())
throw Exception("Incorrect number of values");
// prepare empty new_row
Row new_row = table_def->create_empty_row();
Row& new_row = table_def->create_empty_row();
// copy values
for (size_t i = 0; i < node.cols_names.size(); i++) {
ColDefNode col_def = table_def->get_column_def(node.cols_names[i].name);
ColDefNode col_def = table_def->get_column_def(node.cols_names[i].col_name);
auto col_value = eval_value_node(table_def, new_row, node.cols_values[i].get());
new_row.setColumnValue(&col_def, col_value.get());
}
// append new_row
table_def->add_row(new_row);
table_def->commit_row(new_row);
return create_stmt_result_table(0, "insert succeeded", 1);
}
@ -149,14 +167,22 @@ std::unique_ptr<Table> USql::execute_select(SelectFromTableNode &node) {
// find source table
Table *table = find_table(node.table_name);
// expand *
if (node.cols_names->size()==1 && node.cols_names->operator[](0).name == "*") {
node.cols_names->clear();
node.cols_names->reserve(table->columns_count());
for(const auto& col : table->m_col_defs) {
node.cols_names->emplace_back(SelectColNode{std::make_unique<DatabaseValueNode>(col.name), col.name});
}
}
// 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++) {
auto [ src_tbl_col_index, rst_tbl_col_def ] = get_column_definition(table,
&node.cols_names->operator[](i), i);
auto [src_tbl_col_index, rst_tbl_col_def] = get_column_definition(table, &node.cols_names->operator[](i), i);
source_table_col_index.push_back(src_tbl_col_index);
result_tbl_col_defs.push_back(rst_tbl_col_def);
@ -168,72 +194,153 @@ std::unique_ptr<Table> USql::execute_select(SelectFromTableNode &node) {
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
Row new_row = result->create_empty_row();
// prepare empty row and copy column values
Row& new_row = result->create_empty_row();
// copy column values
for (auto idx = 0; idx < result->columns_count(); idx++) {
auto row_col_index = source_table_col_index[idx];
if (row_col_index == -1) { // TODO introduce constant here
auto evaluated_value = eval_value_node(table, *row, node.cols_names->operator[](
idx).value.get());
if (row_col_index == FUNCTION_CALL) {
auto evaluated_value = eval_value_node(table, *row, node.cols_names->operator[](idx).value.get());
ValueNode *col_value = evaluated_value.get();
new_row.setColumnValue(&result_tbl_col_defs[idx], col_value);
} else {
ColValue *col_value = row->ith_column(row_col_index);
ColValue &col_value = row->operator[](row_col_index);
new_row.setColumnValue(&result_tbl_col_defs[idx], col_value);
}
}
// add row to result
result->m_rows.push_back(new_row);
result->commit_row(new_row);
}
}
return std::move(result);
execute_distinct(node, result.get());
execute_order_by(node, table, result.get());
execute_offset_limit(node.offset_limit, result.get());
return result;
}
void USql::execute_distinct(SelectFromTableNode &node, Table *result) {
if (!node.distinct) return;
auto compare_rows = [](const Row &a, const Row &b) { return a.compare(b) >= 0; };
std::sort(result->m_rows.begin(), result->m_rows.end(), compare_rows);
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) {
if (node.order_by.empty()) return;
auto compare_rows = [&node, &result](const Row &a, const Row &b) {
for(const auto& order_by_col_def : node.order_by) {
// TODO validate index
ColDefNode col_def = result->get_column_def(order_by_col_def.col_index - 1);
ColValue &a_val = a[col_def.order];
ColValue &b_val = b[col_def.order];
int compare = a_val.compare(b_val);
if (compare < 0) return order_by_col_def.ascending;
if (compare > 0) return !order_by_col_def.ascending;
}
return false;
};
std::sort(result->m_rows.begin(), result->m_rows.end(), compare_rows);
}
void USql::execute_offset_limit(OffsetLimitNode &node, Table *result) {
if (node.offset > 0)
result->m_rows.erase(result->m_rows.begin(),
result->rows_count() > node.offset ? result->m_rows.begin() + node.offset : result->m_rows.end());
if (node.limit > 0 && node.limit < result->rows_count())
result->m_rows.erase(result->m_rows.begin() + node.limit, result->m_rows.end());
}
std::tuple<int, ColDefNode> USql::get_column_definition(Table *table, SelectColNode *select_col_node, int col_order ) {
std::string new_col_name = select_col_node->name;
return get_node_definition(table, select_col_node->value.get(), select_col_node->name, col_order );
}
if (select_col_node->value->node_type == NodeType::column_name) {
ColDefNode src_cdef = table->get_column_def(new_col_name);
ColDefNode cdef = ColDefNode{new_col_name, src_cdef.type, col_order, src_cdef.length, src_cdef.null};
return std::make_tuple(src_cdef.order, cdef);
std::tuple<int, ColDefNode> USql::get_node_definition(Table *table, Node * node, const std::string & col_name, int col_order ) {
if (node->node_type == NodeType::database_value) {
auto dbval_node = static_cast<DatabaseValueNode *>(node);
} else if (select_col_node->value->node_type == NodeType::function) {
auto node = static_cast<FunctionNode *>(select_col_node->value.get());
ColDefNode src_col_def = table->get_column_def(dbval_node->col_name);
ColDefNode col_def = ColDefNode{col_name, src_col_def.type, col_order, src_col_def.length, src_col_def.null};
return std::make_tuple(src_col_def.order, col_def);
if (node->function == "to_string") {
ColDefNode cdef = ColDefNode{new_col_name, ColumnType::varchar_type, col_order, 64, true};
return std::make_tuple(-1, cdef);
} else if (node->function == "to_date") {
ColDefNode cdef = ColDefNode{new_col_name, ColumnType::integer_type, col_order, 1, true};
return std::make_tuple(-1, cdef);
} else if (node->node_type == NodeType::function) {
auto func_node = static_cast<FunctionNode *>(node);
if (func_node->function == "to_string") {
ColDefNode col_def = ColDefNode{col_name, ColumnType::varchar_type, col_order, 32, true};
return std::make_tuple(-1, col_def);
} else if (func_node->function == "to_date") {
ColDefNode col_def = ColDefNode{col_name, ColumnType::integer_type, col_order, 1, true};
return std::make_tuple(-1, col_def);
} else if (func_node->function == "pp") {
ColDefNode col_def = ColDefNode{col_name, ColumnType::varchar_type, col_order, 10, true};
return std::make_tuple(-1, col_def);
}
throw Exception("Unsupported function");
} else if (node->node_type == NodeType::arithmetical_operator) {
auto ari_node = static_cast<ArithmeticalOperatorNode *>(node);
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 );
ColumnType col_type; // TODO handle varchar and it len
if (left_tbl_col_def.type==ColumnType::float_type || right_tbl_col_def.type==ColumnType::float_type)
col_type = ColumnType::float_type;
else
col_type = ColumnType::integer_type;
ColDefNode col_def = ColDefNode{col_name, col_type, col_order, 1, true};
return std::make_tuple(-1, col_def);
} else if (node->node_type == NodeType::logical_operator) {
ColDefNode col_def = ColDefNode{col_name, ColumnType::bool_type, col_order, 1, true};
return std::make_tuple(-1, col_def);
} else if (node->node_type == NodeType::int_value) {
ColDefNode col_def = ColDefNode{col_name, ColumnType::integer_type, col_order, 1, true};
return std::make_tuple(-1, col_def);
} else if (node->node_type == NodeType::float_value) {
ColDefNode col_def = ColDefNode{col_name, ColumnType::float_type, col_order, 1, true};
return std::make_tuple(-1, col_def);
} else if (node->node_type == NodeType::string_value) {
// TODO right len
ColDefNode col_def = ColDefNode{col_name, ColumnType::varchar_type, col_order, 64, true};
return std::make_tuple(-1, col_def);
}
throw Exception("Unsupported node type");
}
std::unique_ptr<Table> USql::execute_delete(DeleteFromTableNode &node) {
// find source table
Table *table = find_table(node.table_name);
// execute access plan
int affected_rows = 0;
auto it = table->m_rows.begin();
for (; it != table->m_rows.end();) {
if (eval_where(node.where.get(), table, *it)) {
it = table->m_rows.erase(it);
affected_rows++;
} else {
++it;
}
}
auto affected_rows = table->rows_count();
table->m_rows.erase(
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);
}
@ -250,12 +357,13 @@ std::unique_ptr<Table> USql::execute_update(UpdateTableNode &node) {
if (eval_where(node.where.get(), table, *row)) {
int i = 0;
for (const auto& col : node.cols_names) {
ColDefNode col_def = table->get_column_def(col.name); // TODO cache it like in select
// TODO cache it like in select
ColDefNode col_def = table->get_column_def(col.col_name);
std::unique_ptr<ValueNode> new_val = eval_arithmetic_operator(col_def.type,
static_cast<ArithmeticalOperatorNode &>(*node.values[i]),
table, *row);
table->validate_column(&col_def, new_val.get());
usql::Table::validate_column(&col_def, new_val.get());
row->setColumnValue(&col_def, new_val.get());
i++;
}
@ -268,8 +376,8 @@ std::unique_ptr<Table> USql::execute_update(UpdateTableNode &node) {
}
bool USql::eval_where(Node *where, Table *table, Row &row) const {
switch (where->node_type) { // no where clause
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
@ -284,22 +392,27 @@ bool USql::eval_where(Node *where, Table *table, Row &row) const {
}
bool USql::eval_relational_operator(const RelationalOperatorNode &filter, Table *table, Row &row) const {
bool USql::eval_relational_operator(const RelationalOperatorNode &filter, Table *table, Row &row) {
std::unique_ptr<ValueNode> left_value = eval_value_node(table, row, filter.left.get());
std::unique_ptr<ValueNode> right_value = eval_value_node(table, row, filter.right.get());
double comparator;
if (left_value->node_type == NodeType::int_value && right_value->node_type == NodeType::int_value) {
comparator = left_value->getIntValue() - right_value->getIntValue();
comparator = left_value->getIntegerValue() - right_value->getIntegerValue();
} 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::float_value)) {
comparator = left_value->getDoubleValue() - right_value->getDoubleValue();
} else if (left_value->node_type == NodeType::string_value || right_value->node_type == NodeType::string_value) {
comparator = left_value->getStringValue().compare(right_value->getStringValue());
} else if (left_value->node_type == NodeType::bool_value && right_value->node_type == NodeType::bool_value) {
bool bl = left_value->getBooleanValue();
bool br = right_value->getBooleanValue();
comparator = bl == br ? 0 : 1;
// date values are essentially int values so handled above
} else {
// TODO throw exception
throw Exception("Undefined combination of types");
}
switch (filter.op) {
@ -322,16 +435,16 @@ bool USql::eval_relational_operator(const RelationalOperatorNode &filter, Table
std::unique_ptr<ValueNode> USql::eval_value_node(Table *table, Row &row, Node *node) {
if (node->node_type == NodeType::database_value || node->node_type == NodeType::column_name) { // TODO sjednotit
if (node->node_type == NodeType::database_value) {
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) {
} 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);
} else if (node->node_type == NodeType::function) {
return eval_function_value_node(table, row, node);
} else if (node->node_type == NodeType::null_value) {
return std::make_unique<NullValueNode>();
} else if (node->node_type == NodeType::arithmetical_operator) {
return eval_arithmetic_operator(ColumnType::float_type, static_cast<ArithmeticalOperatorNode &>(*node), table, row);
}
throw Exception("unsupported node type");
}
@ -340,17 +453,22 @@ std::unique_ptr<ValueNode> USql::eval_value_node(Table *table, Row &row, Node *n
std::unique_ptr<ValueNode> USql::eval_database_value_node(Table *table, Row &row, Node *node) {
auto *dvl = static_cast<DatabaseValueNode *>(node);
ColDefNode col_def = table->get_column_def( dvl->col_name); // TODO optimize it to just get this def once
auto db_value = row.ith_column(col_def.order);
ColValue &db_value = row[col_def.order];
if (db_value.isNull())
return std::make_unique<NullValueNode>();
if (col_def.type == ColumnType::integer_type)
return std::make_unique<IntValueNode>(db_value.getIntValue());
if (col_def.type == ColumnType::float_type)
return std::make_unique<DoubleValueNode>(db_value.getDoubleValue());
if (col_def.type == ColumnType::varchar_type)
return std::make_unique<StringValueNode>(db_value.getStringValue());
if (col_def.type == ColumnType::bool_type)
return std::make_unique<BooleanValueNode>(db_value.getBoolValue());
if (col_def.type == ColumnType::date_type)
return std::make_unique<IntValueNode>(db_value.getIntValue());
if (col_def.type == ColumnType::integer_type) {
return std::make_unique<IntValueNode>(db_value->getIntValue());
}
if (col_def.type == ColumnType::float_type) {
return std::make_unique<DoubleValueNode>(db_value->getDoubleValue());
}
if (col_def.type == ColumnType::varchar_type) {
return std::make_unique<StringValueNode>(db_value->getStringValue());
}
throw Exception("unknown database value type");
}
@ -368,7 +486,11 @@ std::unique_ptr<ValueNode> USql::eval_literal_value_node(Table *table, Row &row,
auto *ivl = static_cast<StringValueNode *>(node);
return std::make_unique<StringValueNode>(ivl->value);
} else if (node->node_type == NodeType::bool_value) {
auto *ivl = static_cast<BooleanValueNode *>(node);
return std::make_unique<BooleanValueNode>(ivl->value);
}
// Date has no it's own value node (it is passed around as string)
throw Exception("invalid type");
}
@ -382,6 +504,10 @@ std::unique_ptr<ValueNode> USql::eval_function_value_node(Table *table, Row &row
evaluatedPars.push_back(eval_value_node(table, row, param.get()));
}
// at this moment no functions without parameter(s) or first param can be null
if (evaluatedPars.empty() || evaluatedPars[0]->isNull())
return std::make_unique<NullValueNode>();
// TODO use some enum
if (fnc->function == "lower") {
std::string str = evaluatedPars[0]->getStringValue();
@ -398,31 +524,71 @@ std::unique_ptr<ValueNode> USql::eval_function_value_node(Table *table, Row &row
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);
return std::make_unique<IntValueNode>(epoch_time); // No DateValueNode for now
}
if (fnc->function == "to_string") {
long date = evaluatedPars[0]->getIntValue();
long date = evaluatedPars[0]->getDateValue();
std::string format = evaluatedPars[1]->getStringValue();
std::string formated_date = date_to_string(date, format);
return std::make_unique<StringValueNode>(formated_date);
std::string formatted_date = date_to_string(date, format);
return std::make_unique<StringValueNode>(formatted_date);
}
if (fnc->function == "pp") {
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[16] {0};
double value = parsed_value->getDoubleValue();
if (format == "100%")
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());
}
throw Exception("invalid function");
}
bool USql::eval_logical_operator(LogicalOperatorNode &node, Table *pTable, Row &row) const {
bool left = eval_relational_operator(static_cast<const RelationalOperatorNode &>(*node.left), pTable, 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);
if ((node.op == LogicalOperatorType::and_operator && !left) || (node.op == LogicalOperatorType::or_operator && left))
return left;
bool right = eval_relational_operator(static_cast<const RelationalOperatorNode &>(*node.right), pTable, row);
//bool right = eval_relational_operator(static_cast<const RelationalOperatorNode &>(*node.right), pTable, row);
bool right = eval_where(&(*node.right), pTable, row);
return right;
}
std::unique_ptr<ValueNode> USql::eval_arithmetic_operator(ColumnType outType, ArithmeticalOperatorNode &node, Table *table, Row &row) const {
std::unique_ptr<ValueNode> USql::eval_arithmetic_operator(ColumnType outType, ArithmeticalOperatorNode &node, Table *table, Row &row) {
if (node.op == ArithmeticalOperatorType::copy_value) {
return eval_value_node(table, row, node.left.get());
}
@ -430,6 +596,9 @@ std::unique_ptr<ValueNode> USql::eval_arithmetic_operator(ColumnType outType, Ar
std::unique_ptr<ValueNode> left = eval_value_node(table, row, node.left.get());
std::unique_ptr<ValueNode> right = eval_value_node(table, row, node.right.get());
if (left->isNull() || right->isNull())
return std::make_unique<NullValueNode>();
if (outType == ColumnType::float_type) {
double l = ((ValueNode *) left.get())->getDoubleValue();
double r = ((ValueNode *) right.get())->getDoubleValue();
@ -447,8 +616,8 @@ std::unique_ptr<ValueNode> USql::eval_arithmetic_operator(ColumnType outType, Ar
}
} else if (outType == ColumnType::integer_type) {
long l = ((ValueNode *) left.get())->getIntValue();
long r = ((ValueNode *) right.get())->getIntValue();
long l = ((ValueNode *) left.get())->getIntegerValue();
long r = ((ValueNode *) right.get())->getIntegerValue();
switch (node.op) {
case ArithmeticalOperatorType::plus_operator:
return std::make_unique<IntValueNode>(l + r);
@ -472,26 +641,27 @@ std::unique_ptr<ValueNode> USql::eval_arithmetic_operator(ColumnType outType, Ar
throw Exception("implement me!!");
}
}
// TODO date node should support addition and subtraction
throw Exception("implement me!!");
}
std::unique_ptr<Table> USql::create_stmt_result_table(long code, const std::string &text, long affected_rows) {
std::unique_ptr<Table> USql::create_stmt_result_table(long code, const std::string &text, size_t affected_rows) {
std::vector<ColDefNode> result_tbl_col_defs{};
result_tbl_col_defs.push_back(ColDefNode("code", ColumnType::integer_type, 0, 1, false));
result_tbl_col_defs.push_back(ColDefNode("desc", ColumnType::varchar_type, 1, 255, false));
result_tbl_col_defs.push_back(ColDefNode("affected_rows", ColumnType::integer_type, 0, 1, true));
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("affected_rows", ColumnType::integer_type, 0, 1, true);
auto table_def = std::make_unique<Table>("result", result_tbl_col_defs);
Row new_row = table_def->create_empty_row();
new_row.setColumnValue(0, code);
new_row.setColumnValue(1, text);
new_row.setColumnValue(2, affected_rows);
table_def->add_row(new_row);
Row& new_row = table_def->create_empty_row();
new_row.setIntColumnValue(0, code);
new_row.setStringColumnValue(1, text.size() <= 48 ? text : text.substr(0,48));
new_row.setIntColumnValue(2, (long)affected_rows);
table_def->commit_row(new_row);
return std::move(table_def);
return table_def;
}
@ -514,4 +684,5 @@ void USql::check_table_not_exists(const std::string &name) {
}
}
} // namespace

View File

@ -13,7 +13,6 @@ class USql {
public:
USql() = default;
std::unique_ptr<Table> execute(const std::string &command);
private:
@ -24,6 +23,8 @@ private:
std::unique_ptr<Table> execute_load(LoadIntoTableNode &node);
std::unique_ptr<Table> execute_save(SaveTableNode &node);
std::unique_ptr<Table> execute_drop(DropTableNode &node);
static std::unique_ptr<Table> execute_set(SetNode &node);
static std::unique_ptr<Table> execute_show(ShowNode &node);
std::unique_ptr<Table> execute_insert_into_table(InsertIntoTableNode &node);
std::unique_ptr<Table> execute_select(SelectFromTableNode &node);
@ -32,7 +33,7 @@ private:
private:
bool eval_where(Node *where, Table *table, Row &row) const;
static bool eval_where(Node *where, Table *table, Row &row) ;
static std::unique_ptr<ValueNode> eval_value_node(Table *table, Row &row, Node *node);
static std::unique_ptr<ValueNode> eval_database_value_node(Table *table, Row &row, Node *node);
@ -40,13 +41,14 @@ private:
static std::unique_ptr<ValueNode> eval_function_value_node(Table *table, Row &row, Node *node);
bool eval_relational_operator(const RelationalOperatorNode &filter, Table *table, Row &row) const;
bool eval_logical_operator(LogicalOperatorNode &node, Table *pTable, Row &row) const;
std::unique_ptr<ValueNode> eval_arithmetic_operator(ColumnType outType, ArithmeticalOperatorNode &node, Table *table, Row &row) const;
static bool eval_relational_operator(const RelationalOperatorNode &filter, Table *table, Row &row) ;
static bool eval_logical_operator(LogicalOperatorNode &node, Table *pTable, Row &row) ;
static std::unique_ptr<ValueNode> eval_arithmetic_operator(ColumnType outType, ArithmeticalOperatorNode &node, Table *table, Row &row) ;
static std::unique_ptr<Table> create_stmt_result_table(long code, const std::string &text, long affected_rows);
static std::unique_ptr<Table> create_stmt_result_table(long code, const std::string &text, size_t affected_rows);
static std::tuple<int, ColDefNode> get_column_definition(Table *table, SelectColNode *select_col_node, 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);
void check_table_not_exists(const std::string &name);
@ -54,6 +56,10 @@ private:
private:
Parser m_parser;
std::list<Table> m_tables;
static void execute_distinct(SelectFromTableNode &node, Table *result) ;
static void execute_order_by(SelectFromTableNode &node, Table *table, Table *result) ;
static void execute_offset_limit(OffsetLimitNode &node, Table *result) ;
};
} // namespace

View File

@ -1,7 +1,7 @@
#!/bin/sh
gcc -std=c99 -c -O2 -o linenoise.o clib/linenoise.c
c++ -c -O2 -I/usr/local/opt/openssl/include -Iclib --std=c++17 ml.cpp ml_io.cpp ml_date.cpp ml_string.cpp ml_util.cpp ml_profiler.cpp ml_usql.cpp clib/json11.cpp clib/csvparser.cpp clib/sslclient.cpp clib/printf.cpp usql/exception.cpp usql/lexer.cpp usql/parser.cpp usql/usql.cpp usql/table.cpp usql/table.h usql/row.cpp usql/csvreader.cpp usql/usql.cpp
c++ -c -O2 -I/usr/local/opt/openssl/include -Iclib -I./ --std=c++17 ml.cpp ml_io.cpp ml_date.cpp ml_string.cpp ml_util.cpp ml_profiler.cpp ml_usql.cpp clib/json11.cpp clib/csvparser.cpp clib/sslclient.cpp clib/printf.cpp usql/exception.cpp usql/lexer.cpp usql/parser.cpp usql/usql.cpp usql/table.cpp usql/table.h usql/row.cpp usql/csvreader.cpp usql/usql.cpp usql/settings.cpp
c++ -o ml -O2 -L/usr/local/lib -L/usr/local/opt/openssl/lib -lm -lstdc++ -lcrypto -lssl *.o
cp stdlib/*.lsp /usr/local/var/mlisp/

View File

@ -24,7 +24,7 @@ fi
echo "Building ml"
ssh -p 5333 root@46.28.109.184 "cd /tmp/mlisp; gcc -std=c99 -c -O2 -o linenoise.o clib/linenoise.c"
ssh -p 5333 root@46.28.109.184 "cd /tmp/mlisp; c++ -c -O2 -I/usr/local/opt/openssl/include -Iclib --std=c++17 ml.cpp ml_io.cpp ml_date.cpp ml_string.cpp ml_util.cpp ml_profiler.cpp ml_usql.cpp clib/json11.cpp clib/csvparser.cpp clib/sslclient.cpp clib/printf.cpp usql/exception.cpp usql/lexer.cpp usql/parser.cpp usql/usql.cpp usql/table.cpp usql/table.h usql/row.cpp usql/csvreader.cpp usql/usql.cpp"
ssh -p 5333 root@46.28.109.184 "cd /tmp/mlisp; c++ -c -O2 -I/usr/local/opt/openssl/include -Iclib -I./ --std=c++17 ml.cpp ml_io.cpp ml_date.cpp ml_string.cpp ml_util.cpp ml_profiler.cpp ml_usql.cpp clib/json11.cpp clib/csvparser.cpp clib/sslclient.cpp clib/printf.cpp usql/exception.cpp usql/lexer.cpp usql/parser.cpp usql/usql.cpp usql/table.cpp usql/table.h usql/row.cpp usql/csvreader.cpp usql/usql.cpp usql/settings.cpp"
ssh -p 5333 root@46.28.109.184 "cd /tmp/mlisp; c++ -o ml -O2 -L/usr/local/lib -L/usr/local/opt/openssl/lib -lm -lstdc++ -lcrypto -lssl *.o"

34
utils/resetCLion.sh Normal file
View File

@ -0,0 +1,34 @@
#!/bin/bash
#https://github.com/PythonicNinja/jetbrains-reset-trial-mac-osx/blob/master/runme.sh
for product in CLion; do
echo "Closing $product"
ps aux | grep -i MacOs/$product | cut -d " " -f 5 | xargs kill -9
echo "Resetting trial period for $product"
echo "removing evaluation key..."
rm -rf ~/Library/Preferences/$product*/eval
# Above path not working on latest version. Fixed below
rm -rf ~/Library/Application\ Support/JetBrains/$product*/eval
echo "removing all evlsprt properties in options.xml..."
sed -i '' '/evlsprt/d' ~/Library/Preferences/$product*/options/other.xml
# Above path not working on latest version. Fixed below
sed -i '' '/evlsprt/d' ~/Library/Application\ Support/JetBrains/$product*/options/other.xml
echo
done
echo "removing additional plist files..."
rm -f ~/Library/Preferences/com.apple.java.util.prefs.plist
rm -f ~/Library/Preferences/com.jetbrains.*.plist
rm -f ~/Library/Preferences/jetbrains.*.*.plist
echo "restarting cfprefsd"
killall cfprefsd
echo
echo "That's it, enjoy ;)"