better table print, order by/offset/limit improvements
This commit is contained in:
parent
34e432d031
commit
70c036f08c
|
|
@ -70,7 +70,9 @@
|
||||||
"mutex": "cpp",
|
"mutex": "cpp",
|
||||||
"ostream": "cpp",
|
"ostream": "cpp",
|
||||||
"streambuf": "cpp",
|
"streambuf": "cpp",
|
||||||
"thread": "cpp"
|
"thread": "cpp",
|
||||||
|
"compare": "cpp",
|
||||||
|
"complex": "cpp"
|
||||||
},
|
},
|
||||||
"C_Cpp.intelliSenseEngineFallback": "Disabled",
|
"C_Cpp.intelliSenseEngineFallback": "Disabled",
|
||||||
"cmake.configureOnOpen": true,
|
"cmake.configureOnOpen": true,
|
||||||
|
|
|
||||||
|
|
@ -11,8 +11,10 @@ project(usql)
|
||||||
|
|
||||||
set(PROJECT_NAME usql)
|
set(PROJECT_NAME usql)
|
||||||
|
|
||||||
|
include_directories(${CMAKE_SOURCE_DIR}/clib ${CMAKE_SOURCE_DIR})
|
||||||
|
|
||||||
set(SOURCE
|
set(SOURCE
|
||||||
exception.cpp lexer.cpp parser.cpp usql.cpp main.cpp table.cpp table.h row.cpp row.h csvreader.cpp csvreader.h ml_date.cpp)
|
exception.cpp lexer.cpp parser.cpp usql.cpp main.cpp table.cpp table.h row.cpp row.h csvreader.cpp csvreader.h ml_date.cpp clib/ml_string.cpp)
|
||||||
|
|
||||||
add_executable(${PROJECT_NAME} ${SOURCE})
|
add_executable(${PROJECT_NAME} ${SOURCE})
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,16 @@
|
||||||
|
|
||||||
### TODO
|
### TODO
|
||||||
- support for order by, offset, limit
|
- support for distinct
|
||||||
|
- command line interface
|
||||||
|
- better table print
|
||||||
|
- support for order by, offset, limit (allow column name in order by, validate)
|
||||||
|
- add count min and max functions, eg aggregate functions
|
||||||
|
- support for uniqueue indexes
|
||||||
- support for btree indexes
|
- support for btree indexes
|
||||||
- support for parenthesis
|
- support for parenthesis
|
||||||
|
- functions rtrim, ltrim, rpad, lpad
|
||||||
- support for *
|
- support for *
|
||||||
- add pipe | token
|
- add pipe | token
|
||||||
- add count min and max functions, eg aggregate functions
|
|
||||||
- maybe to create iterator on table
|
- maybe to create iterator on table
|
||||||
- add exceptions and rename it to UsqlException
|
- add exceptions and rename it to UsqlException
|
||||||
- class members should have prefix m_
|
- class members should have prefix m_
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,104 @@
|
||||||
|
|
||||||
|
#include "ml_string.h"
|
||||||
|
|
||||||
|
|
||||||
|
// Replace a substring with a replacement string in a source string
|
||||||
|
void replace_substring(std::string &src, const std::string &substr, const std::string &replacement) {
|
||||||
|
size_t i = 0;
|
||||||
|
for (i = src.find(substr, i); i != std::string::npos; i = src.find(substr, i)) {
|
||||||
|
src.replace(i, substr.size(), replacement);
|
||||||
|
i += replacement.size();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Returns true if where contains regex
|
||||||
|
bool regexp_search(const std::string &where, const std::string ®ex_str) {
|
||||||
|
// online tester https://www.regextester.com/97722
|
||||||
|
std::regex regex(regex_str);
|
||||||
|
std::smatch match;
|
||||||
|
|
||||||
|
if (std::regex_search(where, match, regex)) {
|
||||||
|
// std::cout << "matches for '" << where << "'\n";
|
||||||
|
// std::cout << "Prefix: '" << match.prefix() << "'\n";
|
||||||
|
// for (size_t i = 0; i < match.size(); ++i)
|
||||||
|
// std::cout << i << ": " << match[i] << '\n';
|
||||||
|
// std::cout << "Suffix: '" << match.suffix() << "\'\n\n";
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<std::string> regexp_strsplit(const std::string &string_to_split, const std::string &rgx_str) {
|
||||||
|
std::vector<std::string> elems;
|
||||||
|
|
||||||
|
std::regex rgx(rgx_str);
|
||||||
|
std::sregex_token_iterator iter(string_to_split.begin(), string_to_split.end(), rgx, -1);
|
||||||
|
std::sregex_token_iterator end;
|
||||||
|
|
||||||
|
for (; iter != end; ++iter)
|
||||||
|
elems.push_back(*iter);
|
||||||
|
|
||||||
|
return elems;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string string_lucase(std::string s, const std::string &strcase) {
|
||||||
|
if (strcase == "upper")
|
||||||
|
std::transform(s.begin(), s.end(),s.begin(), ::toupper);
|
||||||
|
|
||||||
|
if (strcase == "lower")
|
||||||
|
std::transform(s.begin(), s.end(),s.begin(), ::tolower);
|
||||||
|
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string string_trim(std::string s, const std::string &chars_to_trim, const std::string &rltrim) {
|
||||||
|
if (rltrim == "ltrim" || rltrim == "trim")
|
||||||
|
s.erase(0, s.find_first_not_of(chars_to_trim));
|
||||||
|
|
||||||
|
if (rltrim == "rtrim" || rltrim == "trim")
|
||||||
|
s.erase(s.find_last_not_of(chars_to_trim)+1);
|
||||||
|
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string string_padd(const std::string &str, int pad_len, char fill_char, bool from_right) {
|
||||||
|
int str_len = str.length();
|
||||||
|
|
||||||
|
if (str_len == pad_len)
|
||||||
|
return str;
|
||||||
|
else if (str_len > pad_len) {
|
||||||
|
return (from_right ? str.substr(0, pad_len) : str.substr(str_len - pad_len, std::string::npos));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (from_right)
|
||||||
|
return str + std::string(pad_len - str.size(), fill_char);
|
||||||
|
else
|
||||||
|
return std::string(pad_len - str.size(), fill_char) + str;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
std::string string_substring(const std::string & str, long pos, long count) {
|
||||||
|
size_t start_pos = pos;
|
||||||
|
|
||||||
|
if (pos < 0) {
|
||||||
|
start_pos = str.size() - abs(pos);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( (start_pos >= str.size()) || (count < 1) || (start_pos < 0) ) {
|
||||||
|
throw std::invalid_argument("Invalid parameter(s) for string-substr.");
|
||||||
|
}
|
||||||
|
|
||||||
|
return str.substr(start_pos, count);
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t string_find_substr(const std::string & str, const std::string & pattern, long pos) {
|
||||||
|
if (pos >= str.size()) {
|
||||||
|
throw std::invalid_argument("Invalid parameter(s) for string-find.");
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t p = str.find(pattern, pos);
|
||||||
|
|
||||||
|
return p != str.npos ? p : -1;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,22 @@
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <regex>
|
||||||
|
|
||||||
|
void replace_substring(std::string &src, const std::string &substr, const std::string &replacement);
|
||||||
|
|
||||||
|
// Returns true if where contains regex
|
||||||
|
bool regexp_search(const std::string &where, const std::string ®ex_str);
|
||||||
|
|
||||||
|
std::vector<std::string> regexp_strsplit(const std::string &string_to_split, const std::string &rgx_str);
|
||||||
|
|
||||||
|
std::string string_lucase(std::string s, const std::string &strcase);
|
||||||
|
|
||||||
|
std::string string_trim(std::string s, const std::string &chars_to_trim, const std::string &rltrim);
|
||||||
|
|
||||||
|
std::string string_padd(const std::string & str, int pad_len, char fill_char, bool from_right);
|
||||||
|
|
||||||
|
std::string string_substring(const std::string & str, long pos, long count);
|
||||||
|
|
||||||
|
size_t string_find_substr(const std::string & str, const std::string & pattern, long pos);
|
||||||
10
lexer.cpp
10
lexer.cpp
|
|
@ -155,6 +155,10 @@ namespace usql {
|
||||||
return TokenType::keyword_order;
|
return TokenType::keyword_order;
|
||||||
if (token == "by")
|
if (token == "by")
|
||||||
return TokenType::keyword_by;
|
return TokenType::keyword_by;
|
||||||
|
if (token == "offset")
|
||||||
|
return TokenType::keyword_offset;
|
||||||
|
if (token == "limit")
|
||||||
|
return TokenType::keyword_limit;
|
||||||
if (token == "asc")
|
if (token == "asc")
|
||||||
return TokenType::keyword_asc;
|
return TokenType::keyword_asc;
|
||||||
if (token == "desc")
|
if (token == "desc")
|
||||||
|
|
@ -324,6 +328,12 @@ namespace usql {
|
||||||
case TokenType::keyword_by:
|
case TokenType::keyword_by:
|
||||||
txt = "by";
|
txt = "by";
|
||||||
break;
|
break;
|
||||||
|
case TokenType::keyword_offset:
|
||||||
|
txt = "offset";
|
||||||
|
break;
|
||||||
|
case TokenType::keyword_limit:
|
||||||
|
txt = "limit";
|
||||||
|
break;
|
||||||
case TokenType::keyword_asc:
|
case TokenType::keyword_asc:
|
||||||
txt = "asc";
|
txt = "asc";
|
||||||
break;
|
break;
|
||||||
|
|
|
||||||
2
lexer.h
2
lexer.h
|
|
@ -27,6 +27,8 @@ namespace usql {
|
||||||
keyword_where,
|
keyword_where,
|
||||||
keyword_order,
|
keyword_order,
|
||||||
keyword_by,
|
keyword_by,
|
||||||
|
keyword_offset,
|
||||||
|
keyword_limit,
|
||||||
keyword_asc,
|
keyword_asc,
|
||||||
keyword_desc,
|
keyword_desc,
|
||||||
keyword_delete,
|
keyword_delete,
|
||||||
|
|
|
||||||
12
main.cpp
12
main.cpp
|
|
@ -17,12 +17,12 @@ int main(int argc, char *argv[]) {
|
||||||
"insert into a (i, s) values(5, 'five')",
|
"insert into a (i, s) values(5, 'five')",
|
||||||
"insert into a (i, s) values(to_date('20.12.1973', '%d.%m.%Y'), 'six')",
|
"insert into a (i, s) values(to_date('20.12.1973', '%d.%m.%Y'), 'six')",
|
||||||
"save table a into '/tmp/a.csv'",
|
"save table a into '/tmp/a.csv'",
|
||||||
"select i, s from a where i > 2 order by 1 desc"
|
"select i, s from a where i > 2 order by 1 desc offset 1 limit 1",
|
||||||
// "select i, s from a where i = 1",
|
// "select i, s from a where i = 1",
|
||||||
// "select i, s from a where s = 'two'",
|
// "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 <= 3 and s = 'one'",
|
||||||
// "select i, s from a where i > 0",
|
// "select i, s from a where i > 0",
|
||||||
// "delete from a where i = 4",
|
"delete from a where i = 4",
|
||||||
// "select i, s from a where i > 0",
|
// "select i, s from a where i > 0",
|
||||||
// "update a set f = 9.99 where i = 3",
|
// "update a set f = 9.99 where i = 3",
|
||||||
// "select i, s, f from a where i = 3",
|
// "select i, s, f from a where i = 3",
|
||||||
|
|
@ -37,10 +37,10 @@ int main(int argc, char *argv[]) {
|
||||||
// "drop table x",
|
// "drop table x",
|
||||||
// "select i, s, f from a where i > 300",
|
// "select i, s, f from a where i > 300",
|
||||||
// "select i, to_string(i, '%d.%m.%Y'), s, f from a where i > 300",
|
// "select i, to_string(i, '%d.%m.%Y'), s, f from a where i > 300",
|
||||||
// "create table prices (datetime integer, symbol varchar(8), prev_close float, open float, price float, change float, change_prct varchar(16))",
|
"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'",
|
"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 %')",
|
// "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'"
|
"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"
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -52,7 +52,7 @@ int main(int argc, char *argv[]) {
|
||||||
time_point<high_resolution_clock> end_time = high_resolution_clock::now();
|
time_point<high_resolution_clock> end_time = high_resolution_clock::now();
|
||||||
|
|
||||||
std::cout << command << std::endl;
|
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();
|
result->print();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
39
parser.cpp
39
parser.cpp
|
|
@ -235,12 +235,12 @@ namespace usql {
|
||||||
|
|
||||||
std::unique_ptr<Node> where_node = parse_where_clause();
|
std::unique_ptr<Node> where_node = parse_where_clause();
|
||||||
|
|
||||||
std::vector<ColOrderNode> orderby_node = parse_orderby_clause();
|
std::vector<ColOrderNode> orderby_node = parse_order_by_clause();
|
||||||
|
|
||||||
// if (m_lexer.tokenType() == TokenType::keyword_offset) {}
|
OffsetLimitNode offsetlimit_node = parse_offset_limit_clause();
|
||||||
// if (m_lexer.tokenType() == TokenType::keyword_limit) {}
|
|
||||||
|
|
||||||
return std::make_unique<SelectFromTableNode>(table_name, std::move(cols), std::move(where_node), orderby_node);
|
|
||||||
|
return std::make_unique<SelectFromTableNode>(table_name, std::move(cols), std::move(where_node), orderby_node, offsetlimit_node);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::unique_ptr<Node> Parser::parse_delete_from_table() {
|
std::unique_ptr<Node> Parser::parse_delete_from_table() {
|
||||||
|
|
@ -312,7 +312,7 @@ namespace usql {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
std::vector<ColOrderNode> Parser::parse_orderby_clause() {
|
std::vector<ColOrderNode> Parser::parse_order_by_clause() {
|
||||||
std::vector<ColOrderNode> order_cols;
|
std::vector<ColOrderNode> order_cols;
|
||||||
|
|
||||||
if (m_lexer.tokenType() == TokenType::keyword_order) {
|
if (m_lexer.tokenType() == TokenType::keyword_order) {
|
||||||
|
|
@ -330,7 +330,7 @@ namespace usql {
|
||||||
col_index = std::stoi(tokenString);
|
col_index = std::stoi(tokenString);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
throw Exception("column index alloved in order by clause at this moment");
|
throw Exception("column index allowed in order by clause at this moment");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (m_lexer.tokenType() == TokenType::keyword_asc) {
|
if (m_lexer.tokenType() == TokenType::keyword_asc) {
|
||||||
|
|
@ -344,12 +344,37 @@ namespace usql {
|
||||||
|
|
||||||
m_lexer.skipTokenOptional(TokenType::comma);
|
m_lexer.skipTokenOptional(TokenType::comma);
|
||||||
|
|
||||||
} while (m_lexer.tokenType() != TokenType::eof); // && m_lexer.tokenType() != TokenType::keyword_offset && m_lexer.tokenType() != TokenType::keyword_limit);
|
} while (m_lexer.tokenType() != TokenType::eof && m_lexer.tokenType() != TokenType::keyword_offset && m_lexer.tokenType() != TokenType::keyword_limit);
|
||||||
}
|
}
|
||||||
|
|
||||||
return order_cols;
|
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_relational_expression() {
|
std::unique_ptr<Node> Parser::parse_relational_expression() {
|
||||||
auto left = parse_operand_node();
|
auto left = parse_operand_node();
|
||||||
auto operation = parse_relational_operator();
|
auto operation = parse_relational_operator();
|
||||||
|
|
|
||||||
19
parser.h
19
parser.h
|
|
@ -34,6 +34,7 @@ namespace usql {
|
||||||
save_table,
|
save_table,
|
||||||
drop_table,
|
drop_table,
|
||||||
column_name,
|
column_name,
|
||||||
|
offset_limit,
|
||||||
column_order,
|
column_order,
|
||||||
column_value,
|
column_value,
|
||||||
function,
|
function,
|
||||||
|
|
@ -62,6 +63,16 @@ namespace usql {
|
||||||
ColOrderNode(int index, bool asc) : Node(NodeType::column_name), col_name(""), col_index(index), ascending(asc) {}
|
ColOrderNode(int index, bool asc) : Node(NodeType::column_name), 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 {
|
struct SelectColNode : Node {
|
||||||
std::unique_ptr<Node> value;
|
std::unique_ptr<Node> value;
|
||||||
std::string name;
|
std::string name;
|
||||||
|
|
@ -227,9 +238,10 @@ namespace usql {
|
||||||
std::unique_ptr<std::vector<SelectColNode>> cols_names;
|
std::unique_ptr<std::vector<SelectColNode>> cols_names;
|
||||||
std::unique_ptr<Node> where;
|
std::unique_ptr<Node> where;
|
||||||
std::vector<ColOrderNode> order_by;
|
std::vector<ColOrderNode> order_by;
|
||||||
|
OffsetLimitNode offset_limit;
|
||||||
|
|
||||||
SelectFromTableNode(std::string name, std::unique_ptr<std::vector<SelectColNode>> names, std::unique_ptr<Node> where_clause, std::vector<ColOrderNode> orderby) :
|
SelectFromTableNode(std::string name, std::unique_ptr<std::vector<SelectColNode>> names, std::unique_ptr<Node> where_clause, std::vector<ColOrderNode> orderby, OffsetLimitNode offlim) :
|
||||||
Node(NodeType::select_from), table_name(name), cols_names(std::move(names)), where(std::move(where_clause)), order_by(orderby) {}
|
Node(NodeType::select_from), table_name(name), cols_names(std::move(names)), where(std::move(where_clause)), order_by(orderby), offset_limit(offlim) {}
|
||||||
};
|
};
|
||||||
|
|
||||||
struct CreateTableAsSelectNode : Node {
|
struct CreateTableAsSelectNode : Node {
|
||||||
|
|
@ -303,7 +315,8 @@ namespace usql {
|
||||||
std::unique_ptr<Node> parse_update_table();
|
std::unique_ptr<Node> parse_update_table();
|
||||||
|
|
||||||
std::unique_ptr<Node> parse_where_clause();
|
std::unique_ptr<Node> parse_where_clause();
|
||||||
std::vector<ColOrderNode> parse_orderby_clause();
|
std::vector<ColOrderNode> parse_order_by_clause();
|
||||||
|
OffsetLimitNode parse_offset_limit_clause();
|
||||||
|
|
||||||
std::unique_ptr<Node> parse_operand_node();
|
std::unique_ptr<Node> parse_operand_node();
|
||||||
std::unique_ptr<Node> parse_value();
|
std::unique_ptr<Node> parse_value();
|
||||||
|
|
|
||||||
2762
prices.csv
2762
prices.csv
File diff suppressed because it is too large
Load Diff
14
row.cpp
14
row.cpp
|
|
@ -77,13 +77,17 @@ namespace usql {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Row::print() {
|
void Row::print(const std::vector<int> & col_char_sizes) {
|
||||||
|
std::string out{"| "};
|
||||||
|
|
||||||
for (int ci = 0; ci < m_columns.size(); ci++) {
|
for (int ci = 0; ci < m_columns.size(); ci++) {
|
||||||
if (ci > 0) std::cout << ",";
|
auto value = m_columns[ci]->getStringValue();
|
||||||
auto v = m_columns[ci]->getStringValue();
|
|
||||||
std::cout << v;
|
// TODO use string functions
|
||||||
|
out.append(value + std::string(col_char_sizes[ci] - value.size(), ' ') + " | ");
|
||||||
}
|
}
|
||||||
std::cout << std::endl;
|
|
||||||
|
std::cout << out << std::endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
2
row.h
2
row.h
|
|
@ -86,7 +86,7 @@ namespace usql {
|
||||||
return m_columns[i].get();
|
return m_columns[i].get();
|
||||||
}
|
}
|
||||||
|
|
||||||
void print();
|
void print(const std::vector<int> & col_char_sizes);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::vector<std::unique_ptr<ColValue>> m_columns;
|
std::vector<std::unique_ptr<ColValue>> m_columns;
|
||||||
|
|
|
||||||
40
table.cpp
40
table.cpp
|
|
@ -1,6 +1,7 @@
|
||||||
|
|
||||||
#include "table.h"
|
#include "table.h"
|
||||||
#include "csvreader.h"
|
#include "csvreader.h"
|
||||||
|
#include "ml_string.h"
|
||||||
|
|
||||||
|
|
||||||
namespace usql {
|
namespace usql {
|
||||||
|
|
@ -8,7 +9,16 @@ 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_name = name;
|
||||||
m_col_defs = columns;
|
m_col_defs = columns;
|
||||||
m_rows.clear();
|
m_rows.reserve(16);
|
||||||
|
}
|
||||||
|
|
||||||
|
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) {
|
||||||
|
add_copy_of_row(orig_row);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ColDefNode Table::get_column_def(const std::string &col_name) {
|
ColDefNode Table::get_column_def(const std::string &col_name) {
|
||||||
|
|
@ -96,18 +106,26 @@ int Table::load_csv_string(const std::string &content) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void Table::print() {
|
void Table::print() {
|
||||||
std::cout << "** " << m_name << " **" << std::endl;
|
std::string out{"| "};
|
||||||
for (auto row : m_rows) {
|
std::string out2{"+-"};
|
||||||
row.print();
|
std::vector<int> col_char_sizes{};
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Table::Table(const Table &other) {
|
for(auto col_def : m_col_defs) {
|
||||||
m_name = other.m_name;
|
int col_size = col_def.type == ColumnType::varchar_type ? col_def.length : 10;
|
||||||
m_col_defs = other.m_col_defs;
|
col_char_sizes.push_back(col_size);
|
||||||
for(const Row& orig_row : other.m_rows) {
|
|
||||||
add_copy_of_row(orig_row);
|
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(col_char_sizes);
|
||||||
|
}
|
||||||
|
std::cout << std::endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Table::add_row(const Row &row) {
|
void Table::add_row(const Row &row) {
|
||||||
|
|
|
||||||
4
table.h
4
table.h
|
|
@ -4,7 +4,6 @@
|
||||||
#include "row.h"
|
#include "row.h"
|
||||||
|
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <list>
|
|
||||||
|
|
||||||
namespace usql {
|
namespace usql {
|
||||||
|
|
||||||
|
|
@ -17,6 +16,7 @@ namespace usql {
|
||||||
ColDefNode get_column_def(int col_index);
|
ColDefNode get_column_def(int col_index);
|
||||||
|
|
||||||
int columns_count() const { return m_col_defs.size(); };
|
int columns_count() const { return m_col_defs.size(); };
|
||||||
|
int rows_count() const { return m_rows.size(); };
|
||||||
|
|
||||||
Row create_empty_row(); // TODO this means unnecessary copying
|
Row create_empty_row(); // TODO this means unnecessary copying
|
||||||
void add_row(const Row &row);
|
void add_row(const Row &row);
|
||||||
|
|
@ -33,7 +33,7 @@ namespace usql {
|
||||||
|
|
||||||
std::string m_name;
|
std::string m_name;
|
||||||
std::vector<ColDefNode> m_col_defs;
|
std::vector<ColDefNode> m_col_defs;
|
||||||
std::list<Row> m_rows;
|
std::vector<Row> m_rows;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
45
usql.cpp
45
usql.cpp
|
|
@ -106,7 +106,7 @@ std::unique_ptr<Table> USql::execute_save(SaveTableNode &node) {
|
||||||
file << csv_string;
|
file << csv_string;
|
||||||
file.close();
|
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) {
|
std::unique_ptr<Table> USql::execute_drop(DropTableNode &node) {
|
||||||
|
|
@ -191,18 +191,20 @@ std::unique_ptr<Table> USql::execute_select(SelectFromTableNode &node) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// order by
|
// order by
|
||||||
execute_order_by(node, table, result);
|
execute_order_by(node, table, result.get());
|
||||||
|
|
||||||
|
// offset & limit
|
||||||
|
execute_offset_limit(node.offset_limit, result.get());
|
||||||
|
|
||||||
return std::move(result);
|
return std::move(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
void USql::execute_order_by(SelectFromTableNode &node, Table *table, std::__unique_if<Table>::__unique_single &result) const {
|
void USql::execute_order_by(SelectFromTableNode &node, Table *table, Table *result) const {
|
||||||
if (node.order_by.size() == 0) return;
|
if (node.order_by.size() == 0) return;
|
||||||
|
|
||||||
auto compare_rows = [&node, &table, this](const Row &a, const Row &b) {
|
auto compare_rows = [&node, &result, this](const Row &a, const Row &b) {
|
||||||
for(auto order_by_col_def : node.order_by) {
|
for(auto order_by_col_def : node.order_by) {
|
||||||
ColDefNode col_def = table->get_column_def(order_by_col_def.col_index - 1); // TODO validate index
|
ColDefNode col_def = result->get_column_def(order_by_col_def.col_index - 1); // TODO validate index
|
||||||
ColValue *a_val = a.ith_column(col_def.order);
|
ColValue *a_val = a.ith_column(col_def.order);
|
||||||
ColValue *b_val = b.ith_column(col_def.order);
|
ColValue *b_val = b.ith_column(col_def.order);
|
||||||
|
|
||||||
|
|
@ -217,7 +219,16 @@ void USql::execute_order_by(SelectFromTableNode &node, Table *table, std::__uniq
|
||||||
return false;
|
return false;
|
||||||
};
|
};
|
||||||
|
|
||||||
result->m_rows.sort(compare_rows);
|
std::sort(result->m_rows.begin(), result->m_rows.end(), compare_rows);
|
||||||
|
}
|
||||||
|
|
||||||
|
void USql::execute_offset_limit(OffsetLimitNode &node, Table *result) const {
|
||||||
|
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());
|
||||||
}
|
}
|
||||||
|
|
||||||
int USql::compare_col_values(const ColDefNode &col_def, ColValue *a_val, ColValue *b_val) const {
|
int USql::compare_col_values(const ColDefNode &col_def, ColValue *a_val, ColValue *b_val) const {
|
||||||
|
|
@ -247,7 +258,7 @@ std::tuple<int, ColDefNode> USql::get_column_definition(Table *table, SelectColN
|
||||||
auto node = static_cast<FunctionNode *>(select_col_node->value.get());
|
auto node = static_cast<FunctionNode *>(select_col_node->value.get());
|
||||||
|
|
||||||
if (node->function == "to_string") {
|
if (node->function == "to_string") {
|
||||||
ColDefNode cdef = ColDefNode{new_col_name, ColumnType::varchar_type, col_order, 64, true};
|
ColDefNode cdef = ColDefNode{new_col_name, ColumnType::varchar_type, col_order, 32, true};
|
||||||
return std::make_tuple(-1, cdef);
|
return std::make_tuple(-1, cdef);
|
||||||
} else if (node->function == "to_date") {
|
} else if (node->function == "to_date") {
|
||||||
ColDefNode cdef = ColDefNode{new_col_name, ColumnType::integer_type, col_order, 1, true};
|
ColDefNode cdef = ColDefNode{new_col_name, ColumnType::integer_type, col_order, 1, true};
|
||||||
|
|
@ -264,16 +275,14 @@ std::unique_ptr<Table> USql::execute_delete(DeleteFromTableNode &node) {
|
||||||
Table *table = find_table(node.table_name);
|
Table *table = find_table(node.table_name);
|
||||||
|
|
||||||
// execute access plan
|
// execute access plan
|
||||||
int affected_rows = 0;
|
int affected_rows = table->rows_count();
|
||||||
auto it = table->m_rows.begin();
|
|
||||||
for (; it != table->m_rows.end();) {
|
table->m_rows.erase(
|
||||||
if (eval_where(node.where.get(), table, *it)) {
|
std::remove_if(table->m_rows.begin(), table->m_rows.end(),
|
||||||
it = table->m_rows.erase(it);
|
[&node, table, this](Row &row){return eval_where(node.where.get(), table, row);}),
|
||||||
affected_rows++;
|
table->m_rows.end());
|
||||||
} else {
|
|
||||||
++it;
|
affected_rows -= table->rows_count();
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return create_stmt_result_table(0, "delete succeeded", affected_rows);
|
return create_stmt_result_table(0, "delete succeeded", affected_rows);
|
||||||
}
|
}
|
||||||
|
|
@ -520,7 +529,7 @@ std::unique_ptr<ValueNode> USql::eval_arithmetic_operator(ColumnType outType, Ar
|
||||||
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, long affected_rows) {
|
||||||
std::vector<ColDefNode> result_tbl_col_defs{};
|
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("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("desc", ColumnType::varchar_type, 1, 48, false));
|
||||||
result_tbl_col_defs.push_back(ColDefNode("affected_rows", ColumnType::integer_type, 0, 1, true));
|
result_tbl_col_defs.push_back(ColDefNode("affected_rows", ColumnType::integer_type, 0, 1, true));
|
||||||
|
|
||||||
auto table_def = std::make_unique<Table>("result", result_tbl_col_defs);
|
auto table_def = std::make_unique<Table>("result", result_tbl_col_defs);
|
||||||
|
|
|
||||||
5
usql.h
5
usql.h
|
|
@ -57,8 +57,9 @@ private:
|
||||||
|
|
||||||
int compare_col_values(const ColDefNode &col_def, ColValue *a_val, ColValue *b_val) const;
|
int compare_col_values(const ColDefNode &col_def, ColValue *a_val, ColValue *b_val) const;
|
||||||
|
|
||||||
void
|
void execute_order_by(SelectFromTableNode &node, Table *table, Table *result) const;
|
||||||
execute_order_by(SelectFromTableNode &node, Table *table, std::__unique_if<Table>::__unique_single &result) const;
|
|
||||||
|
void execute_offset_limit(OffsetLimitNode &node, Table *result) const;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
Loading…
Reference in New Issue