/* * Copyright (c) 2017-2024 OARC, Inc. * Copyright (c) 2011-2017, IIS - The Internet Foundation in Sweden * All rights reserved. * * This file is part of PacketQ. * * PacketQ is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * PacketQ is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with PacketQ. If not, see . */ #ifndef __packetq_sql_h #define __packetq_sql_h #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifndef s6_addr32 // For *BSD #define s6_addr32 __u6_addr.__u6_addr32 #endif #include #include "refcountstring.h" #include "variant.h" #ifdef WIN32 #define snprintf _snprintf #endif #define RE_LEN 64 namespace packetq { extern int g_allocs; static const int max_func_param = 4; extern bool verbose; inline void vlog(const char* fmt, ...) { if (!verbose) return; char string[1024]; va_list ap; va_start(ap, fmt); vsnprintf(string, sizeof(string), fmt, ap); va_end(ap); fprintf(stderr, "%s", string); } class Error { public: Error(const char* fmt, ...) { char string[1024]; va_list ap; va_start(ap, fmt); vsnprintf(string, sizeof(string), fmt, ap); va_end(ap); m_err = string; } std::string m_err; }; class Query; class Table; class Parser; class Row; class Ordering_terms; inline std::string lower(const char* s) { std::string str = s; transform(str.begin(), str.end(), str.begin(), tolower); return str; } inline bool cmpi(const std::string& i_a, const char* b) { std::string str = i_a; transform(str.begin(), str.end(), str.begin(), tolower); return (str.compare(b) == 0); } inline bool cmpii(const std::string& i_a, const char* b) { std::string stra = i_a; transform(stra.begin(), stra.end(), stra.begin(), tolower); std::string strb = b; transform(strb.begin(), strb.end(), strb.begin(), tolower); return (stra.compare(strb) == 0); } inline const char* ind(int in) { static const char spc[] = " "; if (in < 0) in = 0; if (in > sizeof(spc) - 2) in = sizeof(spc) - 2; return &spc[sizeof(spc) - 1 - in]; } class DB { private: class Item { public: std::string m_function; int m_key; bool operator<(const Item& r) const { if (m_key < r.m_key) return true; if (m_key > r.m_key) return false; if (m_function < r.m_function) return true; return false; } }; public: DB(); ~DB(); bool query(const char* q); Table* create_table(const char* name); Table* create_or_use_table(const char* name); Table* get_table(const char* name); void add_lut(const char* table, int key, const char* value) { Item i; i.m_function = table; i.m_key = key; m_lut[i] = value; } RefCountString* get_value(const char* table, int key) { Item i; i.m_function = table; i.m_key = key; auto it = m_lut.find(i); if (it != m_lut.end()) return RefCountString::construct(it->second.c_str()); return 0; } private: std::map m_tables; std::map m_lut; }; extern DB g_db; class Coldef { public: int m_size; int m_align; }; template class Allocator { private: Allocator& operator=(const Allocator& other); Allocator(Allocator&& other) noexcept; Allocator const& operator=(Allocator&& other); public: Allocator(int size, int buffersize) : m_buffersize(buffersize) , m_size(size) { add_buffer(); } ~Allocator() { auto it = m_buffers.begin(); while (it != m_buffers.end()) { delete *it; m_buffers.erase(it); it = m_buffers.begin(); } } void add_buffer() { m_curr_buffer = new _Buffer(*this); m_buffers.push_back(m_curr_buffer); } T* allocate() { T* obj = m_curr_buffer->allocate(); if (!obj) { for (auto it = m_buffers.begin(); it != m_buffers.end(); it++) { if ((*it)->m_has_space) obj = (*it)->allocate(); if (obj) { m_curr_buffer = *it; break; } } } if (!obj) { add_buffer(); obj = m_curr_buffer->allocate(); } return obj; } void deallocate(T* item) { _Buffer** buffptr = (_Buffer**)item; buffptr[-1]->deallocate(item); } private: class _Buffer { private: _Buffer& operator=(const _Buffer& other); _Buffer(_Buffer&& other) noexcept; _Buffer const& operator=(_Buffer&& other); public: friend class Allocator; _Buffer(Allocator& allocator) : m_allocator(allocator) { m_has_space = true; m_used = 0; m_stride = (sizeof(_Buffer*) + m_allocator.m_size); // align size of m_stride to that of a pointer m_stride = ((m_stride / sizeof(void*)) + 1) * sizeof(void*); m_memory = (char*)calloc(m_stride, m_allocator.m_buffersize); } ~_Buffer() { free(m_memory); } T* allocate() { T* obj = 0; if (m_free_list.size() > 0) { obj = m_free_list.top(); m_free_list.pop(); } if (!obj && m_used < m_allocator.m_buffersize) { char* ptr = &m_memory[m_stride * m_used++]; _Buffer** b = (_Buffer**)ptr; *b = this; obj = (T*)(&b[1]); } m_has_space = true; if (!obj) m_has_space = false; return obj; } void deallocate(T* item) { m_has_space = true; memset(item, 0, m_allocator.m_size); m_free_list.push(item); } bool m_has_space; int m_stride; std::stack m_free_list; Allocator& m_allocator; int m_used; char* m_memory; }; _Buffer* m_curr_buffer; std::list<_Buffer*> m_buffers; int m_buffersize; int m_size; }; class Column { public: static const bool HIDDEN = true; static Coldef m_coldefs[COLTYPE_MAX]; Column(const char* name, Coltype::Type type, int id, bool hidden); // called at startup by DB static void init_defs() { m_coldefs[Coltype::_bool].m_size = bool_size; m_coldefs[Coltype::_bool].m_align = bool_align; m_coldefs[Coltype::_int].m_size = int_size; m_coldefs[Coltype::_int].m_align = int_align; m_coldefs[Coltype::_float].m_size = float_size; m_coldefs[Coltype::_float].m_align = float_align; m_coldefs[Coltype::_text].m_size = text_size; m_coldefs[Coltype::_text].m_align = text_align; } std::string m_name; Coltype::Type m_type; Coldef& m_def; bool m_hidden; int m_id; // numeric id used by packet parsers for speed int m_offset; }; // for accessing a field in a row template class Accessor { public: Accessor() : m_offset(0) { } T& value(Row* row); int m_offset; }; typedef Accessor Bool_accessor; typedef Accessor Int_accessor; typedef Accessor Float_accessor; typedef Accessor Text_accessor; // for writing a variant to a field in a row class GenericAccessor { public: void set(Row* row, const Variant& v); int m_offset; Coltype::Type m_type; }; class Table { private: Table& operator=(const Table& other); Table(Table&& other) noexcept; Table const& operator=(Table&& other); public: Table(const char* name = 0, const char* query = 0) : m_rsize(0) , m_dsize(0) { m_row_allocator = 0; m_name = name ? name : "result"; m_qstring = query ? query : ""; m_curpos = 0; } ~Table() { for (auto i = m_rows.begin(), end = m_rows.end(); i != end; ++i) delete_row(*i); for (auto i = m_cols.begin(), end = m_cols.end(); i != end; ++i) delete *i; delete m_row_allocator; } static int align(int pos, int align) { int res = pos; int rem = pos % align; if (rem) res = pos + align - rem; return res; } int get_col_index(const char* col) { int i = 0; for (auto it = m_cols.begin(); it != m_cols.end(); it++) { if (cmpii(m_cols[i]->m_name, col)) return i; i++; } return -1; } int get_column_id(const char* col) { for (auto i = m_cols.begin(); i != m_cols.end(); ++i) { if (cmpii((*i)->m_name, col)) return (*i)->m_id; } return -1; } template Accessor get_accessor(const char* col) { Accessor res; res.m_offset = -1; int i = get_col_index(col); if (i >= 0) res.m_offset = m_cols[i]->m_offset; return res; } void dump(); void json(bool trailing_comma); void csv(bool format = false); void xml(); Column* add_column(const char* name, Coltype::Type type, int id = -1, bool hidden = false); Column* add_column(const char* name, const char* type, int id = -1, bool hidden = false); void merge_sort(Ordering_terms& order); void per_sort(Ordering_terms& order); Row* create_row(); void delete_row(Row* row); void add_row(Row* row); void limit(int limit, int offset = 0); std::vector m_cols; std::list m_rows; int m_curpos; std::string m_name; std::string m_qstring; Allocator* m_row_allocator; int m_rsize; int m_dsize; std::vector m_text_column_offsets; }; #define ROW_DUMMY_SIZE sizeof(void*) class Row { public: void zero_text_columns(const std::vector& text_column_offsets) { for (auto i = text_column_offsets.begin(), end = text_column_offsets.end(); i != end; ++i) access_column(*i) = 0; } void decref_text_columns(const std::vector& text_column_offsets) { for (auto i = text_column_offsets.begin(), end = text_column_offsets.end(); i != end; ++i) { text_column& t = access_column(*i); if (t) t->dec_refcount(); } } void reset_text_columns(const std::vector& text_column_offsets) { for (auto i = text_column_offsets.begin(), end = text_column_offsets.end(); i != end; ++i) { text_column& t = access_column(*i); if (t) { t->dec_refcount(); t = 0; } } } template T& access_column(int offset) { void* ptr = m_data + offset; return *reinterpret_cast(ptr); } char m_data[ROW_DUMMY_SIZE]; // dummy }; template inline T& Accessor::value(Row* row) { return row->access_column(m_offset); } inline void GenericAccessor::set(Row* row, const Variant& v) { switch (m_type) { case Coltype::_bool: row->access_column(m_offset) = v.get_bool(); break; case Coltype::_int: row->access_column(m_offset) = v.get_int(); break; case Coltype::_float: row->access_column(m_offset) = v.get_float(); break; case Coltype::_text: // reference count on string has already been incremented by get_text() // so we can assign the pointer directly row->access_column(m_offset) = v.get_text(); break; } } class Token { public: enum Type { _invalid = 0, _label = 1, _number = 2, _op = 3, _uop = 4, _string = 5, _column = 6, _paren = 7, _function = 8, _semicolon = 9, _end = 10 }; Token(const Type type, const char* token) { m_type = type; m_token = token; } const char* get_token() const { return m_token.c_str(); } void set_token(const char* istr) { m_token = istr; } Type get_type() const { return m_type; } void set_type(const Type type) { m_type = type; } private: Type m_type; std::string m_token; }; class OP : public Token { private: OP& operator=(const OP& other); OP(OP&& other) noexcept; OP const& operator=(OP&& other); public: static int is_binary(const char* str) { const char* bin_ops[] = { "||", "*", "/", "%", "+", "-", "<<", ">>", "&", "|", "<", "<=", ">", ">=", "=", "==", "!=", "<>", "is", "is not", "in", "like", "not like", "or", "and" }; int pre_ops[] = { 8, 7, 7, 7, 6, 6, 5, 5, 5, 5, 4, 4, 4, 4, 3, 3, 3, 3, 3, 3, 3, 3, 3, 2, 1 }; int len = sizeof(bin_ops) / sizeof(const char*); int idx = len - 1; while (idx >= 0) { if (cmpi(std::string(str), bin_ops[idx])) { return pre_ops[idx]; } idx--; } return 0; } OP(const OP& op) : Token(op.get_type(), op.get_token()) { for (int i = 0; i < max_param(); i++) { m_param[i] = op.m_param[i]; } m_left = op.m_left; m_right = op.m_right; m_row_index = op.m_row_index; m_t = op.m_t; m_name = op.m_name; m_has_aggregate_function = op.m_has_aggregate_function; precedence(); } OP(const Token& tok) : Token(tok.get_type(), tok.get_token()) { for (int i = 0; i < max_param(); i++) { m_param[i] = 0; } m_left = m_right = 0; m_row_index = -1; m_t = Coltype::_int; m_name = ""; m_has_aggregate_function = false; precedence(); } virtual ~OP() { for (int i = 0; i < max_param(); i++) if (m_param[i]) delete m_param[i]; if (m_left) delete m_left; if (m_right) delete m_right; } static int max_param() { return max_func_param; } void clear_ptr() { for (int i = 0; i < max_param(); i++) { m_param[i] = 0; } m_left = m_right = 0; } void precedence() { m_precedence = is_binary(get_token()); } const char* get_name() { if (m_name.length() > 0) return m_name.c_str(); m_name = ""; if (m_left) { m_name += "("; m_name += m_left->get_name(); } m_name += get_token(); if (m_right) { if (!m_left) m_name += "("; m_name += m_right->get_name(); m_name += ")"; } if (m_param[0]) { m_name += "("; for (int i = 0; i < max_param(); i++) if (m_param[i]) { if (i > 0) m_name += ","; m_name += m_param[i]->get_name(); } m_name += ")"; } return m_name.c_str(); } Coltype::Type ret_type() { return m_t; } // add a hidden column for storing intermediate results template T add_intermediate_column(Table* table, std::string name_suffix, Coltype::Type type) { std::string name = std::string(get_name()) + name_suffix; Column* column = table->add_column(name.c_str(), type, -1, Column::HIDDEN); T res; res.m_offset = column->m_offset; return res; } virtual void evaluate(Row** rows, Variant& v) { throw Error("evaluate() called on abstract OP class"); }; virtual void evaluate_aggregate_operands(Row** rows); virtual void combine_aggregate(Row* base_row, Row* other_row); OP* compile(const std::vector& tables, const std::vector& search_order, Query& q); int m_row_index; std::string m_name; int m_precedence; OP* m_param[max_func_param]; OP* m_left; OP* m_right; Coltype::Type m_t; bool m_has_aggregate_function; }; ////////////////// column accessors class Column_access_int : public OP { public: Column_access_int(const OP& op, int offset) : OP(op) { m_accessor.m_offset = offset; } void evaluate(Row** rows, Variant& v) { v = m_accessor.value(rows[m_row_index]); } Int_accessor m_accessor; }; class Column_access_bool : public OP { public: Column_access_bool(const OP& op, int offset) : OP(op) { m_accessor.m_offset = offset; } void evaluate(Row** rows, Variant& v) { v = m_accessor.value(rows[m_row_index]); } Bool_accessor m_accessor; }; class Column_access_float : public OP { public: Column_access_float(const OP& op, int offset) : OP(op) { m_accessor.m_offset = offset; } void evaluate(Row** rows, Variant& v) { v = m_accessor.value(rows[m_row_index]); } Float_accessor m_accessor; }; class Column_access_text : public OP { public: Column_access_text(const OP& op, int offset) : OP(op) { m_accessor.m_offset = offset; } void evaluate(Row** rows, Variant& v) { v = m_accessor.value(rows[m_row_index]); } Text_accessor m_accessor; }; ///////////////// Static numbers class Static_int : public OP { public: Static_int(const OP& op) : OP(op) { m_val = atoi(get_token()); } void evaluate(Row** rows, Variant& v) { v = m_val; } int m_val; }; class Static_float : public OP { public: Static_float(const OP& op) : OP(op) { m_val = atof(get_token()); } void evaluate(Row** rows, Variant& v) { v = m_val; } double m_val; }; class Static_text : public OP { public: Static_text(const OP& op) : OP(op) { m_val.set(RefCountString::construct(get_token())); } void evaluate(Row** rows, Variant& v) { v = *m_val; } RefCountStringHandle m_val; }; ///////////////// Functions class Netmask_func : public OP { public: Netmask_func(const OP& op) : OP(op) { } void evaluate(Row** rows, Variant& v) { Variant orig_ip; m_param[0]->evaluate(rows, orig_ip); if (!valid_masks) set_masks(rows); RefCountStringHandle src(orig_ip.get_text()); RefCountStringHandle dest(RefCountString::allocate(INET6_ADDRSTRLEN + 1)); if (strchr((*src)->data, ':')) { struct in6_addr a6; if (inet_pton(AF_INET6, (*src)->data, &a6) == 1) { a6.s6_addr32[0] &= v6_mask[0]; a6.s6_addr32[1] &= v6_mask[1]; a6.s6_addr32[2] &= v6_mask[2]; a6.s6_addr32[3] &= v6_mask[3]; if (inet_ntop(AF_INET6, &a6, (*dest)->data, INET6_ADDRSTRLEN)) { v = *dest; return; } } } else { struct in_addr a4; if (inet_pton(AF_INET, (*src)->data, &a4) == 1) { a4.s_addr &= v4_mask; if (inet_ntop(AF_INET, &a4, (*dest)->data, INET6_ADDRSTRLEN)) { v = *dest; return; } } } // Operation on non-IP address text RefCountStringHandle empty(RefCountString::construct("")); v = *empty; } private: void set_masks(Row** rows) { if (m_param[1]) { Variant v4cidr; m_param[1]->evaluate(rows, v4cidr); int v4size = v4cidr.get_int(); if (v4size > -1 && v4size < 33) { v4_mask = htonl(0xffffffff << (32 - v4size)); } } if (m_param[2]) { Variant v6cidr; m_param[2]->evaluate(rows, v6cidr); int v6size = v6cidr.get_int(); if (v6size > -1 && v6size < 129) { for (int i = 0; i < 4; i++) { if (v6size >= 32) { v6_mask[i] = 0xffffffff; v6size -= 32; } else if (v6size) { v6_mask[i] = htonl(0xffffffff << (32 - v6size)); v6size = 0; } else { v6_mask[i] = 0; } } } } valid_masks = true; } uint32_t v4_mask = htonl(0xffffff00); uint32_t v6_mask[4] = { 0xffffffff, htonl(0xffff0000), 0, 0 }; bool valid_masks = false; }; class Cc_func : public OP { public: Cc_func(const OP& op); ~Cc_func() { } void evaluate(Row** rows, Variant& v); }; class Asn_func : public OP { public: Asn_func(const OP& op); ~Asn_func() { } void evaluate(Row** rows, Variant& v); }; class Truncate_func : public OP { public: Truncate_func(const OP& op) : OP(op) { } void evaluate(Row** rows, Variant& v) { Variant val; m_param[0]->evaluate(rows, val); v = val.get_int(); } }; class Name_func : public OP { public: Name_func(const OP& op) : OP(op) { } void evaluate(Row** rows, Variant& v) { Variant str, num; m_param[0]->evaluate(rows, str); m_param[1]->evaluate(rows, num); int_column n = num.get_int(); RefCountStringHandle lookup(str.get_text()); RefCountStringHandle r(g_db.get_value((*lookup)->data, n)); if (!*r) r.set(num.get_text()); v = *r; } }; class Lower_func : public OP { public: Lower_func(const OP& op) : OP(op) { } void evaluate(Row** rows, Variant& v) { Variant str; m_param[0]->evaluate(rows, str); RefCountStringHandle src(str.get_text()); int l = strlen((*src)->data); int p = 0; RefCountStringHandle dest(RefCountString::allocate(l + 1)); while ((*src)->data[p]) { char c = (*src)->data[p]; if (c >= 'A' && c <= 'Z') { c = c - 'A' + 'a'; } (*dest)->data[p] = c; p++; } (*dest)->data[p] = 0; v = *dest; } }; class Rsplit_func : public OP { public: Rsplit_func(const OP& op) : OP(op) { } void evaluate(Row** rows, Variant& v) { char sep = '.'; Variant str, num; m_param[0]->evaluate(rows, str); m_param[1]->evaluate(rows, num); if (m_param[2]) { Variant vsep; m_param[2]->evaluate(rows, vsep); RefCountStringHandle sep_text(vsep.get_text()); const char* s = (*sep_text)->data; if (s) sep = s[0]; } int n = num.get_int(); RefCountStringHandle src(str.get_text()); const char* s = (*src)->data; int l = strlen(s); if (!l) { RefCountStringHandle res(RefCountString::construct("")); v = *res; return; } int p = l - 1; int found = 0, end = l, start = 0; if (n == 0) end = p + 1; while (p >= 0) { char c = s[p]; if (c == sep) { found++; if (found == n) end = p; if (found == n + 1) start = p + 1; } p--; } if (found < n || start >= l) { RefCountStringHandle res(RefCountString::construct("")); v = *res; return; } size_t buflen = start < end ? end - start + 1 : 1; char buf[buflen]; p = 0; while (start < end) buf[p++] = s[start++]; buf[p] = 0; RefCountStringHandle res(RefCountString::construct(buf)); v = *res; } }; class Len_func : public OP { public: Len_func(const OP& op) : OP(op) { } void evaluate(Row** rows, Variant& v) { Variant str; m_param[0]->evaluate(rows, str); RefCountStringHandle t(str.get_text()); v = int(strlen((*t)->data)); } }; class Trim_func : public OP { public: Trim_func(const OP& op) : OP(op) { } void evaluate(Row** rows, Variant& v); }; class If_func : public OP { public: If_func(const OP& op) : OP(op) { } void evaluate(Row** rows, Variant& v) { Variant cond; m_param[0]->evaluate(rows, cond); if (cond.get_bool()) m_param[1]->evaluate(rows, v); else m_param[2]->evaluate(rows, v); } }; // Aggregate functions, generally these store their calculations in hidden // columns class Min_func_int : public OP { public: Min_func_int(const OP& op, Table* dest_table) : OP(op) { m_has_aggregate_function = true; acc_min = add_intermediate_column(dest_table, ".min", Coltype::_int); } virtual void evaluate_aggregate_operands(Row** rows) { Variant p; m_param[0]->evaluate(rows, p); acc_min.value(rows[m_row_index]) = p.get_int(); } virtual void combine_aggregate(Row* base_row, Row* next_row) { int_column n = acc_min.value(next_row); if (n < acc_min.value(base_row)) acc_min.value(base_row) = n; } void evaluate(Row** rows, Variant& v) { v = acc_min.value(rows[m_row_index]); } Int_accessor acc_min; }; class Min_func_float : public OP { public: Min_func_float(const OP& op, Table* dest_table) : OP(op) { m_has_aggregate_function = true; acc_min = add_intermediate_column(dest_table, ".min", Coltype::_float); } virtual void evaluate_aggregate_operands(Row** rows) { Variant p; m_param[0]->evaluate(rows, p); acc_min.value(rows[m_row_index]) = p.get_float(); } virtual void combine_aggregate(Row* base_row, Row* next_row) { float_column n = acc_min.value(next_row); if (n < acc_min.value(base_row)) acc_min.value(base_row) = n; } void evaluate(Row** rows, Variant& v) { v = acc_min.value(rows[m_row_index]); } Float_accessor acc_min; }; class Max_func_int : public OP { public: Max_func_int(const OP& op, Table* dest_table) : OP(op) { m_has_aggregate_function = true; acc_max = add_intermediate_column(dest_table, ".max", Coltype::_int); } virtual void evaluate_aggregate_operands(Row** rows) { Variant p; m_param[0]->evaluate(rows, p); acc_max.value(rows[m_row_index]) = p.get_int(); } virtual void combine_aggregate(Row* base_row, Row* next_row) { int_column n = acc_max.value(next_row); if (n > acc_max.value(base_row)) acc_max.value(base_row) = n; } void evaluate(Row** rows, Variant& v) { v = acc_max.value(rows[m_row_index]); } Int_accessor acc_max; }; class Max_func_float : public OP { public: Max_func_float(const OP& op, Table* dest_table) : OP(op) { m_has_aggregate_function = true; acc_max = add_intermediate_column(dest_table, ".max", Coltype::_float); } virtual void evaluate_aggregate_operands(Row** rows) { Variant p; m_param[0]->evaluate(rows, p); acc_max.value(rows[m_row_index]) = p.get_float(); } virtual void combine_aggregate(Row* base_row, Row* next_row) { float_column n = acc_max.value(next_row); if (n > acc_max.value(base_row)) acc_max.value(base_row) = n; } void evaluate(Row** rows, Variant& v) { v = acc_max.value(rows[m_row_index]); } Float_accessor acc_max; }; class Stdev_func : public OP { public: Stdev_func(const OP& op, Table* dest_table) : OP(op) { m_has_aggregate_function = true; acc_sum = add_intermediate_column(dest_table, ".sum", Coltype::_float); acc_sum_sq = add_intermediate_column(dest_table, ".sumsquared", Coltype::_float); acc_count = add_intermediate_column(dest_table, ".count", Coltype::_int); } virtual void evaluate_aggregate_operands(Row** rows) { Variant p; m_param[0]->evaluate(rows, p); float_column val = p.get_float(); acc_sum.value(rows[m_row_index]) = val; acc_sum_sq.value(rows[m_row_index]) = val * val; acc_count.value(rows[m_row_index]) = 1; } virtual void combine_aggregate(Row* base_row, Row* next_row) { acc_sum.value(base_row) = acc_sum.value(base_row) + acc_sum.value(next_row); acc_sum_sq.value(base_row) = acc_sum_sq.value(base_row) + acc_sum_sq.value(next_row); acc_count.value(base_row) = acc_count.value(base_row) + acc_count.value(next_row); } void evaluate(Row** rows, Variant& v) { Row* row = rows[m_row_index]; int c = acc_count.value(row); if (c == 0) { c = 1; } double mean = acc_sum.value(row) / c; double variance = acc_sum_sq.value(row) / c - mean * mean; v = sqrt(variance); } Float_accessor acc_sum, acc_sum_sq; Int_accessor acc_count; }; class Avg_func : public OP { public: Avg_func(const OP& op, Table* dest_table) : OP(op) { m_has_aggregate_function = true; acc_sum = add_intermediate_column(dest_table, ".sum", Coltype::_float); acc_count = add_intermediate_column(dest_table, ".count", Coltype::_int); } virtual void evaluate_aggregate_operands(Row** rows) { Variant p; m_param[0]->evaluate(rows, p); acc_sum.value(rows[m_row_index]) = p.get_float(); acc_count.value(rows[m_row_index]) = 1; } virtual void combine_aggregate(Row* base_row, Row* next_row) { acc_sum.value(base_row) = acc_sum.value(base_row) + acc_sum.value(next_row); acc_count.value(base_row) = acc_count.value(base_row) + acc_count.value(next_row); } void evaluate(Row** rows, Variant& v) { v = acc_sum.value(rows[m_row_index]) / acc_count.value(rows[m_row_index]); } Float_accessor acc_sum; Int_accessor acc_count; }; class Sum_func_int : public OP { public: Sum_func_int(const OP& op, Table* dest_table) : OP(op) { m_has_aggregate_function = true; acc_sum = add_intermediate_column(dest_table, ".sum", Coltype::_int); } virtual void evaluate_aggregate_operands(Row** rows) { Variant p; m_param[0]->evaluate(rows, p); acc_sum.value(rows[m_row_index]) = p.get_int(); } virtual void combine_aggregate(Row* base_row, Row* next_row) { acc_sum.value(base_row) = acc_sum.value(base_row) + acc_sum.value(next_row); } void evaluate(Row** rows, Variant& v) { v = acc_sum.value(rows[m_row_index]); } Int_accessor acc_sum; }; class Sum_func_float : public OP { public: Sum_func_float(const OP& op, Table* dest_table) : OP(op) { m_has_aggregate_function = true; acc_sum = add_intermediate_column(dest_table, ".sum", Coltype::_float); } virtual void evaluate_aggregate_operands(Row** rows) { Variant p; m_param[0]->evaluate(rows, p); acc_sum.value(rows[m_row_index]) = p.get_float(); } virtual void combine_aggregate(Row* base_row, Row* next_row) { acc_sum.value(base_row) = acc_sum.value(base_row) + acc_sum.value(next_row); } void evaluate(Row** rows, Variant& v) { v = acc_sum.value(rows[m_row_index]); } Float_accessor acc_sum; }; class Count_func : public OP { public: Count_func(const OP& op, Table* dest_table) : OP(op) { m_has_aggregate_function = true; acc_count = add_intermediate_column(dest_table, ".count", Coltype::_int); } virtual void evaluate_aggregate_operands(Row** rows) { acc_count.value(rows[m_row_index]) = 1; } virtual void combine_aggregate(Row* base_row, Row* next_row) { acc_count.value(base_row) = acc_count.value(base_row) + acc_count.value(next_row); } void evaluate(Row** rows, Variant& v) { v = acc_count.value(rows[m_row_index]); } Int_accessor acc_count; }; //////////////// Binary ops class Bin_op_eq : public OP { public: Bin_op_eq(const OP& op) : OP(op) { } void evaluate(Row** rows, Variant& v) { Variant lhs, rhs; m_left->evaluate(rows, lhs); m_right->evaluate(rows, rhs); v = bool(lhs == rhs); } }; class Bin_op_not_eq : public OP { public: Bin_op_not_eq(const OP& op) : OP(op) { } void evaluate(Row** rows, Variant& v) { Variant lhs, rhs; m_left->evaluate(rows, lhs); m_right->evaluate(rows, rhs); v = !bool(lhs == rhs); } }; class Bin_op_or : public OP { public: Bin_op_or(const OP& op) : OP(op) { } void evaluate(Row** rows, Variant& v) { m_left->evaluate(rows, v); if (v.get_bool()) { v = true; return; } m_right->evaluate(rows, v); v = v.get_bool(); } }; class Bin_op_and : public OP { public: Bin_op_and(const OP& op) : OP(op) { } void evaluate(Row** rows, Variant& v) { m_left->evaluate(rows, v); if (!v.get_bool()) { v = false; return; } m_right->evaluate(rows, v); v = v.get_bool(); } }; class Bin_op_lt : public OP { public: Bin_op_lt(const OP& op) : OP(op) { } void evaluate(Row** rows, Variant& v) { Variant lhs, rhs; m_left->evaluate(rows, lhs); m_right->evaluate(rows, rhs); v = bool(lhs < rhs); } }; class Bin_op_gt : public OP { public: Bin_op_gt(const OP& op) : OP(op) { } void evaluate(Row** rows, Variant& v) { Variant lhs, rhs; m_left->evaluate(rows, lhs); m_right->evaluate(rows, rhs); v = bool(rhs < lhs); } }; class Bin_op_lteq : public OP { public: Bin_op_lteq(const OP& op) : OP(op) { } void evaluate(Row** rows, Variant& v) { Variant lhs, rhs; m_left->evaluate(rows, lhs); m_right->evaluate(rows, rhs); v = !bool(rhs < lhs); } }; class Bin_op_gteq : public OP { public: Bin_op_gteq(const OP& op) : OP(op) { } void evaluate(Row** rows, Variant& v) { Variant lhs, rhs; m_left->evaluate(rows, lhs); m_right->evaluate(rows, rhs); v = !bool(lhs < rhs); } }; class Bin_op_add : public OP { public: Bin_op_add(const OP& op) : OP(op) { } void evaluate(Row** rows, Variant& v) { Variant lhs, rhs; m_left->evaluate(rows, lhs); m_right->evaluate(rows, rhs); v = lhs.get_int() + rhs.get_int(); } }; class Bin_op_add_float : public OP { public: Bin_op_add_float(const OP& op) : OP(op) { } void evaluate(Row** rows, Variant& v) { Variant lhs, rhs; m_left->evaluate(rows, lhs); m_right->evaluate(rows, rhs); v = lhs.get_float() + rhs.get_float(); } }; class Bin_op_sub : public OP { public: Bin_op_sub(const OP& op) : OP(op) { } void evaluate(Row** rows, Variant& v) { Variant lhs, rhs; m_left->evaluate(rows, lhs); m_right->evaluate(rows, rhs); v = lhs.get_int() - rhs.get_int(); } }; class Bin_op_sub_float : public OP { public: Bin_op_sub_float(const OP& op) : OP(op) { } void evaluate(Row** rows, Variant& v) { Variant lhs, rhs; m_left->evaluate(rows, lhs); m_right->evaluate(rows, rhs); v = lhs.get_float() - rhs.get_float(); } }; class Bin_op_mul : public OP { public: Bin_op_mul(const OP& op) : OP(op) { } void evaluate(Row** rows, Variant& v) { Variant lhs, rhs; m_left->evaluate(rows, lhs); m_right->evaluate(rows, rhs); v = lhs.get_int() * rhs.get_int(); } }; class Bin_op_mul_float : public OP { public: Bin_op_mul_float(const OP& op) : OP(op) { } void evaluate(Row** rows, Variant& v) { Variant lhs, rhs; m_left->evaluate(rows, lhs); m_right->evaluate(rows, rhs); v = lhs.get_float() * rhs.get_float(); } }; class Bin_op_div : public OP { public: Bin_op_div(const OP& op) : OP(op) { } void evaluate(Row** rows, Variant& v) { Variant lhs, rhs; m_left->evaluate(rows, lhs); m_right->evaluate(rows, rhs); v = lhs.get_float() / rhs.get_float(); } }; class Bin_op_modulo : public OP { public: Bin_op_modulo(const OP& op) : OP(op) { } void evaluate(Row** rows, Variant& v) { Variant lhs, rhs; m_left->evaluate(rows, lhs); m_right->evaluate(rows, rhs); v = fmod(lhs.get_float(), rhs.get_float()); } }; class Bin_op_arithmetic_shift_left : public OP { public: Bin_op_arithmetic_shift_left(const OP& op) : OP(op) { } void evaluate(Row** rows, Variant& v) { Variant lhs, rhs; m_left->evaluate(rows, lhs); m_right->evaluate(rows, rhs); v = lhs.get_int() << rhs.get_int(); } }; class Bin_op_arithmetic_shift_right : public OP { public: Bin_op_arithmetic_shift_right(const OP& op) : OP(op) { } void evaluate(Row** rows, Variant& v) { Variant lhs, rhs; m_left->evaluate(rows, lhs); m_right->evaluate(rows, rhs); v = lhs.get_int() >> rhs.get_int(); } }; class Bin_op_bitwise_and : public OP { public: Bin_op_bitwise_and(const OP& op) : OP(op) { } void evaluate(Row** rows, Variant& v) { Variant lhs, rhs; m_left->evaluate(rows, lhs); m_right->evaluate(rows, rhs); v = lhs.get_int() & rhs.get_int(); } }; class Bin_op_bitwise_or : public OP { public: Bin_op_bitwise_or(const OP& op) : OP(op) { } void evaluate(Row** rows, Variant& v) { Variant lhs, rhs; m_left->evaluate(rows, lhs); m_right->evaluate(rows, rhs); v = lhs.get_int() | rhs.get_int(); } }; class Bin_op_concatenate : public OP { public: Bin_op_concatenate(const OP& op) : OP(op) { } void evaluate(Row** rows, Variant& v) { Variant lhs, rhs; m_left->evaluate(rows, lhs); m_right->evaluate(rows, rhs); RefCountStringHandle lhandle(lhs.get_text()), rhandle(rhs.get_text()); const char* lhs_str = (*lhandle)->data; const char* rhs_str = (*rhandle)->data; int l = (int)strlen(lhs_str); int r = (int)strlen(rhs_str); RefCountStringHandle res(RefCountString::allocate(l + r + 1)); memcpy((*res)->data, lhs_str, l); memcpy((*res)->data + l, rhs_str, r + 1); // copy the zero terminator v = *res; } }; class Bin_op_like : public OP { private: Bin_op_like& operator=(const Bin_op_like& other); Bin_op_like(Bin_op_like&& other) noexcept; Bin_op_like const& operator=(Bin_op_like&& other); regex_t m_re; char m_re_str[RE_LEN]; bool m_compiled; int m_err; public: Bin_op_like(const OP& op) : OP(op) , m_re() { m_err = 0; m_compiled = false; } void regex_from_like(const char* s, char* r, int l) { char* stop = r + l - 4; if (r < stop) { *r++ = '^'; while (r < stop and *s) { // printf("s: %s\n", s); if (*s == '\\') { s++; if (*s) { *r = *s; } else { s--; } } else if (*s == '.') { *r++ = '\\'; *r = '.'; } else if (*s == '*') { *r++ = '\\'; *r = '*'; } else if (*s == '%') { *r++ = '.'; *r = '*'; } else if (*s == '_') { *r = '.'; } else { *r = *s; } s++; r++; // printf("r: %s\n\n", start); } *r++ = '$'; *r = '\0'; } // printf("r: %s\n\n", start); // printf("Done\n\n"); } void evaluate(Row** rows, Variant& v) { Variant lhs, rhs; m_left->evaluate(rows, lhs); m_right->evaluate(rows, rhs); RefCountStringHandle lhandle(lhs.get_text()), rhandle(rhs.get_text()); const char* lstr = (*lhandle)->data; const char* rstr = (*rhandle)->data; if (!m_compiled) { m_compiled = true; // Set this before we try; no need to try again if we fail regex_from_like(rstr, m_re_str, RE_LEN); m_err = regcomp(&m_re, m_re_str, REG_NOSUB); if (m_err) { char errstr[RE_LEN]; regerror(m_err, &m_re, errstr, RE_LEN); printf("Error compiling regex: %d: %s", m_err, errstr); } } if (m_err) v = false; else v = regexec(&m_re, lstr, 0, 0, 0) == 0; } ~Bin_op_like() { if (m_compiled) { regfree(&m_re); } } }; class Bin_op_not_like : public Bin_op_like { public: Bin_op_not_like(const OP& op) : Bin_op_like(op) { } void evaluate(Row** rows, Variant& v) { Bin_op_like::evaluate(rows, v); v = !v.get_bool(); } }; ////////////////// unary ops class Un_op_not : public OP { public: Un_op_not(const OP& op) : OP(op) { } void evaluate(Row** rows, Variant& v) { Variant rhs; m_right->evaluate(rows, rhs); v = !rhs.get_bool(); } }; class Un_op_neg : public OP { public: Un_op_neg(const OP& op) : OP(op) { } void evaluate(Row** rows, Variant& v) { Variant rhs; m_right->evaluate(rows, rhs); v = -rhs.get_int(); } }; class Un_op_neg_float : public OP { public: Un_op_neg_float(const OP& op) : OP(op) { } void evaluate(Row** rows, Variant& v) { Variant rhs; m_right->evaluate(rows, rhs); v = -rhs.get_float(); } }; class Un_op_ones_complement : public OP { public: Un_op_ones_complement(const OP& op) : OP(op) { } void evaluate(Row** rows, Variant& v) { Variant rhs; m_right->evaluate(rows, rhs); v = ~rhs.get_int(); } }; class Ordering_terms { private: Ordering_terms& operator=(const Ordering_terms& other); Ordering_terms(Ordering_terms&& other) noexcept; Ordering_terms const& operator=(Ordering_terms&& other); public: Ordering_terms() { m_terms.clear(); } ~Ordering_terms() { auto it = m_terms.begin(); while (it != m_terms.end()) { delete it->m_op; it->m_op = 0; it++; } } class OP_dir { public: OP_dir(OP* op, bool asc) { m_op = op; m_asc = asc; } OP* m_op; bool m_asc; }; bool exist() { return !m_terms.empty(); } void compile(const std::vector& tables, const std::vector& search_order, Query& q); std::vector m_terms; }; class Reader; class Query { private: Query& operator=(const Query& other); Query(Query&& other) noexcept; Query const& operator=(Query&& other); public: Query(const char* name, const char* query) { m_sample = 0; m_where = 0; m_having = 0; m_from = 0; m_limit = -1; m_offset = 0; m_result = new Table(name, query); m_sql = query; } ~Query() { if (m_from) delete m_from; if (m_result) delete m_result; if (m_where) delete m_where; if (m_having) delete m_having; for (auto i = m_select.begin(); i != m_select.end(); ++i) delete *i; } void parse(); void execute(Reader& reader); std::vector m_select; OP* m_where; OP* m_having; Ordering_terms m_order_by; Ordering_terms m_group_by; int m_limit; int m_offset; int m_sample; std::string m_from_name; std::vector m_used_from_column_ids; Table* m_from; Table* m_result; private: void replace_star_column_with_all_columns(); void process_from(); void process_select(Row** rows, Row* dest, GenericAccessor dest_accessors[]); void combine_aggregates_in_select(Row* base_row, Row* other_row); void process_aggregates_in_select(Row** rows, Row* dest, GenericAccessor dest_accessors[]); bool process_where(Row** rows); bool process_having(Row** rows); bool has_aggregate_functions(); std::string m_sql; }; } // namespace packetq namespace std { // support for hashing std::vector template <> struct hash> { size_t operator()(const std::vector& seq) const { // combination procedure from boost::hash_combine std::size_t accumulator = 0; for (auto i = seq.begin(), end = seq.end(); i != end; ++i) accumulator ^= i->hash() + 0x9e3779b9 + (accumulator << 6) + (accumulator >> 2); return accumulator; } }; } // namespace std #endif // __packetq_sql_h