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

#include <tightdb/array.hpp>
#include <tightdb/column_basic.hpp>
#include <tightdb/column_fwd.hpp>
#include <tightdb/query.hpp>
#include <tightdb/query_engine.hpp>

using namespace std;
using namespace tightdb;

namespace {
const size_t thread_chunk_size = 1000;
}

Query::Query() 
{
    Create();
//    expression(static_cast<Expression*>(this));
}

Query::Query(Table& table) : m_table(table.get_table_ref())
{
    Create();
}

Query::Query(const Table& table) : m_table((const_cast<Table&>(table)).get_table_ref())
{
    Create();
}

void Query::Create()
{
    update.push_back(0);
    update_override.push_back(0);
    first.push_back(0);
#if TIGHTDB_MULTITHREAD_QUERY
    m_threadcount = 0;
#endif
    do_delete = true;
}

// FIXME: Try to remove this
Query::Query(const Query& copy)
{
    m_table = copy.m_table;
    all_nodes = copy.all_nodes;
    update = copy.update;
    update_override = copy.update_override;
    first = copy.first;
    error_code = copy.error_code;
#if TIGHTDB_MULTITHREAD_QUERY
    m_threadcount = copy.m_threadcount;
#endif
    //    copy.first[0] = 0;
    copy.do_delete = false;
    do_delete = true;
}

Query::~Query() TIGHTDB_NOEXCEPT
{
#if TIGHTDB_MULTITHREAD_QUERY
    for (size_t i = 0; i < m_threadcount; i++)
        pthread_detach(threads[i]);
#endif

    if (do_delete) {
        for (size_t t = 0; t < all_nodes.size(); t++) {
            ParentNode *p = all_nodes[t];
            std::vector<ParentNode *>::iterator it = std::find(all_nodes.begin(), all_nodes.begin() + t, p);
            if(it == all_nodes.begin() + t)
                delete p;
        }
    }
}
/*
// use and_query() instead!
Expression* Query::get_expression() {
    return (static_cast<ExpressionNode*>(first[first.size()-1]))->m_compare;
}
*/
Query& Query::expression(Expression* compare, bool auto_delete)
{
    ParentNode* const p = new ExpressionNode(compare, auto_delete);
    UpdatePointers(p, &p->m_child);
    return *this;
}

// Makes query search only in rows contained in tv
Query& Query::tableview(const TableView& tv)
{
    const Array& arr = tv.get_ref_column();
    return tableview(arr);
}

// Makes query search only in rows contained in tv
Query& Query::tableview(const Array &arr)
{
    ParentNode* const p = new ArrayNode(arr);
    UpdatePointers(p, &p->m_child);
    return *this;
}


// Binary
Query& Query::equal(size_t column_ndx, BinaryData b)
{
    ParentNode* const p = new BinaryNode<Equal>(b, column_ndx);
    UpdatePointers(p, &p->m_child);
    return *this;
}
Query& Query::not_equal(size_t column_ndx, BinaryData b)
{
    ParentNode* const p = new BinaryNode<NotEqual>(b, column_ndx);
    UpdatePointers(p, &p->m_child);
    return *this;
}
Query& Query::begins_with(size_t column_ndx, BinaryData b)
{
    ParentNode* p = new BinaryNode<BeginsWith>(b, column_ndx);
    UpdatePointers(p, &p->m_child);
    return *this;
}
Query& Query::ends_with(size_t column_ndx, BinaryData b)
{
    ParentNode* p = new BinaryNode<EndsWith>(b, column_ndx);
    UpdatePointers(p, &p->m_child);
    return *this;
}
Query& Query::contains(size_t column_ndx, BinaryData b)
{
    ParentNode* p = new BinaryNode<Contains>(b, column_ndx);
    UpdatePointers(p, &p->m_child);
    return *this;
}

// Generic 'simple type' condition
template <typename T, class N>
Query& Query::add_condition(size_t column_ndx, T value)
{
    ParentNode* const parent = new N(value, column_ndx);
    UpdatePointers(parent, &parent->m_child);
    return *this;
}


template <class TColumnType> Query& Query::equal(size_t column_ndx1, size_t column_ndx2)
{
    ParentNode* const p = new TwoColumnsNode<TColumnType, Equal>(column_ndx1, column_ndx2);
    UpdatePointers(p, &p->m_child);
    return *this;
}

