work on order by began

This commit is contained in:
VaclavT 2021-07-28 22:29:05 +02:00
parent 7d91319f0b
commit 34e432d031
10 changed files with 184 additions and 92 deletions

View File

@ -1,7 +1,9 @@
### TODO ### TODO
- support for *
- support for order by, offset, limit - support for order by, offset, limit
- support for btree indexes
- support for parenthesis
- support for *
- add pipe | token - add pipe | token
- add count min and max functions, eg aggregate functions - add count min and max functions, eg aggregate functions
- maybe to create iterator on table - maybe to create iterator on table

View File

@ -119,115 +119,86 @@ namespace usql {
// TODO, FIXME 'one is evaluated as identifier // TODO, FIXME 'one is evaluated as identifier
if (token == ";") if (token == ";")
return TokenType::semicolon; return TokenType::semicolon;
if (token == "+") if (token == "+")
return TokenType::plus; return TokenType::plus;
if (token == "-") if (token == "-")
return TokenType::minus; return TokenType::minus;
if (token == "*") if (token == "*")
return TokenType::multiply; return TokenType::multiply;
if (token == "/") if (token == "/")
return TokenType::divide; return TokenType::divide;
if (token == "(") if (token == "(")
return TokenType::open_paren; return TokenType::open_paren;
if (token == ")") if (token == ")")
return TokenType::close_paren; return TokenType::close_paren;
if (token == "=") if (token == "=")
return TokenType::equal; return TokenType::equal;
if (token == "!=") if (token == "!=")
return TokenType::not_equal; return TokenType::not_equal;
if (token == ">") if (token == ">")
return TokenType::greater; return TokenType::greater;
if (token == ">=") if (token == ">=")
return TokenType::greater_equal; return TokenType::greater_equal;
if (token == "<") if (token == "<")
return TokenType::lesser; return TokenType::lesser;
if (token == "<=") if (token == "<=")
return TokenType::lesser_equal; return TokenType::lesser_equal;
if (token == "as") if (token == "as")
return TokenType::keyword_as; return TokenType::keyword_as;
if (token == "create") if (token == "create")
return TokenType::keyword_create; return TokenType::keyword_create;
if (token == "drop") if (token == "drop")
return TokenType::keyword_drop; return TokenType::keyword_drop;
if (token == "where") if (token == "where")
return TokenType::keyword_where; return TokenType::keyword_where;
if (token == "order")
return TokenType::keyword_order;
if (token == "by")
return TokenType::keyword_by;
if (token == "asc")
return TokenType::keyword_asc;
if (token == "desc")
return TokenType::keyword_desc;
if (token == "from") if (token == "from")
return TokenType::keyword_from; return TokenType::keyword_from;
if (token == "delete") if (token == "delete")
return TokenType::keyword_delete; return TokenType::keyword_delete;
if (token == "table") if (token == "table")
return TokenType::keyword_table; return TokenType::keyword_table;
if (token == "insert") if (token == "insert")
return TokenType::keyword_insert; return TokenType::keyword_insert;
if (token == "into") if (token == "into")
return TokenType::keyword_into; return TokenType::keyword_into;
if (token == "values") if (token == "values")
return TokenType::keyword_values; return TokenType::keyword_values;
if (token == "select") if (token == "select")
return TokenType::keyword_select; return TokenType::keyword_select;
if (token == "set") if (token == "set")
return TokenType::keyword_set; return TokenType::keyword_set;
if (token == "copy") if (token == "copy")
return TokenType::keyword_copy; return TokenType::keyword_copy;
if (token == "update") if (token == "update")
return TokenType::keyword_update; return TokenType::keyword_update;
if (token == "load") if (token == "load")
return TokenType::keyword_load; return TokenType::keyword_load;
if (token == "save") if (token == "save")
return TokenType::keyword_save; return TokenType::keyword_save;
if (token == "not") if (token == "not")
return TokenType::keyword_not; return TokenType::keyword_not;
if (token == "null") if (token == "null")
return TokenType::keyword_null; return TokenType::keyword_null;
if (token == "integer") if (token == "integer")
return TokenType::keyword_integer; return TokenType::keyword_integer;
if (token == "float") if (token == "float")
return TokenType::keyword_float; return TokenType::keyword_float;
if (token == "varchar") if (token == "varchar")
return TokenType::keyword_varchar; return TokenType::keyword_varchar;
if (token == "or") if (token == "or")
return TokenType::logical_or; return TokenType::logical_or;
if (token == "and") if (token == "and")
return TokenType::logical_and; return TokenType::logical_and;
if (token == ",") if (token == ",")
return TokenType::comma; return TokenType::comma;
if (token == "\n" || token == "\r\n" || token == "\r") if (token == "\n" || token == "\r\n" || token == "\r")
return TokenType::newline; return TokenType::newline;
@ -347,6 +318,18 @@ namespace usql {
case TokenType::keyword_where: case TokenType::keyword_where:
txt = "where"; txt = "where";
break; break;
case TokenType::keyword_order:
txt = "order";
break;
case TokenType::keyword_by:
txt = "by";
break;
case TokenType::keyword_asc:
txt = "asc";
break;
case TokenType::keyword_desc:
txt = "desc";
break;
case TokenType::keyword_table: case TokenType::keyword_table:
txt = "table"; txt = "table";
break; break;

View File

@ -25,6 +25,10 @@ namespace usql {
keyword_drop, keyword_drop,
keyword_table, keyword_table,
keyword_where, keyword_where,
keyword_order,
keyword_by,
keyword_asc,
keyword_desc,
keyword_delete, keyword_delete,
keyword_update, keyword_update,
keyword_load, keyword_load,

View File

@ -9,7 +9,7 @@ int main(int argc, char *argv[]) {
std::vector<std::string> sql_commands{ std::vector<std::string> sql_commands{
"create table a (i integer not null, s varchar(64), f float null)", "create table a (i integer not null, s varchar(64), f float null)",
"insert into a (i, s) values(1, upper('one'))", "insert into a (i, s) values(1, upper('one'))",
"update table a set s = 'null string aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'", // "update table a set s = 'null string aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'",
// "update table a set i = null", // "update table a set i = null",
"insert into a (i, s) values(2, 'two')", "insert into a (i, s) values(2, 'two')",
"insert into a (i, s) values(3, 'two')", "insert into a (i, s) values(3, 'two')",
@ -17,30 +17,30 @@ 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", "select i, s from a where i > 2 order by 1 desc"
"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",
"update a set s = 'three', f = f + 0.01 where i = 3", // "update a set s = 'three', f = f + 0.01 where i = 3",
"select i, s, f from a where i = 3", // "select i, s, f from a where i = 3",
"create table data (ticker varchar(8), price float null)", // "create table data (ticker varchar(8), price float null)",
"load data from '/Users/vaclavt/Library/Mobile Documents/com~apple~CloudDocs/Development/usql/data.csv')", // "load data from '/Users/vaclavt/Library/Mobile Documents/com~apple~CloudDocs/Development/usql/data.csv')",
"select ticker, price from data", // "select ticker, price from data",
"select i, s, f from a where i < 300", // "select i, s, f from a where i < 300",
"create table x as select i, s, f from a where i < 300", // "create table x as select i, s, f from a where i < 300",
"select i, s, f from x where i < 300", // "select i, s, f from x where i < 300",
"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 from prices where symbol = 'SYF'"
}; };

View File

@ -235,11 +235,12 @@ std::unique_ptr<Node> Parser::parse_select_from_table() {
std::unique_ptr<Node> where_node = parse_where_clause(); std::unique_ptr<Node> where_node = parse_where_clause();
// if (m_lexer.tokenType() == TokenType::keyword_order_by) {} std::vector<ColOrderNode> orderby_node = parse_orderby_clause();
// if (m_lexer.tokenType() == TokenType::keyword_offset) {} // if (m_lexer.tokenType() == TokenType::keyword_offset) {}
// if (m_lexer.tokenType() == TokenType::keyword_limit) {} // if (m_lexer.tokenType() == TokenType::keyword_limit) {}
return std::make_unique<SelectFromTableNode>(table_name, std::move(cols), std::move(where_node)); return std::make_unique<SelectFromTableNode>(table_name, std::move(cols), std::move(where_node), orderby_node);
} }
std::unique_ptr<Node> Parser::parse_delete_from_table() { std::unique_ptr<Node> Parser::parse_delete_from_table() {
@ -291,9 +292,6 @@ std::unique_ptr<Node> Parser::parse_select_from_table() {
} }
std::unique_ptr<Node> Parser::parse_where_clause() { 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) { if (m_lexer.tokenType() != TokenType::keyword_where) {
return std::make_unique<TrueNode>(); return std::make_unique<TrueNode>();
} }
@ -308,11 +306,50 @@ std::unique_ptr<Node> Parser::parse_select_from_table() {
std::unique_ptr<Node> node2 = parse_relational_expression(); std::unique_ptr<Node> node2 = parse_relational_expression();
node = std::make_unique<LogicalOperatorNode>(operation, std::move(node), std::move(node2)); node = std::make_unique<LogicalOperatorNode>(operation, std::move(node), std::move(node2));
} }
} while (m_lexer.tokenType() != TokenType::eof); // until whole where clause parsed } while (m_lexer.tokenType() != TokenType::eof && m_lexer.tokenType() != TokenType::keyword_order);
return node; return node;
} }
std::vector<ColOrderNode> Parser::parse_orderby_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);
do {
int col_index = -1;
bool asc = true;
auto token_type = m_lexer.tokenType();
std::string tokenString = m_lexer.consumeCurrentToken().token_string;
switch (token_type) {
case TokenType::int_number:
col_index = std::stoi(tokenString);
break;
default:
throw Exception("column index alloved 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.push_back(ColOrderNode{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;
}
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();
@ -336,7 +373,7 @@ std::unique_ptr<Node> Parser::parse_select_from_table() {
return std::make_unique<DatabaseValueNode>(tokenString); return std::make_unique<DatabaseValueNode>(tokenString);
case TokenType::keyword_null: case TokenType::keyword_null:
return std::make_unique<NullValueNode>(); return std::make_unique<NullValueNode>();
default:; default:
throw Exception("Unknown operand node"); throw Exception("Unknown operand node");
} }
} }

