1 /*************************************************************************
3 * Copyright 2016 Realm Inc.
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
9 * http://www.apache.org/licenses/LICENSE-2.0
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
17 **************************************************************************/
20 This file lets you write queries in C++ syntax like: Expression* e = (first + 1 / second >= third + 12.3);
22 Type conversion/promotion semantics is the same as in the C++ expressions, e.g float + int > double == float +
27 -----------------------------------------------------------------------------------------------------------------------
28 Expression: Subexpr2<T> Compare<Cond, T> Subexpr2<T>
33 Subexpr2<T> Operator<Oper<T> Subexpr2<T>
34 power(Subexpr2<T>) // power(x) = x * x, as example of unary operator
38 Operator<Oper<T>>: +, -, *, /
40 Compare<Cond, T>: ==, !=, >=, <=, >, <
42 T: bool, int, int64_t, float, double, StringData
46 -----------------------------------------------------------------------------------------------------------------------
48 void evaluate(size_t i, ValueBase* destination)
50 Compare: public Subexpr2
51 size_t find_first(size_t start, size_t end) // main method that executes query
53 unique_ptr<Subexpr2> m_left; // left expression subtree
54 unique_ptr<Subexpr2> m_right; // right expression subtree
56 Operator: public Subexpr2
57 void evaluate(size_t i, ValueBase* destination)
58 unique_ptr<Subexpr2> m_left; // left expression subtree
59 unique_ptr<Subexpr2> m_right; // right expression subtree
61 Value<T>: public Subexpr2
62 void evaluate(size_t i, ValueBase* destination)
65 Columns<T>: public Subexpr2
66 void evaluate(size_t i, ValueBase* destination)
67 SequentialGetter<T> sg; // class bound to a column, lets you read values in a fast way
70 class ColumnAccessor<>: public Columns<double>
74 -----------------------------------------------------------------------------------------------------------------------
75 Example of 'table.first > 34.6 + table.second':
77 size_t Compare<Greater>::find_first()-------------+
81 +--> Columns<float>::evaluate() +--------> Operator<Plus>::evaluate()
83 Value<float>::evaluate() Columns<float>::evaluate()
85 Operator, Value and Columns have an evaluate(size_t i, ValueBase* destination) method which returns a Value<T>
86 containing 8 values representing table rows i...i + 7.
88 So Value<T> contains 8 concecutive values and all operations are based on these chunks. This is
89 to save overhead by virtual calls needed for evaluating a query that has been dynamically constructed at runtime.
93 -----------------------------------------------------------------------------------------------------------------------
94 Subexpressions created by the end-user are stack allocated. They are cloned to the heap when passed to UnaryOperator,
95 Operator, and Compare. Those types own the clones and deallocate them when destroyed.
98 Caveats, notes and todos
99 -----------------------------------------------------------------------------------------------------------------------
100 * Perhaps disallow columns from two different tables in same expression
101 * The name Columns (with s) an be confusing because we also have Column (without s)
102 * We have Columns::m_table, Query::m_table and ColumnAccessorBase::m_table that point at the same thing, even with
103 ColumnAccessor<> extending Columns. So m_table is redundant, but this is in order to keep class dependencies and
104 entanglement low so that the design is flexible (if you perhaps later want a Columns class that is not dependent
108 -----------------------------------------------------------------------------------------------------------------------
109 First note that at array level, nulls are distinguished between non-null in different ways:
111 m_data == 0 && m_size == 0
113 Integer, Bool, OldDateTime stored in ArrayIntNull:
114 value == get(0) (entry 0 determins a magic value that represents nulls)
117 null::is_null(value) which tests if value bit-matches one specific bit pattern reserved for null
119 The Columns class encapsulates all this into a simple class that, for any type T has
120 evaluate(size_t index) that reads values from a column, taking nulls in count
127 #ifndef REALM_QUERY_EXPRESSION_HPP
128 #define REALM_QUERY_EXPRESSION_HPP
130 #include <realm/column_link.hpp>
131 #include <realm/column_linklist.hpp>
132 #include <realm/column_table.hpp>
133 #include <realm/column_type_traits.hpp>
134 #include <realm/impl/sequential_getter.hpp>
135 #include <realm/link_view.hpp>
136 #include <realm/metrics/query_info.hpp>
137 #include <realm/query_operators.hpp>
138 #include <realm/util/optional.hpp>
139 #include <realm/util/serializer.hpp>
143 // Normally, if a next-generation-syntax condition is supported by the old query_engine.hpp, a query_engine node is
144 // created because it's faster (by a factor of 5 - 10). Because many of our existing next-generation-syntax unit
145 // unit tests are indeed simple enough to fallback to old query_engine, query_expression gets low test coverage. Undef
146 // flag to get higher query_expression test coverage. This is a good idea to try out each time you develop on/modify
149 #define REALM_OLDQUERY_FALLBACK
156 return a < b ? a : b;
159 #ifdef REALM_OLDQUERY_FALLBACK
160 // Hack to avoid template instantiation errors. See create(). Todo, see if we can simplify only_numeric somehow
163 template <class T, class U>
164 inline T only_numeric(U in)
166 return static_cast<T>(util::unwrap(in));
170 inline int only_numeric(const StringData&)
177 inline int only_numeric(const BinaryData&)
184 inline StringData only_string(T in)
187 static_cast<void>(in);
191 inline StringData only_string(StringData in)
196 template <class T, class U>
197 inline T no_timestamp(U in)
199 return static_cast<T>(util::unwrap(in));
203 inline int no_timestamp(const Timestamp&)
211 #endif // REALM_OLDQUERY_FALLBACK
215 T operator()(T v1, T v2) const
219 static std::string description()
228 T operator()(T v1, T v2) const
232 static std::string description()
241 T operator()(T v1, T v2) const
245 static std::string description()
254 T operator()(T v1, T v2) const
258 static std::string description()
260 return "multiplied by";
268 T operator()(T v) const
272 static std::string description()
274 return "to the power of";
279 // Finds a common type for T1 and T2 according to C++ conversion/promotion in arithmetic (float + int => float, etc)
280 template <class T1, class T2, bool T1_is_int = std::numeric_limits<T1>::is_integer || std::is_same<T1, null>::value,
281 bool T2_is_int = std::numeric_limits<T2>::is_integer || std::is_same<T2, null>::value,
282 bool T1_is_widest = (sizeof(T1) > sizeof(T2) || std::is_same<T2, null>::value)>
284 template <class T1, class T2, bool b>
285 struct Common<T1, T2, b, b, true> {
288 template <class T1, class T2, bool b>
289 struct Common<T1, T2, b, b, false> {
292 template <class T1, class T2, bool b>
293 struct Common<T1, T2, false, true, b> {
296 template <class T1, class T2, bool b>
297 struct Common<T1, T2, true, false, b> {
311 explicit RowIndex(size_t row_index)
312 : m_row_index(row_index)
315 RowIndex(DetachedTag)
320 bool is_attached() const
322 return bool(m_row_index);
326 return is_attached() && *m_row_index == npos;
329 bool operator==(const RowIndex& other) const
331 // Row indexes that are detached are never equal to any other row index.
332 if (!is_attached() || !other.is_attached())
334 return m_row_index == other.m_row_index;
336 bool operator!=(const RowIndex& other) const
338 return !(*this == other);
340 template <class C, class T>
341 friend std::basic_ostream<C, T>& operator<<(std::basic_ostream<C, T>&, const RowIndex&);
344 util::Optional<size_t> m_row_index;
347 template <class C, class T>
348 inline std::basic_ostream<C, T>& operator<<(std::basic_ostream<C, T>& out, const RowIndex& r)
350 if (!r.is_attached()) {
351 out << "detached row";
352 } else if (r.is_null()) {
355 out << r.m_row_index;
361 static const size_t default_size = 8;
362 virtual void export_bool(ValueBase& destination) const = 0;
363 virtual void export_Timestamp(ValueBase& destination) const = 0;
364 virtual void export_int(ValueBase& destination) const = 0;
365 virtual void export_float(ValueBase& destination) const = 0;
366 virtual void export_int64_t(ValueBase& destination) const = 0;
367 virtual void export_double(ValueBase& destination) const = 0;
368 virtual void export_StringData(ValueBase& destination) const = 0;
369 virtual void export_BinaryData(ValueBase& destination) const = 0;
370 virtual void export_RowIndex(ValueBase& destination) const = 0;
371 virtual void export_null(ValueBase& destination) const = 0;
372 virtual void import(const ValueBase& destination) = 0;
374 // If true, all values in the class come from a link list of a single field in the parent table (m_table). If
375 // false, then values come from successive rows of m_table (query operations are operated on in bulks for speed)
376 bool m_from_link_list;
378 // Number of values stored in the class.
387 virtual ~Expression()
391 virtual size_t find_first(size_t start, size_t end) const = 0;
392 virtual void set_base_table(const Table* table) = 0;
393 virtual void verify_column() const = 0;
394 virtual const Table* get_base_table() const = 0;
395 virtual std::string description() const = 0;
397 virtual std::unique_ptr<Expression> clone(QueryNodeHandoverPatches*) const = 0;
398 virtual void apply_handover_patch(QueryNodeHandoverPatches&, Group&)
403 template <typename T, typename... Args>
404 std::unique_ptr<Expression> make_expression(Args&&... args)
406 return std::unique_ptr<Expression>(new T(std::forward<Args>(args)...));
415 virtual std::unique_ptr<Subexpr> clone(QueryNodeHandoverPatches* = nullptr) const = 0;
416 virtual void apply_handover_patch(QueryNodeHandoverPatches&, Group&)
420 // When the user constructs a query, it always "belongs" to one single base/parent table (regardless of
421 // any links or not and regardless of any queries assembled with || or &&). When you do a Query::find(),
422 // then Query::m_table is set to this table, and set_base_table() is called on all Columns and LinkMaps in
423 // the query expression tree so that they can set/update their internals as required.
425 // During thread-handover of a Query, set_base_table() is also called to make objects point at the new table
426 // instead of the old one from the old thread.
427 virtual void set_base_table(const Table*)
431 virtual void verify_column() const = 0;
432 virtual std::string description() const = 0;
434 // Recursively fetch tables of columns in expression tree. Used when user first builds a stand-alone expression
436 // binds it to a Query at a later time
437 virtual const Table* get_base_table() const
442 virtual void evaluate(size_t index, ValueBase& destination) = 0;
445 template <typename T, typename... Args>
446 std::unique_ptr<Subexpr> make_subexpr(Args&&... args)
448 return std::unique_ptr<Subexpr>(new T(std::forward<Args>(args)...));
455 class ConstantStringValue;
458 template <class oper, class TLeft = Subexpr, class TRight = Subexpr>
460 template <class oper, class TLeft = Subexpr>
462 template <class oper, class TLeft = Subexpr>
464 template <class TCond, class T, class TLeft = Subexpr, class TRight = Subexpr>
466 template <bool has_links>
467 class UnaryLinkCompare;
468 class ColumnAccessorBase;
471 // Handle cases where left side is a constant (int, float, int64_t, double, StringData)
472 template <class Cond, class L, class R>
473 Query create(L left, const Subexpr2<R>& right)
475 // Purpose of below code is to intercept the creation of a condition and test if it's supported by the old
476 // query_engine.hpp which is faster. If it's supported, create a query_engine.hpp node, otherwise create a
477 // query_expression.hpp node.
479 // This method intercepts only Value <cond> Subexpr2. Interception of Subexpr2 <cond> Subexpr is elsewhere.
481 #ifdef REALM_OLDQUERY_FALLBACK // if not defined, then never fallback to query_engine.hpp; always use query_expression
482 const Columns<R>* column = dynamic_cast<const Columns<R>*>(&right);
483 // TODO: recognize size operator expressions
484 // auto size_operator = dynamic_cast<const SizeOperator<Size<StringData>, Subexpr>*>(&right);
486 if (column && ((std::numeric_limits<L>::is_integer && std::numeric_limits<R>::is_integer) ||
487 (std::is_same<L, double>::value && std::is_same<R, double>::value) ||
488 (std::is_same<L, float>::value && std::is_same<R, float>::value) ||
489 (std::is_same<L, Timestamp>::value && std::is_same<R, Timestamp>::value) ||
490 (std::is_same<L, StringData>::value && std::is_same<R, StringData>::value) ||
491 (std::is_same<L, BinaryData>::value && std::is_same<R, BinaryData>::value)) &&
492 !column->links_exist()) {
493 const Table* t = column->get_base_table();
496 if (std::is_same<Cond, Less>::value)
497 q.greater(column->column_ndx(), _impl::only_numeric<R>(left));
498 else if (std::is_same<Cond, Greater>::value)
499 q.less(column->column_ndx(), _impl::only_numeric<R>(left));
500 else if (std::is_same<Cond, Equal>::value)
501 q.equal(column->column_ndx(), left);
502 else if (std::is_same<Cond, NotEqual>::value)
503 q.not_equal(column->column_ndx(), left);
504 else if (std::is_same<Cond, LessEqual>::value)
505 q.greater_equal(column->column_ndx(), _impl::only_numeric<R>(left));
506 else if (std::is_same<Cond, GreaterEqual>::value)
507 q.less_equal(column->column_ndx(), _impl::only_numeric<R>(left));
508 else if (std::is_same<Cond, EqualIns>::value)
509 q.equal(column->column_ndx(), _impl::only_string(left), false);
510 else if (std::is_same<Cond, NotEqualIns>::value)
511 q.not_equal(column->column_ndx(), _impl::only_string(left), false);
512 else if (std::is_same<Cond, BeginsWith>::value)
513 q.begins_with(column->column_ndx(), _impl::only_string(left));
514 else if (std::is_same<Cond, BeginsWithIns>::value)
515 q.begins_with(column->column_ndx(), _impl::only_string(left), false);
516 else if (std::is_same<Cond, EndsWith>::value)
517 q.ends_with(column->column_ndx(), _impl::only_string(left));
518 else if (std::is_same<Cond, EndsWithIns>::value)
519 q.ends_with(column->column_ndx(), _impl::only_string(left), false);
520 else if (std::is_same<Cond, Contains>::value)
521 q.contains(column->column_ndx(), _impl::only_string(left));
522 else if (std::is_same<Cond, ContainsIns>::value)
523 q.contains(column->column_ndx(), _impl::only_string(left), false);
524 else if (std::is_same<Cond, Like>::value)
525 q.like(column->column_ndx(), _impl::only_string(left));
526 else if (std::is_same<Cond, LikeIns>::value)
527 q.like(column->column_ndx(), _impl::only_string(left), false);
529 // query_engine.hpp does not support this Cond. Please either add support for it in query_engine.hpp or
530 // fallback to using use 'return new Compare<>' instead.
533 // Return query_engine.hpp node
539 // Return query_expression.hpp node
540 using CommonType = typename Common<L, R>::type;
542 typename std::conditional<std::is_same<L, StringData>::value, ConstantStringValue, Value<L>>::type;
543 return make_expression<Compare<Cond, CommonType>>(make_subexpr<ValueType>(left), right.clone());
548 // All overloads where left-hand-side is Subexpr2<L>:
550 // left-hand-side operator right-hand-side
551 // Subexpr2<L> +, -, *, /, <, >, ==, !=, <=, >= R, Subexpr2<R>
553 // For L = R = {int, int64_t, float, double, StringData, Timestamp}:
554 template <class L, class R>
556 typedef typename Common<L, R>::type CommonType;
558 std::unique_ptr<Subexpr> clone_subexpr() const
560 return static_cast<const Subexpr2<L>&>(*this).clone();
564 // Arithmetic, right side constant
565 Operator<Plus<CommonType>> operator+(R right) const
567 return {clone_subexpr(), make_subexpr<Value<R>>(right)};
569 Operator<Minus<CommonType>> operator-(R right) const
571 return {clone_subexpr(), make_subexpr<Value<R>>(right)};
573 Operator<Mul<CommonType>> operator*(R right) const
575 return {clone_subexpr(), make_subexpr<Value<R>>(right)};
577 Operator<Div<CommonType>> operator/(R right) const
579 return {clone_subexpr(), make_subexpr<Value<R>>(right)};
582 // Arithmetic, right side subexpression
583 Operator<Plus<CommonType>> operator+(const Subexpr2<R>& right) const
585 return {clone_subexpr(), right.clone()};
587 Operator<Minus<CommonType>> operator-(const Subexpr2<R>& right) const
589 return {clone_subexpr(), right.clone()};
591 Operator<Mul<CommonType>> operator*(const Subexpr2<R>& right) const
593 return {clone_subexpr(), right.clone()};
595 Operator<Div<CommonType>> operator/(const Subexpr2<R>& right) const
597 return {clone_subexpr(), right.clone()};
600 // Compare, right side constant
601 Query operator>(R right)
603 return create<Less>(right, static_cast<Subexpr2<L>&>(*this));
605 Query operator<(R right)
607 return create<Greater>(right, static_cast<Subexpr2<L>&>(*this));
609 Query operator>=(R right)
611 return create<LessEqual>(right, static_cast<Subexpr2<L>&>(*this));
613 Query operator<=(R right)
615 return create<GreaterEqual>(right, static_cast<Subexpr2<L>&>(*this));
617 Query operator==(R right)
619 return create<Equal>(right, static_cast<Subexpr2<L>&>(*this));
621 Query operator!=(R right)
623 return create<NotEqual>(right, static_cast<Subexpr2<L>&>(*this));
626 // Purpose of this method is to intercept the creation of a condition and test if it's supported by the old
627 // query_engine.hpp which is faster. If it's supported, create a query_engine.hpp node, otherwise create a
628 // query_expression.hpp node.
630 // This method intercepts Subexpr2 <cond> Subexpr2 only. Value <cond> Subexpr2 is intercepted elsewhere.
631 template <class Cond>
632 Query create2(const Subexpr2<R>& right)
634 #ifdef REALM_OLDQUERY_FALLBACK // if not defined, never fallback query_engine; always use query_expression
635 // Test if expressions are of type Columns. Other possibilities are Value and Operator.
636 const Columns<R>* left_col = dynamic_cast<const Columns<R>*>(static_cast<Subexpr2<L>*>(this));
637 const Columns<R>* right_col = dynamic_cast<const Columns<R>*>(&right);
639 // query_engine supports 'T-column <op> <T-column>' for T = {int64_t, float, double}, op = {<, >, ==, !=, <=,
641 // but only if both columns are non-nullable, and aren't in linked tables.
642 if (left_col && right_col && std::is_same<L, R>::value && !left_col->is_nullable() &&
643 !right_col->is_nullable() && !left_col->links_exist() && !right_col->links_exist() &&
644 !std::is_same<L, Timestamp>::value) {
645 const Table* t = left_col->get_base_table();
648 if (std::numeric_limits<L>::is_integer || std::is_same<L, OldDateTime>::value) {
649 if (std::is_same<Cond, Less>::value)
650 q.less_int(left_col->column_ndx(), right_col->column_ndx());
651 else if (std::is_same<Cond, Greater>::value)
652 q.greater_int(left_col->column_ndx(), right_col->column_ndx());
653 else if (std::is_same<Cond, Equal>::value)
654 q.equal_int(left_col->column_ndx(), right_col->column_ndx());
655 else if (std::is_same<Cond, NotEqual>::value)
656 q.not_equal_int(left_col->column_ndx(), right_col->column_ndx());
657 else if (std::is_same<Cond, LessEqual>::value)
658 q.less_equal_int(left_col->column_ndx(), right_col->column_ndx());
659 else if (std::is_same<Cond, GreaterEqual>::value)
660 q.greater_equal_int(left_col->column_ndx(), right_col->column_ndx());
665 else if (std::is_same<L, float>::value) {
666 if (std::is_same<Cond, Less>::value)
667 q.less_float(left_col->column_ndx(), right_col->column_ndx());
668 else if (std::is_same<Cond, Greater>::value)
669 q.greater_float(left_col->column_ndx(), right_col->column_ndx());
670 else if (std::is_same<Cond, Equal>::value)
671 q.equal_float(left_col->column_ndx(), right_col->column_ndx());
672 else if (std::is_same<Cond, NotEqual>::value)
673 q.not_equal_float(left_col->column_ndx(), right_col->column_ndx());
674 else if (std::is_same<Cond, LessEqual>::value)
675 q.less_equal_float(left_col->column_ndx(), right_col->column_ndx());
676 else if (std::is_same<Cond, GreaterEqual>::value)
677 q.greater_equal_float(left_col->column_ndx(), right_col->column_ndx());
682 else if (std::is_same<L, double>::value) {
683 if (std::is_same<Cond, Less>::value)
684 q.less_double(left_col->column_ndx(), right_col->column_ndx());
685 else if (std::is_same<Cond, Greater>::value)
686 q.greater_double(left_col->column_ndx(), right_col->column_ndx());
687 else if (std::is_same<Cond, Equal>::value)
688 q.equal_double(left_col->column_ndx(), right_col->column_ndx());
689 else if (std::is_same<Cond, NotEqual>::value)
690 q.not_equal_double(left_col->column_ndx(), right_col->column_ndx());
691 else if (std::is_same<Cond, LessEqual>::value)
692 q.less_equal_double(left_col->column_ndx(), right_col->column_ndx());
693 else if (std::is_same<Cond, GreaterEqual>::value)
694 q.greater_equal_double(left_col->column_ndx(), right_col->column_ndx());
702 // Return query_engine.hpp node
708 // Return query_expression.hpp node
709 return make_expression<Compare<Cond, typename Common<L, R>::type>>(clone_subexpr(), right.clone());
713 // Compare, right side subexpression
714 Query operator==(const Subexpr2<R>& right)
716 return create2<Equal>(right);
718 Query operator!=(const Subexpr2<R>& right)
720 return create2<NotEqual>(right);
722 Query operator>(const Subexpr2<R>& right)
724 return create2<Greater>(right);
726 Query operator<(const Subexpr2<R>& right)
728 return create2<Less>(right);
730 Query operator>=(const Subexpr2<R>& right)
732 return create2<GreaterEqual>(right);
734 Query operator<=(const Subexpr2<R>& right)
736 return create2<LessEqual>(right);
740 // With this wrapper class we can define just 20 overloads inside Overloads<L, R> instead of 5 * 20 = 100. Todo: We
742 // consider if it's simpler/better to remove this class completely and just list all 100 overloads manually anyway.
744 class Subexpr2 : public Subexpr,
745 public Overloads<T, const char*>,
746 public Overloads<T, int>,
747 public Overloads<T, float>,
748 public Overloads<T, double>,
749 public Overloads<T, int64_t>,
750 public Overloads<T, StringData>,
751 public Overloads<T, bool>,
752 public Overloads<T, Timestamp>,
753 public Overloads<T, OldDateTime>,
754 public Overloads<T, null> {
760 #define RLM_U2(t, o) using Overloads<T, t>::operator o;
766 RLM_U2(StringData, o) RLM_U2(bool, o) RLM_U2(OldDateTime, o) RLM_U2(Timestamp, o) RLM_U2(null, o)
767 RLM_U(+) RLM_U(-) RLM_U(*) RLM_U(/) RLM_U(>) RLM_U(<) RLM_U(==) RLM_U(!=) RLM_U(>=) RLM_U(<=)
770 // Subexpr2<Link> only provides equality comparisons. Their implementations can be found later in this file.
772 class Subexpr2<Link> : public Subexpr {
776 class Subexpr2<StringData> : public Subexpr, public Overloads<StringData, StringData> {
778 Query equal(StringData sd, bool case_sensitive = true);
779 Query equal(const Subexpr2<StringData>& col, bool case_sensitive = true);
780 Query not_equal(StringData sd, bool case_sensitive = true);
781 Query not_equal(const Subexpr2<StringData>& col, bool case_sensitive = true);
782 Query begins_with(StringData sd, bool case_sensitive = true);
783 Query begins_with(const Subexpr2<StringData>& col, bool case_sensitive = true);
784 Query ends_with(StringData sd, bool case_sensitive = true);
785 Query ends_with(const Subexpr2<StringData>& col, bool case_sensitive = true);
786 Query contains(StringData sd, bool case_sensitive = true);
787 Query contains(const Subexpr2<StringData>& col, bool case_sensitive = true);
788 Query like(StringData sd, bool case_sensitive = true);
789 Query like(const Subexpr2<StringData>& col, bool case_sensitive = true);
793 This class is used to store N values of type T = {int64_t, bool, OldDateTime or StringData}, and allows an entry
794 to be null too. It's used by the Value class for internal storage.
796 To indicate nulls, we could have chosen a separate bool vector or some other bitmask construction. But for
797 performance, we customize indication of nulls to match the same indication that is used in the persisted database
800 Queries in query_expression.hpp execute by processing chunks of 8 rows at a time. Assume you have a column:
802 price (int) = {1, 2, 3, null, 1, 6, 6, 9, 5, 2, null}
806 Query q = (price + 2 == 5);
808 query_expression.hpp will then create a NullableVector<int> = {5, 5, 5, 5, 5, 5, 5, 5} and then read values
809 NullableVector<int> = {1, 2, 3, null, 1, 6, 6, 9} from the column, and then perform `+` and `==` on these chunks.
811 See the top of this file for more information on all this.
813 Assume the user specifies the null constant in a query:
815 Query q = (price == null)
817 The query system will then construct a NullableVector of type `null` (NullableVector<null>). This allows compile
818 time optimizations for these cases.
821 template <class T, size_t prealloc = 8>
822 struct NullableVector {
823 using Underlying = typename util::RemoveOptional<T>::type;
825 typename std::conditional<std::is_same<Underlying, bool>::value || std::is_same<Underlying, int>::value,
826 int64_t, Underlying>::type;
832 NullableVector& operator=(const NullableVector& other)
834 if (this != &other) {
836 realm::safe_copy_n(other.m_first, other.m_size, m_first);
837 m_null = other.m_null;
842 NullableVector(const NullableVector& other)
845 realm::safe_copy_n(other.m_first, other.m_size, m_first);
846 m_null = other.m_null;
854 T operator[](size_t index) const
856 REALM_ASSERT_3(index, <, m_size);
857 return static_cast<T>(m_first[index]);
860 inline bool is_null(size_t index) const
862 REALM_ASSERT((std::is_same<t_storage, int64_t>::value));
863 return m_first[index] == m_null;
866 inline void set_null(size_t index)
868 REALM_ASSERT((std::is_same<t_storage, int64_t>::value));
869 m_first[index] = m_null;
872 template <typename Type = t_storage>
873 typename std::enable_if<std::is_same<Type, int64_t>::value, void>::type set(size_t index, t_storage value)
875 REALM_ASSERT((std::is_same<t_storage, int64_t>::value));
877 // If value collides with magic null value, then switch to a new unique representation for null
878 if (REALM_UNLIKELY(value == m_null)) {
879 // adding a prime will generate 2^64 unique values. Todo: Only works on 2's complement architecture
880 uint64_t candidate = static_cast<uint64_t>(m_null) + 0xfffffffbULL;
881 while (std::find(m_first, m_first + m_size, static_cast<int64_t>(candidate)) != m_first + m_size)
882 candidate += 0xfffffffbULL;
883 std::replace(m_first, m_first + m_size, m_null, static_cast<int64_t>(candidate));
885 m_first[index] = value;
888 template <typename Type = T>
889 typename std::enable_if<realm::is_any<Type, float, double, OldDateTime, BinaryData, StringData, RowIndex,
890 Timestamp, ConstTableRef, null>::value,
892 set(size_t index, t_storage value)
894 m_first[index] = value;
897 inline util::Optional<T> get(size_t index) const
902 return util::make_optional((*this)[index]);
905 inline void set(size_t index, util::Optional<Underlying> value)
908 Underlying v = *value;
918 for (size_t t = 0; t < m_size; t++) {
919 if (std::is_same<T, null>::value)
926 void init(size_t size)
934 if (m_size > prealloc)
935 m_first = reinterpret_cast<t_storage*>(new t_storage[m_size]);
941 void init(size_t size, T values)
950 if (m_size > prealloc)
956 t_storage m_cache[prealloc];
957 t_storage* m_first = &m_cache[0];
960 int64_t m_null = reinterpret_cast<int64_t>(&m_null); // choose magic value to represent nulls
964 // NOTE: fails in gcc 4.8 without `inline`. Do not remove. Same applies for all methods below.
966 inline bool NullableVector<double>::is_null(size_t index) const
968 return null::is_null_float(m_first[index]);
972 inline void NullableVector<double>::set_null(size_t index)
974 m_first[index] = null::get_null_float<double>();
979 inline bool NullableVector<float>::is_null(size_t index) const
981 return null::is_null_float(m_first[index]);
985 inline void NullableVector<float>::set_null(size_t index)
987 m_first[index] = null::get_null_float<float>();
993 inline void NullableVector<null>::set_null(size_t)
998 inline bool NullableVector<null>::is_null(size_t) const
1006 inline bool NullableVector<OldDateTime>::is_null(size_t index) const
1008 return m_first[index].get_olddatetime() == m_null;
1013 inline void NullableVector<OldDateTime>::set_null(size_t index)
1015 m_first[index] = m_null;
1021 inline bool NullableVector<StringData>::is_null(size_t index) const
1023 return m_first[index].is_null();
1027 inline void NullableVector<StringData>::set_null(size_t index)
1029 m_first[index] = StringData();
1035 inline bool NullableVector<BinaryData>::is_null(size_t index) const
1037 return m_first[index].is_null();
1041 inline void NullableVector<BinaryData>::set_null(size_t index)
1043 m_first[index] = BinaryData();
1048 inline bool NullableVector<RowIndex>::is_null(size_t index) const
1050 return m_first[index].is_null();
1053 inline void NullableVector<RowIndex>::set_null(size_t index)
1055 m_first[index] = RowIndex();
1062 inline bool NullableVector<Timestamp>::is_null(size_t index) const
1064 return m_first[index].is_null();
1068 inline void NullableVector<Timestamp>::set_null(size_t index)
1070 m_first[index] = Timestamp{};
1075 inline bool NullableVector<ConstTableRef>::is_null(size_t index) const
1077 return !bool(m_first[index]);
1080 inline void NullableVector<ConstTableRef>::set_null(size_t index)
1082 m_first[index].reset();
1085 template <typename Operator>
1086 struct OperatorOptionalAdapter {
1087 template <typename L, typename R>
1088 util::Optional<typename Operator::type> operator()(const util::Optional<L>& left, const util::Optional<R>& right)
1090 if (!left || !right)
1092 return Operator()(*left, *right);
1095 template <typename T>
1096 util::Optional<typename Operator::type> operator()(const util::Optional<T>& arg)
1100 return Operator()(*arg);
1105 struct TrueExpression : Expression {
1106 size_t find_first(size_t start, size_t end) const override
1108 REALM_ASSERT(start <= end);
1112 return realm::not_found;
1114 void set_base_table(const Table*) override
1117 const Table* get_base_table() const override
1121 void verify_column() const override
1124 std::string description() const override
1126 return "TRUEPREDICATE";
1128 std::unique_ptr<Expression> clone(QueryNodeHandoverPatches*) const override
1130 return std::unique_ptr<Expression>(new TrueExpression(*this));
1135 struct FalseExpression : Expression {
1136 size_t find_first(size_t, size_t) const override
1138 return realm::not_found;
1140 void set_base_table(const Table*) override
1143 void verify_column() const override
1146 std::string description() const override
1148 return "FALSEPREDICATE";
1150 const Table* get_base_table() const override
1154 std::unique_ptr<Expression> clone(QueryNodeHandoverPatches*) const override
1156 return std::unique_ptr<Expression>(new FalseExpression(*this));
1161 // Stores N values of type T. Can also exchange data with other ValueBase of different types
1163 class Value : public ValueBase, public Subexpr2<T> {
1167 init(false, ValueBase::default_size, T());
1171 init(false, ValueBase::default_size, v);
1174 Value(bool from_link_list, size_t values)
1176 init(from_link_list, values, T());
1179 Value(bool from_link_list, size_t values, T v)
1181 init(from_link_list, values, v);
1184 Value(const Value&) = default;
1185 Value& operator=(const Value&) = default;
1187 void init(bool from_link_list, size_t values, T v)
1189 m_storage.init(values, v);
1190 ValueBase::m_from_link_list = from_link_list;
1191 ValueBase::m_values = values;
1194 void init(bool from_link_list, size_t values)
1196 m_storage.init(values);
1197 ValueBase::m_from_link_list = from_link_list;
1198 ValueBase::m_values = values;
1201 void verify_column() const override
1205 virtual std::string description() const override
1207 if (ValueBase::m_from_link_list) {
1208 return util::serializer::print_value(util::to_string(ValueBase::m_values)
1209 + (ValueBase::m_values == 1 ? " value" : " values"));
1211 if (m_storage.m_size > 0) {
1212 return util::serializer::print_value(m_storage[0]);
1217 void evaluate(size_t, ValueBase& destination) override
1219 destination.import(*this);
1223 template <class TOperator>
1224 REALM_FORCEINLINE void fun(const Value* left, const Value* right)
1226 OperatorOptionalAdapter<TOperator> o;
1228 if (!left->m_from_link_list && !right->m_from_link_list) {
1229 // Operate on values one-by-one (one value is one row; no links)
1230 size_t min = std::min(left->m_values, right->m_values);
1233 for (size_t i = 0; i < min; i++) {
1234 m_storage.set(i, o(left->m_storage.get(i), right->m_storage.get(i)));
1237 else if (left->m_from_link_list && right->m_from_link_list) {
1238 // FIXME: Many-to-many links not supported yet. Need to specify behaviour
1239 REALM_ASSERT_DEBUG(false);
1241 else if (!left->m_from_link_list && right->m_from_link_list) {
1242 // Right values come from link. Left must come from single row.
1243 REALM_ASSERT_DEBUG(left->m_values > 0);
1244 init(true, right->m_values);
1246 auto left_value = left->m_storage.get(0);
1247 for (size_t i = 0; i < right->m_values; i++) {
1248 m_storage.set(i, o(left_value, right->m_storage.get(i)));
1251 else if (left->m_from_link_list && !right->m_from_link_list) {
1252 // Same as above, but with left values coming from links
1253 REALM_ASSERT_DEBUG(right->m_values > 0);
1254 init(true, left->m_values);
1256 auto right_value = right->m_storage.get(0);
1257 for (size_t i = 0; i < left->m_values; i++) {
1258 m_storage.set(i, o(left->m_storage.get(i), right_value));
1263 template <class TOperator>
1264 REALM_FORCEINLINE void fun(const Value* value)
1266 init(value->m_from_link_list, value->m_values);
1268 OperatorOptionalAdapter<TOperator> o;
1269 for (size_t i = 0; i < value->m_values; i++) {
1270 m_storage.set(i, o(value->m_storage.get(i)));
1275 // Below import and export methods are for type conversion between float, double, int64_t, etc.
1277 typename std::enable_if<std::is_convertible<T, D>::value>::type REALM_FORCEINLINE
1278 export2(ValueBase& destination) const
1280 Value<D>& d = static_cast<Value<D>&>(destination);
1281 d.init(ValueBase::m_from_link_list, ValueBase::m_values, D());
1282 for (size_t t = 0; t < ValueBase::m_values; t++) {
1283 if (m_storage.is_null(t))
1284 d.m_storage.set_null(t);
1286 d.m_storage.set(t, static_cast<D>(m_storage[t]));
1292 typename std::enable_if<!std::is_convertible<T, D>::value>::type REALM_FORCEINLINE export2(ValueBase&) const
1294 // export2 is instantiated for impossible conversions like T=StringData, D=int64_t. These are never
1295 // performed at runtime but would result in a compiler error if we did not provide this implementation.
1296 REALM_ASSERT_DEBUG(false);
1299 REALM_FORCEINLINE void export_Timestamp(ValueBase& destination) const override
1301 export2<Timestamp>(destination);
1304 REALM_FORCEINLINE void export_bool(ValueBase& destination) const override
1306 export2<bool>(destination);
1309 REALM_FORCEINLINE void export_int64_t(ValueBase& destination) const override
1311 export2<int64_t>(destination);
1314 REALM_FORCEINLINE void export_float(ValueBase& destination) const override
1316 export2<float>(destination);
1319 REALM_FORCEINLINE void export_int(ValueBase& destination) const override
1321 export2<int>(destination);
1324 REALM_FORCEINLINE void export_double(ValueBase& destination) const override
1326 export2<double>(destination);
1328 REALM_FORCEINLINE void export_StringData(ValueBase& destination) const override
1330 export2<StringData>(destination);
1332 REALM_FORCEINLINE void export_BinaryData(ValueBase& destination) const override
1334 export2<BinaryData>(destination);
1336 REALM_FORCEINLINE void export_RowIndex(ValueBase& destination) const override
1338 export2<RowIndex>(destination);
1340 REALM_FORCEINLINE void export_null(ValueBase& destination) const override
1342 Value<null>& d = static_cast<Value<null>&>(destination);
1343 d.init(m_from_link_list, m_values);
1346 REALM_FORCEINLINE void import(const ValueBase& source) override
1348 if (std::is_same<T, int>::value)
1349 source.export_int(*this);
1350 else if (std::is_same<T, Timestamp>::value)
1351 source.export_Timestamp(*this);
1352 else if (std::is_same<T, bool>::value)
1353 source.export_bool(*this);
1354 else if (std::is_same<T, float>::value)
1355 source.export_float(*this);
1356 else if (std::is_same<T, double>::value)
1357 source.export_double(*this);
1358 else if (std::is_same<T, int64_t>::value || std::is_same<T, bool>::value ||
1359 std::is_same<T, OldDateTime>::value)
1360 source.export_int64_t(*this);
1361 else if (std::is_same<T, StringData>::value)
1362 source.export_StringData(*this);
1363 else if (std::is_same<T, BinaryData>::value)
1364 source.export_BinaryData(*this);
1365 else if (std::is_same<T, RowIndex>::value)
1366 source.export_RowIndex(*this);
1367 else if (std::is_same<T, null>::value)
1368 source.export_null(*this);
1370 REALM_ASSERT_DEBUG(false);
1373 // Given a TCond (==, !=, >, <, >=, <=) and two Value<T>, return index of first match
1374 template <class TCond>
1375 REALM_FORCEINLINE static size_t compare(Value<T>* left, Value<T>* right)
1379 if (!left->m_from_link_list && !right->m_from_link_list) {
1380 // Compare values one-by-one (one value is one row; no link lists)
1381 size_t min = minimum(left->ValueBase::m_values, right->ValueBase::m_values);
1382 for (size_t m = 0; m < min; m++) {
1384 if (c(left->m_storage[m], right->m_storage[m], left->m_storage.is_null(m),
1385 right->m_storage.is_null(m)))
1389 else if (left->m_from_link_list && right->m_from_link_list) {
1390 // FIXME: Many-to-many links not supported yet. Need to specify behaviour
1391 REALM_ASSERT_DEBUG(false);
1393 else if (!left->m_from_link_list && right->m_from_link_list) {
1394 // Right values come from link list. Left must come from single row. Semantics: Match if at least 1
1395 // linked-to-value fulfills the condition
1396 REALM_ASSERT_DEBUG(left->m_values > 0);
1397 for (size_t r = 0; r < right->m_values; r++) {
1398 if (c(left->m_storage[0], right->m_storage[r], left->m_storage.is_null(0),
1399 right->m_storage.is_null(r)))
1403 else if (left->m_from_link_list && !right->m_from_link_list) {
1404 // Same as above, but with left values coming from link list.
1405 REALM_ASSERT_DEBUG(right->m_values > 0);
1406 for (size_t l = 0; l < left->m_values; l++) {
1407 if (c(left->m_storage[l], right->m_storage[0], left->m_storage.is_null(l),
1408 right->m_storage.is_null(0)))
1413 return not_found; // no match
1416 std::unique_ptr<Subexpr> clone(QueryNodeHandoverPatches*) const override
1418 return make_subexpr<Value<T>>(*this);
1421 NullableVector<T> m_storage;
1424 class ConstantStringValue : public Value<StringData> {
1426 ConstantStringValue(const StringData& string)
1428 , m_string(string.is_null() ? util::none : util::make_optional(std::string(string)))
1430 init(false, ValueBase::default_size, m_string);
1433 std::unique_ptr<Subexpr> clone(QueryNodeHandoverPatches*) const override
1435 return std::unique_ptr<Subexpr>(new ConstantStringValue(*this));
1439 ConstantStringValue(const ConstantStringValue& other)
1441 , m_string(other.m_string)
1443 init(other.m_from_link_list, other.m_values, m_string);
1446 util::Optional<std::string> m_string;
1449 // All overloads where left-hand-side is L:
1451 // left-hand-side operator right-hand-side
1452 // L +, -, *, /, <, >, ==, !=, <=, >= Subexpr2<R>
1454 // For L = R = {int, int64_t, float, double, Timestamp}:
1455 // Compare numeric values
1457 Query operator>(double left, const Subexpr2<R>& right)
1459 return create<Greater>(left, right);
1462 Query operator>(float left, const Subexpr2<R>& right)
1464 return create<Greater>(left, right);
1467 Query operator>(int left, const Subexpr2<R>& right)
1469 return create<Greater>(left, right);
1472 Query operator>(int64_t left, const Subexpr2<R>& right)
1474 return create<Greater>(left, right);
1477 Query operator>(Timestamp left, const Subexpr2<R>& right)
1479 return create<Greater>(left, right);
1483 Query operator<(double left, const Subexpr2<R>& right)
1485 return create<Less>(left, right);
1488 Query operator<(float left, const Subexpr2<R>& right)
1490 return create<Less>(left, right);
1493 Query operator<(int left, const Subexpr2<R>& right)
1495 return create<Less>(left, right);
1498 Query operator<(int64_t left, const Subexpr2<R>& right)
1500 return create<Less>(left, right);
1503 Query operator<(Timestamp left, const Subexpr2<R>& right)
1505 return create<Less>(left, right);
1508 Query operator==(double left, const Subexpr2<R>& right)
1510 return create<Equal>(left, right);
1513 Query operator==(float left, const Subexpr2<R>& right)
1515 return create<Equal>(left, right);
1518 Query operator==(int left, const Subexpr2<R>& right)
1520 return create<Equal>(left, right);
1523 Query operator==(int64_t left, const Subexpr2<R>& right)
1525 return create<Equal>(left, right);
1528 Query operator==(Timestamp left, const Subexpr2<R>& right)
1530 return create<Equal>(left, right);
1533 Query operator>=(double left, const Subexpr2<R>& right)
1535 return create<GreaterEqual>(left, right);
1538 Query operator>=(float left, const Subexpr2<R>& right)
1540 return create<GreaterEqual>(left, right);
1543 Query operator>=(int left, const Subexpr2<R>& right)
1545 return create<GreaterEqual>(left, right);
1548 Query operator>=(int64_t left, const Subexpr2<R>& right)
1550 return create<GreaterEqual>(left, right);
1553 Query operator>=(Timestamp left, const Subexpr2<R>& right)
1555 return create<GreaterEqual>(left, right);
1558 Query operator<=(double left, const Subexpr2<R>& right)
1560 return create<LessEqual>(left, right);
1563 Query operator<=(float left, const Subexpr2<R>& right)
1565 return create<LessEqual>(left, right);
1568 Query operator<=(int left, const Subexpr2<R>& right)
1570 return create<LessEqual>(left, right);
1573 Query operator<=(int64_t left, const Subexpr2<R>& right)
1575 return create<LessEqual>(left, right);
1578 Query operator<=(Timestamp left, const Subexpr2<R>& right)
1580 return create<LessEqual>(left, right);
1583 Query operator!=(double left, const Subexpr2<R>& right)
1585 return create<NotEqual>(left, right);
1588 Query operator!=(float left, const Subexpr2<R>& right)
1590 return create<NotEqual>(left, right);
1593 Query operator!=(int left, const Subexpr2<R>& right)
1595 return create<NotEqual>(left, right);
1598 Query operator!=(int64_t left, const Subexpr2<R>& right)
1600 return create<NotEqual>(left, right);
1603 Query operator!=(Timestamp left, const Subexpr2<R>& right)
1605 return create<NotEqual>(left, right);
1610 Operator<Plus<typename Common<R, double>::type>> operator+(double left, const Subexpr2<R>& right)
1612 return {make_subexpr<Value<double>>(left), right.clone()};
1615 Operator<Plus<typename Common<R, float>::type>> operator+(float left, const Subexpr2<R>& right)
1617 return {make_subexpr<Value<float>>(left), right.clone()};
1620 Operator<Plus<typename Common<R, int>::type>> operator+(int left, const Subexpr2<R>& right)
1622 return {make_subexpr<Value<int>>(left), right.clone()};
1625 Operator<Plus<typename Common<R, int64_t>::type>> operator+(int64_t left, const Subexpr2<R>& right)
1627 return {make_subexpr<Value<int64_t>>(left), right.clone()};
1630 Operator<Minus<typename Common<R, double>::type>> operator-(double left, const Subexpr2<R>& right)
1632 return {make_subexpr<Value<double>>(left), right.clone()};
1635 Operator<Minus<typename Common<R, float>::type>> operator-(float left, const Subexpr2<R>& right)
1637 return {make_subexpr<Value<float>>(left), right.clone()};
1640 Operator<Minus<typename Common<R, int>::type>> operator-(int left, const Subexpr2<R>& right)
1642 return {make_subexpr<Value<int>>(left), right.clone()};
1645 Operator<Minus<typename Common<R, int64_t>::type>> operator-(int64_t left, const Subexpr2<R>& right)
1647 return {make_subexpr<Value<int64_t>>(left), right.clone()};
1650 Operator<Mul<typename Common<R, double>::type>> operator*(double left, const Subexpr2<R>& right)
1652 return {make_subexpr<Value<double>>(left), right.clone()};
1655 Operator<Mul<typename Common<R, float>::type>> operator*(float left, const Subexpr2<R>& right)
1657 return {make_subexpr<Value<float>>(left), right.clone()};
1660 Operator<Mul<typename Common<R, int>::type>> operator*(int left, const Subexpr2<R>& right)
1662 return {make_subexpr<Value<int>>(left), right.clone()};
1665 Operator<Mul<typename Common<R, int64_t>::type>> operator*(int64_t left, const Subexpr2<R>& right)
1667 return {make_subexpr<Value<int64_t>>(left), right.clone()};
1670 Operator<Div<typename Common<R, double>::type>> operator/(double left, const Subexpr2<R>& right)
1672 return {make_subexpr<Value<double>>(left), right.clone()};
1675 Operator<Div<typename Common<R, float>::type>> operator/(float left, const Subexpr2<R>& right)
1677 return {make_subexpr<Value<float>>(left), right.clone()};
1680 Operator<Div<typename Common<R, int>::type>> operator/(int left, const Subexpr2<R>& right)
1682 return {make_subexpr<Value<int>>(left), right.clone()};
1685 Operator<Div<typename Common<R, int64_t>::type>> operator/(int64_t left, const Subexpr2<R>& right)
1687 return {make_subexpr<Value<int64_t>>(left), right.clone()};
1692 UnaryOperator<Pow<T>> power(const Subexpr2<T>& left)
1694 return {left.clone()};
1697 // Classes used for LinkMap (see below).
1698 struct LinkMapFunction {
1699 // Your consume() method is given row index of the linked-to table as argument, and you must return whether or
1700 // not you want the LinkMapFunction to exit (return false) or continue (return true) harvesting the link tree
1701 // for the current main table row index (it will be a link tree if you have multiple type_LinkList columns
1702 // in a link()->link() query.
1703 virtual bool consume(size_t row_index) = 0;
1706 struct FindNullLinks : public LinkMapFunction {
1707 bool consume(size_t row_index) override
1709 static_cast<void>(row_index);
1711 return false; // we've found a row index, so this can't be a null-link, so exit link harvesting
1714 bool m_has_link = false;
1717 struct MakeLinkVector : public LinkMapFunction {
1718 MakeLinkVector(std::vector<size_t>& result)
1723 bool consume(size_t row_index) override
1725 m_links.push_back(row_index);
1726 return true; // continue evaluation
1728 std::vector<size_t>& m_links;
1731 struct CountLinks : public LinkMapFunction {
1732 bool consume(size_t) override
1738 size_t result() const
1740 return m_link_count;
1743 size_t m_link_count = 0;
1748 The LinkMap and LinkMapFunction classes are used for query conditions on links themselves (contrary to conditions on
1749 the value payload they point at).
1751 MapLink::map_links() takes a row index of the link column as argument and follows any link chain stated in the query
1752 (through the link()->link() methods) until the final payload table is reached, and then applies LinkMapFunction on
1753 the linked-to row index(es).
1755 If all link columns are type_Link, then LinkMapFunction is only invoked for a single row index. If one or more
1756 columns are type_LinkList, then it may result in multiple row indexes.
1758 The reason we use this map pattern is that we can exit the link-tree-traversal as early as possible, e.g. when we've
1759 found the first link that points to row '5'. Other solutions could be a std::vector<size_t> harvest_all_links(), or an
1760 iterator pattern. First solution can't exit, second solution requires internal state.
1764 LinkMap() = default;
1765 LinkMap(const Table* table, std::vector<size_t> columns)
1766 : m_link_column_indexes(std::move(columns))
1768 set_base_table(table);
1771 LinkMap(LinkMap const& other, QueryNodeHandoverPatches* patches)
1777 m_link_column_indexes.clear();
1778 const Table* table = base_table();
1780 for (auto column : m_link_columns) {
1781 m_link_column_indexes.push_back(column->get_column_index());
1782 if (table->get_real_column_type(m_link_column_indexes.back()) == col_type_BackLink)
1783 table = &static_cast<const BacklinkColumn*>(column)->get_origin_table();
1785 table = &static_cast<const LinkColumnBase*>(column)->get_target_table();
1789 void set_base_table(const Table* table)
1791 if (table == base_table())
1795 m_tables.push_back(table);
1796 m_link_columns.clear();
1797 m_link_types.clear();
1798 m_only_unary_links = true;
1800 for (size_t link_column_index : m_link_column_indexes) {
1801 // Link column can be either LinkList or single Link
1802 const Table* t = m_tables.back();
1803 ColumnType type = t->get_real_column_type(link_column_index);
1804 REALM_ASSERT(Table::is_link_type(type) || type == col_type_BackLink);
1805 m_link_types.push_back(type);
1807 if (type == col_type_LinkList) {
1808 const LinkListColumn& cll = t->get_column_link_list(link_column_index);
1809 m_link_columns.push_back(&cll);
1810 m_only_unary_links = false;
1811 m_tables.push_back(&cll.get_target_table());
1813 else if (type == col_type_Link) {
1814 const LinkColumn& cl = t->get_column_link(link_column_index);
1815 m_link_columns.push_back(&cl);
1816 m_tables.push_back(&cl.get_target_table());
1818 else if (type == col_type_BackLink) {
1819 const BacklinkColumn& bl = t->get_column_backlink(link_column_index);
1820 m_link_columns.push_back(&bl);
1821 m_only_unary_links = false;
1822 m_tables.push_back(&bl.get_origin_table());
1827 void verify_columns() const
1829 for (size_t i = 0; i < m_link_column_indexes.size(); i++) {
1830 m_tables[i]->verify_column(m_link_column_indexes[i], m_link_columns[i]);
1834 virtual std::string description() const
1837 for (size_t i = 0; i < m_link_column_indexes.size(); ++i) {
1838 if (i < m_tables.size() && m_tables[i]) {
1839 if (m_link_types[i] == col_type_BackLink) {
1841 } else if (m_link_column_indexes[i] < m_tables[i]->get_column_count()) {
1842 s += std::string(m_tables[i]->get_column_name(m_link_column_indexes[i]));
1844 if (i != m_link_column_indexes.size() - 1) {
1845 s += util::serializer::value_separator;
1852 std::vector<size_t> get_links(size_t index)
1854 std::vector<size_t> res;
1855 get_links(index, res);
1859 size_t count_links(size_t row)
1862 map_links(row, counter);
1863 return counter.result();
1866 void map_links(size_t row, LinkMapFunction& lm)
1868 map_links(0, row, lm);
1871 bool only_unary_links() const
1873 return m_only_unary_links;
1876 const Table* base_table() const
1878 return m_tables.empty() ? nullptr : m_tables[0];
1881 const Table* target_table() const
1883 REALM_ASSERT(!m_tables.empty());
1884 return m_tables.back();
1887 std::vector<const ColumnBase*> m_link_columns;
1890 void map_links(size_t column, size_t row, LinkMapFunction& lm)
1892 bool last = (column + 1 == m_link_columns.size());
1893 ColumnType type = m_link_types[column];
1894 if (type == col_type_Link) {
1895 const LinkColumn& cl = *static_cast<const LinkColumn*>(m_link_columns[column]);
1896 size_t r = to_size_t(cl.get(row));
1899 r--; // LinkColumn stores link to row N as N + 1
1901 bool continue2 = lm.consume(r);
1906 map_links(column + 1, r, lm);
1908 else if (type == col_type_LinkList) {
1909 const LinkListColumn& cll = *static_cast<const LinkListColumn*>(m_link_columns[column]);
1910 ConstLinkViewRef lvr = cll.get(row);
1911 for (size_t t = 0; t < lvr->size(); t++) {
1912 size_t r = lvr->get(t).get_index();
1914 bool continue2 = lm.consume(r);
1919 map_links(column + 1, r, lm);
1922 else if (type == col_type_BackLink) {
1923 const BacklinkColumn& bl = *static_cast<const BacklinkColumn*>(m_link_columns[column]);
1924 size_t count = bl.get_backlink_count(row);
1925 for (size_t i = 0; i < count; ++i) {
1926 size_t r = bl.get_backlink(row, i);
1928 bool continue2 = lm.consume(r);
1933 map_links(column + 1, r, lm);
1939 void get_links(size_t row, std::vector<size_t>& result)
1941 MakeLinkVector mlv = MakeLinkVector(result);
1942 map_links(row, mlv);
1945 std::vector<size_t> m_link_column_indexes;
1946 std::vector<ColumnType> m_link_types;
1947 std::vector<const Table*> m_tables;
1948 bool m_only_unary_links = true;
1951 friend Query compare(const Subexpr2<Link>&, const ConstRow&);
1954 template <class T, class S, class I>
1955 Query string_compare(const Subexpr2<StringData>& left, T right, bool case_insensitive);
1956 template <class S, class I>
1957 Query string_compare(const Subexpr2<StringData>& left, const Subexpr2<StringData>& right, bool case_insensitive);
1960 Value<T> make_value_for_link(bool only_unary_links, size_t size)
1963 if (only_unary_links) {
1964 REALM_ASSERT(size <= 1);
1965 value.init(false, 1);
1966 value.m_storage.set_null(0);
1969 value.init(true, size);
1975 // If we add a new Realm type T and quickly want Query support for it, then simply inherit from it like
1976 // `template <> class Columns<T> : public SimpleQuerySupport<T>` and you're done. Any operators of the set
1977 // { ==, >=, <=, !=, >, < } that are supported by T will be supported by the "query expression syntax"
1978 // automatically. NOTE: This method of Query support will be slow because it goes through Table::get<T>.
1979 // To get faster Query support, either add SequentialGetter support (faster) or create a query_engine.hpp
1980 // node for it (super fast).
1983 class SimpleQuerySupport : public Subexpr2<T> {
1985 SimpleQuerySupport(size_t column, const Table* table, std::vector<size_t> links = {})
1986 : m_column_ndx(column)
1987 , m_link_map(table, std::move(links))
1989 m_column = &m_link_map.target_table()->get_column_base(m_column_ndx);
1992 bool is_nullable() const noexcept
1994 return m_link_map.base_table()->is_nullable(m_column->get_column_index());
1997 const Table* get_base_table() const override
1999 return m_link_map.base_table();
2002 void set_base_table(const Table* table) override
2004 if (table != get_base_table()) {
2005 m_link_map.set_base_table(table);
2006 m_column = &m_link_map.target_table()->get_column_base(m_column_ndx);
2010 void verify_column() const override
2013 m_link_map.verify_columns();
2014 // verify target table
2015 const Table* target_table = m_link_map.target_table();
2016 if (target_table && m_column_ndx != npos) {
2017 target_table->verify_column(m_column_ndx, m_column);
2021 void evaluate(size_t index, ValueBase& destination) override
2023 Value<T>& d = static_cast<Value<T>&>(destination);
2024 size_t col = column_ndx();
2026 if (links_exist()) {
2027 std::vector<size_t> links = m_link_map.get_links(index);
2028 Value<T> v = make_value_for_link<T>(m_link_map.only_unary_links(), links.size());
2030 for (size_t t = 0; t < links.size(); t++) {
2031 size_t link_to = links[t];
2032 v.m_storage.set(t, m_link_map.target_table()->template get<T>(col, link_to));
2034 destination.import(v);
2037 // Not a link column
2038 const Table* target_table = m_link_map.target_table();
2039 for (size_t t = 0; t < destination.m_values && index + t < target_table->size(); t++) {
2040 d.m_storage.set(t, target_table->get<T>(col, index + t));
2045 bool links_exist() const
2047 return m_link_map.m_link_columns.size() > 0;
2050 virtual std::string description() const override
2053 if (links_exist()) {
2054 desc = m_link_map.description() + util::serializer::value_separator;
2056 const Table* target_table = m_link_map.target_table();
2057 if (target_table && target_table->is_attached()) {
2058 desc += std::string(target_table->get_column_name(m_column_ndx));
2063 std::unique_ptr<Subexpr> clone(QueryNodeHandoverPatches* patches = nullptr) const override
2065 return make_subexpr<Columns<T>>(static_cast<const Columns<T>&>(*this), patches);
2068 SimpleQuerySupport(SimpleQuerySupport const& other, QueryNodeHandoverPatches* patches)
2069 : Subexpr2<T>(other)
2070 , m_column_ndx(other.m_column_ndx)
2071 , m_column(other.m_column)
2072 , m_link_map(other.m_link_map, patches)
2074 if (patches && m_column) {
2075 m_column_ndx = column_ndx();
2080 size_t column_ndx() const
2082 return m_column->get_column_index();
2085 SizeOperator<Size<T>> size()
2087 return SizeOperator<Size<T>>(this->clone(nullptr));
2091 // Column index of payload column of m_table
2092 mutable size_t m_column_ndx;
2093 const ColumnBase* m_column;
2099 class Columns<Timestamp> : public SimpleQuerySupport<Timestamp> {
2100 using SimpleQuerySupport::SimpleQuerySupport;
2104 class Columns<BinaryData> : public SimpleQuerySupport<BinaryData> {
2105 using SimpleQuerySupport::SimpleQuerySupport;
2109 class Columns<StringData> : public SimpleQuerySupport<StringData> {
2111 Columns(size_t column, const Table* table, std::vector<size_t> links = {})
2112 : SimpleQuerySupport(column, table, links)
2116 Columns(Columns const& other, QueryNodeHandoverPatches* patches = nullptr)
2117 : SimpleQuerySupport(other, patches)
2121 Columns(Columns&& other)
2122 : SimpleQuerySupport(other)
2127 template <class T, class S, class I>
2128 Query string_compare(const Subexpr2<StringData>& left, T right, bool case_sensitive)
2130 StringData sd(right);
2132 return create<S>(sd, left);
2134 return create<I>(sd, left);
2137 template <class S, class I>
2138 Query string_compare(const Subexpr2<StringData>& left, const Subexpr2<StringData>& right, bool case_sensitive)
2141 return make_expression<Compare<S, StringData>>(right.clone(), left.clone());
2143 return make_expression<Compare<I, StringData>>(right.clone(), left.clone());
2146 // Columns<String> == Columns<String>
2147 inline Query operator==(const Columns<StringData>& left, const Columns<StringData>& right)
2149 return string_compare<Equal, EqualIns>(left, right, true);
2152 // Columns<String> != Columns<String>
2153 inline Query operator!=(const Columns<StringData>& left, const Columns<StringData>& right)
2155 return string_compare<NotEqual, NotEqualIns>(left, right, true);
2158 // String == Columns<String>
2160 Query operator==(T left, const Columns<StringData>& right)
2162 return operator==(right, left);
2165 // String != Columns<String>
2167 Query operator!=(T left, const Columns<StringData>& right)
2169 return operator!=(right, left);
2172 // Columns<String> == String
2174 Query operator==(const Columns<StringData>& left, T right)
2176 return string_compare<T, Equal, EqualIns>(left, right, true);
2179 // Columns<String> != String
2181 Query operator!=(const Columns<StringData>& left, T right)
2183 return string_compare<T, NotEqual, NotEqualIns>(left, right, true);
2187 inline Query operator==(const Columns<BinaryData>& left, BinaryData right)
2189 return create<Equal>(right, left);
2192 inline Query operator==(BinaryData left, const Columns<BinaryData>& right)
2194 return create<Equal>(left, right);
2197 inline Query operator!=(const Columns<BinaryData>& left, BinaryData right)
2199 return create<NotEqual>(right, left);
2202 inline Query operator!=(BinaryData left, const Columns<BinaryData>& right)
2204 return create<NotEqual>(left, right);
2208 // This class is intended to perform queries on the *pointers* of links, contrary to performing queries on *payload*
2209 // in linked-to tables. Queries can be "find first link that points at row X" or "find first null-link". Currently
2210 // only "find first null link" and "find first non-null link" is supported. More will be added later. When we add
2211 // more, I propose to remove the <bool has_links> template argument from this class and instead template it by
2212 // a criteria-class (like the FindNullLinks class below in find_first()) in some generalized fashion.
2213 template <bool has_links>
2214 class UnaryLinkCompare : public Expression {
2216 UnaryLinkCompare(LinkMap lm)
2217 : m_link_map(std::move(lm))
2221 void set_base_table(const Table* table) override
2223 m_link_map.set_base_table(table);
2226 void verify_column() const override
2228 m_link_map.verify_columns();
2231 // Return main table of query (table on which table->where()... is invoked). Note that this is not the same as
2232 // any linked-to payload tables
2233 const Table* get_base_table() const override
2235 return m_link_map.base_table();
2238 size_t find_first(size_t start, size_t end) const override
2240 for (; start < end;) {
2242 m_link_map.map_links(start, fnl);
2243 if (fnl.m_has_link == has_links)
2252 virtual std::string description() const override
2254 return m_link_map.description() + (has_links ? " != NULL" : " == NULL");
2257 std::unique_ptr<Expression> clone(QueryNodeHandoverPatches* patches) const override
2259 return std::unique_ptr<Expression>(new UnaryLinkCompare(*this, patches));
2263 UnaryLinkCompare(const UnaryLinkCompare& other, QueryNodeHandoverPatches* patches = nullptr)
2265 , m_link_map(other.m_link_map, patches)
2269 mutable LinkMap m_link_map;
2272 class LinkCount : public Subexpr2<Int> {
2274 LinkCount(LinkMap link_map)
2275 : m_link_map(std::move(link_map))
2278 LinkCount(LinkCount const& other, QueryNodeHandoverPatches* patches)
2279 : Subexpr2<Int>(other)
2280 , m_link_map(other.m_link_map, patches)
2284 std::unique_ptr<Subexpr> clone(QueryNodeHandoverPatches* patches) const override
2286 return make_subexpr<LinkCount>(*this, patches);
2289 const Table* get_base_table() const override
2291 return m_link_map.base_table();
2294 void set_base_table(const Table* table) override
2296 m_link_map.set_base_table(table);
2299 void verify_column() const override
2301 m_link_map.verify_columns();
2304 void evaluate(size_t index, ValueBase& destination) override
2306 size_t count = m_link_map.count_links(index);
2307 destination.import(Value<Int>(false, 1, count));
2310 virtual std::string description() const override
2312 return m_link_map.description() + util::serializer::value_separator + "@count";
2319 template <class oper, class TExpr>
2320 class SizeOperator : public Subexpr2<Int> {
2322 SizeOperator(std::unique_ptr<TExpr> left)
2323 : m_expr(std::move(left))
2327 // See comment in base class
2328 void set_base_table(const Table* table) override
2330 m_expr->set_base_table(table);
2333 void verify_column() const override
2335 m_expr->verify_column();
2338 // Recursively fetch tables of columns in expression tree. Used when user first builds a stand-alone expression
2339 // and binds it to a Query at a later time
2340 const Table* get_base_table() const override
2342 return m_expr->get_base_table();
2345 // destination = operator(left)
2346 void evaluate(size_t index, ValueBase& destination) override
2348 REALM_ASSERT_DEBUG(dynamic_cast<Value<Int>*>(&destination) != nullptr);
2349 Value<Int>* d = static_cast<Value<Int>*>(&destination);
2353 m_expr->evaluate(index, v);
2355 size_t sz = v.m_values;
2356 d->init(v.m_from_link_list, sz);
2358 for (size_t i = 0; i < sz; i++) {
2359 auto elem = v.m_storage.get(i);
2361 d->m_storage.set_null(i);
2364 d->m_storage.set(i, oper()(*elem));
2369 std::string description() const override
2372 return m_expr->description() + util::serializer::value_separator + "@size";
2377 std::unique_ptr<Subexpr> clone(QueryNodeHandoverPatches* patches) const override
2379 return std::unique_ptr<Subexpr>(new SizeOperator(*this, patches));
2382 void apply_handover_patch(QueryNodeHandoverPatches& patches, Group& group) override
2384 m_expr->apply_handover_patch(patches, group);
2388 SizeOperator(const SizeOperator& other, QueryNodeHandoverPatches* patches)
2389 : m_expr(other.m_expr->clone(patches))
2393 typedef typename oper::type T;
2394 std::unique_ptr<TExpr> m_expr;
2397 struct ConstantRowValueHandoverPatch : public QueryNodeHandoverPatch {
2398 std::unique_ptr<RowBaseHandoverPatch> row_patch;
2401 class ConstantRowValue : public Subexpr2<Link> {
2403 ConstantRowValue(const ConstRow& row)
2408 void set_base_table(const Table*) override
2412 void verify_column() const override
2416 const Table* get_base_table() const override
2421 void evaluate(size_t, ValueBase& destination) override
2423 if (m_row.is_attached()) {
2424 Value<RowIndex> v(RowIndex(m_row.get_index()));
2425 destination.import(v);
2428 Value<RowIndex> v(RowIndex::Detached);
2429 destination.import(v);
2433 virtual std::string description() const override
2435 if (!m_row.is_attached()) {
2436 return util::serializer::print_value("detached object");
2438 return util::serializer::print_value(m_row.get_index());
2441 std::unique_ptr<Subexpr> clone(QueryNodeHandoverPatches* patches) const override
2443 return std::unique_ptr<Subexpr>(new ConstantRowValue(*this, patches));
2446 void apply_handover_patch(QueryNodeHandoverPatches& patches, Group& group) override
2448 REALM_ASSERT(patches.size());
2449 std::unique_ptr<QueryNodeHandoverPatch> abstract_patch = std::move(patches.back());
2452 auto patch = dynamic_cast<ConstantRowValueHandoverPatch*>(abstract_patch.get());
2453 REALM_ASSERT(patch);
2455 m_row.apply_and_consume_patch(patch->row_patch, group);
2459 ConstantRowValue(const ConstantRowValue& source, QueryNodeHandoverPatches* patches)
2460 : m_row(patches ? ConstRow() : source.m_row)
2465 std::unique_ptr<ConstantRowValueHandoverPatch> patch(new ConstantRowValueHandoverPatch);
2466 ConstRow::generate_patch(source.m_row, patch->row_patch);
2467 patches->emplace_back(patch.release());
2473 template <typename T>
2476 // This is for LinkList and BackLink too since they're declared as typedefs of Link.
2478 class Columns<Link> : public Subexpr2<Link> {
2482 if (m_link_map.m_link_columns.size() > 1)
2483 throw std::runtime_error("Combining link() and is_null() is currently not supported");
2484 // Todo, it may be useful to support the above, but we would need to figure out an intuitive behaviour
2485 return make_expression<UnaryLinkCompare<false>>(m_link_map);
2490 if (m_link_map.m_link_columns.size() > 1)
2491 throw std::runtime_error("Combining link() and is_not_null() is currently not supported");
2492 // Todo, it may be useful to support the above, but we would need to figure out an intuitive behaviour
2493 return make_expression<UnaryLinkCompare<true>>(m_link_map);
2496 LinkCount count() const
2498 return LinkCount(m_link_map);
2501 template <typename C>
2502 SubColumns<C> column(size_t column_ndx) const
2504 return SubColumns<C>(Columns<C>(column_ndx, m_link_map.target_table()), m_link_map);
2507 const LinkMap& link_map() const
2512 const Table* get_base_table() const override
2514 return m_link_map.base_table();
2516 void set_base_table(const Table* table) override
2518 m_link_map.set_base_table(table);
2521 void verify_column() const override
2523 m_link_map.verify_columns();
2526 std::string description() const override
2528 return m_link_map.description();
2531 std::unique_ptr<Subexpr> clone(QueryNodeHandoverPatches* patches) const override
2533 return std::unique_ptr<Subexpr>(new Columns<Link>(*this, patches));
2536 void evaluate(size_t index, ValueBase& destination) override;
2543 Columns(size_t column_ndx, const Table* table, const std::vector<size_t>& links = {})
2544 : m_link_map(table, links)
2546 static_cast<void>(column_ndx);
2548 Columns(const Columns& other, QueryNodeHandoverPatches* patches)
2549 : Subexpr2<Link>(other)
2550 , m_link_map(other.m_link_map, patches)
2555 template <typename T>
2557 template <typename T, typename Operation>
2558 class ListColumnAggregate;
2559 namespace aggregate_operations {
2560 template <typename T>
2562 template <typename T>
2564 template <typename T>
2566 template <typename T>
2571 class Columns<SubTable> : public Subexpr2<SubTable> {
2573 const Table* get_base_table() const override
2575 return m_link_map.base_table();
2578 void set_base_table(const Table* table) override
2580 m_link_map.set_base_table(table);
2581 m_column = &m_link_map.target_table()->get_column_table(m_column_ndx);
2584 void verify_column() const override
2586 m_link_map.verify_columns();
2587 m_link_map.target_table()->verify_column(m_column_ndx, m_column);
2590 std::string description() const override
2592 return m_link_map.description();
2595 std::unique_ptr<Subexpr> clone(QueryNodeHandoverPatches* patches) const override
2597 return std::unique_ptr<Subexpr>(new Columns<SubTable>(*this, patches));
2600 void evaluate(size_t index, ValueBase& destination) override
2602 evaluate_internal(index, destination, ValueBase::default_size);
2605 void evaluate_internal(size_t index, ValueBase& destination, size_t nb_elements);
2607 template <typename T>
2608 ListColumns<T> column(size_t ndx) const
2610 return ListColumns<T>(ndx, Columns<SubTable>(*this, nullptr));
2613 template <typename T>
2614 ListColumns<T> list() const
2616 return column<T>(0);
2619 SizeOperator<Size<ConstTableRef>> size()
2621 return SizeOperator<Size<ConstTableRef>>(this->clone(nullptr));
2626 size_t m_column_ndx;
2627 const SubtableColumn* m_column = nullptr;
2630 friend class ListColumnsBase;
2631 template <class T, class U>
2632 friend class ListColumnAggregate;
2634 Columns(size_t column_ndx, const Table* table, const std::vector<size_t>& links = {})
2635 : m_link_map(table, links)
2636 , m_column_ndx(column_ndx)
2637 , m_column(&m_link_map.target_table()->get_column_table(column_ndx))
2641 Columns(const Columns<SubTable>& other, QueryNodeHandoverPatches* patches)
2642 : Subexpr2<SubTable>(other)
2643 , m_link_map(other.m_link_map, patches)
2644 , m_column_ndx(other.m_column_ndx)
2645 , m_column(other.m_column)
2647 if (m_column && patches)
2648 m_column_ndx = m_column->get_column_index();
2652 template <typename T>
2653 class ListColumnsBase : public Subexpr2<T> {
2655 ListColumnsBase(size_t column_ndx, Columns<SubTable> column)
2656 : m_column_ndx(column_ndx)
2657 , m_subtable_column(std::move(column))
2661 ListColumnsBase(const ListColumnsBase& other, QueryNodeHandoverPatches* patches)
2662 : m_column_ndx(other.m_column_ndx)
2663 , m_subtable_column(other.m_subtable_column, patches)
2667 std::unique_ptr<Subexpr> clone(QueryNodeHandoverPatches* patches) const override
2669 return make_subexpr<ListColumns<T>>(*this, patches);
2672 const Table* get_base_table() const override
2674 return m_subtable_column.get_base_table();
2677 void set_base_table(const Table* table) override
2679 m_subtable_column.set_base_table(table);
2682 void verify_column() const override
2684 m_subtable_column.verify_column();
2687 void evaluate(size_t index, ValueBase& destination) override
2689 Value<ConstTableRef> subtables;
2690 m_subtable_column.evaluate_internal(index, subtables, 1);
2692 for (size_t i = 0; i < subtables.m_values; i++) {
2693 auto val = subtables.m_storage[i];
2697 auto v = make_value_for_link<typename util::RemoveOptional<T>::type>(false, sz);
2699 for (size_t i = 0; i < subtables.m_values; i++) {
2700 auto table = subtables.m_storage[i];
2702 size_t s = table->size();
2703 for (size_t j = 0; j < s; j++) {
2704 if (!table->is_null(m_column_ndx, j)) {
2705 v.m_storage.set(k++, table->get<T>(m_column_ndx, j));
2710 destination.import(v);
2713 virtual std::string description() const override
2715 const Table* table = get_base_table();
2716 if (table && table->is_attached()) {
2717 if (m_subtable_column.m_column) {
2718 return std::string(table->get_column_name(m_subtable_column.m_column_ndx));
2722 return std::string(table->get_column_name(m_column_ndx));
2728 ListColumnAggregate<T, aggregate_operations::Minimum<T>> min() const
2730 return {m_column_ndx, m_subtable_column};
2733 ListColumnAggregate<T, aggregate_operations::Maximum<T>> max() const
2735 return {m_column_ndx, m_subtable_column};
2738 ListColumnAggregate<T, aggregate_operations::Sum<T>> sum() const
2740 return {m_column_ndx, m_subtable_column};
2743 ListColumnAggregate<T, aggregate_operations::Average<T>> average() const
2745 return {m_column_ndx, m_subtable_column};
2750 // Storing the column index here could be a potential problem if the column
2751 // changes id due to insertion/deletion.
2752 size_t m_column_ndx;
2753 Columns<SubTable> m_subtable_column;
2757 class ListColumns : public ListColumnsBase<T> {
2759 using ListColumnsBase<T>::ListColumnsBase;
2763 class ListColumns<StringData> : public ListColumnsBase<StringData> {
2765 ListColumns(size_t column_ndx, Columns<SubTable> column)
2766 : ListColumnsBase(column_ndx, column)
2770 ListColumns(const ListColumnsBase& other, QueryNodeHandoverPatches* patches)
2771 : ListColumnsBase(other, patches)
2775 ListColumns(ListColumns&& other)
2776 : ListColumnsBase(other)
2781 template <typename T, typename Operation>
2782 class ListColumnAggregate : public Subexpr2<typename Operation::ResultType> {
2784 using R = typename Operation::ResultType;
2786 ListColumnAggregate(size_t column_ndx, Columns<SubTable> column)
2787 : m_column_ndx(column_ndx)
2788 , m_subtable_column(std::move(column))
2792 ListColumnAggregate(const ListColumnAggregate& other, QueryNodeHandoverPatches* patches)
2793 : m_column_ndx(other.m_column_ndx)
2794 , m_subtable_column(other.m_subtable_column, patches)
2798 std::unique_ptr<Subexpr> clone(QueryNodeHandoverPatches* patches) const override
2800 return make_subexpr<ListColumnAggregate>(*this, patches);
2803 const Table* get_base_table() const override
2805 return m_subtable_column.get_base_table();
2808 void set_base_table(const Table* table) override
2810 m_subtable_column.set_base_table(table);
2813 void verify_column() const override
2815 m_subtable_column.verify_column();
2818 void evaluate(size_t index, ValueBase& destination) override
2820 Value<ConstTableRef> subtables;
2821 m_subtable_column.evaluate_internal(index, subtables, 1);
2822 REALM_ASSERT_DEBUG(subtables.m_values > 0 || subtables.m_from_link_list);
2823 size_t sz = subtables.m_values;
2824 // The result is an aggregate value for each table
2825 auto v = make_value_for_link<R>(!subtables.m_from_link_list, sz);
2826 for (unsigned i = 0; i < sz; i++) {
2827 auto table = subtables.m_storage[i];
2830 size_t s = table->size();
2831 for (unsigned j = 0; j < s; j++) {
2832 op.accumulate(table->get<T>(m_column_ndx, j));
2836 v.m_storage.set_null(i);
2839 v.m_storage.set(i, op.result());
2842 destination.import(v);
2845 virtual std::string description() const override
2847 const Table* table = get_base_table();
2848 if (table && table->is_attached()) {
2849 return std::string(table->get_column_name(m_column_ndx)) + util::serializer::value_separator + Operation::description() + "()";
2855 size_t m_column_ndx;
2856 Columns<SubTable> m_subtable_column;
2859 template <class Operator>
2860 Query compare(const Subexpr2<Link>& left, const ConstRow& row)
2862 static_assert(std::is_same<Operator, Equal>::value || std::is_same<Operator, NotEqual>::value,
2863 "Links can only be compared for equality.");
2864 const Columns<Link>* column = dynamic_cast<const Columns<Link>*>(&left);
2866 const LinkMap& link_map = column->link_map();
2867 REALM_ASSERT(link_map.target_table() == row.get_table() || !row.is_attached());
2868 #ifdef REALM_OLDQUERY_FALLBACK
2869 if (link_map.m_link_columns.size() == 1) {
2870 // We can fall back to Query::links_to for != and == operations on links, but only
2871 // for == on link lists. This is because negating query.links_to() is equivalent to
2872 // to "ALL linklist != row" rather than the "ANY linklist != row" semantics we're after.
2873 if (link_map.m_link_types[0] == col_type_Link ||
2874 (link_map.m_link_types[0] == col_type_LinkList && std::is_same<Operator, Equal>::value)) {
2875 const Table* t = column->get_base_table();
2878 if (std::is_same<Operator, NotEqual>::value) {
2879 // Negate the following `links_to`.
2882 query.links_to(link_map.m_link_column_indexes[0], row);
2888 return make_expression<Compare<Operator, RowIndex>>(left.clone(), make_subexpr<ConstantRowValue>(row));
2891 inline Query operator==(const Subexpr2<Link>& left, const ConstRow& row)
2893 return compare<Equal>(left, row);
2895 inline Query operator!=(const Subexpr2<Link>& left, const ConstRow& row)
2897 return compare<NotEqual>(left, row);
2899 inline Query operator==(const ConstRow& row, const Subexpr2<Link>& right)
2901 return compare<Equal>(right, row);
2903 inline Query operator!=(const ConstRow& row, const Subexpr2<Link>& right)
2905 return compare<NotEqual>(right, row);
2908 template <class Operator>
2909 Query compare(const Subexpr2<Link>& left, null)
2911 static_assert(std::is_same<Operator, Equal>::value || std::is_same<Operator, NotEqual>::value,
2912 "Links can only be compared for equality.");
2913 return make_expression<Compare<Operator, RowIndex>>(left.clone(), make_subexpr<Value<RowIndex>>());
2916 inline Query operator==(const Subexpr2<Link>& left, null)
2918 return compare<Equal>(left, null());
2920 inline Query operator!=(const Subexpr2<Link>& left, null)
2922 return compare<NotEqual>(left, null());
2924 inline Query operator==(null, const Subexpr2<Link>& right)
2926 return compare<Equal>(right, null());
2928 inline Query operator!=(null, const Subexpr2<Link>& right)
2930 return compare<NotEqual>(right, null());
2935 class Columns : public Subexpr2<T> {
2937 using ColType = typename ColumnTypeTraits<T>::column_type;
2939 Columns(size_t column, const Table* table, std::vector<size_t> links = {})
2940 : m_link_map(table, std::move(links))
2941 , m_column_ndx(column)
2942 , m_nullable(m_link_map.target_table()->is_nullable(m_column_ndx))
2946 Columns(const Columns& other, QueryNodeHandoverPatches* patches = nullptr)
2947 : m_link_map(other.m_link_map, patches)
2948 , m_column_ndx(other.m_column_ndx)
2949 , m_nullable(other.m_nullable)
2955 m_column_ndx = other.get_column_base().get_column_index();
2958 if (m_nullable && std::is_same<typename ColType::value_type, int64_t>::value) {
2959 init<IntNullColumn>(&other.get_column_base());
2962 init<ColType>(&other.get_column_base());
2967 Columns& operator=(const Columns& other)
2969 if (this != &other) {
2970 m_link_map = other.m_link_map;
2972 m_column_ndx = other.m_column_ndx;
2973 m_nullable = other.m_nullable;
2978 std::unique_ptr<Subexpr> clone(QueryNodeHandoverPatches* patches) const override
2980 return make_subexpr<Columns<T>>(*this, patches);
2983 // See comment in base class
2984 void set_base_table(const Table* table) override
2986 if (m_sg && table == get_base_table())
2989 m_link_map.set_base_table(table);
2990 m_nullable = m_link_map.target_table()->is_nullable(m_column_ndx);
2992 const ColumnBase* c = &m_link_map.target_table()->get_column_base(m_column_ndx);
2993 if (m_nullable && std::is_same<typename ColType::value_type, int64_t>::value) {
2994 init<IntNullColumn>(c);
3001 void verify_column() const override
3004 m_link_map.verify_columns();
3005 // verify target table
3006 const Table* target_table = m_link_map.target_table();
3007 if (target_table && m_column_ndx != npos) {
3008 target_table->verify_column(m_column_ndx, &get_column_base());
3012 template <class ActualColType>
3013 void init(const ColumnBase* c)
3015 REALM_ASSERT_DEBUG(dynamic_cast<const ActualColType*>(c));
3016 if (m_sg == nullptr) {
3017 m_sg.reset(new SequentialGetter<ActualColType>());
3019 static_cast<SequentialGetter<ActualColType>&>(*m_sg).init(static_cast<const ActualColType*>(c));
3022 // Recursively fetch tables of columns in expression tree. Used when user first builds a stand-alone expression
3023 // and binds it to a Query at a later time
3024 const Table* get_base_table() const override
3026 return m_link_map.base_table();
3029 template <class ColType2 = ColType>
3030 void evaluate_internal(size_t index, ValueBase& destination)
3032 REALM_ASSERT_DEBUG(m_sg.get());
3033 REALM_ASSERT_DEBUG(dynamic_cast<SequentialGetter<ColType2>*>(m_sg.get()));
3035 using U = typename ColType2::value_type;
3036 auto sgc = static_cast<SequentialGetter<ColType2>*>(m_sg.get());
3037 REALM_ASSERT_DEBUG(sgc->m_column);
3039 if (links_exist()) {
3040 // LinkList with more than 0 values. Create Value with payload for all fields
3042 std::vector<size_t> links = m_link_map.get_links(index);
3043 auto v = make_value_for_link<typename util::RemoveOptional<U>::type>(m_link_map.only_unary_links(),
3046 for (size_t t = 0; t < links.size(); t++) {
3047 size_t link_to = links[t];
3048 sgc->cache_next(link_to);
3050 if (sgc->m_column->is_null(link_to))
3051 v.m_storage.set_null(t);
3053 v.m_storage.set(t, sgc->get_next(link_to));
3055 destination.import(v);
3058 // Not a Link column
3059 // make sequential getter load the respective leaf to access data at column row 'index'
3060 sgc->cache_next(index);
3061 size_t colsize = sgc->m_column->size();
3063 // Now load `ValueBase::default_size` rows from from the leaf into m_storage. If it's an integer
3064 // leaf, then it contains the method get_chunk() which copies these values in a super fast way (first
3065 // case of the `if` below. Otherwise, copy the values one by one in a for-loop (the `else` case).
3066 if (std::is_same<U, int64_t>::value && index + ValueBase::default_size <= sgc->m_leaf_end) {
3069 // If you want to modify 'default_size' then update Array::get_chunk()
3070 REALM_ASSERT_3(ValueBase::default_size, ==, 8);
3072 auto sgc_2 = static_cast<SequentialGetter<ColType>*>(m_sg.get());
3073 sgc_2->m_leaf_ptr->get_chunk(index - sgc->m_leaf_start, v.m_storage.m_first);
3075 destination.import(v);
3078 size_t rows = colsize - index;
3079 if (rows > ValueBase::default_size)
3080 rows = ValueBase::default_size;
3081 Value<typename util::RemoveOptional<U>::type> v(false, rows);
3083 for (size_t t = 0; t < rows; t++)
3084 v.m_storage.set(t, sgc->get_next(index + t));
3086 destination.import(v);
3091 virtual std::string description() const override
3093 std::string desc = "";
3094 if (links_exist()) {
3095 desc = m_link_map.description() + util::serializer::value_separator;
3097 const Table* target_table = m_link_map.target_table();
3098 if (target_table && target_table->is_attached() && m_column_ndx != npos) {
3099 desc += std::string(target_table->get_column_name(m_column_ndx));
3105 // Load values from Column into destination
3106 void evaluate(size_t index, ValueBase& destination) override
3108 if (m_nullable && std::is_same<typename ColType::value_type, int64_t>::value) {
3109 evaluate_internal<IntNullColumn>(index, destination);
3112 evaluate_internal<ColType>(index, destination);
3116 bool links_exist() const
3118 return m_link_map.m_link_columns.size() > 0;
3121 bool is_nullable() const
3126 size_t column_ndx() const noexcept
3128 return m_sg ? get_column_base().get_column_index() : m_column_ndx;
3134 // Fast (leaf caching) value getter for payload column (column in table on which query condition is executed)
3135 std::unique_ptr<SequentialGetterBase> m_sg;
3137 // Column index of payload column of m_table
3138 size_t m_column_ndx;
3140 // set to false by default for stand-alone Columns declaration that are not yet associated with any table
3141 // or oclumn. Call init() to update it or use a constructor that takes table + column index as argument.
3142 bool m_nullable = false;
3144 const ColumnBase& get_column_base() const noexcept
3146 if (m_nullable && std::is_same<int64_t, T>::value)
3147 return *static_cast<SequentialGetter<IntNullColumn>&>(*m_sg).m_column;
3149 return *static_cast<SequentialGetter<ColType>&>(*m_sg).m_column;
3153 template <typename T, typename Operation>
3154 class SubColumnAggregate;
3156 template <typename T>
3157 class SubColumns : public Subexpr {
3159 SubColumns(Columns<T> column, LinkMap link_map)
3160 : m_column(std::move(column))
3161 , m_link_map(std::move(link_map))
3165 std::unique_ptr<Subexpr> clone(QueryNodeHandoverPatches*) const override
3167 return make_subexpr<SubColumns<T>>(*this);
3170 const Table* get_base_table() const override
3172 return m_link_map.base_table();
3175 void set_base_table(const Table* table) override
3177 m_link_map.set_base_table(table);
3178 m_column.set_base_table(m_link_map.target_table());
3181 void verify_column() const override
3183 m_link_map.verify_columns();
3184 m_column.verify_column();
3187 void evaluate(size_t, ValueBase&) override
3189 // SubColumns can only be used in an expression in conjunction with its aggregate methods.
3190 REALM_ASSERT(false);
3193 virtual std::string description() const override
3195 return ""; // by itself there are no conditions, see SubColumnAggregate
3198 SubColumnAggregate<T, aggregate_operations::Minimum<T>> min() const
3200 return {m_column, m_link_map};
3203 SubColumnAggregate<T, aggregate_operations::Maximum<T>> max() const
3205 return {m_column, m_link_map};
3208 SubColumnAggregate<T, aggregate_operations::Sum<T>> sum() const
3210 return {m_column, m_link_map};
3213 SubColumnAggregate<T, aggregate_operations::Average<T>> average() const
3215 return {m_column, m_link_map};
3219 Columns<T> m_column;
3223 template <typename T, typename Operation>
3224 class SubColumnAggregate : public Subexpr2<typename Operation::ResultType> {
3226 SubColumnAggregate(Columns<T> column, LinkMap link_map)
3227 : m_column(std::move(column))
3228 , m_link_map(std::move(link_map))
3231 SubColumnAggregate(SubColumnAggregate const& other, QueryNodeHandoverPatches* patches)
3232 : m_column(other.m_column, patches)
3233 , m_link_map(other.m_link_map, patches)
3237 std::unique_ptr<Subexpr> clone(QueryNodeHandoverPatches* patches) const override
3239 return make_subexpr<SubColumnAggregate>(*this, patches);
3242 const Table* get_base_table() const override
3244 return m_link_map.base_table();
3247 void set_base_table(const Table* table) override
3249 m_link_map.set_base_table(table);
3250 m_column.set_base_table(m_link_map.target_table());
3253 void verify_column() const override
3255 m_link_map.verify_columns();
3256 m_column.verify_column();
3259 void evaluate(size_t index, ValueBase& destination) override
3261 std::vector<size_t> links = m_link_map.get_links(index);
3262 std::sort(links.begin(), links.end());
3265 for (size_t link_index = 0; link_index < links.size();) {
3267 size_t link = links[link_index];
3268 m_column.evaluate(link, value);
3270 // Columns<T>::evaluate fetches values in chunks of ValueBase::default_size. Process all values
3271 // within the chunk that came from rows that we link to.
3272 const auto& value_storage = value.m_storage;
3273 for (size_t value_index = 0; value_index < value.m_values;) {
3274 if (!value_storage.is_null(value_index)) {
3275 op.accumulate(value_storage[value_index]);
3277 if (++link_index >= links.size()) {
3281 size_t previous_link = link;
3282 link = links[link_index];
3283 value_index += link - previous_link;
3287 destination.import(Value<null>(false, 1, null()));
3290 destination.import(Value<typename Operation::ResultType>(false, 1, op.result()));
3294 virtual std::string description() const override
3296 return m_link_map.description() + util::serializer::value_separator + Operation::description() + util::serializer::value_separator + m_column.description();
3300 Columns<T> m_column;
3304 struct SubQueryCountHandoverPatch : QueryNodeHandoverPatch {
3305 QueryHandoverPatch m_query;
3308 class SubQueryCount : public Subexpr2<Int> {
3310 SubQueryCount(Query q, LinkMap link_map)
3311 : m_query(std::move(q))
3312 , m_link_map(std::move(link_map))
3316 const Table* get_base_table() const override
3318 return m_link_map.base_table();
3321 void set_base_table(const Table* table) override
3323 m_link_map.set_base_table(table);
3326 void verify_column() const override
3328 m_link_map.verify_columns();
3331 void evaluate(size_t index, ValueBase& destination) override
3333 std::vector<size_t> links = m_link_map.get_links(index);
3334 std::sort(links.begin(), links.end());
3336 size_t count = std::accumulate(links.begin(), links.end(), size_t(0), [this](size_t running_count, size_t link) {
3337 return running_count + m_query.count(link, link + 1, 1);
3340 destination.import(Value<Int>(false, 1, size_t(count)));
3343 virtual std::string description() const override
3345 return m_link_map.description() + util::serializer::value_separator + "SUBQUERY(" + m_query.get_description() + ")"
3346 + util::serializer::value_separator + "@count";
3349 std::unique_ptr<Subexpr> clone(QueryNodeHandoverPatches* patches) const override
3352 return std::unique_ptr<Subexpr>(new SubQueryCount(*this, patches));
3354 return make_subexpr<SubQueryCount>(*this);
3357 void apply_handover_patch(QueryNodeHandoverPatches& patches, Group& group) override
3359 REALM_ASSERT(patches.size());
3360 std::unique_ptr<QueryNodeHandoverPatch> abstract_patch = std::move(patches.back());
3363 auto patch = dynamic_cast<SubQueryCountHandoverPatch*>(abstract_patch.get());
3364 REALM_ASSERT(patch);
3366 m_query.apply_patch(patch->m_query, group);
3370 SubQueryCount(const SubQueryCount& other, QueryNodeHandoverPatches* patches)
3371 : m_link_map(other.m_link_map, patches)
3373 std::unique_ptr<SubQueryCountHandoverPatch> patch(new SubQueryCountHandoverPatch);
3374 m_query = Query(other.m_query, patch->m_query, ConstSourcePayload::Copy);
3375 patches->emplace_back(patch.release());
3382 // The unused template parameter is a hack to avoid a circular dependency between table.hpp and query_expression.hpp.
3386 SubQuery(Columns<Link> link_column, Query query)
3387 : m_query(std::move(query))
3388 , m_link_map(link_column.link_map())
3390 REALM_ASSERT(m_link_map.target_table() == m_query.get_table());
3393 SubQueryCount count() const
3395 return SubQueryCount(m_query, m_link_map);
3403 namespace aggregate_operations {
3404 template <typename T, typename Derived, typename R = T>
3405 class BaseAggregateOperation {
3406 static_assert(std::is_same<T, Int>::value || std::is_same<T, Float>::value || std::is_same<T, Double>::value,
3407 "Numeric aggregates can only be used with subcolumns of numeric types");
3410 using ResultType = R;
3412 void accumulate(T value)
3415 m_result = Derived::apply(m_result, value);
3418 bool is_null() const
3420 return m_count == 0;
3422 ResultType result() const
3429 ResultType m_result = Derived::initial_value();
3432 template <typename T>
3433 class Minimum : public BaseAggregateOperation<T, Minimum<T>> {
3435 static T initial_value()
3437 return std::numeric_limits<T>::max();
3439 static T apply(T a, T b)
3441 return std::min(a, b);
3443 static std::string description()
3449 template <typename T>
3450 class Maximum : public BaseAggregateOperation<T, Maximum<T>> {
3452 static T initial_value()
3454 return std::numeric_limits<T>::min();
3456 static T apply(T a, T b)
3458 return std::max(a, b);
3460 static std::string description()
3466 template <typename T>
3467 class Sum : public BaseAggregateOperation<T, Sum<T>> {
3469 static T initial_value()
3473 static T apply(T a, T b)
3477 bool is_null() const
3481 static std::string description()
3487 template <typename T>
3488 class Average : public BaseAggregateOperation<T, Average<T>, double> {
3489 using Base = BaseAggregateOperation<T, Average<T>, double>;
3492 static double initial_value()
3496 static double apply(double a, T b)
3500 double result() const
3502 return Base::m_result / Base::m_count;
3504 static std::string description()
3512 template <class oper, class TLeft>
3513 class UnaryOperator : public Subexpr2<typename oper::type> {
3515 UnaryOperator(std::unique_ptr<TLeft> left)
3516 : m_left(std::move(left))
3520 UnaryOperator(const UnaryOperator& other, QueryNodeHandoverPatches* patches)
3521 : m_left(other.m_left->clone(patches))
3525 UnaryOperator& operator=(const UnaryOperator& other)
3527 if (this != &other) {
3528 m_left = other.m_left->clone();
3533 UnaryOperator(UnaryOperator&&) = default;
3534 UnaryOperator& operator=(UnaryOperator&&) = default;
3536 // See comment in base class
3537 void set_base_table(const Table* table) override
3539 m_left->set_base_table(table);
3542 void verify_column() const override
3544 m_left->verify_column();
3547 // Recursively fetch tables of columns in expression tree. Used when user first builds a stand-alone expression
3548 // and binds it to a Query at a later time
3549 const Table* get_base_table() const override
3551 return m_left->get_base_table();
3554 // destination = operator(left)
3555 void evaluate(size_t index, ValueBase& destination) override
3559 m_left->evaluate(index, left);
3560 result.template fun<oper>(&left);
3561 destination.import(result);
3564 virtual std::string description() const override
3567 return m_left->description();
3572 std::unique_ptr<Subexpr> clone(QueryNodeHandoverPatches* patches) const override
3574 return make_subexpr<UnaryOperator>(*this, patches);
3577 void apply_handover_patch(QueryNodeHandoverPatches& patches, Group& group) override
3579 m_left->apply_handover_patch(patches, group);
3583 typedef typename oper::type T;
3584 std::unique_ptr<TLeft> m_left;
3588 template <class oper, class TLeft, class TRight>
3589 class Operator : public Subexpr2<typename oper::type> {
3591 Operator(std::unique_ptr<TLeft> left, std::unique_ptr<TRight> right)
3592 : m_left(std::move(left))
3593 , m_right(std::move(right))
3597 Operator(const Operator& other, QueryNodeHandoverPatches* patches)
3598 : m_left(other.m_left->clone(patches))
3599 , m_right(other.m_right->clone(patches))
3603 Operator& operator=(const Operator& other)
3605 if (this != &other) {
3606 m_left = other.m_left->clone();
3607 m_right = other.m_right->clone();
3612 Operator(Operator&&) = default;
3613 Operator& operator=(Operator&&) = default;
3615 // See comment in base class
3616 void set_base_table(const Table* table) override
3618 m_left->set_base_table(table);
3619 m_right->set_base_table(table);
3622 void verify_column() const override
3624 m_left->verify_column();
3625 m_right->verify_column();
3628 // Recursively fetch tables of columns in expression tree. Used when user first builds a stand-alone expression
3630 // binds it to a Query at a later time
3631 const Table* get_base_table() const override
3633 const Table* l = m_left->get_base_table();
3634 const Table* r = m_right->get_base_table();
3636 // Queries do not support multiple different tables; all tables must be the same.
3637 REALM_ASSERT(l == nullptr || r == nullptr || l == r);
3639 // nullptr pointer means expression which isn't yet associated with any table, or is a Value<T>
3643 // destination = operator(left, right)
3644 void evaluate(size_t index, ValueBase& destination) override
3649 m_left->evaluate(index, left);
3650 m_right->evaluate(index, right);
3651 result.template fun<oper>(&left, &right);
3652 destination.import(result);
3655 virtual std::string description() const override
3659 s += m_left->description();
3661 s += oper::description();
3663 s += m_right->description();
3668 std::unique_ptr<Subexpr> clone(QueryNodeHandoverPatches* patches) const override
3670 return make_subexpr<Operator>(*this, patches);
3673 void apply_handover_patch(QueryNodeHandoverPatches& patches, Group& group) override
3675 m_right->apply_handover_patch(patches, group);
3676 m_left->apply_handover_patch(patches, group);
3680 typedef typename oper::type T;
3681 std::unique_ptr<TLeft> m_left;
3682 std::unique_ptr<TRight> m_right;
3686 template <class TCond, class T, class TLeft, class TRight>
3687 class Compare : public Expression {
3689 Compare(std::unique_ptr<TLeft> left, std::unique_ptr<TRight> right)
3690 : m_left(std::move(left))
3691 , m_right(std::move(right))
3695 // See comment in base class
3696 void set_base_table(const Table* table) override
3698 m_left->set_base_table(table);
3699 m_right->set_base_table(table);
3702 void verify_column() const override
3704 m_left->verify_column();
3705 m_right->verify_column();
3708 // Recursively fetch tables of columns in expression tree. Used when user first builds a stand-alone expression
3710 // binds it to a Query at a later time
3711 const Table* get_base_table() const override
3713 const Table* l = m_left->get_base_table();
3714 const Table* r = m_right->get_base_table();
3716 // All main tables in each subexpression of a query (table.columns() or table.link()) must be the same.
3717 REALM_ASSERT(l == nullptr || r == nullptr || l == r);
3719 // nullptr pointer means expression which isn't yet associated with any table, or is a Value<T>
3723 size_t find_first(size_t start, size_t end) const override
3729 for (; start < end;) {
3730 m_left->evaluate(start, left);
3731 m_right->evaluate(start, right);
3732 match = Value<T>::template compare<TCond>(&left, &right);
3734 if (match != not_found && match + start < end)
3735 return start + match;
3738 (left.m_from_link_list || right.m_from_link_list) ? 1 : minimum(right.m_values, left.m_values);
3742 return not_found; // no match
3745 virtual std::string description() const override
3747 if (std::is_same<TCond, BeginsWith>::value
3748 || std::is_same<TCond, BeginsWithIns>::value
3749 || std::is_same<TCond, EndsWith>::value
3750 || std::is_same<TCond, EndsWithIns>::value
3751 || std::is_same<TCond, Contains>::value
3752 || std::is_same<TCond, ContainsIns>::value
3753 || std::is_same<TCond, Like>::value
3754 || std::is_same<TCond, LikeIns>::value) {
3755 // these string conditions have the arguments reversed but the order is important
3756 // operations ==, and != can be reversed because the produce the same results both ways
3757 return util::serializer::print_value(m_right->description() + " " + TCond::description()
3758 + " " + m_left->description());
3760 return util::serializer::print_value(m_left->description() + " " + TCond::description()
3761 + " " + m_right->description());
3764 std::unique_ptr<Expression> clone(QueryNodeHandoverPatches* patches) const override
3766 return std::unique_ptr<Expression>(new Compare(*this, patches));
3769 void apply_handover_patch(QueryNodeHandoverPatches& patches, Group& group) override
3771 m_right->apply_handover_patch(patches, group);
3772 m_left->apply_handover_patch(patches, group);
3776 Compare(const Compare& other, QueryNodeHandoverPatches* patches)
3777 : m_left(other.m_left->clone(patches))
3778 , m_right(other.m_right->clone(patches))
3782 std::unique_ptr<TLeft> m_left;
3783 std::unique_ptr<TRight> m_right;
3786 #endif // REALM_QUERY_EXPRESSION_HPP