// Two column methods, any type
template <class TColumnType> Query& Query::less(size_t column_ndx1, size_t column_ndx2)
{
    ParentNode* const p = new TwoColumnsNode<TColumnType, Less>(column_ndx1, column_ndx2);
    UpdatePointers(p, &p->m_child);
    return *this;
}
template <class TColumnType> Query& Query::less_equal(size_t column_ndx1, size_t column_ndx2)
{
    ParentNode* const p = new TwoColumnsNode<TColumnType, LessEqual>(column_ndx1, column_ndx2);
    UpdatePointers(p, &p->m_child);
    return *this;
}
template <class TColumnType> Query& Query::greater(size_t column_ndx1, size_t column_ndx2)
{
    ParentNode* const p = new TwoColumnsNode<TColumnType, Greater>(column_ndx1, column_ndx2);
    UpdatePointers(p, &p->m_child);
    return *this;
}
template <class TColumnType> Query& Query::greater_equal(size_t column_ndx1, size_t column_ndx2)
{
    ParentNode* const p = new TwoColumnsNode<TColumnType, GreaterEqual>(column_ndx1, column_ndx2);
    UpdatePointers(p, &p->m_child);
    return *this;
}
template <class TColumnType> Query& Query::not_equal(size_t column_ndx1, size_t column_ndx2)
{
    ParentNode* const p = new TwoColumnsNode<TColumnType, NotEqual>(column_ndx1, column_ndx2);
    UpdatePointers(p, &p->m_child);
    return *this;
}

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

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

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

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

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

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


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

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

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

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

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

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

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

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

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

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


// 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));
}

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

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


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


// Strings, StringData()

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


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

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

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

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

    if (first.size() == 0 || first[0] == 0) {
        // 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);
    }

    // Aggregate with criteria
    Init(*m_table);
    size_t matchcount = 0;
    QueryState<R> st;
    st.init(action, NULL, limit);
    R r = first[0]->aggregate<action, R, T>(&st, start, end, column_ndx, &matchcount);
    if (resultcount)
        *resultcount = matchcount;
    return r;
}

// Sum

int64_t Query::sum_int(size_t column_ndx, size_t* resultcount, size_t start, size_t end, size_t limit) const
{
    return aggregate<act_Sum, int64_t>(&Column::sum, column_ndx, resultcount, start, end, limit);
}
double Query::sum_float(size_t column_ndx, size_t* resultcount, size_t start, size_t end, size_t limit) const
{
    return aggregate<act_Sum, float>(&ColumnFloat::sum, column_ndx, resultcount, start, end, limit);
}
double Query::sum_double(size_t column_ndx, size_t* resultcount, size_t start, size_t end, size_t limit) const
{
    return aggregate<act_Sum, double>(&ColumnDouble::sum, column_ndx, resultcount, start, end, limit);
}

// Maximum

int64_t Query::maximum_int(size_t column_ndx, size_t* resultcount, size_t start, size_t end, size_t limit) const
{
    return aggregate<act_Max, int64_t>(&Column::maximum, column_ndx, resultcount, start, end, limit);
}
float Query::maximum_float(size_t column_ndx, size_t* resultcount, size_t start, size_t end, size_t limit) const
{
    return aggregate<act_Max, float>(&ColumnFloat::maximum, column_ndx, resultcount, start, end, limit);
}
double Query::maximum_double(size_t column_ndx, size_t* resultcount, size_t start, size_t end, size_t limit) const
{
    return aggregate<act_Max, double>(&ColumnDouble::maximum, column_ndx, resultcount, start, end, limit);
}

// Minimum

int64_t Query::minimum_int(size_t column_ndx, size_t* resultcount, size_t start, size_t end, size_t limit) const
{
    return aggregate<act_Min, int64_t>(&Column::minimum, column_ndx, resultcount, start, end, limit);
}
float Query::minimum_float(size_t column_ndx, size_t* resultcount, size_t start, size_t end, size_t limit) const
{
    return aggregate<act_Min, float>(&ColumnFloat::minimum, column_ndx, resultcount, start, end, limit);
}
double Query::minimum_double(size_t column_ndx, size_t* resultcount, size_t start, size_t end, size_t limit) const
{
    return aggregate<act_Min, double>(&ColumnDouble::minimum, column_ndx, resultcount, start, end, limit);
}

