<commit_before>#include <cstdio>
#include <algorithm>

#include <realm/array.hpp>
#include <realm/column_fwd.hpp>
#include <realm/query.hpp>
#include <realm/query_engine.hpp>
#include <realm/descriptor.hpp>
#include <realm/table_view.hpp>
#include <realm/link_view.hpp>

using namespace realm;

Query::Query() : m_view(nullptr), m_source_table_view(nullptr), m_owns_source_table_view(false)
{
    create();
}

Query::Query(Table& table, TableViewBase* tv)
    : m_table(table.get_table_ref()), m_view(tv), m_source_table_view(tv), m_owns_source_table_view(false)
{
    REALM_ASSERT_DEBUG(m_view == nullptr || m_view->cookie == m_view->cookie_expected);
    create();
}

Query::Query(const Table& table, const LinkViewRef& lv):
    m_table((const_cast<Table&>(table)).get_table_ref()),
    m_view(lv.get()),
    m_source_link_view(lv), m_source_table_view(nullptr), m_owns_source_table_view(false)
{
    REALM_ASSERT_DEBUG(m_view == nullptr || m_view->cookie == m_view->cookie_expected);
    create();
}

Query::Query(const Table& table, TableViewBase* tv)
    : m_table((const_cast<Table&>(table)).get_table_ref()), m_view(tv), m_source_table_view(tv), m_owns_source_table_view(false)
{
    REALM_ASSERT_DEBUG(m_view == nullptr ||m_view->cookie == m_view->cookie_expected);
    create();
}

Query::Query(const Table& table, std::unique_ptr<TableViewBase> tv)
    : m_table((const_cast<Table&>(table)).get_table_ref()), m_view(tv.get()), m_source_table_view(tv.get()), m_owns_source_table_view(true)
{
    tv.release();

    REALM_ASSERT_DEBUG(m_view == nullptr ||m_view->cookie == m_view->cookie_expected);
    create();
}

void Query::create()
{
    m_groups.emplace_back();
    if (m_table)
        fetch_descriptor();
}

Query::Query(const Query& copy)
{
    m_table = copy.m_table;
    m_groups = copy.m_groups;
    error_code = copy.error_code;
    m_view = copy.m_view;
    m_source_link_view = copy.m_source_link_view;
    m_source_table_view = copy.m_source_table_view;
    m_owns_source_table_view = false;
    m_current_descriptor = copy.m_current_descriptor;
}

Query& Query::operator = (const Query& source)
{
    if (this != &source) {
        m_groups = source.m_groups;
        m_table = source.m_table;
        m_view = source.m_view;
        m_source_link_view = source.m_source_link_view;
        m_source_table_view = source.m_source_table_view;

        if (m_table)
            fetch_descriptor();
    }
    return *this;
}

Query::Query(Query&&) = default;
Query& Query::operator=(Query&&) = default;

Query::~Query() noexcept
{
    if (m_owns_source_table_view)
        delete m_source_table_view;
}

Query::Query(Query& source, HandoverPatch& patch, MutableSourcePayload mode)
    : m_table(TableRef()), m_source_link_view(LinkViewRef()), m_source_table_view(nullptr)
{
    Table::generate_patch(source.m_table, patch.m_table);
    if (source.m_source_table_view) {
        m_source_table_view =
            source.m_source_table_view->clone_for_handover(patch.table_view_data, mode).release();
        m_owns_source_table_view = true;
    }
    else {
        patch.table_view_data = nullptr;
        m_owns_source_table_view = false;
    }
    LinkView::generate_patch(source.m_source_link_view, patch.link_view_data);

    m_groups.reserve(source.m_groups.size());
    for (const auto& group: source.m_groups) {
        m_groups.emplace_back(group, patch.m_node_data);
    }
}

Query::Query(const Query& source, HandoverPatch& patch, ConstSourcePayload mode)
    : m_table(TableRef()), m_source_link_view(LinkViewRef()), m_source_table_view(nullptr)
{
    Table::generate_patch(source.m_table, patch.m_table);
    if (source.m_source_table_view) {
        m_source_table_view =
            source.m_source_table_view->clone_for_handover(patch.table_view_data, mode).release();
        m_owns_source_table_view = true;
    }
    else {
        patch.table_view_data = nullptr;
        m_owns_source_table_view = false;
    }
    LinkView::generate_patch(source.m_source_link_view, patch.link_view_data);

    m_groups.reserve(source.m_groups.size());
    for (const auto& group: source.m_groups) {
        m_groups.emplace_back(group, patch.m_node_data);
    }
}

Query::Query(std::unique_ptr<Expression> expr) : Query()
{
    if (auto table = const_cast<Table*>(expr->get_base_table()))
        set_table(table->get_table_ref());

    add_expression_node(std::move(expr));
}

void Query::set_table(TableRef tr)
{
    if (tr == m_table) {
        return;
    }

    m_table = tr;
    if (m_table) {
        fetch_descriptor();
    }
    else {
        m_current_descriptor.reset(nullptr);
    }
}


void Query::apply_patch(HandoverPatch& patch, Group& group)
{
    if (m_source_table_view) {
        m_source_table_view->apply_and_consume_patch(patch.table_view_data, group);
    }
    m_source_link_view = LinkView::create_from_and_consume_patch(patch.link_view_data, group);
    if (m_source_link_view)
        m_view = m_source_link_view.get();
    else if (m_source_table_view)
        m_view = m_source_table_view;
    else
        m_view = nullptr;
    // not going through Table::create_from_and_consume_patch because we need to use
    // set_table() to update all table references
    if (patch.m_table) {
        set_table(group.get_table(patch.m_table->m_table_num));
    }

    for (auto it = m_groups.rbegin(); it != m_groups.rend(); ++it) {
        if (auto& root_node = it->m_root_node)
            root_node->apply_handover_patch(patch.m_node_data, group);
    }
    REALM_ASSERT(patch.m_node_data.empty());
}

void Query::add_expression_node(std::unique_ptr<Expression> expression)
{
    add_node(std::unique_ptr<ParentNode>(new ExpressionNode(std::move(expression))));
}

