#pragma once #include #include #include #include #include "../fields/field.h" #include "../fields/fields.h" namespace cpphibernate { namespace mariadb { struct filter_t; struct schema_t; struct base_context; struct init_context; struct read_context; struct create_update_context; struct destroy_context; enum init_stage { unknown = 0, stage1, stage2, }; /** * @brief Abstract table class. */ struct table_t { public: using size_vector = std::vector; //!< vector of size_t using table_vector = std::vector; //!< vector of constant field pointers using field_vector = std::vector; //!< vector of constant field pointers using table_set = std::set; //!< set of tables public: size_t id { 0 }; //!< unique id of the table assigned by the user size_t dataset_id { 0 }; //!< unique id of the dataset type size_t base_dataset_id { 0 }; //!< unique id of the dataset type size_vector derived_dataset_ids; //!< vector of ids of all derived dataset bool is_used_in_container { false }; //!< indicates if this table is used inside a container std::string name; //!< name of the table const schema_t& schema; //!< schema this table is owned by fields_t fields; //!< vector of fields managed by this table const table_t * base_table { nullptr }; //!< base table (if has one) table_vector derived_tables; //!< vector of pointers of all derived tables const field_t * primary_key_field { nullptr }; //!< primary key field field_vector foreign_key_fields; //!< vector of pointers of all foreign key fields field_vector foreign_table_fields; //!< vector of pointers of all foreign table fields field_vector foreign_table_one_fields; //!< vector of pointers of all foreign table one fields field_vector foreign_table_many_fields; //!< vector of pointers of all foreign table many fields field_vector data_fields; //!< vector of pointers of all normal data fields public: /** * @brief Default constructor. */ inline table_t() = default; /** * @brief Value constructor. Creates a mariadb table from the cpphibernate table. * * @param[in] p_owner Owner of the table. * @param[in] p_schema Cpphibernate schema the table belongs to. * @param[in] p_table Cpphibernate table to create mariadb table for. */ template< typename T_schema, typename T_table> inline table_t( const schema_t& p_owner, const T_schema& p_schema, const T_table& p_table); /** * @brief Move constructor. */ inline table_t(table_t&& other) = delete; /** * @brief Copy constructor. */ inline table_t(const table_t&) = delete; /** * @brief Destructor. */ virtual ~table_t() = 0; /** * @brief Print the table to the passed stream. */ std::ostream& print(std::ostream& os) const; public: /** * @brief Initialize the table using the passed context. * * The initialization is splitted into two stages. In the first stage the * table is created. In the second stage the table contraints are added. * The first stage must be completed for all tables before stage two of * any table is executed. * * @param[in] context Context that stores the needed data for the operation. * @param[in] stage Stage to execute. */ void init(const init_context& context, init_stage stage) const; /** * @brief Execute a read operation on the current table. * * @param[in] context Context that stores the needed data for the operation. */ void read(const read_context& context) const; /** * @brief Execute a create/update operation on the current table. * * For a polymorphic type this will check the derived tables before executing the actual operation. * If the dataset matches one of the dericed tables, the operation is executed on this table. * This operation also updates the base table if the dataset has one. * * @param[in] context Context that stores the needed data for the operation. * * @return Returns the key of the created/updated dataset in it's string representation. */ virtual std::string create_update(const create_update_context& context) const; /** * @brief Execute a destroy operation on the current table. * * For a polymorphic type this will check the derived tables before executing the actual operation. * If the dataset matches one of the dericed tables, the operation is executed on this table. * This operation also updates the base table if the dataset has one. * * @param[in] context Context that stores the needed data for the operation. */ virtual void destroy(const destroy_context& context) const; /** * @brief Cleanup orphaned datasets beginning from this table. * * This operation will iterate through the different base, derived and foreign tables of this table * and delete all datasets that are not referenced at least by one other dataset. * * @param[in] context Context that stores the needed data for the operation. * @param[in] processed Contains all tables that are already cleared (to handle ring dependencies). * @param[in] check_derived Check the derived tables. * @param[in] check_base Check the base table. */ void cleanup(const base_context& context, table_set& processed, bool check_derived, bool check_base) const; public: /** * @brief Get the value of the primary key of this table. * * @param[in] context Data context to get the dataset from. * * @return Value of the primary key. */ std::string get_primary_key(const data_context& context) const; /** * @brief Get the value of the primary key of this table, * by fetching it from the base table. * * @param[in] context Data context to get the dataset from. * * @return Value of the primary key. */ std::string get_key_from_base(const data_context& context) const; /** * @brief Execute the actual create/update operation. * * Other than the normal create_update method this will not check the derived tables. * It will execute the query and forward the operation to the base table if the dataset has one. */ std::string create_update_exec(const create_update_context& context) const; /** * @brief Execute the actual destroy operation. * * Other than the normal destroy method this will not check the derived tables. * It will execute the query and forward the operation to the base table if the dataset has one. */ void destroy_exec(const destroy_context& context) const; /** * @brief Delete all datasets from the table that foreign keys are all set to NULL. * * @param[in] context Context that stores the needed data for this operation. */ void foreign_many_delete_exec(const base_context& context) const; /** * @brief Build the delete query for this table. * * @param[in] where Where expression to add to the delete query. * * @return The requested delete query. */ std::string build_delete_query(const std::string* where) const; public: /** * @brief Emplace new dataset of the type the table represents. * * @param[in] context Context to emplace new dataset in. */ virtual void emplace(const read_context& context) const; /** * @brief Get the derived table by it's dataset id * * @param[in] p_id Dataset id to get derived table for. * * @return Derived table or nullptr if table was not found. */ inline const table_t* get_derived_by_dataset_id(size_t p_id) const; /** * @brief Get the derived table by it's table id * * @param[in] p_id Table id to get derived table for. * * @return Derived table or nullptr if table was not found. */ inline const table_t* get_derived_by_table_id(size_t p_id) const; private: using statement_ptr_u = std::unique_ptr<::cppmariadb::statement>; using statement_key = std::tuple; using statement_map = std::map; mutable statement_ptr_u _statement_key_from_base; //!< Statement to fetch the key of this table from the base table. mutable statement_ptr_u _statement_init_stage1; //!< Statement for init stage 1 (create table). mutable statement_ptr_u _statement_init_stage2; //!< Statement for init stage 2 (alter table). mutable statement_ptr_u _statement_insert_into; //!< Statement for create operation (inser into) mutable statement_map _statement_select_static; //!< Statement to select simple datasets from the database mutable statement_map _statement_select_dynamic; //!< Statement to select dynamic/polymorphic datasets from the database mutable statement_map _statement_update; //!< Map of all update statements mutable statement_ptr_u _statement_foreign_many_delete; //!< Statement to delete all datasets from the table that foreign keys are all set to NULL. mutable statement_ptr_u _statement_delete; //!< Statement to delete datasets from the database /** * @brief Get the statement to fetch the key of this table from the base table. */ ::cppmariadb::statement& get_statement_key_from_base() const; /** * @brief Get or create the mariadb statement for init stage 1. */ ::cppmariadb::statement& get_statement_init_stage1() const; /** * @brief Get or create the mariadb statement for init stage 2. */ ::cppmariadb::statement* get_statement_init_stage2() const; /** * Get the statement for the create operation. If the statement is empty nullptr is returned. */ ::cppmariadb::statement* get_statement_insert_into() const; /** * Get the statement for select operations. * * @param[in] filter Filter to apply to the statement. * @param[in] dynamic Get select statement for dynamic/polymorphic datasets, for simple datasets otherwise. */ ::cppmariadb::statement& get_statement_select(const filter_t& filter, bool dynamic) const; /** * Get the statement for the update operation. If the statement is empty nullptr is returned; * * @param[in] filter Filter to use for the update statement. * @param[in] owner Field the current dataset is owned by (if this table is used in a foreign field). */ ::cppmariadb::statement* get_statement_update(const filter_t& filter, const field_t * owner) const; /** * @brief Get the statement to delete all datasets from the table that foreign keys are all set to NULL. */ ::cppmariadb::statement& get_statement_foreign_many_delete() const; /** * @brief Get statement to delete datasets from the database. */ ::cppmariadb::statement& get_statement_delete() const; }; using table_ptr_u = std::unique_ptr; namespace __impl { /** * @brief Helper type to build table. */ template struct table_builder; } /** * @brief Predicate to create mariadb table class. */ constexpr decltype(auto) make_table = mp::generic_predicate<__impl::table_builder> { }; } }