Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

enable name aliasing in the query parser's sort and distinct #4555

Merged
merged 1 commit into from
Mar 24, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
### Fixed
* <How to hit and notice issue? what was the impact?> ([#????](https://github.com/realm/realm-core/issues/????), since v?.?.?)
* Potential/unconfirmed fix for crashes associated with failure to memory map (low on memory, low on virtual address space). For example ([#4514](https://github.com/realm/realm-core/issues/4514)).
* Fixed name aliasing not working in sort/distinct clauses of the query parser. ([#4550](https://github.com/realm/realm-core/issues/4550), never before working).

### Breaking changes
* None.
Expand Down
10 changes: 6 additions & 4 deletions src/realm/parser/driver.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1145,18 +1145,20 @@ std::unique_ptr<DescriptorOrdering> DescriptorOrderingNode::visit(ParserDriver*
std::vector<std::vector<ColKey>> property_columns;
for (auto& col_names : cur_ordering->columns) {
std::vector<ColKey> columns;
ConstTableRef cur_table = target;
LinkChain link_chain(target);
for (size_t ndx_in_path = 0; ndx_in_path < col_names.size(); ++ndx_in_path) {
ColKey col_key = cur_table->get_column_key(col_names[ndx_in_path]);
std::string path_elem = drv->translate(link_chain, col_names[ndx_in_path]);
ColKey col_key = link_chain.get_current_table()->get_column_key(path_elem);
if (!col_key) {
throw InvalidQueryError(
util::format("No property '%1' found on object type '%2' specified in '%3' clause",
col_names[ndx_in_path], drv->get_printable_name(cur_table->get_name()),
col_names[ndx_in_path],
drv->get_printable_name(link_chain.get_current_table()->get_name()),
is_distinct ? "distinct" : "sort"));
}
columns.push_back(col_key);
if (ndx_in_path < col_names.size() - 1) {
cur_table = cur_table->get_link_target(col_key);
link_chain.link(col_key);
}
}
property_columns.push_back(columns);
Expand Down
1 change: 0 additions & 1 deletion src/realm/table.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -1015,7 +1015,6 @@ class LinkChain {
return SubQuery<T>(column<T>(origin, origin_col_key), std::move(subquery));
}


template <class T>
BacklinkCount<T> get_backlink_count()
{
Expand Down
176 changes: 121 additions & 55 deletions test/test_parser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2394,13 +2394,11 @@ TEST(Parser_SortAndDistinctSerialisation)
CHECK(description.find("SORT(account.balance ASC, account.num_transactions DESC)") != std::string::npos);
}

TableView get_sorted_view(TableRef t, std::string query_string)
TableView get_sorted_view(TableRef t, std::string query_string, query_parser::KeyPathMapping mapping = {})
{
Query q = t->query(query_string);

std::string query_description = q.get_description();
Query q2 = t->query(query_description);

Query q = t->query(query_string, {}, mapping);
std::string query_description = q.get_description(mapping.get_backlink_class_prefix());
Query q2 = t->query(query_description, {}, mapping);
return q2.find_all();
}

Expand Down Expand Up @@ -2440,75 +2438,143 @@ TEST(Parser_SortAndDistinct)
p3.set(age_col, 28);
p3.set(account_col, account2.get_key());

query_parser::KeyPathMapping mapping;
mapping.add_mapping(people, "sol_rotations", "age");
mapping.add_mapping(people, "nominal_identifier", "name");
mapping.add_mapping(people, "holdings", "account");
mapping.add_mapping(accounts, "funds", "balance");
mapping.add_mapping(accounts, "sum_of_actions", "num_transactions");

// person: | account:
// name age account | balance num_transactions
// Adam 28 0 -> | 50.55 2
// Frank 30 1 -> | 50.55 73
// Ben 28 2 -> | 98.92 17

// sort serialisation
TableView tv = get_sorted_view(people, "age > 0 SORT(age ASC)");
for (size_t row_ndx = 1; row_ndx < tv.size(); ++row_ndx) {
CHECK(tv.get(row_ndx - 1).get<Int>(age_col) <= tv.get(row_ndx).get<Int>(age_col));
{
auto check_tv = [&](TableView tv) {
for (size_t row_ndx = 1; row_ndx < tv.size(); ++row_ndx) {
CHECK(tv.get(row_ndx - 1).get<Int>(age_col) <= tv.get(row_ndx).get<Int>(age_col));
}
};
check_tv(get_sorted_view(people, "age > 0 SORT(age ASC)"));
check_tv(get_sorted_view(people, "sol_rotations > 0 SORT(sol_rotations ASC)", mapping));
}

tv = get_sorted_view(people, "age > 0 SORT(age DESC)");
for (size_t row_ndx = 1; row_ndx < tv.size(); ++row_ndx) {
CHECK(tv.get(row_ndx - 1).get<Int>(age_col) >= tv.get(row_ndx).get<Int>(age_col));
{
auto check_tv = [&](TableView tv) {
for (size_t row_ndx = 1; row_ndx < tv.size(); ++row_ndx) {
CHECK(tv.get(row_ndx - 1).get<Int>(age_col) >= tv.get(row_ndx).get<Int>(age_col));
}
};
check_tv(get_sorted_view(people, "age > 0 SORT(age DESC)"));
check_tv(get_sorted_view(people, "sol_rotations > 0 SORT(sol_rotations DESC)", mapping));
}

tv = get_sorted_view(people, "age > 0 SORT(age ASC, name DESC)");
CHECK_EQUAL(tv.size(), 3);
CHECK_EQUAL(tv.get(0).get<String>(name_col), "Ben");
CHECK_EQUAL(tv.get(1).get<String>(name_col), "Adam");
CHECK_EQUAL(tv.get(2).get<String>(name_col), "Frank");

tv = get_sorted_view(people, "TRUEPREDICATE SORT(account.balance ascending)");
for (size_t row_ndx = 1; row_ndx < tv.size(); ++row_ndx) {
ObjKey link_ndx1 = tv.get(row_ndx - 1).get<ObjKey>(account_col);
ObjKey link_ndx2 = tv.get(row_ndx).get<ObjKey>(account_col);
CHECK(accounts->get_object(link_ndx1).get<double>(balance_col) <=
accounts->get_object(link_ndx2).get<double>(balance_col));
{
auto check_tv = [&](TableView tv) {
CHECK_EQUAL(tv.size(), 3);
CHECK_EQUAL(tv.get(0).get<String>(name_col), "Ben");
CHECK_EQUAL(tv.get(1).get<String>(name_col), "Adam");
CHECK_EQUAL(tv.get(2).get<String>(name_col), "Frank");
};
check_tv(get_sorted_view(people, "age > 0 SORT(age ASC, name DESC)"));
check_tv(
get_sorted_view(people, "sol_rotations > 0 SORT(sol_rotations ASC, nominal_identifier DESC)", mapping));
}

tv = get_sorted_view(people, "TRUEPREDICATE SORT(account.balance descending)");
for (size_t row_ndx = 1; row_ndx < tv.size(); ++row_ndx) {
ObjKey link_ndx1 = tv.get(row_ndx - 1).get<ObjKey>(account_col);
ObjKey link_ndx2 = tv.get(row_ndx).get<ObjKey>(account_col);
CHECK(accounts->get_object(link_ndx1).get<double>(balance_col) >=
accounts->get_object(link_ndx2).get<double>(balance_col));
{
auto check_tv = [&](TableView tv) {
for (size_t row_ndx = 1; row_ndx < tv.size(); ++row_ndx) {
ObjKey link_ndx1 = tv.get(row_ndx - 1).get<ObjKey>(account_col);
ObjKey link_ndx2 = tv.get(row_ndx).get<ObjKey>(account_col);
CHECK(accounts->get_object(link_ndx1).get<double>(balance_col) <=
accounts->get_object(link_ndx2).get<double>(balance_col));
}
};
check_tv(get_sorted_view(people, "TRUEPREDICATE SORT(account.balance ascending)", mapping));
check_tv(get_sorted_view(people, "TRUEPREDICATE SORT(holdings.funds ascending)", mapping));
check_tv(get_sorted_view(people, "TRUEPREDICATE SORT(account.funds ascending)", mapping));
check_tv(get_sorted_view(people, "TRUEPREDICATE SORT(holdings.balance ascending)", mapping));
}

tv = get_sorted_view(people, "TRUEPREDICATE DISTINCT(age)");
CHECK_EQUAL(tv.size(), 2);
for (size_t row_ndx = 1; row_ndx < tv.size(); ++row_ndx) {
CHECK(tv.get(row_ndx - 1).get<Int>(age_col) != tv.get(row_ndx).get<Int>(age_col));
{
auto check_tv = [&](TableView tv) {
for (size_t row_ndx = 1; row_ndx < tv.size(); ++row_ndx) {
ObjKey link_ndx1 = tv.get(row_ndx - 1).get<ObjKey>(account_col);
ObjKey link_ndx2 = tv.get(row_ndx).get<ObjKey>(account_col);
CHECK(accounts->get_object(link_ndx1).get<double>(balance_col) >=
accounts->get_object(link_ndx2).get<double>(balance_col));
}
};
check_tv(get_sorted_view(people, "TRUEPREDICATE SORT(account.balance descending)", mapping));
check_tv(get_sorted_view(people, "TRUEPREDICATE SORT(holdings.funds descending)", mapping));
}

tv = get_sorted_view(people, "TRUEPREDICATE DISTINCT(age, account.balance)");
CHECK_EQUAL(tv.size(), 3);
CHECK_EQUAL(tv.get(0).get<String>(name_col), "Adam");
CHECK_EQUAL(tv.get(1).get<String>(name_col), "Frank");
CHECK_EQUAL(tv.get(2).get<String>(name_col), "Ben");
{
auto check_tv = [&](TableView tv) {
CHECK_EQUAL(tv.size(), 2);
for (size_t row_ndx = 1; row_ndx < tv.size(); ++row_ndx) {
CHECK(tv.get(row_ndx - 1).get<Int>(age_col) != tv.get(row_ndx).get<Int>(age_col));
}
};
check_tv(get_sorted_view(people, "TRUEPREDICATE DISTINCT(age)"));
check_tv(get_sorted_view(people, "TRUEPREDICATE DISTINCT(sol_rotations)", mapping));
}

tv = get_sorted_view(people, "TRUEPREDICATE DISTINCT(age) DISTINCT(account.balance)");
CHECK_EQUAL(tv.size(), 1);
CHECK_EQUAL(tv.get(0).get<String>(name_col), "Adam");
{
auto check_tv = [&](TableView tv) {
CHECK_EQUAL(tv.size(), 3);
CHECK_EQUAL(tv.get(0).get<String>(name_col), "Adam");
CHECK_EQUAL(tv.get(1).get<String>(name_col), "Frank");
CHECK_EQUAL(tv.get(2).get<String>(name_col), "Ben");
};
check_tv(get_sorted_view(people, "TRUEPREDICATE DISTINCT(age, account.balance)", mapping));
check_tv(get_sorted_view(people, "TRUEPREDICATE DISTINCT(sol_rotations, holdings.funds)", mapping));
}

tv = get_sorted_view(people, "TRUEPREDICATE SORT(age ASC) DISTINCT(age)");
CHECK_EQUAL(tv.size(), 2);
CHECK_EQUAL(tv.get(0).get<Int>(age_col), 28);
CHECK_EQUAL(tv.get(1).get<Int>(age_col), 30);
{
auto check_tv = [&](TableView tv) {
CHECK_EQUAL(tv.size(), 1);
CHECK_EQUAL(tv.get(0).get<String>(name_col), "Adam");
};
check_tv(get_sorted_view(people, "TRUEPREDICATE DISTINCT(age) DISTINCT(account.balance)"));
check_tv(get_sorted_view(people, "TRUEPREDICATE DISTINCT(sol_rotations) DISTINCT(holdings.funds)", mapping));
}

tv = get_sorted_view(people, "TRUEPREDICATE SORT(name DESC) DISTINCT(age) SORT(name ASC) DISTINCT(name)");
CHECK_EQUAL(tv.size(), 2);
CHECK_EQUAL(tv.get(0).get<String>(name_col), "Ben");
CHECK_EQUAL(tv.get(1).get<String>(name_col), "Frank");
{
auto check_tv = [&](TableView tv) {
CHECK_EQUAL(tv.size(), 2);
CHECK_EQUAL(tv.get(0).get<Int>(age_col), 28);
CHECK_EQUAL(tv.get(1).get<Int>(age_col), 30);
};
check_tv(get_sorted_view(people, "TRUEPREDICATE SORT(age ASC) DISTINCT(age)"));
check_tv(get_sorted_view(people, "TRUEPREDICATE SORT(sol_rotations ASC) DISTINCT(sol_rotations)", mapping));
}

tv = get_sorted_view(people, "account.num_transactions > 10 SORT(name ASC)");
CHECK_EQUAL(tv.size(), 2);
CHECK_EQUAL(tv.get(0).get<String>(name_col), "Ben");
CHECK_EQUAL(tv.get(1).get<String>(name_col), "Frank");
{
auto check_tv = [&](TableView tv) {
CHECK_EQUAL(tv.size(), 2);
CHECK_EQUAL(tv.get(0).get<String>(name_col), "Ben");
CHECK_EQUAL(tv.get(1).get<String>(name_col), "Frank");
};
check_tv(
get_sorted_view(people, "TRUEPREDICATE SORT(name DESC) DISTINCT(age) SORT(name ASC) DISTINCT(name)"));
check_tv(get_sorted_view(people,
"TRUEPREDICATE SORT(nominal_identifier DESC) DISTINCT(sol_rotations) "
"SORT(nominal_identifier ASC) DISTINCT(nominal_identifier)",
mapping));
}

{
auto check_tv = [&](TableView tv) {
CHECK_EQUAL(tv.size(), 2);
CHECK_EQUAL(tv.get(0).get<String>(name_col), "Ben");
CHECK_EQUAL(tv.get(1).get<String>(name_col), "Frank");
};
check_tv(get_sorted_view(people, "account.num_transactions > 10 SORT(name ASC)"));
check_tv(get_sorted_view(people, "holdings.sum_of_actions > 10 SORT(nominal_identifier ASC)", mapping));
}

std::string message;
CHECK_THROW_ANY_GET_MESSAGE(get_sorted_view(people, "TRUEPREDICATE DISTINCT(balance)"), message);
Expand Down