// Average

template <typename T>
double Query::average(size_t column_ndx, size_t* resultcount, size_t start, size_t end, size_t limit) const
{
    if(m_table->is_degenerate()) {
        if (resultcount)
            *resultcount = 0;
        return 0.;
    }

    size_t resultcount2 = 0;
    typedef typename ColumnTypeTraits<T>::column_type ColType;
    typedef typename ColumnTypeTraits<T>::sum_type SumType;
    const SumType sum1 = aggregate<act_Sum, T>(&ColType::sum, column_ndx, &resultcount2, start, end, limit);
    double avg1 = 0;
    if (resultcount2 != 0)
        avg1 = static_cast<double>(sum1) / resultcount2;
    if (resultcount)
        *resultcount = resultcount2;
    return avg1;
}

double Query::average_int(size_t column_ndx, size_t* resultcount, size_t start, size_t end, size_t limit) const
{
    return average<int64_t>(column_ndx, resultcount, start, end, limit);
}
double Query::average_float(size_t column_ndx, size_t* resultcount, size_t start, size_t end, size_t limit) const
{
    return average<float>(column_ndx, resultcount, start, end, limit);
}
double Query::average_double(size_t column_ndx, size_t* resultcount, size_t start, size_t end, size_t limit) const
{
    return average<double>(column_ndx, resultcount, start, end, limit);
}


// Grouping
Query& Query::group()
{
    update.push_back(0);
    update_override.push_back(0);
    first.push_back(0);
    return *this;
}
Query& Query::end_group()
{
    if (first.size() < 2) {
        error_code = "Unbalanced blockBegin/blockEnd";
        return *this;
    }

    if (update[update.size()-2] != 0)
        *update[update.size()-2] = first[first.size()-1];

    if (first[first.size()-2] == 0)
        first[first.size()-2] = first[first.size()-1];

    if (update_override[update_override.size()-1] != 0)
        update[update.size() - 2] = update_override[update_override.size()-1];
    else if (update[update.size()-1] != 0)
        update[update.size() - 2] = update[update.size()-1];

    first.pop_back();
    update.pop_back();
    update_override.pop_back();
    return *this;
}

Query& Query::Or()
{
    ParentNode* const o = new OrNode(first[first.size()-1]);
    all_nodes.push_back(o);

    first[first.size()-1] = o;
    update[update.size()-1] = &((OrNode*)o)->m_cond[1];
    update_override[update_override.size()-1] = &((OrNode*)o)->m_child;
    return *this;
}

void Query::subtable(size_t column)
{
    ParentNode* const p = new SubtableNode(column);
    UpdatePointers(p, &p->m_child);
    // once subtable conditions have been evaluated, resume evaluation from m_child2
    subtables.push_back(&((SubtableNode*)p)->m_child2);
    group();
}

void Query::end_subtable()
{
    end_group();

    if (update[update.size()-1] != 0)
        update[update.size()-1] = subtables[subtables.size()-1];

    subtables.pop_back();
}


size_t Query::find(size_t begin_at_table_row)
{
    if(m_table->is_degenerate())
        return not_found;

    Init(*m_table);

    const size_t end = m_table->size();
    const size_t res = first[0]->find_first(begin_at_table_row, end);

    return (res == end) ? not_found : res;
}