View File

@ -34,6 +34,7 @@ namespace usql {
save_table, save_table,
drop_table, drop_table,
column_name, column_name,
column_order,
column_value, column_value,
function, function,
column_def, column_def,
@ -52,6 +53,15 @@ namespace usql {
ColNameNode(const std::string col_name) : Node(NodeType::column_name), name(col_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::column_name), col_name(""), col_index(index), ascending(asc) {}
};
struct SelectColNode : Node { struct SelectColNode : Node {
std::unique_ptr<Node> value; std::unique_ptr<Node> value;
std::string name; std::string name;
@ -216,9 +226,10 @@ namespace usql {
std::string table_name; std::string table_name;
std::unique_ptr<std::vector<SelectColNode>> cols_names; std::unique_ptr<std::vector<SelectColNode>> cols_names;
std::unique_ptr<Node> where; std::unique_ptr<Node> where;
std::vector<ColOrderNode> order_by;
SelectFromTableNode(std::string name, std::unique_ptr<std::vector<SelectColNode>> names, std::unique_ptr<Node> where_clause) : SelectFromTableNode(std::string name, std::unique_ptr<std::vector<SelectColNode>> names, std::unique_ptr<Node> where_clause, std::vector<ColOrderNode> orderby) :
Node(NodeType::select_from), table_name(name), cols_names(std::move(names)), where(std::move(where_clause)) {} Node(NodeType::select_from), table_name(name), cols_names(std::move(names)), where(std::move(where_clause)), order_by(orderby) {}
}; };
struct CreateTableAsSelectNode : Node { struct CreateTableAsSelectNode : Node {
@ -292,6 +303,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::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();
RelationalOperatorType parse_relational_operator(); RelationalOperatorType parse_relational_operator();

View File

@ -21,6 +21,13 @@ ColDefNode Table::get_column_def(const std::string &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() { Row Table::create_empty_row() {
return Row(columns_count()); return Row(columns_count());

View File

@ -14,6 +14,7 @@ namespace usql {
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(const std::string &col_name);
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(); };

View File

@ -155,8 +155,7 @@ std::unique_ptr<Table> USql::execute_select(SelectFromTableNode &node) {
std::vector<int> source_table_col_index{}; std::vector<int> source_table_col_index{};
for (int i = 0; i < node.cols_names->size(); i++) { for (int i = 0; i < node.cols_names->size(); i++) {
auto [ src_tbl_col_index, rst_tbl_col_def ] = get_column_definition(table, auto [ src_tbl_col_index, rst_tbl_col_def ] = get_column_definition(table, &node.cols_names->operator[](i), i);
&node.cols_names->operator[](i), i);
source_table_col_index.push_back(src_tbl_col_index); source_table_col_index.push_back(src_tbl_col_index);
result_tbl_col_defs.push_back(rst_tbl_col_def); result_tbl_col_defs.push_back(rst_tbl_col_def);
@ -176,8 +175,7 @@ std::unique_ptr<Table> USql::execute_select(SelectFromTableNode &node) {
auto row_col_index = source_table_col_index[idx]; auto row_col_index = source_table_col_index[idx];
if (row_col_index == -1) { // TODO introduce constant here if (row_col_index == -1) { // TODO introduce constant here
auto evaluated_value = eval_value_node(table, *row, node.cols_names->operator[]( auto evaluated_value = eval_value_node(table, *row, node.cols_names->operator[](idx).value.get());
idx).value.get());
ValueNode *col_value = evaluated_value.get(); ValueNode *col_value = evaluated_value.get();
new_row.setColumnValue(&result_tbl_col_defs[idx], col_value); new_row.setColumnValue(&result_tbl_col_defs[idx], col_value);
@ -192,9 +190,51 @@ std::unique_ptr<Table> USql::execute_select(SelectFromTableNode &node) {
} }
} }
// order by
execute_order_by(node, table, result);
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 {
if (node.order_by.size() == 0) return;
auto compare_rows = [&node, &table, this](const Row &a, const Row &b) {
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
ColValue *a_val = a.ith_column(col_def.order);
ColValue *b_val = b.ith_column(col_def.order);
if (a_val->isNull() && b_val->isNull()) return true; // both is null so a goes to end
if (!a_val->isNull() && b_val->isNull()) return true; // b is null so goes to end
if (a_val->isNull() && !b_val->isNull()) return false; // a is null so goes to end
int compare = compare_col_values(col_def, a_val, b_val);
if (compare < 0) return order_by_col_def.ascending ? true : false;
if (compare > 0) return order_by_col_def.ascending ? false : true;
}
return false;
};
result->m_rows.sort(compare_rows);
}
int USql::compare_col_values(const ColDefNode &col_def, ColValue *a_val, ColValue *b_val) const {
double c;
switch (col_def.type) {
case (ColumnType::integer_type):
return a_val->getIntValue() - b_val->getIntValue();
case (ColumnType::float_type):
c = a_val->getDoubleValue() - b_val->getDoubleValue();
return c < 0 ? -1 : c==0.0 ? 0 : 1;
case (ColumnType::varchar_type):
return a_val->getStringValue().compare(b_val->getStringValue());
default:
throw Exception("Unsupported data type");
}
}
std::tuple<int, ColDefNode> USql::get_column_definition(Table *table, SelectColNode *select_col_node, int col_order ) { std::tuple<int, ColDefNode> USql::get_column_definition(Table *table, SelectColNode *select_col_node, int col_order ) {
std::string new_col_name = select_col_node->name; std::string new_col_name = select_col_node->name;

5
usql.h
View File

@ -54,6 +54,11 @@ private:
private: private:
Parser m_parser; Parser m_parser;
std::list<Table> m_tables; std::list<Table> m_tables;
int compare_col_values(const ColDefNode &col_def, ColValue *a_val, ColValue *b_val) const;
void
execute_order_by(SelectFromTableNode &node, Table *table, std::__unique_if<Table>::__unique_single &result) const;
}; };
} // namespace } // namespace