// Binary
Query& Query::equal(size_t column_ndx, BinaryData b)
{
    add_condition<Equal>(column_ndx, b);
    return *this;
}
Query& Query::not_equal(size_t column_ndx, BinaryData b)
{
    add_condition<NotEqual>(column_ndx, b);
    return *this;
}
Query& Query::begins_with(size_t column_ndx, BinaryData b)
{
    add_condition<BeginsWith>(column_ndx, b);
    return *this;
}
Query& Query::ends_with(size_t column_ndx, BinaryData b)
{
    add_condition<EndsWith>(column_ndx, b);
    return *this;
}
Query& Query::contains(size_t column_ndx, BinaryData b)
{
    add_condition<Contains>(column_ndx, b);
    return *this;
}


namespace {

template<class Node>
struct MakeConditionNode {
    static std::unique_ptr<ParentNode> make(size_t col_ndx, typename Node::TConditionValue value)
    {
        return std::unique_ptr<ParentNode>{new Node(std::move(value), col_ndx)};
    }

    static std::unique_ptr<ParentNode> make(size_t col_ndx, null)
    {
        return std::unique_ptr<ParentNode>{new Node(null{}, col_ndx)};
    }

    template<class T = typename Node::TConditionValue>
    static typename std::enable_if<!std::is_same<typename util::RemoveOptional<T>::type, T>::value, std::unique_ptr<ParentNode>>::type
    make(size_t col_ndx, typename util::RemoveOptional<T>::type value)
    {
        return std::unique_ptr<ParentNode>{new Node(std::move(value), col_ndx)};
    }

    template<class T>
    static std::unique_ptr<ParentNode> make(size_t, T)
    {
        throw LogicError{LogicError::type_mismatch};
    }
};

template<class Cond>
struct MakeConditionNode<IntegerNode<IntegerColumn, Cond>> {
    static std::unique_ptr<ParentNode> make(size_t col_ndx, int64_t value)
    {
        return std::unique_ptr<ParentNode>{new IntegerNode<IntegerColumn, Cond>(std::move(value), col_ndx)};
    }

    template<class T>
    static std::unique_ptr<ParentNode> make(size_t, T)
    {
        throw LogicError{LogicError::type_mismatch};
    }
};

template<class Cond>
struct MakeConditionNode<StringNode<Cond>> {
    static std::unique_ptr<ParentNode> make(size_t col_ndx, StringData value)
    {
        return std::unique_ptr<ParentNode>{new StringNode<Cond>(std::move(value), col_ndx)};
    }

    static std::unique_ptr<ParentNode> make(size_t col_ndx, null)
    {
        return std::unique_ptr<ParentNode>{new StringNode<Cond>(null{}, col_ndx)};
    }

    template<class T>
    static std::unique_ptr<ParentNode> make(size_t, T)
    {
        throw LogicError{LogicError::type_mismatch};
    }
};

template<class Cond, class T>
std::unique_ptr<ParentNode> make_condition_node(const Descriptor& descriptor, size_t column_ndx, T value)
{
    DataType type = descriptor.get_column_type(column_ndx);
    bool is_nullable = descriptor.is_nullable(column_ndx);
    switch (type) {
        case type_Int:
        case type_Bool:
        case type_DateTime: {
            if (is_nullable) {
                return MakeConditionNode<IntegerNode<IntNullColumn, Cond>>::make(column_ndx, value);
            }
            else {
                return MakeConditionNode<IntegerNode<IntegerColumn, Cond>>::make(column_ndx, value);
            }
        }
        case type_Float: {
            return MakeConditionNode<FloatDoubleNode<FloatColumn, Cond>>::make(column_ndx, value);
        }
        case type_Double: {
            return MakeConditionNode<FloatDoubleNode<DoubleColumn, Cond>>::make(column_ndx, value);
        }
        case type_String: {
            return MakeConditionNode<StringNode<Cond>>::make(column_ndx, value);
        }
        case type_Binary: {
            return MakeConditionNode<BinaryNode<Cond>>::make(column_ndx, value);
        }
        default: {
            throw LogicError{LogicError::type_mismatch};
        }
    }
}

} // anonymous namespace

void Query::fetch_descriptor()
{
    ConstDescriptorRef desc = m_table->get_descriptor();
    for (size_t i = 0; i < m_subtable_path.size(); ++i) {
        desc = desc->get_subdescriptor(m_subtable_path[i]);
    }
    m_current_descriptor = desc;
}


template<typename TConditionFunction, class T>
Query& Query::add_condition(size_t column_ndx, T value)
{
    REALM_ASSERT_DEBUG(m_current_descriptor);
    auto node = make_condition_node<TConditionFunction>(*m_current_descriptor, column_ndx, value);
    add_node(std::move(node));
    return *this;
}


template<class ColumnType>
Query& Query::equal(size_t column_ndx1, size_t column_ndx2)
{
    auto node = std::unique_ptr<ParentNode>(new TwoColumnsNode<ColumnType, Equal>(column_ndx1, column_ndx2));
    add_node(std::move(node));
    return *this;
}

// Two column methods, any type
template<class ColumnType>
Query& Query::less(size_t column_ndx1, size_t column_ndx2)
{
    auto node = std::unique_ptr<ParentNode>(new TwoColumnsNode<ColumnType, Less>(column_ndx1, column_ndx2));
    add_node(std::move(node));
    return *this;
}
template<class ColumnType>
Query& Query::less_equal(size_t column_ndx1, size_t column_ndx2)
{
    auto node = std::unique_ptr<ParentNode>(new TwoColumnsNode<ColumnType, LessEqual>(column_ndx1, column_ndx2));
    add_node(std::move(node));
    return *this;
}
template<class ColumnType>
Query& Query::greater(size_t column_ndx1, size_t column_ndx2)
{
    auto node = std::unique_ptr<ParentNode>(new TwoColumnsNode<ColumnType, Greater>(column_ndx1, column_ndx2));
    add_node(std::move(node));
    return *this;
}
template<class ColumnType>
Query& Query::greater_equal(size_t column_ndx1, size_t column_ndx2)
{
    auto node = std::unique_ptr<ParentNode>(new TwoColumnsNode<ColumnType, GreaterEqual>(column_ndx1, column_ndx2));
    add_node(std::move(node));
    return *this;
}
template<class ColumnType>
Query& Query::not_equal(size_t column_ndx1, size_t column_ndx2)
{
    auto node = std::unique_ptr<ParentNode>(new TwoColumnsNode<ColumnType, NotEqual>(column_ndx1, column_ndx2));
    add_node(std::move(node));
    return *this;
}