TableView Query::find_all(size_t start, size_t end, size_t limit)
{
    if(m_table->is_degenerate())
        return TableView(*m_table);

    Init(*m_table);

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

    // User created query with no criteria; return everything
    if (first.size() == 0 || first[0] == 0) {
        TableView tv(*m_table);
        for (size_t i = start; i < end && i - start < limit; i++)
            tv.get_ref_column().add(i);
        return move(tv);
    }

#if TIGHTDB_MULTITHREAD_QUERY<commit_msg>Fixed bug in Query::find() with no search criterias<commit_after>#include <cstdio>
#include <algorithm>

#include <tightdb/array.hpp>
#include <tightdb/column_basic.hpp>
#include <tightdb/column_fwd.hpp>
#include <tightdb/query.hpp>
#include <tightdb/query_engine.hpp>

using namespace std;
using namespace tightdb;

namespace {
const size_t thread_chunk_size = 1000;
}

Query::Query() 
{
    Create();
//    expression(static_cast<Expression*>(this));
}

Query::Query(Table& table) : m_table(table.get_table_ref())
{
    Create();
}

Query::Query(const Table& table) : m_table((const_cast<Table&>(table)).get_table_ref())
{
    Create();
}

void Query::Create()
{
    update.push_back(0);
    update_override.push_back(0);
    first.push_back(0);
#if TIGHTDB_MULTITHREAD_QUERY
    m_threadcount = 0;
#endif
    do_delete = true;
}

// FIXME: Try to remove this
Query::Query(const Query& copy)
{
    m_table = copy.m_table;
    all_nodes = copy.all_nodes;
    update = copy.update;
    update_override = copy.update_override;
    first = copy.first;
    error_code = copy.error_code;
#if TIGHTDB_MULTITHREAD_QUERY
    m_threadcount = copy.m_threadcount;
#endif
    //    copy.first[0] = 0;
    copy.do_delete = false;
    do_delete = true;
}

Query::~Query() TIGHTDB_NOEXCEPT
{
#if TIGHTDB_MULTITHREAD_QUERY
    for (size_t i = 0; i < m_threadcount; i++)
        pthread_detach(threads[i]);
#endif

    if (do_delete) {
        for (size_t t = 0; t < all_nodes.size(); t++) {
            ParentNode *p = all_nodes[t];
            std::vector<ParentNode *>::iterator it = std::find(all_nodes.begin(), all_nodes.begin() + t, p);
            if(it == all_nodes.begin() + t)
                delete p;
        }
    }
}
/*
// use and_query() instead!
Expression* Query::get_expression() {
    return (static_cast<ExpressionNode*>(first[first.size()-1]))->m_compare;
}
*/
Query& Query::expression(Expression* compare, bool auto_delete)
{
    ParentNode* const p = new ExpressionNode(compare, auto_delete);
    UpdatePointers(p, &p->m_child);
    return *this;
}

// Makes query search only in rows contained in tv
Query& Query::tableview(const TableView& tv)
{
    const Array& arr = tv.get_ref_column();
    return tableview(arr);
}

// Makes query search only in rows contained in tv
Query& Query::tableview(const Array &arr)
{
    ParentNode* const p = new ArrayNode(arr);
    UpdatePointers(p, &p->m_child);
    return *this;
}


// Binary
Query& Query::equal(size_t column_ndx, BinaryData b)
{
    ParentNode* const p = new BinaryNode<Equal>(b, column_ndx);
    UpdatePointers(p, &p->m_child);
    return *this;
}
Query& Query::not_equal(size_t column_ndx, BinaryData b)
{
    ParentNode* const p = new BinaryNode<NotEqual>(b, column_ndx);
    UpdatePointers(p, &p->m_child);
    return *this;
}
Query& Query::begins_with(size_t column_ndx, BinaryData b)
{
    ParentNode* p = new BinaryNode<BeginsWith>(b, column_ndx);
    UpdatePointers(p, &p->m_child);
    return *this;
}
Query& Query::ends_with(size_t column_ndx, BinaryData b)
{
    ParentNode* p = new BinaryNode<EndsWith>(b, column_ndx);
    UpdatePointers(p, &p->m_child);
    return *this;
}
Query& Query::contains(size_t column_ndx, BinaryData b)
{
    ParentNode* p = new BinaryNode<Contains>(b, column_ndx);
    UpdatePointers(p, &p->m_child);
    return *this;
}

// Generic 'simple type' condition
template <typename T, class N>
Query& Query::add_condition(size_t column_ndx, T value)
{
    ParentNode* const parent = new N(value, column_ndx);
    UpdatePointers(parent, &parent->m_child);
    return *this;
}


template <class TColumnType> Query& Query::equal(size_t column_ndx1, size_t column_ndx2)
{
    ParentNode* const p = new TwoColumnsNode<TColumnType, Equal>(column_ndx1, column_ndx2);
    UpdatePointers(p, &p->m_child);
    return *this;
}

// Two column methods, any type
template <class TColumnType> Query& Query::less(size_t column_ndx1, size_t column_ndx2)
{
    ParentNode* const p = new TwoColumnsNode<TColumnType, Less>(column_ndx1, column_ndx2);
    UpdatePointers(p, &p->m_child);
    return *this;
}
template <class TColumnType> Query& Query::less_equal(size_t column_ndx1, size_t column_ndx2)
{
    ParentNode* const p = new TwoColumnsNode<TColumnType, LessEqual>(column_ndx1, column_ndx2);
    UpdatePointers(p, &p->m_child);
    return *this;
}
template <class TColumnType> Query& Query::greater(size_t column_ndx1, size_t column_ndx2)
{
    ParentNode* const p = new TwoColumnsNode<TColumnType, Greater>(column_ndx1, column_ndx2);
    UpdatePointers(p, &p->m_child);
    return *this;
}
template <class TColumnType> Query& Query::greater_equal(size_t column_ndx1, size_t column_ndx2)
{
    ParentNode* const p = new TwoColumnsNode<TColumnType, GreaterEqual>(column_ndx1, column_ndx2);
    UpdatePointers(p, &p->m_child);
    return *this;
}
template <class TColumnType> Query& Query::not_equal(size_t column_ndx1, size_t column_ndx2)
{
    ParentNode* const p = new TwoColumnsNode<TColumnType, NotEqual>(column_ndx1, column_ndx2);
    UpdatePointers(p, &p->m_child);
    return *this;
}

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

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

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

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

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

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


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

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

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

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

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

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

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

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

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

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


// 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));
}

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

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


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


