|
- #include <cpphibernate/driver/mariadb/impl/filter.inl>
- #include <cpphibernate/driver/mariadb/context/read_context.inl>
- #include <cpphibernate/driver/mariadb/classes/tables/table.inl>
-
- using namespace ::cpphibernate;
- using namespace ::cpphibernate::mariadb;
-
- struct foreign_many_tuple_t
- {
- const field_t& field;
- read_context_ptr_u context;
- std::string owner;
- };
-
- struct foreign_many_list_t :
- public std::list<foreign_many_tuple_t>
- { };
-
- struct data_extractor_t
- {
- const table_t& _table;
- const read_context& _context;
- const ::cppmariadb::row& _row;
- foreign_many_list_t& _foreign_many_list;
-
- const filter_t& _filter;
- mutable size_t _index;
-
- data_extractor_t(
- const table_t& p_table,
- const read_context& p_context,
- const ::cppmariadb::row& p_row,
- foreign_many_list_t& p_foreign_many_list)
- : _table (p_table)
- , _context (p_context)
- , _row (p_row)
- , _foreign_many_list(p_foreign_many_list)
- , _filter (p_context.filter)
- , _index (0)
- { }
-
- inline bool has_value() const
- { return !_row.at(_index).is_null(); }
-
- inline value_t get_value() const
- {
- value_t ret;
- auto f = _row.at(_index);
- if (!f.is_null())
- ret = f.get<std::string>();
- return ret;
- }
-
- inline void next_field() const
- { ++_index; }
-
- inline void read_field(const field_t& field, const read_context& context, bool skip = false) const
- {
- auto value = get_value();
- ++_index;
- if (!skip)
- field.set(context, value);
- }
-
- inline bool read_table(const table_t& table, const read_context& context, bool read_base, bool read_derived, bool skip = false) const
- {
- /* read the base table */
- if (read_base && table.base_table)
- {
- skip = read_table(*table.base_table, context, true, false, skip);
- }
-
- /* create a dynamic dataset depending on the derived table */
- else if ( read_base
- && context.is_dynamic
- && !table.derived_tables.empty())
- {
- auto value = get_value();
- next_field();
- if (static_cast<bool>(value) && !skip)
- {
- auto type = cppcore::from_string<uint>(*value);
- auto derived = _table.get_derived_by_table_id(type);
- if (!derived)
- throw exception(std::string("unable to find dereived table for id ") + std::to_string(type));
- derived->emplace(context);
- }
- else
- {
- skip = true;
- }
- }
-
- /* create a static dataset */
- else if (has_value() && !skip)
- {
- if (read_base)
- {
- context.emplace();
- }
- }
-
- /* no data -> skip */
- else
- {
- skip = true;
- }
-
- if (_context.filter.is_excluded(table))
- return skip;
-
- /* primary key */
- assert(table.primary_key_field);
- read_field(*table.primary_key_field, context, skip);
-
- /* data fields */
- for (auto& ptr : table.data_fields)
- {
- assert(ptr);
- auto& field = *ptr;
- if (!_context.filter.is_excluded(field))
- read_field(field, context, skip);
- }
-
- /* foreign table one */
- for (auto& ptr : table.foreign_table_one_fields)
- {
- assert(ptr);
- assert(ptr->referenced_table);
-
- auto& field = *ptr;
- auto& ref_table = *field.referenced_table;
-
- if ( _filter.is_excluded(field)
- || _filter.is_excluded(ref_table))
- continue;
-
- auto next_context = field.foreign_read(context, skip);
- assert(static_cast<bool>(next_context));
- read_table(ref_table, *next_context, true, true, skip);
- next_context->finish();
- }
-
- /* foreign table many */
- if (!skip)
- {
- for (auto& ptr : table.foreign_table_many_fields)
- {
- assert(ptr);
- assert(ptr->referenced_table);
-
- auto& field = *ptr;
- auto& ref_table = *field.referenced_table;
-
- if ( _filter.is_excluded(field)
- || _filter.is_excluded(ref_table))
- continue;
-
- _foreign_many_list.emplace_back(
- foreign_many_tuple_t {
- field,
- field.foreign_read(context, false),
- *table.primary_key_field->get(context),
- });
- }
- }
-
- /* derived tables */
- if (read_derived && context.is_dynamic)
- {
- for (auto& ptr : table.derived_tables)
- {
- assert(ptr);
- auto& derived_table = *ptr;
- read_table(derived_table, context, false, true, skip);
- }
- }
-
- return skip;
- }
-
- inline void operator()() const
- {
- _index = 0;
- read_table(_table, _context, true, true, false);
- if (_index != _row.size())
- throw exception("result was not completely read!");
- }
- };
-
- /* select_query_builder_t */
-
- struct select_query_builder_t
- {
- struct local_context
- {
- const table_t& table;
- std::string alias;
- bool add_base;
- bool add_derived;
- bool is_dynamic;
- };
-
- const table_t& _table;
- const filter_t& _filter;
- bool _is_dynamic;
-
- size_t alias_id { 0 };
- size_t index { 0 };
- std::ostringstream os;
- std::list<std::string> joins;
-
- select_query_builder_t(
- const table_t& p_table,
- const filter_t& p_filter,
- bool p_is_dynamic)
- : _table (p_table)
- , _filter (p_filter)
- , _is_dynamic(p_is_dynamic)
- { }
-
- inline std::string make_alias()
- { return std::string("T") + std::to_string(alias_id++); }
-
- inline void add_field(const field_t& field, const std::string& alias)
- {
- if (index++) os << ", ";
- os << field.convert_from_open
- << "`"
- << alias
- << "`.`"
- << field.name
- << "`"
- << field.convert_from_close;
- }
-
- inline bool add_table(const local_context& ctx)
- {
- bool ret = false;
- auto has_alias = !ctx.alias.empty();
- auto real_alias = has_alias
- ? ctx.alias
- : ctx.table.name;
-
- if (ctx.table.base_table && ctx.add_base)
- {
- assert(ctx.table.base_table->primary_key_field);
-
- auto& base_table = *ctx.table.base_table;
- auto& base_key = *base_table.primary_key_field;
- auto base_alias = has_alias ? make_alias() : std::string();
- auto real_base_alias = has_alias ? base_alias : base_table.name;
-
- std::ostringstream ss;
- ss << " JOIN `"
- << base_table.name;
- if (has_alias)
- {
- ss << "` AS `"
- << base_alias;
- }
- ss << "` ON `"
- << real_alias
- << "`.`"
- << base_key.name
- << "`=`"
- << real_base_alias
- << "`.`"
- << base_key.name
- << "`";
-
- auto it = joins.insert(joins.end(), ss.str());
- if (add_table({
- base_table,
- base_alias,
- true,
- false,
- ctx.is_dynamic
- }))
- {
- ret = true;
- }
- else
- {
- joins.erase(it);
- }
- }
-
- /* __type */
- if ( ctx.is_dynamic
- && !ctx.table.base_table
- && !ctx.table.derived_tables.empty())
- {
- if (index++) os << ", ";
- os << "`"
- << ctx.table.name
- << "`.`__type` AS `__type`";
- ret = true;
- }
-
- if (_filter.is_excluded(ctx.table))
- return ret;
-
- /* primary key */
- assert(ctx.table.primary_key_field);
- add_field(*ctx.table.primary_key_field, real_alias);
- ret = true;
-
- /* data fields */
- for (auto& ptr : ctx.table.data_fields)
- {
- assert(ptr);
- auto& field = *ptr;
- if (!_filter.is_excluded(field))
- {
- add_field(field, real_alias);
- }
- }
-
- /* foreign table one */
- for (auto& ptr : ctx.table.foreign_table_one_fields)
- {
- assert(ptr);
- assert(ptr->table.primary_key_field);
- assert(ptr->referenced_table);
- assert(ptr->referenced_table->primary_key_field);
-
- auto& field = *ptr;
- auto& table = field.table;
- auto& own_key = *table.primary_key_field;
- auto& ref_table = *field.referenced_table;
- auto& ref_key = *ref_table.primary_key_field;
-
- if ( _filter.is_excluded(field)
- || _filter.is_excluded(ref_table))
- continue;
-
- auto new_alias = make_alias();
-
- std::ostringstream ss;
- if (ref_table.is_used_in_container)
- {
- ss << " LEFT JOIN `"
- << ref_table.name
- << "` AS `"
- << new_alias
- << "` ON `"
- << real_alias
- << "`.`"
- << own_key.name
- << "`=`"
- << new_alias
- << "`.`"
- << table.name
- << "_id_"
- << field.name
- << "`";
- }
- else
- {
- ss << " LEFT JOIN `"
- << ref_table.name
- << "` AS `"
- << new_alias
- << "` ON `"
- << real_alias
- << "`.`"
- << ref_key.table.name
- << "_id_"
- << field.name
- << "`=`"
- << new_alias
- << "`.`"
- << ref_key.name
- << "`";
- }
-
- auto it = joins.insert(joins.end(), ss.str());
- if (!add_table({
- ref_table,
- new_alias,
- true,
- true,
- field.value_is_pointer
- }))
- {
- joins.erase(it);
- }
- }
-
- /* derived tables */
- if (ctx.add_derived && ctx.is_dynamic)
- {
- for (auto& ptr : ctx.table.derived_tables)
- {
- assert(ptr);
- assert(ptr->primary_key_field);
- auto& derived_table = *ptr;
- auto& primary_key = *ctx.table.primary_key_field;
- auto derived_alias = has_alias ? make_alias() : std::string();
- auto real_derived_alias = has_alias ? derived_alias : derived_table.name;
-
- std::ostringstream ss;
- ss << " LEFT JOIN `"
- << derived_table.name;
- if (has_alias)
- {
- ss << "` AS `"
- << derived_alias;
- }
- ss << "` ON `"
- << real_alias
- << "`.`"
- << primary_key.name
- << "`=`"
- << real_derived_alias
- << "`.`"
- << primary_key.name
- << "`";
-
- auto it = joins.insert(joins.end(), ss.str());
- if (!add_table({
- derived_table,
- derived_alias,
- false,
- true,
- ctx.is_dynamic,
- }))
- {
- joins.erase(it);
- }
- }
- }
-
- return ret;
- }
-
- inline std::string operator()()
- {
- os << "SELECT ";
- add_table({
- _table,
- "",
- true,
- true,
- _is_dynamic,
- });
- os << " FROM `"
- << _table.name
- << "`";
-
- for (auto& join : joins)
- os << join;
-
- os << " ?where! ?order! ?limit!";
- return os.str();
- }
- };
-
- void table_t::read(const read_context& context) const
- {
- auto& statement = get_statement_select(context.filter, context.is_dynamic);
- auto& connection = context.connection;
-
- statement.set(0, context.where);
- statement.set(1, context.order_by);
- statement.set(2, context.limit);
-
- cpphibernate_log_debug("execute SELECT query: " << std::endl << statement.query(connection) << std::endl);
- auto result = connection.execute_used(statement);
- if (!result)
- throw exception("Unable to fetching data from database!");
-
- ::cppmariadb::row * row;
- foreign_many_list_t foreign_many_list;
- while ((row = result->next()))
- {
- data_extractor_t(*this, context, *row, foreign_many_list)();
- }
- context.finish();
-
- for (auto& tuple : foreign_many_list)
- {
- auto& field = tuple.field;
- auto& next_context = *tuple.context;
- assert(field.referenced_table);
- assert(field.referenced_table->primary_key_field);
- auto& ref_table = *field.referenced_table;
- auto& ref_field = *ref_table.primary_key_field;
-
- {
- std::ostringstream ss;
- ss << "WHERE (`"
- << ref_table.name
- << "`.`"
- << field.table.name
- << "_id_"
- << field.name
- << "`="
- << ref_field.convert_to_open
- << "'"
- << context.connection.escape(tuple.owner)
- << "'"
- << ref_field.convert_to_close
- << ")";
- next_context.where = ss.str();
- }
-
- {
- std::ostringstream ss;
- ss << "ORDER BY `"
- << ref_table.name
- << "`.`"
- << field.table.name
- << "_index_"
- << field.name
- << "` ASC";
- next_context.order_by = ss.str();
- }
-
- ref_table.read(next_context);
- }
- }
-
- void table_t::emplace(const read_context& context) const
- { throw exception(std::string("'") + name + "' does not implement the emplace() method!"); }
-
- ::cppmariadb::statement& table_t::get_statement_select(const filter_t& filter, bool dynamic) const
- {
- auto& map = dynamic
- ? _statement_select_dynamic
- : _statement_select_static;
- auto key = std::make_tuple(filter.cache_id, static_cast<const field_t*>(nullptr));
- auto it = map.find(key);
- if (it == map.end())
- {
- auto query = select_query_builder_t(*this, filter, dynamic)();
- it = map.emplace(key, ::cppmariadb::statement(query)).first;
- }
- return it->second;
- }
|