// column vs column, integer
Query& Query::equal_int(size_t column_ndx1, size_t column_ndx2)
{
    return equal<IntegerColumn>(column_ndx1, column_ndx2);
}

Query& Query::not_equal_int(size_t column_ndx1, size_t column_ndx2)
{
    return not_equal<IntegerColumn>(column_ndx1, column_ndx2);
}

Query& Query::less_int(size_t column_ndx1, size_t column_ndx2)
{
    return less<IntegerColumn>(column_ndx1, column_ndx2);
}

Query& Query::greater_equal_int(size_t column_ndx1, size_t column_ndx2)
{
    return greater_equal<IntegerColumn>(column_ndx1, column_ndx2);
}

Query& Query::less_equal_int(size_t column_ndx1, size_t column_ndx2)
{
    return less_equal<IntegerColumn>(column_ndx1, column_ndx2);
}

Query& Query::greater_int(size_t column_ndx1, size_t column_ndx2)
{
    return greater<IntegerColumn>(column_ndx1, column_ndx2);
}


// column vs column, float
Query& Query::not_equal_float(size_t column_ndx1, size_t column_ndx2)
{
    return not_equal<FloatColumn>(column_ndx1, column_ndx2);
}

Query& Query::less_float(size_t column_ndx1, size_t column_ndx2)
{
    return less<FloatColumn>(column_ndx1, column_ndx2);
}

Query& Query::greater_float(size_t column_ndx1, size_t column_ndx2)
{
    return greater<FloatColumn>(column_ndx1, column_ndx2);
}

Query& Query::greater_equal_float(size_t column_ndx1, size_t column_ndx2)
{
    return greater_equal<FloatColumn>(column_ndx1, column_ndx2);
}

Query& Query::less_equal_float(size_t column_ndx1, size_t column_ndx2)
{
    return less_equal<FloatColumn>(column_ndx1, column_ndx2);
}

Query& Query::equal_float(size_t column_ndx1, size_t column_ndx2)
{
    return equal<FloatColumn>(column_ndx1, column_ndx2);
}

// column vs column, double
Query& Query::equal_double(size_t column_ndx1, size_t column_ndx2)
{
    return equal<DoubleColumn>(column_ndx1, column_ndx2);
}

Query& Query::less_equal_double(size_t column_ndx1, size_t column_ndx2)
{
    return less_equal<DoubleColumn>(column_ndx1, column_ndx2);
}

Query& Query::greater_equal_double(size_t column_ndx1, size_t column_ndx2)
{
    return greater_equal<DoubleColumn>(column_ndx1, column_ndx2);
}
Query& Query::greater_double(size_t column_ndx1, size_t column_ndx2)
{
    return greater<DoubleColumn>(column_ndx1, column_ndx2);
}
Query& Query::less_double(size_t column_ndx1, size_t column_ndx2)
{
    return less<DoubleColumn>(column_ndx1, column_ndx2);
}

Query& Query::not_equal_double(size_t column_ndx1, size_t column_ndx2)
{
    return not_equal<DoubleColumn>(column_ndx1, column_ndx2);
}

// null vs column
Query& Query::equal(size_t column_ndx, null)
{
    add_condition<Equal>(column_ndx, null{});
    return *this;
}

Query& Query::not_equal(size_t column_ndx, null)
{
    add_condition<NotEqual>(column_ndx, null());
    return *this;
}

// int constant vs column (we need those because '1234' is ambiguous, can convert to float/double/int64_t)
Query& Query::equal(size_t column_ndx, int value)
{
    return equal(column_ndx, static_cast<int64_t>(value));
}
Query& Query::not_equal(size_t column_ndx, int value)
{
    return not_equal(column_ndx, static_cast<int64_t>(value));
}
Query& Query::greater(size_t column_ndx, int value)
{
    return greater(column_ndx, static_cast<int64_t>(value));
}
Query& Query::greater_equal(size_t column_ndx, int value)
{
    return greater_equal(column_ndx, static_cast<int64_t>(value));
}
Query& Query::less_equal(size_t column_ndx, int value)
{
    return less_equal(column_ndx, static_cast<int64_t>(value));
}
Query& Query::less(size_t column_ndx, int value)
{
    return less(column_ndx, static_cast<int64_t>(value));
}
Query& Query::between(size_t column_ndx, int from, int to)
{
    return between(column_ndx, static_cast<int64_t>(from), static_cast<int64_t>(to));
}

Query& Query::links_to(size_t origin_column, const ConstRow& target_row)
{
    add_node(std::unique_ptr<ParentNode>(new LinksToNode(origin_column, target_row)));
    return *this;
}

// int64 constant vs column
Query& Query::equal(size_t column_ndx, int64_t value)
{
    add_condition<Equal>(column_ndx, value);
    return *this;
}
Query& Query::not_equal(size_t column_ndx, int64_t value)
{
    add_condition<NotEqual>(column_ndx, value);
    return *this;
}
Query& Query::greater(size_t column_ndx, int64_t value)
{
    add_condition<Greater>(column_ndx, value);
    return *this;
}
Query& Query::greater_equal(size_t column_ndx, int64_t value)
{
    if (value > LLONG_MIN) {
        add_condition<Greater>(column_ndx, value - 1);
    }
    // field >= LLONG_MIN has no effect
    return *this;
}
Query& Query::less_equal(size_t column_ndx, int64_t value)
{
    if (value < LLONG_MAX) {
        add_condition<Less>(column_ndx, value + 1);
    }
    // field <= LLONG_MAX has no effect
    return *this;
}
Query& Query::less(size_t column_ndx, int64_t value)
{
    add_condition<Less>(column_ndx, value);
    return *this;
}
Query& Query::between(size_t column_ndx, int64_t from, int64_t to)
{
    group();
    greater_equal(column_ndx, from);
    less_equal(column_ndx, to);
    end_group();
    return *this;
}
Query& Query::equal(size_t column_ndx, bool value)
{
    add_condition<Equal>(column_ndx, int64_t(value));
    return *this;
}