// Strings, StringData()

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


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

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

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

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

    if (first.size() == 0 || first[0] == 0) {
        // 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);
    }

    // Aggregate with criteria
    Init(*m_table);
    size_t matchcount = 0;
    QueryState<R> st;
    st.init(action, NULL, limit);
    R r = first[0]->aggregate<action, R, T>(&st, start, end, column_ndx, &matchcount);
    if (resultcount)
        *resultcount = matchcount;
    return r;
}

// Sum

int64_t Query::sum_int(size_t column_ndx, size_t* resultcount, size_t start, size_t end, size_t limit) const
{
    return aggregate<act_Sum, int64_t>(&Column::sum, column_ndx, resultcount, start, end, limit);
}
double Query::sum_float(size_t column_ndx, size_t* resultcount, size_t start, size_t end, size_t limit) const
{
    return aggregate<act_Sum, float>(&ColumnFloat::sum, column_ndx, resultcount, start, end, limit);
}
double Query::sum_double(size_t column_ndx, size_t* resultcount, size_t start, size_t end, size_t limit) const
{
    return aggregate<act_Sum, double>(&ColumnDouble::sum, column_ndx, resultcount, start, end, limit);
}

// Maximum

int64_t Query::maximum_int(size_t column_ndx, size_t* resultcount, size_t start, size_t end, size_t limit) const
{
    return aggregate<act_Max, int64_t>(&Column::maximum, column_ndx, resultcount, start, end, limit);
}
float Query::maximum_float(size_t column_ndx, size_t* resultcount, size_t start, size_t end, size_t limit) const
{
    return aggregate<act_Max, float>(&ColumnFloat::maximum, column_ndx, resultcount, start, end, limit);
}
double Query::maximum_double(size_t column_ndx, size_t* resultcount, size_t start, size_t end, size_t limit) const
{
    return aggregate<act_Max, double>(&ColumnDouble::maximum, column_ndx, resultcount, start, end, limit);
}

// Minimum

int64_t Query::minimum_int(size_t column_ndx, size_t* resultcount, size_t start, size_t end, size_t limit) const
{
    return aggregate<act_Min, int64_t>(&Column::minimum, column_ndx, resultcount, start, end, limit);
}
float Query::minimum_float(size_t column_ndx, size_t* resultcount, size_t start, size_t end, size_t limit) const
{
    return aggregate<act_Min, float>(&ColumnFloat::minimum, column_ndx, resultcount, start, end, limit);
}
double Query::minimum_double(size_t column_ndx, size_t* resultcount, size_t start, size_t end, size_t limit) const
{
    return aggregate<act_Min, double>(&ColumnDouble::minimum, column_ndx, resultcount, start, end, limit);
}

// Average

