|
- #include <cpphibernate/driver/mariadb/types.h>
-
- #include <cpphibernate/types.inl>
- #include <cpphibernate/driver/mariadb/impl/filter.inl>
- #include <cpphibernate/driver/mariadb/classes/tables/table.inl>
- #include <cpphibernate/driver/mariadb/context/create_update_context.inl>
-
- using namespace ::cpphibernate;
- using namespace ::cpphibernate::mariadb;
-
- static std::string build_create_update_query(
- const table_t& table,
- const filter_t * filter,
- const field_t * owner);
-
- static std::string execute_create_update(
- const table_t& table,
- const create_update_context& context,
- ::cppmariadb::statement * statement);
-
- /* table_t */
-
- std::string table_t::create_update(const create_update_context& context) const
- { return create_update_exec(context); }
-
- std::string table_t::create_update_exec(const create_update_context& context) const
- {
- auto * statement = context.is_update()
- ? get_statement_update(*context.filter, context.owner_field)
- : get_statement_insert_into();
- return execute_create_update(*this, context, statement);
- }
-
- ::cppmariadb::statement* table_t::get_statement_insert_into() const
- {
- if (!_statement_insert_into)
- {
- auto query = build_create_update_query(*this, nullptr, nullptr);
- _statement_insert_into.reset(new ::cppmariadb::statement(query));
- }
- return _statement_insert_into->empty()
- ? nullptr
- : _statement_insert_into.get();
- }
-
- ::cppmariadb::statement* table_t::get_statement_update(const filter_t& filter, const field_t * owner) const
- {
- auto key = std::make_tuple(filter.cache_id, owner);
- auto it = _statement_update.find(key);
- if (it == _statement_update.end())
- {
- auto query = build_create_update_query(*this, &filter, owner);
- it = _statement_update.emplace(key, ::cppmariadb::statement(query)).first;
- }
- return it->second.empty()
- ? nullptr
- : &it->second;
- }
-
- std::string build_create_update_query(const table_t& table, const filter_t* filter, const field_t* owner)
- {
- std::ostringstream os;
-
- size_t index = 0;
- bool is_update = static_cast<bool>(filter);
- bool is_create = !is_update;
-
- /* INSER INTO / UPDATE */
- os << (is_update
- ? "UPDATE"
- : "INSERT INTO")
- << " `"
- << table.name
- << "`";
-
- /* primary key */
- if (is_create)
- {
- assert(table.primary_key_field);
- auto& key_info = *table.primary_key_field;
- if (!key_info.value_is_auto_incremented)
- {
- if (index++)
- os << ", ";
- else
- os << " SET ";
- os << "`"
- << key_info.name
- << "`="
- << key_info.convert_to_open
- << "?"
- << key_info.name
- << "?"
- << key_info.convert_to_close;
- }
- }
-
- /* base table key fields */
- if ( static_cast<bool>(table.base_table)
- && ( is_create
- || !filter->is_excluded(*table.base_table)))
- {
- if (index++)
- os << ", ";
- else
- os << " SET ";
- auto& base_table_info = *table.base_table;
- assert(base_table_info.primary_key_field);
- auto& key_info = *base_table_info.primary_key_field;
- os << "`"
- << key_info.name
- << "`="
- << key_info.convert_to_open
- << "?"
- << key_info.name
- << "?"
- << key_info.convert_to_close;
- }
-
- /* foreign table one fields */
- for (auto& ptr : table.foreign_table_one_fields)
- {
- assert(static_cast<bool>(ptr));
- auto& field_info = *ptr;
- if (is_update && filter->is_excluded(field_info))
- continue;
- assert(field_info.referenced_table);
- assert(field_info.referenced_table->primary_key_field);
- if (field_info.referenced_table->is_used_in_container)
- continue;
- if (index++)
- os << ", ";
- else
- os << " SET ";
- auto& key_info = *field_info.referenced_table->primary_key_field;
- os << "`"
- << key_info.table.name
- << "_id_"
- << field_info.name
- << "`="
- << key_info.convert_to_open
- << "?"
- << key_info.table.name
- << "_id_"
- << field_info.name
- << "?"
- << key_info.convert_to_close;
- }
-
- /* foreign fields */
- for (auto& ptr : table.foreign_key_fields)
- {
- assert(static_cast<bool>(ptr));
- if (is_update && ptr != owner)
- continue;
- if (index++)
- os << ", ";
- else
- os << " SET ";
- auto& field_info = *ptr;
- assert(field_info.table.primary_key_field);
- auto& key_info = *field_info.table.primary_key_field;
- os << "`"
- << field_info.table.name
- << "_id_"
- << field_info.name
- << "`="
- << key_info.convert_to_open
- << "?"
- << field_info.table.name
- << "_id_"
- << field_info.name
- << "?"
- << key_info.convert_to_close;
- if (field_info.value_is_ordered)
- {
- if (index++)
- os << ", ";
- else
- os << " SET ";
- os << "`"
- << field_info.table.name
- << "_index_"
- << field_info.name
- << "`=?\?";
- }
- }
-
- /* data fields */
- for (auto& ptr : table.data_fields)
- {
- assert(ptr);
- auto& field_info = *ptr;
- if (is_update && filter->is_excluded(field_info))
- continue;
- if (index++)
- os << ", ";
- else
- os << " SET ";
- os << "`"
- << field_info.name
- << "`="
- << field_info.convert_to_open
- << "?"
- << field_info.name
- << "?"
- << field_info.convert_to_close;
- }
-
- /* type field for derived tables */
- if ( !table.derived_tables.empty()
- && !table.base_table
- && is_create)
- {
- if (index++)
- os << ", ";
- else
- os << " SET ";
- os << "`__type`=?__type?";
- }
-
- /* where primary key (for update) */
- if (is_update)
- {
- assert(table.primary_key_field);
- auto& key_info = *table.primary_key_field;
- os << " WHERE `"
- << key_info.name
- << "`="
- << key_info.convert_to_open
- << "?"
- << key_info.name
- << "?"
- << key_info.convert_to_close;
- }
-
- return index == 0 && !(table.primary_key_field->value_is_auto_incremented && is_create)
- ? std::string()
- : os.str();
- }
-
- std::string execute_create_update(
- const table_t& table,
- const create_update_context& context,
- ::cppmariadb::statement * statement)
- {
- auto& connection = context.connection;
- auto* filter = context.filter;
-
- size_t index = 0;
- bool is_update = context.is_update();
- bool is_create = context.is_create();
-
- std::string primary_key;
- if (statement) statement->clear();
-
- /* primary key */
- assert(table.primary_key_field);
- if (is_update)
- {
- primary_key = table.get_primary_key(context);
- }
- else if (!table.primary_key_field->value_is_auto_incremented)
- {
- primary_key = table.primary_key_field->generate_value(context.connection);
- if (statement) statement->set(index, primary_key);
- ++index;
- }
-
- /* base_key */
- if ( table.base_table
- && ( is_create
- || !filter->is_excluded(*table.base_table)))
- {
- auto new_context = context;
- if (!new_context.derived_table)
- new_context.derived_table = &table;
- std::string key = table.base_table->create_update_exec(new_context);
- if (statement) statement->set(index, std::move(key));
- ++index;
- }
-
- if (is_update && filter->is_excluded(table))
- return primary_key;
-
- /* foreign table one fields */
- for (auto& ptr : table.foreign_table_one_fields)
- {
- assert(ptr);
- assert(ptr->referenced_table);
-
- auto& field = *ptr;
- if (is_update && filter->is_excluded(field))
- continue;
- if (field.referenced_table->is_used_in_container)
- continue;
-
- /* insert/update dataset */
- value_t key = field.foreign_create_update(context);
- if (key.has_value())
- {
- if (statement) statement->set(index, *key);
- }
- else if (field.value_is_nullable)
- {
- if (statement) statement->set_null(index);
- }
- else
- {
- throw exception("Received null key for non nullable foreign dataset!");
- }
- ++index;
-
- /* cleanup old dataset (if new one was created) */
- if (context.is_update())
- {
- field.foreign_one_delete(context, primary_key, key);
- }
- }
-
- /* foreign key fields */
- for (auto& ptr : table.foreign_key_fields)
- {
- assert(ptr);
- if (is_update && ptr != context.owner_field)
- continue;
-
- auto& field_info = *ptr;
- bool set_value =
- context.owner_field
- && ptr == context.owner_field;
-
- if (set_value)
- {
- assert(!context.owner_key.empty());
- if (statement) statement->set(index, context.owner_key);
- }
- else
- {
- if (statement) statement->set_null(index);
- }
- ++index;
-
- if (field_info.value_is_ordered)
- {
- if (set_value)
- {
- if (statement) statement->set(index, context.index);
- }
- else
- {
- if (statement) statement->set(index, 0);
- }
- ++index;
- }
- }
-
- /* data fields */
- for (auto& ptr : table.data_fields)
- {
- assert(ptr);
- if (is_update && filter->is_excluded(*ptr))
- continue;
-
- auto& field_info = *ptr;
- auto value = field_info.get(context);
-
- if (value.has_value())
- {
- if (statement) statement->set(index, *value);
- }
- else
- {
- if (statement) statement->set_null(index);
- }
- ++index;
- }
-
- /* type field for derived tables */
- if ( !table.derived_tables.empty()
- && !table.base_table
- && is_create)
- {
- if (statement) statement->set(index, context.derived_table
- ? context.derived_table->id
- : table.id);
- ++index;
- }
-
- /* where primary key (for update) */
- if (is_update)
- {
- assert(table.primary_key_field);
- if (statement) statement->set(index, *table.primary_key_field->get(context));
- ++index;
- }
-
- /* execute */
- if (statement)
- {
- if (is_create)
- {
- cpphibernate_log_debug("execute INSERT query: " << std::endl << statement->query(connection) << std::endl);
- }
- else
- {
- cpphibernate_log_debug("execute UPDATE query: " << std::endl << statement->query(connection) << std::endl);
- }
-
- if ( table.primary_key_field->value_is_auto_incremented
- && is_create)
- {
- auto id = connection.execute_id(*statement);
- primary_key = cppcore::to_string(id);
- }
- else
- {
- auto count = connection.execute_rows(*statement);
- if (count > 1)
- throw exception("Expected one/ row to be inserted/updated!");
- cpphibernate_log_debug(count << " rows inserted/updated" << std::endl);
- }
- table.primary_key_field->set(context, primary_key);
- }
-
- /* foreign table many fields */
- for (auto& ptr : table.foreign_table_fields)
- {
- assert(ptr);
- assert(ptr->referenced_table);
-
- auto& field = *ptr;
- auto& ref_table = *field.referenced_table;
-
- if (!ref_table.is_used_in_container)
- continue;
-
- if ( is_update
- && ( filter->is_excluded(field)
- || filter->is_excluded(ref_table)))
- continue;
-
- /* set foreign keys of existing elements to null */
- if (context.is_update())
- {
- field.foreign_many_update(context, primary_key);
- }
-
- /* update elements */
- auto next_context = context;
- next_context.owner_field = ptr;
- next_context.owner_key = primary_key;
- next_context.derived_table = nullptr;
- field.foreign_create_update(next_context);
-
- /* delete non referenced elements */
- if (context.is_update())
- {
- table_t::table_set processed;
- ref_table.cleanup(context, processed, true, true);
- }
- }
-
- return primary_key;
- }
|