// ------------- float
Query& Query::equal(size_t column_ndx, float value)
{
    return add_condition<Equal>(column_ndx, value);
}
Query& Query::not_equal(size_t column_ndx, float value)
{
    return add_condition<NotEqual>(column_ndx, value);
}
Query& Query::greater(size_t column_ndx, float value)
{
    return add_condition<Greater>(column_ndx, value);
}
Query& Query::greater_equal(size_t column_ndx, float value)
{
    return add_condition<GreaterEqual>(column_ndx, value);
}
Query& Query::less_equal(size_t column_ndx, float value)
{
    return add_condition<LessEqual>(column_ndx, value);
}
Query& Query::less(size_t column_ndx, float value)
{
    return add_condition<Less>(column_ndx, value);
}
Query& Query::between(size_t column_ndx, float from, float to)
{
    group();
    greater_equal(column_ndx, from);
    less_equal(column_ndx, to);
    end_group();
    return *this;
}


// ------------- double
Query& Query::equal(size_t column_ndx, double value)
{
    return add_condition<Equal>(column_ndx, value);
}
Query& Query::not_equal(size_t column_ndx, double value)
{
    return add_condition<NotEqual>(column_ndx, value);
}
Query& Query::greater(size_t column_ndx, double value)
{
    return add_condition<Greater>(column_ndx, value);
}
Query& Query::greater_equal(size_t column_ndx, double value)
{
    return add_condition<GreaterEqual>(column_ndx, value);
}
Query& Query::less_equal(size_t column_ndx, double value)
{
    return add_condition<LessEqual>(column_ndx, value);
}
Query& Query::less(size_t column_ndx, double value)
{
    return add_condition<Less>(column_ndx, value);
}
Query& Query::between(size_t column_ndx, double from, double to)
{
    group();
    greater_equal(column_ndx, from);
    less_equal(column_ndx, to);
    end_group();
    return *this;
}


// Strings, StringData()

Query& Query::equal(size_t column_ndx, StringData value, bool case_sensitive)
{
    if (case_sensitive)
        add_condition<Equal>(column_ndx, value);
    else
        add_condition<EqualIns>(column_ndx, value);
    return *this;
}
Query& Query::begins_with(size_t column_ndx, StringData value, bool case_sensitive)
{
    if (case_sensitive)
        add_condition<BeginsWith>(column_ndx, value);
    else
        add_condition<BeginsWithIns>(column_ndx, value);
    return *this;
}
Query& Query::ends_with(size_t column_ndx, StringData value, bool case_sensitive)
{
    if (case_sensitive)
        add_condition<EndsWith>(column_ndx, value);
    else
        add_condition<EndsWithIns>(column_ndx, value);
    return *this;
}
Query& Query::contains(size_t column_ndx, StringData value, bool case_sensitive)
{
    if (case_sensitive)
        add_condition<Contains>(column_ndx, value);
    else
        add_condition<ContainsIns>(column_ndx, value);
    return *this;
}
Query& Query::not_equal(size_t column_ndx, StringData value, bool case_sensitive)
{
    if (case_sensitive)
        add_condition<NotEqual>(column_ndx, value);
    else
        add_condition<NotEqualIns>(column_ndx, value);
    return *this;
}


// Aggregates =================================================================================

size_t Query::peek_tableview(size_t tv_index) const
{
    REALM_ASSERT(m_view);
    REALM_ASSERT_DEBUG(m_view->cookie == m_view->cookie_expected);
    REALM_ASSERT_3(tv_index, <, m_view->size());

    // Cannot use to_size_t() because the get() may return -1
    size_t tablerow = static_cast<size_t>(m_view->m_row_indexes.get(tv_index));

    if (has_conditions())
        return root_node()->find_first(tablerow, tablerow + 1);

    return tablerow;
}