template <typename T>
double Query::average(size_t column_ndx, size_t* resultcount, size_t start, size_t end, size_t limit) const
{
    if(m_table->is_degenerate()) {
        if (resultcount)
            *resultcount = 0;
        return 0.;
    }

    size_t resultcount2 = 0;
    typedef typename ColumnTypeTraits<T>::column_type ColType;
    typedef typename ColumnTypeTraits<T>::sum_type SumType;
    const SumType sum1 = aggregate<act_Sum, T>(&ColType::sum, column_ndx, &resultcount2, start, end, limit);
    double avg1 = 0;
    if (resultcount2 != 0)
        avg1 = static_cast<double>(sum1) / resultcount2;
    if (resultcount)
        *resultcount = resultcount2;
    return avg1;
}

double Query::average_int(size_t column_ndx, size_t* resultcount, size_t start, size_t end, size_t limit) const
{
    return average<int64_t>(column_ndx, resultcount, start, end, limit);
}
double Query::average_float(size_t column_ndx, size_t* resultcount, size_t start, size_t end, size_t limit) const
{
    return average<float>(column_ndx, resultcount, start, end, limit);
}
double Query::average_double(size_t column_ndx, size_t* resultcount, size_t start, size_t end, size_t limit) const
{
    return average<double>(column_ndx, resultcount, start, end, limit);
}


// Grouping
Query& Query::group()
{
    update.push_back(0);
    update_override.push_back(0);
    first.push_back(0);
    return *this;
}
Query& Query::end_group()
{
    if (first.size() < 2) {
        error_code = "Unbalanced blockBegin/blockEnd";
        return *this;
    }

    if (update[update.size()-2] != 0)
        *update[update.size()-2] = first[first.size()-1];

    if (first[first.size()-2] == 0)
        first[first.size()-2] = first[first.size()-1];

    if (update_override[update_override.size()-1] != 0)
        update[update.size() - 2] = update_override[update_override.size()-1];
    else if (update[update.size()-1] != 0)
        update[update.size() - 2] = update[update.size()-1];

    first.pop_back();
    update.pop_back();
    update_override.pop_back();
    return *this;
}

Query& Query::Or()
{
    ParentNode* const o = new OrNode(first[first.size()-1]);
    all_nodes.push_back(o);

    first[first.size()-1] = o;
    update[update.size()-1] = &((OrNode*)o)->m_cond[1];
    update_override[update_override.size()-1] = &((OrNode*)o)->m_child;
    return *this;
}

void Query::subtable(size_t column)
{
    ParentNode* const p = new SubtableNode(column);
    UpdatePointers(p, &p->m_child);
    // once subtable conditions have been evaluated, resume evaluation from m_child2
    subtables.push_back(&((SubtableNode*)p)->m_child2);
    group();
}

void Query::end_subtable()
{
    end_group();

    if (update[update.size()-1] != 0)
        update[update.size()-1] = subtables[subtables.size()-1];

    subtables.pop_back();
}

// todo, add size_t end? could be useful
size_t Query::find(size_t begin_at_table_row)
{
    if(m_table->is_degenerate())
        return not_found;

    TIGHTDB_ASSERT(begin_at_table_row <= m_table->size());

    Init(*m_table);

    // User created query with no criteria; return first
    if (first.size() == 0 || first[0] == 0) {
        return m_table->size() == 0 ? not_found : begin_at_table_row;
    }

    const size_t end = m_table->size();
    const size_t res = first[0]->find_first(begin_at_table_row, end);

    return (res == end) ? not_found : res;
}

TableView Query::find_all(size_t start, size_t end, size_t limit)
{
    if(m_table->is_degenerate())
        return TableView(*m_table);

    TIGHTDB_ASSERT(start <= m_table->size());

    Init(*m_table);

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

    // User created query with no criteria; return everything
    if (first.size() == 0 || first[0] == 0) {
        TableView tv(*m_table);
        for (size_t i = start; i < end && i - start < limit; i++)
            tv.get_ref_column().add(i);
        return move(tv);
    }

#if TIGHTDB_MULTITHREAD_QUERY
{'fraction_non_alphanumeric': 0.1339668254386175, 'fraction_numerical': 0.01196773498647646, 'mean_word_length': 4.550684203533945, 'pattern_counts': {'":': 0, '<': 259, '<?xml version=': 0, '>': 330, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1165507', 'n_tokens_mistral': 15369, 'n_tokens_neox': 14916, 'n_words': 3267}