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

Query serialisation part 3 #2989

Merged
merged 16 commits into from
Feb 14, 2018
Merged

Query serialisation part 3 #2989

merged 16 commits into from
Feb 14, 2018

Conversation

ironage
Copy link
Contributor

@ironage ironage commented Jan 30, 2018

This adds support for subquery expressions and backlinks to the query serialiser and parser.
It also sets up the architecture for key path aliasing which could be useful for querying over named backlinks or when bindings allow referencing properties by other user defined names.

This is part of #2943

@ironage ironage self-assigned this Jan 30, 2018
@realm-ci
Copy link
Contributor

@astigsen
Copy link
Contributor

Does this also cover the simplified shortcut syntax (IN, ANY, ALL...), or does it only support full subqueries (SUBQUERY())?

@ironage
Copy link
Contributor Author

ironage commented Jan 30, 2018

Currently only the full subquery syntax is supported here SUBQUERY(...).@count as this is the only type of subquery expression that any of the bindings currently support. But I think some of those shortcuts could be built on top of this work.

@bdash
Copy link
Contributor

bdash commented Jan 30, 2018

Serialization of Cocoa queries that use ANY is already supported because it's implicit in core's query engine for any query that traverses a to-many relationship. It's simply not present from the serialized form. It doesn't look like the parser supports parsing it either, but I'm not sure how much that matters.

No bindings support the equivalent of ALL at the moment. It could be supported by converting it to a SUBQUERY, but it'd be more efficient if it were implemented directly in the query engine.

IN isn't related to subquery support at all, AFAICT.

@astigsen
Copy link
Contributor

IN isn't related to subquery support at all, AFAICT.

x IN listproperty should be equivalent to ANY listproperty = x.

@ironage
Copy link
Contributor Author

ironage commented Jan 30, 2018

For JS queries we could potentially support these shortcuts without native core query support by getting the parser to make the following mappings:

  • ANY items.price > 10 or 10 IN items.price becomes SUBQUERY(items, $x, $x.price > 10).@count > 0
  • ALL items.price > 10 becomes SUBQUERY(items, $x, $x.price > 10).@count == items.@count

@astigsen
Copy link
Contributor

Yes, I think it is worth supporting them all, even if the initial mapping means that the performance is less than what we could get with a native implementation.

@bdash
Copy link
Contributor

bdash commented Jan 30, 2018

x IN listproperty should be equivalent to ANY listproperty = x.

Ah, yes. But again, serializing ANY listproperty = x is already supported.

Please also keep in mind that adding support for parsing additional query features is outside of the scope of the current query serialization work. It needs to be evaluated and prioritized against other work.

@codecov-io
Copy link

codecov-io commented Jan 30, 2018

Codecov Report

Merging #2989 into master will increase coverage by 0.05%.
The diff coverage is 95.66%.

Impacted file tree graph

@@            Coverage Diff             @@
##           master    #2989      +/-   ##
==========================================
+ Coverage   93.17%   93.22%   +0.05%     
==========================================
  Files         266      270       +4     
  Lines       77914    78627     +713     
==========================================
+ Hits        72594    73302     +708     
- Misses       5320     5325       +5
Impacted Files Coverage Δ
src/realm/parser/expression_container.hpp 100% <ø> (ø) ⬆️
src/realm/query.hpp 100% <ø> (ø) ⬆️
src/realm/table.hpp 97.4% <ø> (+0.01%) ⬆️
src/realm/parser/parser.hpp 100% <ø> (ø) ⬆️
src/realm/parser/parser_utils.hpp 83.33% <ø> (ø) ⬆️
src/realm/table.cpp 90.83% <100%> (+0.03%) ⬆️
src/realm/parser/property_expression.hpp 100% <100%> (ø) ⬆️
src/realm/parser/keypath_mapping.hpp 100% <100%> (ø)
src/realm/parser/parser.cpp 94.68% <100%> (+0.93%) ⬆️
src/realm/parser/property_expression.cpp 100% <100%> (ø) ⬆️
... and 30 more

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 170f778...bb1cf77. Read the comment docs.

@realm-ci
Copy link
Contributor

@ironage ironage changed the base branch from js/query_serialization to master February 3, 2018 04:50
@realm-ci
Copy link
Contributor

realm-ci commented Feb 3, 2018

@realm-ci
Copy link
Contributor

realm-ci commented Feb 4, 2018

@realm-ci
Copy link
Contributor

realm-ci commented Feb 6, 2018

std::string guess = guess_prefix + add_char;
bool found_duplicate = false;
for (size_t i = 0; i < subquery_prefix_list.size(); ++i) {
if (guess.compare(subquery_prefix_list[i]) == 0) {
Copy link
Contributor

@rrrlasse rrrlasse Feb 6, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think guess == subquery_prefix_list[i] is more well known and thus easier to read (I had to look-up how compare() worked to check if there was any special reason why it was called)

}
};

while (true) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing assert or other handling if the column is never found (should be there even if it by design can't happen)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

By design it will loop endlessly until a unique name is found. I assume there must be a finite number of column names to search through and a finite number of subquery nestings with different variable names. In practice though it'll find a match on the first pass. Would you suggest putting an upper limit on the number of loop iterations we allow?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

oh okay, I misread the code and thought it would loop until it had somehow found a specific column :) no worries then

char add_char = start_char;

auto next_guess = [&]() {
add_char = (((add_char + 1) - 'a') % 26) + 'a';
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

% ('z' - 'a' + 1) is easier to read I think

template <typename RetType>
struct SubqueryGetter<RetType,
typename std::enable_if_t<
std::is_same<RetType, Int>::value ||
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we have realm::is_any :)

} else {
list_property_name = pre_link_table->get_column_name(pe.get_dest_ndx());
}
precondition(pe.get_dest_type() == type_LinkList || pe.dest_type_is_backlink(),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this precondition macro is defined in a .hpp file (parser_utils.hpp), so it must be prefixed with REALM_ in order not to pollute users´ name spaces


// This may be premature optimisation, but it'll be super fast and it doesn't
// bother dragging in anything locale specific for case insensitive comparisons.
bool is_backlinks_prefix(std::string s) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If it's super fast, maybe also pass a reference instead of a value :D

static Table* table_getter(TableRef table, const std::vector<KeyPathElement>& links);
protected:
bool m_allow_backlinks;
std::map<std::pair<ConstTableRef, std::string>, std::string> m_mapping;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

an unordered_map might be alot faster (and has same API)


void KeyPathMapping::add_mapping(ConstTableRef table, std::string name, std::string alias)
{
m_mapping[{table, name}] = alias;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

error handling if the table already exists?

@realm-ci
Copy link
Contributor

realm-ci commented Feb 6, 2018

@realm-ci
Copy link
Contributor

realm-ci commented Feb 9, 2018

@realm-ci
Copy link
Contributor

@realm-ci
Copy link
Contributor

@ironage ironage merged commit 905345f into master Feb 14, 2018
@ironage ironage deleted the js/query_serialisation_2 branch February 14, 2018 17:50
@ironage ironage mentioned this pull request Mar 6, 2018
@github-actions github-actions bot locked as resolved and limited conversation to collaborators Mar 23, 2024
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

7 participants