template<Action action, typename T, typename R, class ColType>
    R Query::aggregate(R(ColType::*aggregateMethod)(size_t start, size_t end, size_t limit,
                                                    size_t* return_ndx) const,
                       size_t column_ndx, size_t* resultcount, size_t start, size_t end, size_t limit,
                       size_t* return_ndx) const
{
    if(limit == 0 || m_table->is_degenerate()) {
        if (resultcount)
            *resultcount = 0;
        return static_cast<R>(0);
    }

    if (end == size_t(-1))
        end = m_view ? m_view->size() : m_table->size();

    const ColType& column =
        m_table->get_column<ColType, ColumnType(ColumnTypeTraits<T>::id)>(column_ndx);

    if (!has_conditions() && !m_view) {
        // No criteria, so call aggregate METHODS directly on columns
        // - this bypasses the query system and is faster
        // User created query with no criteria; aggregate range
        if (resultcount) {
            *resultcount = limit < (end - start) ? limit : (end - start);
        }
        // direct aggregate on the column
        return (column.*aggregateMethod)(start, end, limit, action == act_Sum ? resultcount : return_ndx);
    }
    else {

        // Aggregate with criteria - goes through the nodes in the query system
        init(*m_table);
        QueryState<R> st;
        st.init(action, nullptr, limit);

        SequentialGetter<ColType> source_column(*m_table, column_ndx);

        if (!m_view) {
            aggregate_internal(action, ColumnTypeTraits<T>::id, ColType::nullable, root_node(), &st, start, end, &source_column);
        }
        else {
            for (size_t t = start; t < end && st.m_match_count < limit; t++) {
                size_t r = peek_tableview(t);
                if (r != not_found)
                    st.template match<action, false>(r, 0, source_column.get_next(m_view->m_row_indexes.get(t)));
            }
        }

        if (resultcount) {<commit_msg>In Query::aggregate, a single int64_t to size_t cast was done with a REALM_ASSERT as well.<commit_after>#include <cstdio>
#include <algorithm>

#include <realm/array.hpp>
#include <realm/column_fwd.hpp>
#include <realm/query.hpp>
#include <realm/query_engine.hpp>
#include <realm/descriptor.hpp>
#include <realm/table_view.hpp>
#include <realm/link_view.hpp>

using namespace realm;

Query::Query() : m_view(nullptr), m_source_table_view(nullptr), m_owns_source_table_view(false)
{
    create();
}

Query::Query(Table& table, TableViewBase* tv)
    : m_table(table.get_table_ref()), m_view(tv), m_source_table_view(tv), m_owns_source_table_view(false)
{
    REALM_ASSERT_DEBUG(m_view == nullptr || m_view->cookie == m_view->cookie_expected);
    create();
}

Query::Query(const Table& table, const LinkViewRef& lv):
    m_table((const_cast<Table&>(table)).get_table_ref()),
    m_view(lv.get()),
    m_source_link_view(lv), m_source_table_view(nullptr), m_owns_source_table_view(false)
{
    REALM_ASSERT_DEBUG(m_view == nullptr || m_view->cookie == m_view->cookie_expected);
    create();
}

Query::Query(const Table& table, TableViewBase* tv)
    : m_table((const_cast<Table&>(table)).get_table_ref()), m_view(tv), m_source_table_view(tv), m_owns_source_table_view(false)
{
    REALM_ASSERT_DEBUG(m_view == nullptr ||m_view->cookie == m_view->cookie_expected);
    create();
}

Query::Query(const Table& table, std::unique_ptr<TableViewBase> tv)
    : m_table((const_cast<Table&>(table)).get_table_ref()), m_view(tv.get()), m_source_table_view(tv.get()), m_owns_source_table_view(true)
{
    tv.release();

    REALM_ASSERT_DEBUG(m_view == nullptr ||m_view->cookie == m_view->cookie_expected);
    create();
}

void Query::create()
{
    m_groups.emplace_back();
    if (m_table)
        fetch_descriptor();
}

Query::Query(const Query& copy)
{
    m_table = copy.m_table;
    m_groups = copy.m_groups;
    error_code = copy.error_code;
    m_view = copy.m_view;
    m_source_link_view = copy.m_source_link_view;
    m_source_table_view = copy.m_source_table_view;
    m_owns_source_table_view = false;
    m_current_descriptor = copy.m_current_descriptor;
}

Query& Query::operator = (const Query& source)
{
    if (this != &source) {
        m_groups = source.m_groups;
        m_table = source.m_table;
        m_view = source.m_view;
        m_source_link_view = source.m_source_link_view;
        m_source_table_view = source.m_source_table_view;

        if (m_table)
            fetch_descriptor();
    }
    return *this;
}

Query::Query(Query&&) = default;
Query& Query::operator=(Query&&) = default;

Query::~Query() noexcept
{
    if (m_owns_source_table_view)
        delete m_source_table_view;
}

Query::Query(Query& source, HandoverPatch& patch, MutableSourcePayload mode)
    : m_table(TableRef()), m_source_link_view(LinkViewRef()), m_source_table_view(nullptr)
{
    Table::generate_patch(source.m_table, patch.m_table);
    if (source.m_source_table_view) {
        m_source_table_view =
            source.m_source_table_view->clone_for_handover(patch.table_view_data, mode).release();
        m_owns_source_table_view = true;
    }
    else {
        patch.table_view_data = nullptr;
        m_owns_source_table_view = false;
    }
    LinkView::generate_patch(source.m_source_link_view, patch.link_view_data);

    m_groups.reserve(source.m_groups.size());
    for (const auto& group: source.m_groups) {
        m_groups.emplace_back(group, patch.m_node_data);
    }
}

Query::Query(const Query& source, HandoverPatch& patch, ConstSourcePayload mode)
    : m_table(TableRef()), m_source_link_view(LinkViewRef()), m_source_table_view(nullptr)
{
    Table::generate_patch(source.m_table, patch.m_table);
    if (source.m_source_table_view) {
        m_source_table_view =
            source.m_source_table_view->clone_for_handover(patch.table_view_data, mode).release();
        m_owns_source_table_view = true;
    }
    else {
        patch.table_view_data = nullptr;
        m_owns_source_table_view = false;
    }
    LinkView::generate_patch(source.m_source_link_view, patch.link_view_data);

    m_groups.reserve(source.m_groups.size());
    for (const auto& group: source.m_groups) {
        m_groups.emplace_back(group, patch.m_node_data);
    }
}

Query::Query(std::unique_ptr<Expression> expr) : Query()
{
    if (auto table = const_cast<Table*>(expr->get_base_table()))
        set_table(table->get_table_ref());

    add_expression_node(std::move(expr));
}

void Query::set_table(TableRef tr)
{
    if (tr == m_table) {
        return;
    }

    m_table = tr;
    if (m_table) {
        fetch_descriptor();
    }
    else {
        m_current_descriptor.reset(nullptr);
    }
}


void Query::apply_patch(HandoverPatch& patch, Group& group)
{
    if (m_source_table_view) {
        m_source_table_view->apply_and_consume_patch(patch.table_view_data, group);
    }
    m_source_link_view = LinkView::create_from_and_consume_patch(patch.link_view_data, group);
    if (m_source_link_view)
        m_view = m_source_link_view.get();
    else if (m_source_table_view)
        m_view = m_source_table_view;
    else
        m_view = nullptr;
    // not going through Table::create_from_and_consume_patch because we need to use
    // set_table() to update all table references
    if (patch.m_table) {
        set_table(group.get_table(patch.m_table->m_table_num));
    }

    for (auto it = m_groups.rbegin(); it != m_groups.rend(); ++it) {
        if (auto& root_node = it->m_root_node)
            root_node->apply_handover_patch(patch.m_node_data, group);
    }
    REALM_ASSERT(patch.m_node_data.empty());
}

void Query::add_expression_node(std::unique_ptr<Expression> expression)
{
    add_node(std::unique_ptr<ParentNode>(new ExpressionNode(std::move(expression))));
}

// Binary
Query& Query::equal(size_t column_ndx, BinaryData b)
{
    add_condition<Equal>(column_ndx, b);
    return *this;
}
Query& Query::not_equal(size_t column_ndx, BinaryData b)
{
    add_condition<NotEqual>(column_ndx, b);
    return *this;
}
Query& Query::begins_with(size_t column_ndx, BinaryData b)
{
    add_condition<BeginsWith>(column_ndx, b);
    return *this;
}
Query& Query::ends_with(size_t column_ndx, BinaryData b)
{
    add_condition<EndsWith>(column_ndx, b);
    return *this;
}
Query& Query::contains(size_t column_ndx, BinaryData b)
{
    add_condition<Contains>(column_ndx, b);
    return *this;
}


namespace {

template<class Node>
struct MakeConditionNode {
    static std::unique_ptr<ParentNode> make(size_t col_ndx, typename Node::TConditionValue value)
    {
        return std::unique_ptr<ParentNode>{new Node(std::move(value), col_ndx)};
    }

    static std::unique_ptr<ParentNode> make(size_t col_ndx, null)
    {
        return std::unique_ptr<ParentNode>{new Node(null{}, col_ndx)};
    }

    template<class T = typename Node::TConditionValue>
    static typename std::enable_if<!std::is_same<typename util::RemoveOptional<T>::type, T>::value, std::unique_ptr<ParentNode>>::type
    make(size_t col_ndx, typename util::RemoveOptional<T>::type value)
    {
        return std::unique_ptr<ParentNode>{new Node(std::move(value), col_ndx)};
    }

    template<class T>
    static std::unique_ptr<ParentNode> make(size_t, T)
    {
        throw LogicError{LogicError::type_mismatch};
    }
};

template<class Cond>
struct MakeConditionNode<IntegerNode<IntegerColumn, Cond>> {
    static std::unique_ptr<ParentNode> make(size_t col_ndx, int64_t value)
    {
        return std::unique_ptr<ParentNode>{new IntegerNode<IntegerColumn, Cond>(std::move(value), col_ndx)};
    }

    template<class T>
    static std::unique_ptr<ParentNode> make(size_t, T)
    {
        throw LogicError{LogicError::type_mismatch};
    }
};

template<class Cond>
struct MakeConditionNode<StringNode<Cond>> {
    static std::unique_ptr<ParentNode> make(size_t col_ndx, StringData value)
    {
        return std::unique_ptr<ParentNode>{new StringNode<Cond>(std::move(value), col_ndx)};
    }

    static std::unique_ptr<ParentNode> make(size_t col_ndx, null)
    {
        return std::unique_ptr<ParentNode>{new StringNode<Cond>(null{}, col_ndx)};
    }

    template<class T>
    static std::unique_ptr<ParentNode> make(size_t, T)
    {
        throw LogicError{LogicError::type_mismatch};
    }
};

template<class Cond, class T>
std::unique_ptr<ParentNode> make_condition_node(const Descriptor& descriptor, size_t column_ndx, T value)
{
    DataType type = descriptor.get_column_type(column_ndx);
    bool is_nullable = descriptor.is_nullable(column_ndx);
    switch (type) {
        case type_Int:
        case type_Bool:
        case type_DateTime: {
            if (is_nullable) {
                return MakeConditionNode<IntegerNode<IntNullColumn, Cond>>::make(column_ndx, value);
            }
            else {
                return MakeConditionNode<IntegerNode<IntegerColumn, Cond>>::make(column_ndx, value);
            }
        }
        case type_Float: {
            return MakeConditionNode<FloatDoubleNode<FloatColumn, Cond>>::make(column_ndx, value);
        }
        case type_Double: {
            return MakeConditionNode<FloatDoubleNode<DoubleColumn, Cond>>::make(column_ndx, value);
        }
        case type_String: {
            return MakeConditionNode<StringNode<Cond>>::make(column_ndx, value);
        }
        case type_Binary: {
            return MakeConditionNode<BinaryNode<Cond>>::make(column_ndx, value);
        }
        default: {
            throw LogicError{LogicError::type_mismatch};
        }
    }
}

} // anonymous namespace

void Query::fetch_descriptor()
{
    ConstDescriptorRef desc = m_table->get_descriptor();
    for (size_t i = 0; i < m_subtable_path.size(); ++i) {
        desc = desc->get_subdescriptor(m_subtable_path[i]);
    }
    m_current_descriptor = desc;
}


template<typename TConditionFunction, class T>
Query& Query::add_condition(size_t column_ndx, T value)
{
    REALM_ASSERT_DEBUG(m_current_descriptor);
    auto node = make_condition_node<TConditionFunction>(*m_current_descriptor, column_ndx, value);
    add_node(std::move(node));
    return *this;
}


template<class ColumnType>
Query& Query::equal(size_t column_ndx1, size_t column_ndx2)
{
    auto node = std::unique_ptr<ParentNode>(new TwoColumnsNode<ColumnType, Equal>(column_ndx1, column_ndx2));
    add_node(std::move(node));
    return *this;
}

// Two column methods, any type
template<class ColumnType>
Query& Query::less(size_t column_ndx1, size_t column_ndx2)
{
    auto node = std::unique_ptr<ParentNode>(new TwoColumnsNode<ColumnType, Less>(column_ndx1, column_ndx2));
    add_node(std::move(node));
    return *this;
}
template<class ColumnType>
Query& Query::less_equal(size_t column_ndx1, size_t column_ndx2)
{
    auto node = std::unique_ptr<ParentNode>(new TwoColumnsNode<ColumnType, LessEqual>(column_ndx1, column_ndx2));
    add_node(std::move(node));
    return *this;
}
template<class ColumnType>
Query& Query::greater(size_t column_ndx1, size_t column_ndx2)
{
    auto node = std::unique_ptr<ParentNode>(new TwoColumnsNode<ColumnType, Greater>(column_ndx1, column_ndx2));
    add_node(std::move(node));
    return *this;
}
template<class ColumnType>
Query& Query::greater_equal(size_t column_ndx1, size_t column_ndx2)
{
    auto node = std::unique_ptr<ParentNode>(new TwoColumnsNode<ColumnType, GreaterEqual>(column_ndx1, column_ndx2));
    add_node(std::move(node));
    return *this;
}
template<class ColumnType>
Query& Query::not_equal(size_t column_ndx1, size_t column_ndx2)
{
    auto node = std::unique_ptr<ParentNode>(new TwoColumnsNode<ColumnType, NotEqual>(column_ndx1, column_ndx2));
    add_node(std::move(node));
    return *this;
}

// column vs column, integer
Query& Query::equal_int(size_t column_ndx1, size_t column_ndx2)
{
    return equal<IntegerColumn>(column_ndx1, column_ndx2);
}

Query& Query::not_equal_int(size_t column_ndx1, size_t column_ndx2)
{
    return not_equal<IntegerColumn>(column_ndx1, column_ndx2);
}

Query& Query::less_int(size_t column_ndx1, size_t column_ndx2)
{
    return less<IntegerColumn>(column_ndx1, column_ndx2);
}

Query& Query::greater_equal_int(size_t column_ndx1, size_t column_ndx2)
{
    return greater_equal<IntegerColumn>(column_ndx1, column_ndx2);
}

Query& Query::less_equal_int(size_t column_ndx1, size_t column_ndx2)
{
    return less_equal<IntegerColumn>(column_ndx1, column_ndx2);
}

Query& Query::greater_int(size_t column_ndx1, size_t column_ndx2)
{
    return greater<IntegerColumn>(column_ndx1, column_ndx2);
}


// column vs column, float
Query& Query::not_equal_float(size_t column_ndx1, size_t column_ndx2)
{
    return not_equal<FloatColumn>(column_ndx1, column_ndx2);
}

Query& Query::less_float(size_t column_ndx1, size_t column_ndx2)
{
    return less<FloatColumn>(column_ndx1, column_ndx2);
}

Query& Query::greater_float(size_t column_ndx1, size_t column_ndx2)
{
    return greater<FloatColumn>(column_ndx1, column_ndx2);
}

Query& Query::greater_equal_float(size_t column_ndx1, size_t column_ndx2)
{
    return greater_equal<FloatColumn>(column_ndx1, column_ndx2);
}

Query& Query::less_equal_float(size_t column_ndx1, size_t column_ndx2)
{
    return less_equal<FloatColumn>(column_ndx1, column_ndx2);
}

Query& Query::equal_float(size_t column_ndx1, size_t column_ndx2)
{
    return equal<FloatColumn>(column_ndx1, column_ndx2);
}

// column vs column, double
Query& Query::equal_double(size_t column_ndx1, size_t column_ndx2)
{
    return equal<DoubleColumn>(column_ndx1, column_ndx2);
}

Query& Query::less_equal_double(size_t column_ndx1, size_t column_ndx2)
{
    return less_equal<DoubleColumn>(column_ndx1, column_ndx2);
}

Query& Query::greater_equal_double(size_t column_ndx1, size_t column_ndx2)
{
    return greater_equal<DoubleColumn>(column_ndx1, column_ndx2);
}
Query& Query::greater_double(size_t column_ndx1, size_t column_ndx2)
{
    return greater<DoubleColumn>(column_ndx1, column_ndx2);
}
Query& Query::less_double(size_t column_ndx1, size_t column_ndx2)
{
    return less<DoubleColumn>(column_ndx1, column_ndx2);
}

Query& Query::not_equal_double(size_t column_ndx1, size_t column_ndx2)
{
    return not_equal<DoubleColumn>(column_ndx1, column_ndx2);
}

// null vs column
Query& Query::equal(size_t column_ndx, null)
{
    add_condition<Equal>(column_ndx, null{});
    return *this;
}

Query& Query::not_equal(size_t column_ndx, null)
{
    add_condition<NotEqual>(column_ndx, null());
    return *this;
}

// int constant vs column (we need those because '1234' is ambiguous, can convert to float/double/int64_t)
Query& Query::equal(size_t column_ndx, int value)
{
    return equal(column_ndx, static_cast<int64_t>(value));
}
Query& Query::not_equal(size_t column_ndx, int value)
{
    return not_equal(column_ndx, static_cast<int64_t>(value));
}
Query& Query::greater(size_t column_ndx, int value)
{
    return greater(column_ndx, static_cast<int64_t>(value));
}
Query& Query::greater_equal(size_t column_ndx, int value)
{
    return greater_equal(column_ndx, static_cast<int64_t>(value));
}
Query& Query::less_equal(size_t column_ndx, int value)
{
    return less_equal(column_ndx, static_cast<int64_t>(value));
}
Query& Query::less(size_t column_ndx, int value)
{
    return less(column_ndx, static_cast<int64_t>(value));
}
Query& Query::between(size_t column_ndx, int from, int to)
{
    return between(column_ndx, static_cast<int64_t>(from), static_cast<int64_t>(to));
}

Query& Query::links_to(size_t origin_column, const ConstRow& target_row)
{
    add_node(std::unique_ptr<ParentNode>(new LinksToNode(origin_column, target_row)));
    return *this;
}

// int64 constant vs column
Query& Query::equal(size_t column_ndx, int64_t value)
{
    add_condition<Equal>(column_ndx, value);
    return *this;
}
Query& Query::not_equal(size_t column_ndx, int64_t value)
{
    add_condition<NotEqual>(column_ndx, value);
    return *this;
}
Query& Query::greater(size_t column_ndx, int64_t value)
{
    add_condition<Greater>(column_ndx, value);
    return *this;
}
Query& Query::greater_equal(size_t column_ndx, int64_t value)
{
    if (value > LLONG_MIN) {
        add_condition<Greater>(column_ndx, value - 1);
    }
    // field >= LLONG_MIN has no effect
    return *this;
}
Query& Query::less_equal(size_t column_ndx, int64_t value)
{
    if (value < LLONG_MAX) {
        add_condition<Less>(column_ndx, value + 1);
    }
    // field <= LLONG_MAX has no effect
    return *this;
}
Query& Query::less(size_t column_ndx, int64_t value)
{
    add_condition<Less>(column_ndx, value);
    return *this;
}
Query& Query::between(size_t column_ndx, int64_t from, int64_t to)
{
    group();
    greater_equal(column_ndx, from);
    less_equal(column_ndx, to);
    end_group();
    return *this;
}
Query& Query::equal(size_t column_ndx, bool value)
{
    add_condition<Equal>(column_ndx, int64_t(value));
    return *this;
}

// ------------- float
Query& Query::equal(size_t column_ndx, float value)
{
    return add_condition<Equal>(column_ndx, value);
}
Query& Query::not_equal(size_t column_ndx, float value)
{
    return add_condition<NotEqual>(column_ndx, value);
}
Query& Query::greater(size_t column_ndx, float value)
{
    return add_condition<Greater>(column_ndx, value);
}
Query& Query::greater_equal(size_t column_ndx, float value)
{
    return add_condition<GreaterEqual>(column_ndx, value);
}
Query& Query::less_equal(size_t column_ndx, float value)
{
    return add_condition<LessEqual>(column_ndx, value);
}
Query& Query::less(size_t column_ndx, float value)
{
    return add_condition<Less>(column_ndx, value);
}
Query& Query::between(size_t column_ndx, float from, float to)
{
    group();
    greater_equal(column_ndx, from);
    less_equal(column_ndx, to);
    end_group();
    return *this;
}


// ------------- double
Query& Query::equal(size_t column_ndx, double value)
{
    return add_condition<Equal>(column_ndx, value);
}
Query& Query::not_equal(size_t column_ndx, double value)
{
    return add_condition<NotEqual>(column_ndx, value);
}
Query& Query::greater(size_t column_ndx, double value)
{
    return add_condition<Greater>(column_ndx, value);
}
Query& Query::greater_equal(size_t column_ndx, double value)
{
    return add_condition<GreaterEqual>(column_ndx, value);
}
Query& Query::less_equal(size_t column_ndx, double value)
{
    return add_condition<LessEqual>(column_ndx, value);
}
Query& Query::less(size_t column_ndx, double value)
{
    return add_condition<Less>(column_ndx, value);
}
Query& Query::between(size_t column_ndx, double from, double to)
{
    group();
    greater_equal(column_ndx, from);
    less_equal(column_ndx, to);
    end_group();
    return *this;
}


// Strings, StringData()

Query& Query::equal(size_t column_ndx, StringData value, bool case_sensitive)
{
    if (case_sensitive)
        add_condition<Equal>(column_ndx, value);
    else
        add_condition<EqualIns>(column_ndx, value);
    return *this;
}
Query& Query::begins_with(size_t column_ndx, StringData value, bool case_sensitive)
{
    if (case_sensitive)
        add_condition<BeginsWith>(column_ndx, value);
    else
        add_condition<BeginsWithIns>(column_ndx, value);
    return *this;
}
Query& Query::ends_with(size_t column_ndx, StringData value, bool case_sensitive)
{
    if (case_sensitive)
        add_condition<EndsWith>(column_ndx, value);
    else
        add_condition<EndsWithIns>(column_ndx, value);
    return *this;
}
Query& Query::contains(size_t column_ndx, StringData value, bool case_sensitive)
{
    if (case_sensitive)
        add_condition<Contains>(column_ndx, value);
    else
        add_condition<ContainsIns>(column_ndx, value);
    return *this;
}
Query& Query::not_equal(size_t column_ndx, StringData value, bool case_sensitive)
{
    if (case_sensitive)
        add_condition<NotEqual>(column_ndx, value);
    else
        add_condition<NotEqualIns>(column_ndx, value);
    return *this;
}


// Aggregates =================================================================================

size_t Query::peek_tableview(size_t tv_index) const
{
    REALM_ASSERT(m_view);
    REALM_ASSERT_DEBUG(m_view->cookie == m_view->cookie_expected);
    REALM_ASSERT_3(tv_index, <, m_view->size());

    // Cannot use to_size_t() because the get() may return -1
    size_t tablerow = static_cast<size_t>(m_view->m_row_indexes.get(tv_index));

    if (has_conditions())
        return root_node()->find_first(tablerow, tablerow + 1);

    return tablerow;
}

template<Action action, typename T, typename R, class ColType>
    R Query::aggregate(R(ColType::*aggregateMethod)(size_t start, size_t end, size_t limit,
                                                    size_t* return_ndx) const,
                       size_t column_ndx, size_t* resultcount, size_t start, size_t end, size_t limit,
                       size_t* return_ndx) const
{
    if(limit == 0 || m_table->is_degenerate()) {
        if (resultcount)
            *resultcount = 0;
        return static_cast<R>(0);
    }

    if (end == size_t(-1))
        end = m_view ? m_view->size() : m_table->size();

    const ColType& column =
        m_table->get_column<ColType, ColumnType(ColumnTypeTraits<T>::id)>(column_ndx);

    if (!has_conditions() && !m_view) {
        // No criteria, so call aggregate METHODS directly on columns
        // - this bypasses the query system and is faster
        // User created query with no criteria; aggregate range
        if (resultcount) {
            *resultcount = limit < (end - start) ? limit : (end - start);
        }
        // direct aggregate on the column
        return (column.*aggregateMethod)(start, end, limit, action == act_Sum ? resultcount : return_ndx);
    }
    else {

        // Aggregate with criteria - goes through the nodes in the query system
        init(*m_table);
        QueryState<R> st;
        st.init(action, nullptr, limit);

        SequentialGetter<ColType> source_column(*m_table, column_ndx);

        if (!m_view) {
            aggregate_internal(action, ColumnTypeTraits<T>::id, ColType::nullable, root_node(), &st, start, end, &source_column);
        }
        else {
            for (size_t t = start; t < end && st.m_match_count < limit; t++) {
                size_t r = peek_tableview(t);
                if (r != not_found) {
                    int64_t view_row_index = m_view->m_row_indexes.get(t);
                    REALM_ASSERT(!util::int_cast_has_overflow<size_t>(view_row_index));
                    st.template match<action, false>(r, 0, source_column.get_next(size_t(view_row_index))); //       m_view->m_row_indexes.get(t)));
                }
            }
        }

        if (resultcount) {
{'fraction_non_alphanumeric': 0.125951403528237, 'fraction_numerical': 0.006790191944968379, 'mean_word_length': 4.351620947630923, 'pattern_counts': {'":': 0, '<': 330, '<?xml version=': 0, '>': 377, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 4, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3056615', 'n_tokens_mistral': 15911, 'n_tokens_neox': 15683, 'n_words': 2825}