diff --git a/CMakeLists.txt b/CMakeLists.txt index 1de3577..7b41174 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,15 +1,57 @@ # Initialize CMake ################################################################################ -CMake_Minimum_Required ( VERSION 3.5.1 FATAL_ERROR ) -Include ( CTest ) -If ( NOT CMAKE_BUILD_TYPE ) - Set ( CMAKE_BUILD_TYPE "Release" CACHE STRING "Choose the type of build!" FORCE ) -EndIf ( NOT CMAKE_BUILD_TYPE ) -Set ( CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} - "${CMAKE_CURRENT_SOURCE_DIR}/cmake/" - "${CMAKE_CURRENT_SOURCE_DIR}/cmake/modules/" ) - -# Projects ######################################################################################## - -Add_SubDirectory ( ${CMAKE_CURRENT_SOURCE_DIR}/src ) -Add_SubDirectory ( ${CMAKE_CURRENT_SOURCE_DIR}/test ) +CMake_Minimum_Required ( VERSION 3.12.0 FATAL_ERROR ) + +# Set CMAKE_BUILD_TYPE +If ( NOT CMAKE_BUILD_TYPE ) + Set ( CMAKE_BUILD_TYPE "Release" CACHE STRING "Choose the type of build!" FORCE ) +EndIf ( NOT CMAKE_BUILD_TYPE ) +Set_Property ( CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS Debug Release RelWithDebInfo MinSizeRel ) + +# Set CMAKE_MODULE_PATH +If ( EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/cmake/" ) + Set ( CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} + "${CMAKE_CURRENT_SOURCE_DIR}/cmake/" ) +EndIf ( ) +If ( EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/cmake/" ) + Set ( CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} + "${CMAKE_CURRENT_SOURCE_DIR}/cmake/modules" ) +EndIf ( ) + +# Project ######################################################################################### + +Include ( ${CMAKE_CURRENT_SOURCE_DIR}/cmake/cpphibernate-var.cmake ) +Include ( ${CMAKE_CURRENT_SOURCE_DIR}/cmake/cpphibernate-options.cmake ) +Project ( cpphibernate + DESCRIPTION "A simple library" + VERSION "${CPPHIBERNATE_VERSION}" ) +Include ( CTest ) +Include ( GNUInstallDirs ) + +# Subdirectories +Add_SubDirectory ( ${CMAKE_CURRENT_SOURCE_DIR}/src ) +Add_SubDirectory ( ${CMAKE_CURRENT_SOURCE_DIR}/test ) + +# Install +Include ( CMakePackageConfigHelpers ) +Write_Basic_Package_Version_File ( "${CMAKE_CURRENT_BINARY_DIR}/cmake/cpphibernate-config-version.cmake" + VERSION ${CPPHIBERNATE_VERSION} + COMPATIBILITY AnyNewerVersion ) +Configure_File ( "${CMAKE_CURRENT_SOURCE_DIR}/cmake/cpphibernate-config.cmake" + "${CMAKE_CURRENT_BINARY_DIR}/cmake/cpphibernate-config.cmake" + @ONLY ) + +Set ( ConfigPackageLocation "${CPPHIBERNATE_INSTALL_DIR_SHARE}/cmake" ) +Install ( EXPORT + cpphibernate + NAMESPACE + cpphibernate:: + DESTINATION + ${ConfigPackageLocation} ) +Install ( FILES + "${CMAKE_CURRENT_BINARY_DIR}/cmake/cpphibernate-config.cmake" + "${CMAKE_CURRENT_BINARY_DIR}/cmake/cpphibernate-config-version.cmake" + DESTINATION + ${ConfigPackageLocation} + COMPONENT + Devel ) diff --git a/README.md b/README.md index 10c557f..05c656b 100644 --- a/README.md +++ b/README.md @@ -60,7 +60,7 @@ int main(int argc, char** argv) connection c = database::connect("localhost", 3306, "testuser", "password", "", client_flags::empty()); /* create a hibernation context */ - auto context = make_context_ptr(test_schema, c); + auto context = make_context_ptr(test_schema, c); /* initialize the database schema */ context.init(); /* CREATE SCHEMA IF NOT EXISTS `test_schema` DEFAULT CHARACTER SET utf8; @@ -327,7 +327,7 @@ int main(int argc, char** argv) connection c = database::connect("localhost", 3306, "testuser", "password", "", client_flags::empty()); /* create a hibernation context */ - auto context = make_context_ptr(test_schema, c); + auto context = make_context_ptr(test_schema, c); /* initialize the database schema */ context.init(); @@ -405,4 +405,4 @@ int main(int argc, char** argv) ## License -This project is licensed under the MIT License - see the [LICENSE.txt](LICENSE.txt) file for details \ No newline at end of file +This project is licensed under the MIT License - see the [LICENSE.txt](LICENSE.txt) file for details diff --git a/cmake/Findcppmariadb.cmake b/cmake/Findcppmariadb.cmake deleted file mode 100644 index a13a828..0000000 --- a/cmake/Findcppmariadb.cmake +++ /dev/null @@ -1,46 +0,0 @@ -Include ( ExternalProject ) -Include ( FindPackageHandleStandardArgs ) - -Set ( CPPMARIADB_PATH ${CMAKE_BINARY_DIR}/extern/cppmariadb ) - -If ( NOT TARGET cppmariadb_extern ) - ExternalProject_Add ( cppmariadb_extern - PREFIX ${CPPMARIADB_PATH} - TMP_DIR ${CPPMARIADB_PATH}/tmp - STAMP_DIR ${CPPMARIADB_PATH}/stamp - SOURCE_DIR ${CPPMARIADB_PATH}/src - BINARY_DIR ${CPPMARIADB_PATH}/build - INSTALL_DIR ${CPPMARIADB_PATH}/install - GIT_REPOSITORY "https://git.bergmann89.de/cpp/cppmariadb.git" - GIT_TAG "master" - TEST_COMMAND make test - CMAKE_ARGS -DCMAKE_INSTALL_PREFIX= - -DCMAKE_CXX_FLAGS=${CMAKE_CXX_FLAGS} - -DCMAKE_C_FLAGS=${CMAKE_C_FLAGS} - -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} - -DCMAKE_MODULE_PATH=${CMAKE_MODULE_PATH}) -EndIf ( ) - -Set ( CPPMARIADB_LIBRARY ${CPPMARIADB_PATH}/install/lib/libcppmariadb.so ) -Set ( CPPMARIADB_INCLUDE_DIR ${CPPMARIADB_PATH}/install/include ) -Set ( CPPMARIADB_LIBRARIES ${CPPMARIADB_LIBRARY} ) -Set ( CPPMARIADB_INCLUDE_DIRS ${CPPMARIADB_INCLUDE_DIR} ) - -File ( MAKE_DIRECTORY ${CPPMARIADB_INCLUDE_DIR} ) - -Find_Package_Handle_Standard_Args ( cppmariadb DEFAULT_MSG - CPPMARIADB_LIBRARY - CPPMARIADB_INCLUDE_DIR ) - -If ( NOT TARGET cppmariadb ) - Add_Library ( cppmariadb SHARED IMPORTED ) - Add_Dependencies ( cppmariadb cppmariadb_extern ) - Set_Property ( TARGET cppmariadb - PROPERTY IMPORTED_LOCATION ${CPPMARIADB_LIBRARY} ) - Set_Property ( TARGET cppmariadb - PROPERTY INTERFACE_INCLUDE_DIRECTORIES ${CPPMARIADB_INCLUDE_DIRS} ) - Install ( FILES ${CPPMARIADB_LIBRARY} DESTINATION lib ) - If ( CPPHIBERNATE_INSTALL_DEV_FILES ) - Install ( DIRECTORY ${CPPMARIADB_INCLUDE_DIR}/ DESTINATION include ) - EndIf ( ) -EndIf ( ) \ No newline at end of file diff --git a/cmake/Findcpputils.cmake b/cmake/Findcpputils.cmake deleted file mode 100644 index 1fac201..0000000 --- a/cmake/Findcpputils.cmake +++ /dev/null @@ -1,46 +0,0 @@ -Include ( ExternalProject ) -Include ( FindPackageHandleStandardArgs ) - -Set ( CPPUTILS_PATH ${CMAKE_BINARY_DIR}/extern/cpputils ) - -If ( NOT TARGET cpputils_extern ) - ExternalProject_Add ( cpputils_extern - PREFIX ${CPPUTILS_PATH} - TMP_DIR ${CPPUTILS_PATH}/tmp - STAMP_DIR ${CPPUTILS_PATH}/stamp - SOURCE_DIR ${CPPUTILS_PATH}/src - BINARY_DIR ${CPPUTILS_PATH}/build - INSTALL_DIR ${CPPUTILS_PATH}/install - GIT_REPOSITORY "https://git.bergmann89.de/cpp/cpputils.git" - GIT_TAG "master" - TEST_COMMAND make test - CMAKE_ARGS -DCMAKE_INSTALL_PREFIX= - -DCMAKE_CXX_FLAGS=${CMAKE_CXX_FLAGS} - -DCMAKE_C_FLAGS=${CMAKE_C_FLAGS} - -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} - -DCMAKE_MODULE_PATH=${CMAKE_MODULE_PATH}) -EndIf ( ) - -Set ( CPPUTILS_LIBRARY ${CPPUTILS_PATH}/install/lib/libcpputils.so ) -Set ( CPPUTILS_INCLUDE_DIR ${CPPUTILS_PATH}/install/include ) -Set ( CPPUTILS_LIBRARIES ${CPPUTILS_LIBRARY} ) -Set ( CPPUTILS_INCLUDE_DIRS ${CPPUTILS_INCLUDE_DIR} ) - -File ( MAKE_DIRECTORY ${CPPUTILS_INCLUDE_DIR} ) - -Find_Package_Handle_Standard_Args ( cpputils DEFAULT_MSG - CPPUTILS_LIBRARY - CPPUTILS_INCLUDE_DIR ) - -If ( NOT TARGET cpputils ) - Add_Library ( cpputils SHARED IMPORTED ) - Add_Dependencies ( cpputils cpputils_extern ) - Set_Property ( TARGET cpputils - PROPERTY IMPORTED_LOCATION ${CPPUTILS_LIBRARY} ) - Set_Property ( TARGET cpputils - PROPERTY INTERFACE_INCLUDE_DIRECTORIES ${CPPUTILS_INCLUDE_DIRS} ) - Install ( FILES ${CPPUTILS_LIBRARY} DESTINATION lib ) - If ( CPPHIBERNATE_INSTALL_DEV_FILES ) - Install ( DIRECTORY ${CPPUTILS_INCLUDE_DIR}/ DESTINATION include ) - EndIf ( ) -EndIf ( ) \ No newline at end of file diff --git a/cmake/Findmariadb.cmake b/cmake/Findmariadb.cmake deleted file mode 100644 index 8f934a6..0000000 --- a/cmake/Findmariadb.cmake +++ /dev/null @@ -1,34 +0,0 @@ -Find_Library ( MARIADB_LIBRARY - NAMES mariadb mysql - PATH_SUFFIXES mariadb ) - -Find_File ( MARIADB_INCLUDE_DIR - NAMES mysql.h - PATH_SUFFIXES mariadb ) - -Get_Filename_Component ( MARIADB_INCLUDE_DIR - ${MARIADB_INCLUDE_DIR} - DIRECTORY ) - -Include ( FindPackageHandleStandardArgs ) - -Find_Package_Handle_Standard_Args ( mariadb DEFAULT_MSG - MARIADB_LIBRARY - MARIADB_INCLUDE_DIR ) - -Mark_As_Advanced ( MARIADB_LIBRARY - MARIADB_LIBRARIES ) - -If ( MARIADB_FOUND ) - Set ( MARIADB_LIBRARIES ${MARIADB_LIBRARY} ) - Set ( MARIADB_INCLUDE_DIRS ${MARIADB_INCLUDE_DIR} ) - - If ( NOT TARGET mariadb ) - Add_Library ( mariadb UNKNOWN IMPORTED ) - Set_Property ( TARGET mariadb - PROPERTY INTERFACE_INCLUDE_DIRECTORIES "${MARIADB_INCLUDE_DIRS}" ) - Set_Property ( TARGET mariadb - APPEND - PROPERTY IMPORTED_LOCATION "${MARIADB_LIBRARY}") - EndIf ( ) -EndIf ( ) diff --git a/cmake/config.h.in b/cmake/config.h.in new file mode 100644 index 0000000..2a68145 --- /dev/null +++ b/cmake/config.h.in @@ -0,0 +1,34 @@ +#pragma once + +#include +#include + +namespace cpphibernate +{ + + namespace mp = ::cppmp; + namespace hana = ::boost::hana; + +} + +#cmakedefine CPPHIBERNATE_DEBUG +#cmakedefine CPPHIBERNATE_HAS_CPPLOGGING +#cmakedefine CPPHIBERNATE_HAS_CPPMARIADB + +#ifdef CPPHIBERNATE_HAS_CPPLOGGING + #include + #define cpphibernate_log(p_level) \ + cpplogging_global_log(p_level) +#else + #include + #define cpphibernate_log(p_level) \ + ::std::cout << #p_level << ' ' << __FILE__ << ':' << __LINE__ << " - " +#endif + +#ifdef CPPHIBERNATE_DEBUG + #define cpphibernate_log_debug(...) \ + cpphibernate_log(debug) << __VA_ARGS__ +#else + #define cpphibernate_log_debug(...) \ + do { } while (0) +#endif diff --git a/cmake/cpphibernate-config.cmake b/cmake/cpphibernate-config.cmake new file mode 100644 index 0000000..bb83fbc --- /dev/null +++ b/cmake/cpphibernate-config.cmake @@ -0,0 +1,10 @@ +# cpphibernate-config.cmake - package configuration file + +Message ( WARNING "Please configure the dependencies of this package!" ) +# Include ( CMakeFindDependencyMacro ) +# Find_Dependency ( ) + +Include ( FindPackageHandleStandardArgs ) +Set ( ${CMAKE_FIND_PACKAGE_NAME}_CONFIG ${CMAKE_CURRENT_LIST_FILE} ) +Find_Package_Handle_Standard_Args ( cpphibernate CONFIG_MODE ) +Include ( "${CMAKE_CURRENT_LIST_DIR}/cpphibernate.cmake") diff --git a/cmake/cpphibernate-options.cmake b/cmake/cpphibernate-options.cmake new file mode 100644 index 0000000..902f426 --- /dev/null +++ b/cmake/cpphibernate-options.cmake @@ -0,0 +1,21 @@ +Option ( CPPHIBERNATE_USE_CPPLOGGING + "Try to find the cpplogging library and use it as logger instead of logging to std::cout." + OFF ) +Option ( CPPHIBERNATE_INSTALL_HEADER + "Install headers of cpphibernate." + ON ) +Option ( CPPHIBERNATE_INSTALL_STATIC + "Install static library of cpphibernate." + ON ) +Option ( CPPHIBERNATE_INSTALL_SHARED + "Install shared library of cpphibernate." + ON ) +Option ( CPPHIBERNATE_INSTALL_DEBUG + "Install the stripped debug informations of cpphibernate." + OFF ) +Option ( CPPHIBERNATE_NO_STRIP + "Do not strip debug symbols from binary." + OFF ) +Option ( CPPHIBERNATE_DEBUG + "Write extra debug output to the console/logger." + OFF ) diff --git a/cmake/cpphibernate-var.cmake b/cmake/cpphibernate-var.cmake new file mode 100644 index 0000000..04c3853 --- /dev/null +++ b/cmake/cpphibernate-var.cmake @@ -0,0 +1,31 @@ +# Version +Set ( CPPHIBERNATE_VERSION_MAJOR 1 ) +Set ( CPPHIBERNATE_VERSION_MINOR 0 ) +Set ( CPPHIBERNATE_VERSION_PATCH 0 ) +Set ( CPPHIBERNATE_VERSION_BUILD 0 ) +Set ( CPPHIBERNATE_VERSION_SHORT "${CPPHIBERNATE_VERSION_MAJOR}.${CPPHIBERNATE_VERSION_MINOR}" ) +Set ( CPPHIBERNATE_VERSION "${CPPHIBERNATE_VERSION_SHORT}.${CPPHIBERNATE_VERSION_PATCH}.${CPPHIBERNATE_VERSION_BUILD}" ) +Set ( CPPHIBERNATE_NAME "cpphibernate-${CPPHIBERNATE_VERSION_SHORT}" ) +Set ( CPPHIBERNATE_OUTPUTNAME "cpphibernate" ) + +# Install directories +Set ( CPPHIBERNATE_INSTALL_DIR_INCLUDE "include/${CPPHIBERNATE_NAME}" ) +Set ( CPPHIBERNATE_INSTALL_DIR_LIB "lib" ) +Set ( CPPHIBERNATE_INSTALL_DIR_SHARE "share/${CPPHIBERNATE_NAME}" ) + +# C Standard +Set ( CMAKE_C_STANDARD 11 ) +Set ( CMAKE_CXX_STANDARD 17 ) +Set ( CMAKE_C_STANDARD_REQUIRED ON ) +Set ( CMAKE_CXX_STANDARD_REQUIRED ON ) + +# Git Version +Include ( git_helper OPTIONAL RESULT_VARIABLE HAS_GIT_HELPER ) +If ( HAS_GIT_HELPER ) + GitGetVersion ( ${CMAKE_CURRENT_LIST_DIR}/.. + CPPHIBERNATE_VERSION_MAJOR + CPPHIBERNATE_VERSION_MINOR + CPPHIBERNATE_VERSION_PATCH + CPPHIBERNATE_VERSION_BUILD + CPPHIBERNATE_VERSION_HASH ) +EndIf ( ) diff --git a/cmake/modules b/cmake/modules index b125a1a..1a32531 160000 --- a/cmake/modules +++ b/cmake/modules @@ -1 +1 @@ -Subproject commit b125a1a176ae0aada1cd2ec90919061d202dcea9 +Subproject commit 1a32531aef2deeebd5637b1873bc4e976628801c diff --git a/cmake/options.cmake b/cmake/options.cmake deleted file mode 100644 index 3e99498..0000000 --- a/cmake/options.cmake +++ /dev/null @@ -1,9 +0,0 @@ -Option ( CPPHIBERNATE_BUILD_SHARED - "Build cpphibernate shared library" - ON ) -Option ( CPPHIBERNATE_INSTALL_DEV_FILES - "Install development files of cpphibernate" - ON ) -Option ( CPPHIBERNATE_DEBUG - "Enable debug output" - OFF ) diff --git a/cmake/options.h.in b/cmake/options.h.in deleted file mode 100644 index c9b0288..0000000 --- a/cmake/options.h.in +++ /dev/null @@ -1 +0,0 @@ -#cmakedefine CPPHIBERNATE_DEBUG \ No newline at end of file diff --git a/include/cpphibernate.h b/include/cpphibernate.h index d0bdf75..b7475c7 100644 --- a/include/cpphibernate.h +++ b/include/cpphibernate.h @@ -1,8 +1,9 @@ #pragma once -#include -#include -#include -#include -#include -#include \ No newline at end of file +#include "cpphibernate/context.h" +#include "cpphibernate/misc.h" +#include "cpphibernate/modifier.h" +#include "cpphibernate/schema.h" +#include "cpphibernate/types.h" + +#include "cpphibernate/types.inl" diff --git a/include/cpphibernate/config.h b/include/cpphibernate/config.h deleted file mode 100644 index 54b2205..0000000 --- a/include/cpphibernate/config.h +++ /dev/null @@ -1,73 +0,0 @@ -#pragma once - -#include -#include -#include - -#ifdef CPPHIBERNATE_DEBUG -# include -# define cpphibernate_debug_log(...) log_global_message(debug) << __VA_ARGS__ -#else -# define cpphibernate_debug_log(...) do { } while(0) -#endif - -#define cpphibernate_equality_comparable() \ - template \ - constexpr decltype(auto) operator==(T_other&&) const \ - { \ - return ::boost::hana::type<::utl::mp::decay_t> { } == \ - ::boost::hana::type<::utl::mp::decay_t> { }; \ - } \ - \ - template \ - constexpr decltype(auto) operator!=(T_other&&) const \ - { \ - return ::boost::hana::type<::utl::mp::decay_t> { } != \ - ::boost::hana::type<::utl::mp::decay_t> { }; \ - } - -#define cpphibernate_constructable(name, value) \ - name() = value - -#define cpphibernate_copyable(name, value) \ - name(const name&) = value; \ - name& operator=(const name&) = value - -#define cpphibernate_moveable(name, value) \ - name(name&&) = value; \ - name& operator=(name&&) = value - -#define cpphibernate_define_namespace_beg(parent, name) \ - parent { \ - namespace name - -#define cpphibernate_define_namespace_end(parent) \ - } \ - parent - -#define beg_namespace_cpphibernate namespace cpphibernate -#define end_namespace_cpphibernate - -#define beg_namespace_cpphibernate_schema cpphibernate_define_namespace_beg(beg_namespace_cpphibernate, schema) -#define end_namespace_cpphibernate_schema cpphibernate_define_namespace_end(end_namespace_cpphibernate) - -#define beg_namespace_cpphibernate_misc cpphibernate_define_namespace_beg(beg_namespace_cpphibernate, misc) -#define end_namespace_cpphibernate_misc cpphibernate_define_namespace_end(end_namespace_cpphibernate) - -#define beg_namespace_cpphibernate_modifier cpphibernate_define_namespace_beg(beg_namespace_cpphibernate, modifier) -#define end_namespace_cpphibernate_modifier cpphibernate_define_namespace_end(end_namespace_cpphibernate) - -#define beg_namespace_cpphibernate_driver cpphibernate_define_namespace_beg(beg_namespace_cpphibernate, driver) -#define end_namespace_cpphibernate_driver cpphibernate_define_namespace_end(end_namespace_cpphibernate) - -#define beg_namespace_cpphibernate_driver_mariadb cpphibernate_define_namespace_beg(beg_namespace_cpphibernate_driver, mariadb_impl) -#define end_namespace_cpphibernate_driver_mariadb cpphibernate_define_namespace_end(end_namespace_cpphibernate_driver) - -beg_namespace_cpphibernate -{ - - namespace mp = ::utl::mp; - namespace hana = ::boost::hana; - -} -end_namespace_cpphibernate diff --git a/include/cpphibernate/context.h b/include/cpphibernate/context.h index ae0fd24..df99aa4 100644 --- a/include/cpphibernate/context.h +++ b/include/cpphibernate/context.h @@ -1,123 +1,5 @@ #pragma once -#include +#include "context/context.h" -#include -#include -#include -#include -#include -#include - -beg_namespace_cpphibernate -{ - - namespace __impl - { - - template - struct context_t - : public T_driver - { - public: - using base_type = T_driver; - using driver_type = T_driver; - using schema_type = T_schema; - - private: - const schema_type& _schema; - - public: - template - constexpr context_t(const schema_type& p_schema, T_args&&... args) - : base_type (p_schema, std::forward(args)...) - , _schema (p_schema) - { } - - cpphibernate_copyable(context_t, delete); - cpphibernate_moveable(context_t, default); - - /* init */ - - inline void init(bool recreate) - { this->init_impl(recreate); } - - /* create */ - - template - constexpr void create(T_dataset& dataset) - { this->create_impl(dataset); } - - /* read */ - - template - constexpr auto read(T_dataset& dataset, T_modifiers&&... modifiers) - -> mp::enable_if< - modifier::all_are_modifiers...>> - { - using namespace modifier; - using real_dataset_type = misc::real_dataset_t>; - schema::tables::find(_schema.tables, hana::type_c); - this->read_impl(dataset, modifier::make_list(std::forward(modifiers)...)); - } - - template - constexpr auto read(T_dataset& dataset) - -> mp::enable_if_c< - !misc::is_container>::value - && !misc::is_nullable>::value> - { - using namespace modifier; - using real_dataset_type = misc::real_dataset_t>; - auto& table = schema::tables::find(_schema.tables, hana::type_c); - auto& primary_key = schema::table::get_primary_key_field(table); - this->read_impl(dataset, modifier::make_list(where(equal(primary_key, primary_key.getter(dataset))))); - } - - template - constexpr auto read(T_dataset& dataset) - -> mp::enable_if_c< - misc::is_container>::value - || misc::is_nullable>::value> - { - using namespace modifier; - using real_dataset_type = misc::real_dataset_t>; - schema::tables::find(_schema.tables, hana::type_c); - this->read_impl(dataset, modifier::make_list()); - } - - /* update */ - - template - constexpr void update(T_dataset& dataset) - { this->update_impl(dataset); } - - /* destroy */ - - template - constexpr void destroy(T_dataset& dataset) - { this->destroy_impl(dataset); } - }; - - } - - /* make */ - - template - constexpr decltype(auto) make_context(T_schema&& schema, T_args&&... args) - { - using context_type = __impl::context_t; - return context_type(std::forward(schema), std::forward(args)...); - } - - template - constexpr decltype(auto) make_context_ptr(T_schema&& schema, T_args&&... args) - { - using context_type = __impl::context_t; - using pointer_type = std::unique_ptr; - return pointer_type(new context_type(std::forward(schema), std::forward(args)...)); - } - - -} -end_namespace_cpphibernate \ No newline at end of file +#include "context/context.inl" diff --git a/include/cpphibernate/context/context.h b/include/cpphibernate/context/context.h new file mode 100644 index 0000000..acf00a5 --- /dev/null +++ b/include/cpphibernate/context/context.h @@ -0,0 +1,104 @@ +#pragma once + +#include + +namespace cpphibernate +{ + + /** + * @brief Context class for cpphibernate. + */ + template + struct context + : public T_driver + { + public: + using base_type = T_driver; + using driver_type = T_driver; + using schema_type = T_schema; + + private: + const schema_type& _schema; + + public: + /** + * @brief Value constructor. Create a new context object. + * + * @param p_schema Database schema to use. + * @param p_args Arguments to pass to underlying driver. + */ + template + constexpr context(const schema_type& p_schema, T_args&&... p_args); + + /** + * @brief Nove constructor. + */ + constexpr context(context&&) = default; + + /** + * @brief Copy constrcutor. + */ + constexpr context(const context&) = default; + + /** + * @brief Initialize the database. This will create all non exsitsing tables. + */ + template + constexpr decltype(auto) init(T_args&&... args); + + /** + * @brief Create the passed object in the database. + */ + template + constexpr decltype(auto) create(T_args&&... args); + + /** + * @brief Read an object from the database. + */ + template + constexpr decltype(auto) read(T_args&&... args); + + /** + * @brief Update the passed object in the database. + */ + template + constexpr decltype(auto) update(T_args&&... args); + + /** + * @brief Destroy the passed object in the database. + */ + template + constexpr decltype(auto) destroy(T_args&&... args); + }; + + /** + * @brief Create a new cpphibernate context. + * + * @tparam T_driver Hibernate driver to use. + * @tparam T_schema Database schema to use. + * @tparam T_args Arguments to pass to underlying driver implementation. + * + * @param schema Database schema to use. + * @param args Arguments to pass to underlying driver implementation. + * + * @return Created hibernate context. + */ + template + constexpr decltype(auto) make_context(T_schema&& schema, T_args&&... args); + + /** + * @brief Create a new cpphibernate context as a unique pointer. + * + * @tparam T_driver Hibernate driver to use. + * @tparam T_schema Database schema to use. + * @tparam T_args Arguments to pass to underlying driver implementation. + * + * @param schema Database schema to use. + * @param args Arguments to pass to underlying driver implementation. + * + * @return Unique pointer of the created hibernate context. + */ + template + constexpr decltype(auto) make_context_ptr(T_schema&& schema, T_args&&... args); + +} diff --git a/include/cpphibernate/context/context.inl b/include/cpphibernate/context/context.inl new file mode 100644 index 0000000..62d6e78 --- /dev/null +++ b/include/cpphibernate/context/context.inl @@ -0,0 +1,235 @@ +#pragma once + +#include + +#include "context.h" + +namespace cpphibernate +{ + + namespace __impl + { + + /* init_builder */ + + template + struct init_builder + { + template + static constexpr decltype(auto) apply(T_args&&...) + { static_assert(sizeof...(T_args) == -1, "Invalid parameters for context::init(...)!"); } + }; + + constexpr decltype(auto) init = mp::generic_predicate { }; + + template + struct init_builder< + mp::list, + mp::enable_if_t< + mp::is_valid_v().init(std::declval()))> + && mp::is_same_v> + >> + { + static constexpr decltype(auto) apply(T_impl& impl, bool recreate) + { return impl.init(recreate); } + }; + + /* create_builder */ + + template + struct create_builder + { + template + static constexpr decltype(auto) apply(T_args&&...) + { static_assert(sizeof...(T_args) == -1, "Invalid parameters for context::create(...)!"); } + }; + + constexpr decltype(auto) create = mp::generic_predicate { }; + + template + struct create_builder< + mp::list, + mp::enable_if_t< + mp::is_valid_v().create(std::declval()))> + >> + { + static constexpr decltype(auto) apply(T_impl& impl, T_dataset& dataset) + { return impl.create(dataset); } + }; + + /* read_builder */ + + template + struct read_builder + { + template + static constexpr decltype(auto) apply(T_args&&...) + { static_assert(sizeof...(T_args) == -1, "Invalid parameters for context::read(...)!"); } + }; + + constexpr decltype(auto) read = mp::generic_predicate { }; + + template + struct read_builder< + mp::list, + mp::enable_if_t< + schema::is_schema_v> + && is_modifiers_v> + && mp::is_valid_v().read( + std::declval(), + std::declval()))> + >> + { + static constexpr decltype(auto) apply(const T_schema& schema, T_impl& impl, T_dataset& dataset, T_modifiers&& modifiers) + { return impl.read(dataset, std::forward(modifiers)); } + }; + + template + struct read_builder< + mp::list, + mp::enable_if_t< + schema::is_schema_v> + && !is_container_v> + && !is_nullable_v> + >> + { + static constexpr decltype(auto) apply(const T_schema& schema, T_impl& impl, T_dataset& dataset) + { + using real_dataset_type = real_dataset_t>; + + auto& table = schema::find_table(schema.tables, hana::type_c); + auto& primary_key = schema::get_primary_key_field(table); + + return impl.read(dataset, make_modifiers(where(equal(primary_key, primary_key.getter(dataset))))); + } + }; + + template + struct read_builder< + mp::list, + mp::enable_if_t< + schema::is_schema_v> + && mp::is_true_v...> + && ( is_container_v> + || is_nullable_v>) + >> + { + static constexpr decltype(auto) apply(const T_schema& schema, T_impl& impl, T_dataset& dataset, T_modifier&&... modifier) + { return impl.read(dataset, make_modifiers(std::forward(modifier)...)); } + }; + + template + struct read_builder< + mp::list, + mp::enable_if_t< + schema::is_schema_v> + && mp::is_true_v...> + && !is_container_v> + && !is_nullable_v> + && sizeof...(T_modifier) + >> + { + static constexpr decltype(auto) apply(const T_schema& schema, T_impl& impl, T_dataset& dataset, T_modifier&&... modifier) + { return impl.read(dataset, make_modifiers(std::forward(modifier)...)); } + }; + + /* update_builder */ + + template + struct update_builder + { + template + static constexpr decltype(auto) apply(T_args&&...) + { static_assert(sizeof...(T_args) == -1, "Invalid parameters for context::update(...)!"); } + }; + + constexpr decltype(auto) update = mp::generic_predicate { }; + + template + struct update_builder< + mp::list, + mp::enable_if_t< + mp::is_valid_v().update(std::declval()))>>> + { + static constexpr decltype(auto) apply(T_impl& impl, T_dataset& dataset) + { return impl.update(dataset); } + }; + + /* destroy_builder */ + + template + struct destroy_builder + { + template + static constexpr decltype(auto) apply(T_args&&...) + { static_assert(sizeof...(T_args) == -1, "Invalid parameters for context::destroy(...)!"); } + }; + + constexpr decltype(auto) destroy = mp::generic_predicate { }; + + template + struct destroy_builder< + mp::list, + mp::enable_if_t< + mp::is_valid_v().destroy(std::declval()))>>> + { + static constexpr decltype(auto) apply(T_impl& impl, T_dataset& dataset) + { return impl.destroy(dataset); } + }; + + } + + /* context */ + + template + template + constexpr context::context(const schema_type& p_schema, T_args&&... p_args) + : base_type (p_schema, std::forward(p_args)...) + , _schema (p_schema) + { } + + template + template + constexpr decltype(auto) context::init(T_args&&... args) + { return __impl::init(this->impl(), std::forward(args)...); } + + template + template + constexpr decltype(auto) context::create(T_args&&... args) + { return __impl::create(this->impl(), std::forward(args)...); } + + template + template + constexpr decltype(auto) context::read(T_args&&... args) + { return __impl::read(_schema, this->impl(), std::forward(args)...); } + + template + template + constexpr decltype(auto) context::update(T_args&&... args) + { return __impl::update(this->impl(), std::forward(args)...); } + + template + template + constexpr decltype(auto) context::destroy(T_args&&... args) + { return __impl::destroy(this->impl(), std::forward(args)...); } + + /* make_context */ + + template + constexpr decltype(auto) make_context(T_schema&& schema, T_args&&... args) + { + using context_type = context; + return context_type(std::forward(schema), std::forward(args)...); + } + + /* make_context_ptr */ + + template + constexpr decltype(auto) make_context_ptr(T_schema&& schema, T_args&&... args) + { + using context_type = context; + using pointer_type = std::unique_ptr; + return pointer_type(new context_type(std::forward(schema), std::forward(args)...)); + } + +} diff --git a/include/cpphibernate/driver/mariadb.h b/include/cpphibernate/driver/mariadb.h index 3eb45e9..2f7ab51 100644 --- a/include/cpphibernate/driver/mariadb.h +++ b/include/cpphibernate/driver/mariadb.h @@ -1,12 +1,19 @@ #pragma once #include -#include -beg_namespace_cpphibernate_driver -{ - - using mariadb = mariadb_impl::mariadb_driver_t; +#ifdef CPPHIBERNATE_HAS_CPPMARIADB + #include "mariadb/classes.h" + #include "mariadb/context.h" + #include "mariadb/driver.h" + #include "mariadb/driver.inl" + #include "mariadb/helper.h" + #include "mariadb/impl.h" -} -end_namespace_cpphibernate_driver \ No newline at end of file + namespace cpphibernate + { + using mariadb_driver = ::cpphibernate::mariadb::driver_t; + } +#else + #error "cppmariadb library was not found!" +#endif diff --git a/include/cpphibernate/driver/mariadb/classes.h b/include/cpphibernate/driver/mariadb/classes.h new file mode 100644 index 0000000..b04b907 --- /dev/null +++ b/include/cpphibernate/driver/mariadb/classes.h @@ -0,0 +1,6 @@ +#pragma once + +#include "classes/attributes.h" +#include "classes/fields.h" +#include "classes/tables.h" +#include "classes/schema.h" diff --git a/include/cpphibernate/driver/mariadb/classes/attributes.h b/include/cpphibernate/driver/mariadb/classes/attributes.h new file mode 100644 index 0000000..56b5a6f --- /dev/null +++ b/include/cpphibernate/driver/mariadb/classes/attributes.h @@ -0,0 +1,5 @@ +#pragma once + +#include "attributes/attributes.h" + +#include "attributes/attributes.inl" diff --git a/include/cpphibernate/driver/mariadb/classes/attributes/attributes.h b/include/cpphibernate/driver/mariadb/classes/attributes/attributes.h new file mode 100644 index 0000000..5cef869 --- /dev/null +++ b/include/cpphibernate/driver/mariadb/classes/attributes/attributes.h @@ -0,0 +1,46 @@ +#pragma once + +#include + +#include + +namespace cpphibernate { +namespace mariadb { + + namespace __impl + { + + /** + * @brief Helper class to create attributes type. + */ + template + struct attributes_builder; + + } + + /** + * @brief Attributes enumeration + */ + enum class attribute_t + { + hex, //!< The field is stored hex encoded. + compress, //!< The field is stored compressed. + primary_key, //!< The field is the primary key. + }; + + /** + * @brief Set of attributes. + */ + struct attributes_t : + public std::set + { + using base_type = std::set; + using base_type::base_type; + }; + + /** + * @brief Predicate to create attributes set from attributes schema. + */ + constexpr decltype(auto) make_attributes = mp::generic_predicate<__impl::attributes_builder> { }; + +} } diff --git a/include/cpphibernate/driver/mariadb/schema/attributes.inl b/include/cpphibernate/driver/mariadb/classes/attributes/attributes.inl similarity index 82% rename from include/cpphibernate/driver/mariadb/schema/attributes.inl rename to include/cpphibernate/driver/mariadb/classes/attributes/attributes.inl index 87a7ae0..896005a 100644 --- a/include/cpphibernate/driver/mariadb/schema/attributes.inl +++ b/include/cpphibernate/driver/mariadb/classes/attributes/attributes.inl @@ -1,9 +1,12 @@ #pragma once -#include +#include "attributes.h" -beg_namespace_cpphibernate_driver_mariadb -{ +#include +#include + +namespace cpphibernate { +namespace mariadb { namespace __impl { @@ -25,10 +28,10 @@ beg_namespace_cpphibernate_driver_mariadb struct attribute_converter { static constexpr decltype(auto) value = attribute_t::primary_key; }; - /* make_attributes_impl */ + /* attributes_builder */ template - struct make_attributes_impl + struct attributes_builder { template static constexpr decltype(auto) apply(T_args&&... args) @@ -36,10 +39,9 @@ beg_namespace_cpphibernate_driver_mariadb }; template - struct make_attributes_impl< + struct attributes_builder< mp::list, - mp::enable_if_c< - schema::is_attributes>::value>> + mp::enable_if_t>>> { template static constexpr decltype(auto) helper(T_attributes&&, const std::index_sequence&) @@ -58,5 +60,4 @@ beg_namespace_cpphibernate_driver_mariadb } -} -end_namespace_cpphibernate_driver_mariadb \ No newline at end of file +} } diff --git a/include/cpphibernate/driver/mariadb/classes/fields.h b/include/cpphibernate/driver/mariadb/classes/fields.h new file mode 100644 index 0000000..79b9a80 --- /dev/null +++ b/include/cpphibernate/driver/mariadb/classes/fields.h @@ -0,0 +1,17 @@ +#pragma once + +#include "fields/field.h" +#include "fields/fields.h" +#include "fields/field_data.h" +#include "fields/field_foreign_table.h" +#include "fields/field_primary_key.h" +#include "fields/field_simple.h" +#include "fields/field_value.h" + +#include "fields/field.inl" +#include "fields/fields.inl" +#include "fields/field_data.inl" +#include "fields/field_foreign_table.inl" +#include "fields/field_primary_key.inl" +#include "fields/field_simple.inl" +#include "fields/field_value.inl" diff --git a/include/cpphibernate/driver/mariadb/classes/fields/field.h b/include/cpphibernate/driver/mariadb/classes/fields/field.h new file mode 100644 index 0000000..4fe9b28 --- /dev/null +++ b/include/cpphibernate/driver/mariadb/classes/fields/field.h @@ -0,0 +1,214 @@ +#pragma once + +#include + +#include "../../types.h" +#include "../attributes/attributes.h" + +namespace cpphibernate { +namespace mariadb { + + struct table_t; + struct data_context; + struct create_update_context; + struct read_context; + + using read_context_ptr_u = std::unique_ptr; + + /** + * @brief Abstract field class. + */ + struct field_t + { + public: + size_t id { 0 }; //!< unique id of the field + size_t value_id { 0 }; //!< unique id of the value type + size_t real_value_id { 0 }; //!< unique id of the real/unwrapped value type + + bool value_is_nullable { false }; //!< value is stored in a nullable container + bool value_is_pointer { false }; //!< value is stored in a pointer container + bool value_is_container { false }; //!< value is stored in a container + bool value_is_ordered { false }; //!< value is stored in a ordered container (vector, list, ...) + bool value_is_auto_incremented { false }; //!< value is a auto incremented field + + const table_t& table; //!< table this field belongs to + const table_t* referenced_table { nullptr }; //!< table that belongs to the value (if exists) + + std::string name; //!< name of the SQL field + std::string type; //!< SQL type name + std::string create_arguments; //!< additional arguments for CREATE TABLE command + + std::string convert_to_open; //!< SQL code to open the "convert to" operation + std::string convert_to_close; //!< SQL code to close the "convert to" operation + std::string convert_from_open; //!< SQL code to open the "convert from" operation + std::string convert_from_close; //!< SQL code to close the "convert from" operation + + attributes_t attributes; //!< attributes for the field + + public: + /** + * @brief Value constructor. Creates a mariadb field from the cpphibernate field. + * + * @param[in] p_owner Owner of the field. + * @param[in] p_schema Cpphibernate schema the mariadb field belongs to. + * @param[in] p_table Cpphibernate table the mariadb field belongs to. + * @param[in] p_field Cpphibernate field to create mariadb field for. + */ + template< + typename T_schema, + typename T_table, + typename T_field> + inline field_t( + const table_t& p_owner, + const T_schema& p_schema, + const T_table& p_table, + const T_field& p_field); + + /** + * @brief Move constructor. + */ + inline field_t(field_t&& other) = delete; + + /** + * @brief Copy constructor. + */ + inline field_t(const field_t&) = delete; + + /** + * @brief Destructor. + */ + virtual ~field_t() = 0; + + /** + * @brief Print the field values to the passed stream. + */ + std::ostream& print(std::ostream& os) const; + + public: + /** + * @brief Get the value of this field from the current dataset. + * + * @param[in] context Data context to get the dataset from. + * + * @return Value of the field from the current dataset. + */ + virtual value_t get(const data_context& context) const; + + /** + * @brief Set a new value of this field in the current dataset. + * + * @param[in] context Data context to get the dataset from. + * @param[in] value Value of the field to assign to the dataset. + */ + virtual void set(const data_context& context, const value_t& value) const; + + /** + * @brief Check if the value that is represented by this field has the default value. + * + * @param[in] context Data context to receive the value of the assigned dataset. + * + * @retval true If the dataset in the context is the default value for this field. + * @retval false If the dataset in the context is not the default value for this field. + */ + virtual bool is_default(const data_context& context) const; + + /** + * @brief Create a new value that is represented by this field. + * + * @param[in] connection Connection to use to create the value. + * + * @return New created value for this field. + */ + virtual std::string generate_value(::cppmariadb::connection& connection) const; + + public: + /** + * @brief Execute a create/update operation on the foreign table this field represents (if it is a foreign field) + * + * @param[in] context Create/Update context with the needed data for the operation. + * + * @return Key of the created/updated foreign dataset. + */ + virtual value_t foreign_create_update(const create_update_context& context) const; + + /** + * @brief Create a read context for the foreign key field. + * + * @param[in] context Read context to inherit new context from. + * @param[in] create_fake Create a fake context (not data will be written). + * + * @return The created read context. + */ + virtual read_context_ptr_u foreign_read(const read_context& context, bool create_fake) const; + + /** + * @brief Delete all old datasets from the foreign table. + * + * If we update an exsisting foreign field with a new foreign dataset, the key of this dataset + * changes. So we need to delete the old/exsisting foreign dataset from the database. + * + * @param[in] context Create/Update context with the needed data for the operation. + * @param[in] primary_key Primary key of the current database. + * @param[in] foreign_key Primary kes of the new foreign dataset. + */ + void foreign_one_delete( + const create_update_context& context, + const std::string& primary_key, + const value_t& foreign_key) const; + + /** + * @brief Update the foreign dataset. Set the foreign key field to NULL if it matches the passed + * primary key of the owner dataset. + */ + void foreign_many_update( + const create_update_context& context, + const std::string& primary_key) const; + + private: + /** + * @brief Initialize the field. + */ + void init(); + + private: + using statement_ptr_u = std::unique_ptr<::cppmariadb::statement>; + + mutable statement_ptr_u _statement_foreign_one_delete_key_known; //!< Statement to delete foreign datasets within an update operation (for known foreign keys) + mutable statement_ptr_u _statement_foreign_one_delete_key_unknown; //!< Statement to delete foreign datasets within an update operation (for unknown foreign keys) + mutable statement_ptr_u _statement_foreign_many_update; //!< Statement to update foreign many dataset (set foreign key to NULL if primary key of the owner matches) + + /** + * @brief Get the statement to delete foreign datasets within an update operation (for known foreign keys). + */ + ::cppmariadb::statement& get_statement_foreign_one_delete_key_known() const; + + /** + * @brief Get the statement to delete foreign datasets within an update operation (for unknown foreign keys). + */ + ::cppmariadb::statement& get_statement_foreign_one_delete_key_unknown() const; + + /** + * @brief Get the statement to update foreign many dataset (set foreign key to NULL if primary key of the owner matches). + */ + ::cppmariadb::statement& get_statement_foreign_many_update() const; + }; + + using field_ptr_u = std::unique_ptr; + + namespace __impl + { + + /** + * @brief Helper type to build table. + */ + template + struct field_builder; + + } + + /** + * @brief Predicate to create mariadb table class. + */ + constexpr decltype(auto) make_field = mp::generic_predicate<__impl::field_builder> { }; + +} } diff --git a/include/cpphibernate/driver/mariadb/classes/fields/field.inl b/include/cpphibernate/driver/mariadb/classes/fields/field.inl new file mode 100644 index 0000000..d40fe22 --- /dev/null +++ b/include/cpphibernate/driver/mariadb/classes/fields/field.inl @@ -0,0 +1,122 @@ +#pragma once + +#include "field.h" + +#include +#include + +namespace cpphibernate { +namespace mariadb { + + template< + typename T_schema, + typename T_table, + typename T_field> + field_t::field_t( + const table_t& p_owner, + const T_schema& p_schema, + const T_table& p_table, + const T_field& p_field) + : id (get_type_id(hana::type_c>)) + , value_id (get_type_id(hana::type_c)) + , real_value_id (get_type_id(hana::type_c>)) + , value_is_nullable (is_nullable_v ) + , value_is_pointer (is_pointer_v ) + , value_is_container (is_container_v) + , value_is_ordered (is_ordered_v ) + , value_is_auto_incremented (false) + , table (p_owner) + , referenced_table (nullptr) + , name (p_field.name) + , type () + , create_arguments () + , convert_to_open () + , convert_to_close () + , convert_from_open () + , convert_from_close () + , attributes (make_attributes(p_field.attributes)) + { } + + namespace __impl + { + + /* field_builder */ + + template + struct field_builder + { + template + static constexpr decltype(auto) apply(T_args&&... args) + { static_assert(sizeof...(args) == 0, "Invalid parameters for mariadb::make_field(...)!"); } + }; + + template + struct field_builder< + mp::list, + mp::enable_if_t< + mp::is_base_of_v> + && schema::is_schema_v> + && schema::is_table_v> + && schema::is_field_v>>> + { + + /* is_primary_key_field */ + + template + struct is_primary_key_field + : public decltype( + hana::contains( + std::declval>().attributes, + schema::attribute::primary_key)) + { }; + + /* is_foreign_table_field */ + + template + struct is_foreign_table_field + : public decltype( + hana::contains( + hana::transform( + std::declval().tables, + schema::get_wrapped_dataset), + std::declval().wrapped_real_value_type)) + { }; + + /* field_type */ + + template + struct field_type + { using type = field_data_t; }; + + template + struct field_type< + X_schema, + X_field, + mp::enable_if_t::value>> + { using type = field_primary_key_t; }; + + template + struct field_type< + X_schema, + X_field, + mp::enable_if_t::value>> + { using type = field_foreign_table_t; }; + + template + using field_type_t = typename field_type::type; + + /* apply */ + + static constexpr decltype(auto) apply(const table_t& owner, const T_schema& schema, const T_table& table, const T_field& field) + { + using schema_type = mp::decay_t; + using field_type = mp::decay_t; + using return_type = field_type_t; + + return std::make_unique(owner, schema, table, field); + } + }; + + } + +} } diff --git a/include/cpphibernate/driver/mariadb/classes/fields/field_data.h b/include/cpphibernate/driver/mariadb/classes/fields/field_data.h new file mode 100644 index 0000000..3a5b297 --- /dev/null +++ b/include/cpphibernate/driver/mariadb/classes/fields/field_data.h @@ -0,0 +1,37 @@ +#pragma once + +#include "field_value.h" + +namespace cpphibernate { +namespace mariadb { + + /** + * @brief Field that represents a data field. + */ + template + struct field_data_t + : public field_value_t + { + private: + using base_type = field_value_t; + + public: + /** + * @brief Value constructor. Creates a mariadb field from the cpphibernate field. + * + * @param[in] p_owner Owner of the field. + * @param[in] p_schema Cpphibernate schema the mariadb field belongs to. + * @param[in] p_table Cpphibernate table the mariadb field belongs to. + * @param[in] p_field Cpphibernate field to create mariadb field for. + */ + template< + typename T_schema, + typename T_table> + inline field_data_t( + const table_t& p_owner, + const T_schema& p_schema, + const T_table& p_table, + const T_field& p_field); + }; + +} } diff --git a/include/cpphibernate/driver/mariadb/classes/fields/field_data.inl b/include/cpphibernate/driver/mariadb/classes/fields/field_data.inl new file mode 100644 index 0000000..8c90bb0 --- /dev/null +++ b/include/cpphibernate/driver/mariadb/classes/fields/field_data.inl @@ -0,0 +1,27 @@ +#pragma once + +#include "field_data.h" + +namespace cpphibernate { +namespace mariadb { + + /* field_data_t */ + + template< + typename T_field> + template< + typename T_schema, + typename T_table> + field_data_t::field_data_t( + const table_t& p_owner, + const T_schema& p_schema, + const T_table& p_table, + const T_field& p_field) + : field_value_t::field_value_t( + p_owner, + p_schema, + p_table, + p_field) + { } + +} } diff --git a/include/cpphibernate/driver/mariadb/classes/fields/field_foreign_table.h b/include/cpphibernate/driver/mariadb/classes/fields/field_foreign_table.h new file mode 100644 index 0000000..c7fd023 --- /dev/null +++ b/include/cpphibernate/driver/mariadb/classes/fields/field_foreign_table.h @@ -0,0 +1,57 @@ +#pragma once + + + +namespace cpphibernate { +namespace mariadb { + + /** + * @brief Field that represents a foreign table. + */ + template + struct field_foreign_table_t + : public field_simple_t + { + private: + using base_type = field_simple_t; + + public: + /** + * @brief Value constructor. Creates a mariadb field from the cpphibernate field. + * + * @param[in] p_owner Owner of the field. + * @param[in] p_schema Cpphibernate schema the mariadb field belongs to. + * @param[in] p_table Cpphibernate table the mariadb field belongs to. + * @param[in] p_field Cpphibernate field to create mariadb field for. + */ + template< + typename T_schema, + typename T_table> + inline field_foreign_table_t( + const table_t& p_owner, + const T_schema& p_schema, + const T_table& p_table, + const T_field& p_field); + + public: + /** + * @brief Execute a create/update operation on the foreign table this field represents (if it is a foreign field) + * + * @param[in] context Create/Update context with the needed data for the operation. + * + * @return Key of the created/updated foreign dataset. + */ + inline value_t foreign_create_update(const create_update_context& context) const override; + + /** + * @brief Create a read context for the foreign key field. + * + * @param[in] context Read context to inherit new context from. + * @param[in] create_fake Create a fake context (not data will be written). + * + * @return The created read context. + */ + inline read_context_ptr_u foreign_read(const read_context& context, bool create_fake) const override; + }; + +} } diff --git a/include/cpphibernate/driver/mariadb/classes/fields/field_foreign_table.inl b/include/cpphibernate/driver/mariadb/classes/fields/field_foreign_table.inl new file mode 100644 index 0000000..e1a34bf --- /dev/null +++ b/include/cpphibernate/driver/mariadb/classes/fields/field_foreign_table.inl @@ -0,0 +1,73 @@ +#pragma once + +#include "field_foreign_table.h" + +#include "../../impl/create_update.inl" +#include "../../context/read_context.inl" +#include "../../context/create_update_context.inl" + +namespace cpphibernate { +namespace mariadb { + + /* field_foreign_table_t */ + + template< + typename T_field> + template< + typename T_schema, + typename T_table> + field_foreign_table_t::field_foreign_table_t( + const table_t& p_owner, + const T_schema& p_schema, + const T_table& p_table, + const T_field& p_field) + : field_simple_t::field_simple_t( + p_owner, + p_schema, + p_table, + p_field) + { } + + template< + typename T_field> + value_t field_foreign_table_t + ::foreign_create_update(const create_update_context& context) const + { + using dataset_type = typename decltype(+this->_field.wrapped_dataset_type)::type; + + auto& dataset = context.get(); + auto& foreign = this->_field.getter(dataset); + auto next_context = change_context(context, foreign); + + using foreign_dataset_type = mp::decay_t; + return create_update_impl_t::apply( + next_context, + false); + } + + template< + typename T_field> + read_context_ptr_u field_foreign_table_t + ::foreign_read(const read_context& context, bool create_fake) const + { + using dataset_type = typename decltype(+this->_field.wrapped_dataset_type)::type; + using value_type = typename decltype(+this->_field.wrapped_value_type)::type; + + if (create_fake) + { + auto new_context = make_read_context(context.schema, context.connection, hana::type_c, context.filter); + using context_type = mp::decay_t; + return std::make_unique(std::move(new_context)); + } + else + { + auto& dataset = context.get(); + auto& member = this->_field.getter(dataset); + auto new_context = make_read_context(context.schema, context.connection, member, context.filter); + + using context_type = mp::decay_t; + return std::make_unique(std::move(new_context)); + } + } + +} } diff --git a/include/cpphibernate/driver/mariadb/classes/fields/field_primary_key.h b/include/cpphibernate/driver/mariadb/classes/fields/field_primary_key.h new file mode 100644 index 0000000..61f6369 --- /dev/null +++ b/include/cpphibernate/driver/mariadb/classes/fields/field_primary_key.h @@ -0,0 +1,61 @@ +#pragma once + +#include "../../helper/key_properties.h" + +namespace cpphibernate { +namespace mariadb { + + /** + * @brief Field that represents a primary key field. + */ + template + struct field_primary_key_t + : public field_value_t + { + private: + using base_type = field_value_t; + + public: + static constexpr decltype(auto) key_props = + key_properties { }; + + public: + /** + * @brief Value constructor. Creates a mariadb field from the cpphibernate field. + * + * @param[in] p_owner Owner of the field. + * @param[in] p_schema Cpphibernate schema the mariadb field belongs to. + * @param[in] p_table Cpphibernate table the mariadb field belongs to. + * @param[in] p_field Cpphibernate field to create mariadb field for. + */ + template< + typename T_schema, + typename T_table> + inline field_primary_key_t( + const table_t& p_owner, + const T_schema& p_schema, + const T_table& p_table, + const T_field& p_field); + + public: + /** + * @brief Check if the value that is represented by this field has the default value. + * + * @param[in] context Data context to receive the value of the assigned dataset. + * + * @retval true If the dataset in the context is the default value for this field. + * @retval false If the dataset in the context is not the default value for this field. + */ + bool is_default(const data_context& context) const override; + + /** + * @brief Create a new value that is represented by this field. + * + * @param[in] connection Connection to use to create the value. + * + * @return New created value for this field. + */ + std::string generate_value(::cppmariadb::connection& connection) const override; + }; + +} } diff --git a/include/cpphibernate/driver/mariadb/classes/fields/field_primary_key.inl b/include/cpphibernate/driver/mariadb/classes/fields/field_primary_key.inl new file mode 100644 index 0000000..6b348c0 --- /dev/null +++ b/include/cpphibernate/driver/mariadb/classes/fields/field_primary_key.inl @@ -0,0 +1,61 @@ +#pragma once + +#include "field_primary_key.h" + +#include "../../context/data_context.inl" + +namespace cpphibernate { +namespace mariadb { + + /* field_primary_key_t */ + + template< + typename T_field> + template< + typename T_schema, + typename T_table> + field_primary_key_t::field_primary_key_t( + const table_t& p_owner, + const T_schema& p_schema, + const T_table& p_table, + const T_field& p_field) + : field_value_t::field_value_t( + p_owner, + p_schema, + p_table, + p_field) + { + this->value_is_auto_incremented = key_props.is_auto_generated(); + auto args = key_props.create_table_argument(); + if (args) + this->create_arguments = args; + this->name = this->table.name + '_' + this->name; + } + + template< + typename T_field> + bool field_primary_key_t + ::is_default(const data_context& context) const + { + using dataset_type = typename mp::decay_t::dataset_type; + auto& dataset = context.get(); + return key_props.is_default(this->_field.getter(dataset)); + } + + template< + typename T_field> + std::string field_primary_key_t + ::generate_value(::cppmariadb::connection& connection) const + { + auto * query = key_props.create_key_query(); + if (!query) + throw exception("unable to generate key value: no query defined for this key type!"); + + auto ret = connection.execute_used(query); + if (!ret || !ret->next()) + throw exception("unable to generate key value: empty result!"); + + return ret->current()->at(0).template get(); + } + +} } diff --git a/include/cpphibernate/driver/mariadb/classes/fields/field_simple.h b/include/cpphibernate/driver/mariadb/classes/fields/field_simple.h new file mode 100644 index 0000000..ec6a872 --- /dev/null +++ b/include/cpphibernate/driver/mariadb/classes/fields/field_simple.h @@ -0,0 +1,41 @@ +#pragma once + + + +namespace cpphibernate { +namespace mariadb { + + /** + * @brief Field that completely implementes the abstract field_t + */ + template + struct field_simple_t + : public field_t + { + private: + using base_type = field_t; + using field_type = T_field; + + protected: + const field_type& _field; + + public: + /** + * @brief Value constructor. Creates a mariadb field from the cpphibernate field. + * + * @param[in] p_owner Owner of the field. + * @param[in] p_schema Cpphibernate schema the mariadb field belongs to. + * @param[in] p_table Cpphibernate table the mariadb field belongs to. + * @param[in] p_field Cpphibernate field to create mariadb field for. + */ + template< + typename T_schema, + typename T_table> + inline field_simple_t( + const table_t& p_owner, + const T_schema& p_schema, + const T_table& p_table, + const T_field& p_field); + }; + +} } diff --git a/include/cpphibernate/driver/mariadb/classes/fields/field_simple.inl b/include/cpphibernate/driver/mariadb/classes/fields/field_simple.inl new file mode 100644 index 0000000..e793c83 --- /dev/null +++ b/include/cpphibernate/driver/mariadb/classes/fields/field_simple.inl @@ -0,0 +1,28 @@ +#pragma once + +#include "field_simple.h" + +namespace cpphibernate { +namespace mariadb { + + /* field_simple_t */ + + template< + typename T_field> + template< + typename T_schema, + typename T_table> + field_simple_t::field_simple_t( + const table_t& p_owner, + const T_schema& p_schema, + const T_table& p_table, + const T_field& p_field) + : field_t::field_t( + p_owner, + p_schema, + p_table, + p_field) + , _field(p_field) + { } + +} } diff --git a/include/cpphibernate/driver/mariadb/classes/fields/field_value.h b/include/cpphibernate/driver/mariadb/classes/fields/field_value.h new file mode 100644 index 0000000..cb23354 --- /dev/null +++ b/include/cpphibernate/driver/mariadb/classes/fields/field_value.h @@ -0,0 +1,63 @@ +#pragma once + +#include "field_simple.h" +#include "../../helper/type_properties.h" + +namespace cpphibernate { +namespace mariadb { + + /** + * @brief Field that represents a simple value. + */ + template + struct field_value_t + : public field_simple_t + { + private: + using base_type = field_simple_t; + + public: + static constexpr decltype(auto) field_type = + hana::type_c>; + static constexpr decltype(auto) value_type = + hana::type_c; + static constexpr decltype(auto) value_props = + type_properties { }; + + public: + /** + * @brief Value constructor. Creates a mariadb field from the cpphibernate field. + * + * @param[in] p_owner Owner of the field. + * @param[in] p_schema Cpphibernate schema the mariadb field belongs to. + * @param[in] p_table Cpphibernate table the mariadb field belongs to. + * @param[in] p_field Cpphibernate field to create mariadb field for. + */ + template< + typename T_schema, + typename T_table> + inline field_value_t( + const table_t& p_owner, + const T_schema& p_schema, + const T_table& p_table, + const T_field& p_field); + + /** + * @brief Get the value of this field from the current dataset. + * + * @param[in] context Data context to get the dataset from. + * + * @return Value of the field from the current dataset. + */ + virtual value_t get(const data_context& context) const; + + /** + * @brief Set a new value of this field in the current dataset. + * + * @param[in] context Data context to get the dataset from. + * @param[in] value Value of the field to assign to the dataset. + */ + virtual void set(const data_context& context, const value_t& value) const; + }; + +} } diff --git a/include/cpphibernate/driver/mariadb/classes/fields/field_value.inl b/include/cpphibernate/driver/mariadb/classes/fields/field_value.inl new file mode 100644 index 0000000..1243919 --- /dev/null +++ b/include/cpphibernate/driver/mariadb/classes/fields/field_value.inl @@ -0,0 +1,55 @@ +#pragma once + +#include "field_value.h" + +namespace cpphibernate { +namespace mariadb { + + /* field_value_t */ + + template< + typename T_field> + template< + typename T_schema, + typename T_table> + field_value_t::field_value_t( + const table_t& p_owner, + const T_schema& p_schema, + const T_table& p_table, + const T_field& p_field) + : field_simple_t::field_simple_t( + p_owner, + p_schema, + p_table, + p_field) + { + this->type = value_props.type(); + if (value_props.convert_to_open()) + this->convert_to_open = this->convert_to_open + value_props.convert_to_open(); + if (value_props.convert_to_close()) + this->convert_to_close = value_props.convert_to_close() + this->convert_to_close; + if (value_props.convert_from_open()) + this->convert_from_open = this->convert_from_open + value_props.convert_from_open(); + if (value_props.convert_from_close()) + this->convert_from_close = value_props.convert_from_close() + this->convert_from_close; + } + + template + value_t field_value_t:: + get(const data_context& context) const + { + using dataset_type = typename mp::decay_t::dataset_type; + auto& dataset = context.get(); + return value_props.convert_from(this->_field.getter(dataset)); + } + + template + void field_value_t + ::set(const data_context& context, const value_t& value) const + { + using dataset_type = typename mp::decay_t::dataset_type; + auto& dataset = context.get(); + this->_field.setter(dataset, value_props.convert_to(value)); + } + +} } diff --git a/include/cpphibernate/driver/mariadb/classes/fields/fields.h b/include/cpphibernate/driver/mariadb/classes/fields/fields.h new file mode 100644 index 0000000..da1bd0a --- /dev/null +++ b/include/cpphibernate/driver/mariadb/classes/fields/fields.h @@ -0,0 +1,36 @@ +#pragma once + + + + + +namespace cpphibernate { +namespace mariadb { + + /** + * @brief Vector of fields. + */ + struct fields_t + : public std::vector + { + using base_type = std::vector; + using base_type::base_type; + }; + + namespace __impl + { + + /** + * @brief Helper class to create table vector. + */ + template + struct fields_builder; + + } + + /** + * @brief Predicate to create table vector. + */ + constexpr decltype(auto) make_fields = mp::generic_predicate<__impl::fields_builder> { }; + +} } diff --git a/include/cpphibernate/driver/mariadb/classes/fields/fields.inl b/include/cpphibernate/driver/mariadb/classes/fields/fields.inl new file mode 100644 index 0000000..fd7d8bc --- /dev/null +++ b/include/cpphibernate/driver/mariadb/classes/fields/fields.inl @@ -0,0 +1,63 @@ +#pragma once + +#include "field.h" + +namespace cpphibernate { +namespace mariadb { + + namespace __impl + { + + /* fields_builder */ + + template + struct fields_builder + { + template + static constexpr decltype(auto) apply(T_args&&... args) + { static_assert(sizeof...(args) == -1, "Invalid parameters for mariadb::make_fields(...)!"); } + }; + + template + struct fields_builder< + mp::list, + mp::enable_if_t< + mp::is_same_v> + && schema::is_schema_v> + && schema::is_table_v>>> + { + template + static constexpr void emplace( + fields_t& result, + const table_t& owner, + const T_schema& schema, + const T_table& table, + T_index&& index) + { + result.emplace_back(make_field(owner, schema, table, table.fields[index])); + } + + template + static auto helper( + const table_t& owner, + const T_schema& schema, + const T_table& table, + std::index_sequence&&) + { + fields_t ret; + int dummy[] = { 0, (emplace(ret, owner, schema, table, hana::size_c), void(), 0)... }; + (void) dummy; + return ret; + } + + static constexpr decltype(auto) apply( + const table_t& owner, const T_schema& schema, const T_table& table) + { + using size = decltype(hana::size(table.fields)); + return helper(owner, schema, table, std::make_index_sequence { }); + } + }; + + } + +} } diff --git a/include/cpphibernate/driver/mariadb/classes/schema.h b/include/cpphibernate/driver/mariadb/classes/schema.h new file mode 100644 index 0000000..ab9f90e --- /dev/null +++ b/include/cpphibernate/driver/mariadb/classes/schema.h @@ -0,0 +1,5 @@ +#pragma once + +#include "schema/schema.h" + +#include "schema/schema.inl" diff --git a/include/cpphibernate/driver/mariadb/classes/schema/schema.h b/include/cpphibernate/driver/mariadb/classes/schema/schema.h new file mode 100644 index 0000000..7a7ed6c --- /dev/null +++ b/include/cpphibernate/driver/mariadb/classes/schema/schema.h @@ -0,0 +1,97 @@ +#pragma once + +#include + +#include "../tables/table.h" +#include "../tables/tables.h" + +namespace cpphibernate { +namespace mariadb { + + /** + * @brief Class to wrapp all values needed by the maraidb schema. + */ + struct schema_t + { + public: + using table_map = std::map; + using field_map = std::map; + + private: + table_map _table_lookup; //!< dataset id to table pointer map + field_map _field_lookup; //!< field id to field pointer map + + public: + std::string name; //!< name of the schema + tables_t tables; //!< tables that are managed by this schema. + + public: + /** + * @brief Default constructor. + */ + inline schema_t() = default; + + /** + * @brief Value constructor. + * + * @param[in] p_schema Cpphibernate schema to create mariadb schema from. + */ + template + inline schema_t(const T_schema& p_schema); + + /** + * @brief Move constructor. + */ + inline schema_t(schema_t&&) = delete; + + /** + * @brief Copy constructor. + */ + inline schema_t(const schema_t&) = delete; + + /** + * @brief Print the schema to the passed stream. + */ + std::ostream& print(std::ostream& os) const; + + /** + * @brief Initialize the whole schema using the passed context. + */ + void init(const init_context& context) const; + + /** + * @brief Find the table for the passed dataset id in the schema. + */ + const table_t& table(size_t dataset_id) const; + + /** + * @brief Find the field for the passed field id in the schema. + */ + const field_t& field(size_t field_id) const; + + private: + /** + * @brief Initialize the field. + */ + void init(); + }; + + using schema_ptr_u = std::unique_ptr; + + namespace __impl + { + + /** + * @brief Helper type to build schema. + */ + template + struct schema_builder; + + } + + /** + * @brief Predicate to create mariadb schema class. + */ + constexpr decltype(auto) make_schema = mp::generic_predicate<__impl::schema_builder> { }; + +} } diff --git a/include/cpphibernate/driver/mariadb/classes/schema/schema.inl b/include/cpphibernate/driver/mariadb/classes/schema/schema.inl new file mode 100644 index 0000000..31ff90b --- /dev/null +++ b/include/cpphibernate/driver/mariadb/classes/schema/schema.inl @@ -0,0 +1,44 @@ +#pragma once + +#include "schema.h" + +#include +#include "../tables/tables.inl" + +namespace cpphibernate { +namespace mariadb { + + /* schema_t */ + + template + schema_t::schema_t(const T_schema& p_schema) + : name (p_schema.name) + , tables(make_tables(*this, p_schema)) + { init(); } + + namespace __impl + { + + /* schema_builder */ + + template + struct schema_builder + { + template + static constexpr decltype(auto) apply(T_args&&... args) + { static_assert(sizeof...(args) == -1, "Invalid parameters for mariadb::make_schema(...)!"); } + }; + + template + struct schema_builder< + mp::list, + mp::enable_if_t< + schema::is_schema_v>>> + { + static decltype(auto) apply(const T_schema& p_schema) + { return std::make_unique(p_schema); } + }; + + } + +} } diff --git a/include/cpphibernate/driver/mariadb/classes/tables.h b/include/cpphibernate/driver/mariadb/classes/tables.h new file mode 100644 index 0000000..6f70f09 --- /dev/null +++ b/include/cpphibernate/driver/mariadb/classes/tables.h @@ -0,0 +1 @@ +#pragma once diff --git a/include/cpphibernate/driver/mariadb/classes/tables/table.h b/include/cpphibernate/driver/mariadb/classes/tables/table.h new file mode 100644 index 0000000..664cf17 --- /dev/null +++ b/include/cpphibernate/driver/mariadb/classes/tables/table.h @@ -0,0 +1,319 @@ +#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> { }; + +} } diff --git a/include/cpphibernate/driver/mariadb/classes/tables/table.inl b/include/cpphibernate/driver/mariadb/classes/tables/table.inl new file mode 100644 index 0000000..4602f89 --- /dev/null +++ b/include/cpphibernate/driver/mariadb/classes/tables/table.inl @@ -0,0 +1,128 @@ +#pragma once + +#include + +#include "table.h" + +#include "table_simple.inl" +#include "table_polymorphic.inl" + +namespace cpphibernate { +namespace mariadb { + + /* table_t */ + + template< + typename T_schema, + typename T_table> + table_t::table_t( + const schema_t& p_owner, + const T_schema& p_schema, + const T_table& p_table) + : id (hana::value(p_table.table_id)) + , dataset_id(get_type_id(p_table.wrapped_dataset)) + , name (p_table.name) + , schema (p_owner) + , fields (make_fields(*this, p_schema, p_table)) + { } + + const table_t* table_t + ::get_derived_by_dataset_id(size_t p_id) const + { + if (dataset_id == p_id) + return this; + for (auto& ptr : derived_tables) + { + assert(ptr); + auto ret = ptr->get_derived_by_dataset_id(p_id); + if (ret) + return ret; + } + return nullptr; + } + + const table_t* table_t + ::get_derived_by_table_id(size_t p_id) const + { + if (id == p_id) + return this; + for (auto& ptr : derived_tables) + { + assert(ptr); + auto ret = ptr->get_derived_by_table_id(p_id); + if (ret) + return ret; + } + return nullptr; + } + + namespace __impl + { + + /* table_builder */ + + template + struct table_builder + { + template + static constexpr decltype(auto) apply(T_args&&... args) + { static_assert(sizeof...(args) == 0, "Invalid parameters for mariadb::make_table(...)!"); } + }; + + template + struct table_builder< + mp::list, + mp::enable_if_t< + mp::is_same_v> + && schema::is_schema_v> + && schema::is_table_v>>> + { + + /* table_type */ + + template< + typename T_dataset, + typename T_base_dataset, + typename T_derived_datasets, + typename = void> + struct table_type + { using type = table_simple_t /* table_simple_t, mp::decay_t, T_base_dataset> */; }; + + template< + typename T_dataset, + typename T_base_dataset, + typename T_derived_datasets> + struct table_type< + T_dataset, + T_base_dataset, + T_derived_datasets, + mp::enable_if_t< + decltype(hana::size(std::declval()) != hana::size_c<0>)::value + || decltype(hana::not_equal(std::declval(), hana::type_c))::value>> + { using type = table_polymorphic_t, mp::decay_t>; }; + + template< + typename T_dataset, + typename T_base_dataset, + typename T_derived_datasets> + using table_type_t = typename table_type::type; + + /* apply */ + + static decltype(auto) apply(const schema_t& owner, const T_schema& schema, const T_table& table) + { + using dataset_type = decltype(+table.wrapped_dataset); + using base_dataset_type = mp::decay_t; + using derived_datasets_type = mp::decay_t; + using table_type = table_type_t; + + return std::make_unique( + owner, schema, table, + base_dataset_type { }, derived_datasets_type { }); + } + + }; + + } + +} } diff --git a/include/cpphibernate/driver/mariadb/classes/tables/table_polymorphic.h b/include/cpphibernate/driver/mariadb/classes/tables/table_polymorphic.h new file mode 100644 index 0000000..7cfd6e7 --- /dev/null +++ b/include/cpphibernate/driver/mariadb/classes/tables/table_polymorphic.h @@ -0,0 +1,81 @@ +#pragma once + +#include "table.h" + +namespace cpphibernate { +namespace mariadb { + + /** + * @brief Table for polymorphic data types. + */ + template + struct table_polymorphic_t + : public table_t + { + public: + using dataset_type = typename T_table::dataset_type; + + public: + /** + * @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_base_dataset, + typename T_derived_datasets> + inline table_polymorphic_t( + const schema_t& p_owner, + const T_schema& p_schema, + const T_table& p_table, + const T_base_dataset&&, + const T_derived_datasets&&); + + public: + /** + * @brief Execute a create/update operation on the current table. + * + * For a polymorphic type this will check the derived tables before executing the actual opertion. + * 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. + */ + std::string create_update(const create_update_context& context) const override; + + /** + * @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. + */ + void destroy(const destroy_context& context) const override; + + public: + /** + * @brief Emplace new dataset of the type the table represents. + * + * @param[in] context Context to emplace new dataset in. + */ + void emplace(const read_context& context) const override; + + private: + /** + * @brief Execute the predicate for each derived type. + * + * @param[in] dataset Dataset to try to cast to derived type. + * @param[in] include_self True: Also check the dataset type. False: Only check derived types. + * @param[in] pred Predicate to execute if dataset could be cast to a derived type. + */ + template + constexpr void for_each_derived(T_dataset& dataset, const T_include_self& include_self, const T_pred& pred) const; + }; + +} } diff --git a/include/cpphibernate/driver/mariadb/classes/tables/table_polymorphic.inl b/include/cpphibernate/driver/mariadb/classes/tables/table_polymorphic.inl new file mode 100644 index 0000000..5dfa447 --- /dev/null +++ b/include/cpphibernate/driver/mariadb/classes/tables/table_polymorphic.inl @@ -0,0 +1,185 @@ +#pragma once + +#include "table_polymorphic.h" + +#include "../../context/create_update_context.inl" +#include "../../context/destroy_context.inl" + +namespace cpphibernate { +namespace mariadb { + + namespace __impl + { + + /* make_dataset_id_vector */ + + struct make_dataset_id_vector_impl + { + template + static constexpr decltype(auto) helper(const T_datasets& datasets, std::index_sequence) + { + return std::vector({ + get_type_id(datasets[hana::size_c])... + }); + } + + template + constexpr decltype(auto) operator()(const T_datasets& datasets) const + { + using size = mp::decay_t; + return helper(datasets, std::make_index_sequence { }); + } + }; + + static constexpr decltype(auto) make_dataset_id_vector = make_dataset_id_vector_impl { }; + + /* filter_own_type_impl */ + + template + struct filter_self_type_impl + { + template + inline decltype(auto) operator() (T_type&&) const + { + return hana::and_( + hana::not_(hana::trait(T_type{ })), + hana::or_( + T_type{ } != hana::type_c, + T_include_self { })); + } + }; + + template + static constexpr decltype(auto) filter_self_type = filter_self_type_impl { }; + + } + + /* table_polymorphic_t */ + + template< + typename T_schema, + typename T_table> + template< + typename T_base_dataset, + typename T_derived_datasets> + table_polymorphic_t + ::table_polymorphic_t( + const schema_t& p_owner, + const T_schema& p_schema, + const T_table& p_table, + const T_base_dataset&&, + const T_derived_datasets&&) + : table_t( + p_owner, + p_schema, + p_table) + { + this->base_dataset_id = hana::if_( + hana::equal(T_base_dataset { }, hana::type_c), + static_cast(0), + get_type_id(T_base_dataset { })); + this->derived_dataset_ids = __impl::make_dataset_id_vector(T_derived_datasets { }); + } + + template< + typename T_schema, + typename T_table> + std::string table_polymorphic_t + ::create_update(const create_update_context& context) const + { + bool done = false; + auto& dataset = context.get(); + for_each_derived(dataset, hana::false_c, [&](auto& derived_dataset){ + if (!done) + { + using derived_dataset_type = mp::decay_t; + auto derived_dataset_id = get_type_id(hana::type_c); + auto derived_table = this->get_derived_by_dataset_id(derived_dataset_id); + if (!derived_table) + { + throw exception(static_cast(std::ostringstream { } + << "unable to find derived table info for dataset '" + << cppcore::type_helper::name() << "'!").str()); + } + derived_table->create_update(change_context(context, derived_dataset)); + done = true; + } + }); + + return done + ? *this->primary_key_field->get(context) + : this->create_update_exec(context); + } + + template< + typename T_schema, + typename T_table> + void table_polymorphic_t + ::destroy(const destroy_context& context) const + { + bool done = false; + auto& dataset = context.get(); + for_each_derived(dataset, hana::false_c, [&](auto& derived_dataset){ + if (!done) + { + using derived_dataset_type = mp::decay_t; + auto derived_dataset_id = get_type_id(hana::type_c); + auto derived_table = this->get_derived_by_dataset_id(derived_dataset_id); + if (!derived_table) + { + throw exception(static_cast(std::ostringstream { } + << "unable to find derived table info for dataset '" + << cppcore::type_helper::name() << "'!").str()); + } + auto new_context = change_context(context, derived_dataset); + derived_table->destroy(new_context); + done = true; + } + }); + + if (!done) + { + this->destroy_exec(context); + } + } + + template< + typename T_schema, + typename T_table> + void table_polymorphic_t + ::emplace(const read_context& context) const + { + hana::eval_if( + std::is_abstract_v, + [](){ + throw exception(std::string("can not create dataset of abstract type: ") + + cppcore::type_helper::name()); + }, + [&context, this](auto _){ + _(context).template emplace(this); + }); + } + + template< + typename T_schema, + typename T_table> + template< + typename T_dataset, + typename T_pred, + typename T_include_self> + constexpr void table_polymorphic_t + ::for_each_derived(T_dataset& dataset, const T_include_self& include_self, const T_pred& pred) const + { + auto derived_types = decltype(hana::filter( + schema::get_all_derived_types(std::declval(), hana::type_c), + __impl::filter_self_type, mp::decay_t>)) { }; + + hana::for_each(derived_types, [&](auto& type){ + using derived_type = decay_unwrap_t; + auto* derived = dynamic_cast(&dataset); + if (derived) + pred(*derived); + }); + } + +} } diff --git a/include/cpphibernate/driver/mariadb/classes/tables/table_simple.h b/include/cpphibernate/driver/mariadb/classes/tables/table_simple.h new file mode 100644 index 0000000..ff2f557 --- /dev/null +++ b/include/cpphibernate/driver/mariadb/classes/tables/table_simple.h @@ -0,0 +1,35 @@ +#pragma once + +#include "table.h" + +namespace cpphibernate { +namespace mariadb { + + /** + * @brief Table for non polymorphic datatypes. + */ + struct table_simple_t + : public table_t + { + public: + /** + * @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, + typename T_base_dataset, + typename T_derived_datasets> + inline table_simple_t( + const schema_t& p_owner, + const T_schema& p_schema, + const T_table& p_table, + const T_base_dataset&&, + const T_derived_datasets&&); + }; + +} } diff --git a/include/cpphibernate/driver/mariadb/classes/tables/table_simple.inl b/include/cpphibernate/driver/mariadb/classes/tables/table_simple.inl new file mode 100644 index 0000000..d03f4c1 --- /dev/null +++ b/include/cpphibernate/driver/mariadb/classes/tables/table_simple.inl @@ -0,0 +1,27 @@ +#pragma once + +#include "table_simple.h" + +namespace cpphibernate { +namespace mariadb { + + /* table_simple_t */ + + template< + typename T_schema, + typename T_table, + typename T_base_dataset, + typename T_derived_datasets> + table_simple_t::table_simple_t( + const schema_t& p_owner, + const T_schema& p_schema, + const T_table& p_table, + const T_base_dataset&&, + const T_derived_datasets&&) + : table_t( + p_owner, + p_schema, + p_table) + { } + +} } diff --git a/include/cpphibernate/driver/mariadb/classes/tables/tables.h b/include/cpphibernate/driver/mariadb/classes/tables/tables.h new file mode 100644 index 0000000..93c3907 --- /dev/null +++ b/include/cpphibernate/driver/mariadb/classes/tables/tables.h @@ -0,0 +1,32 @@ +#pragma once + +namespace cpphibernate { +namespace mariadb { + + /** + * @brief Vector of tables. + */ + struct tables_t + : public std::vector + { + using base_type = std::vector; + using base_type::base_type; + }; + + namespace __impl + { + + /** + * @brief Helper class to create table vector. + */ + template + struct tables_builder; + + } + + /** + * @brief Predicate to create table vector. + */ + constexpr decltype(auto) make_tables = mp::generic_predicate<__impl::tables_builder> { }; + +} } diff --git a/include/cpphibernate/driver/mariadb/classes/tables/tables.inl b/include/cpphibernate/driver/mariadb/classes/tables/tables.inl new file mode 100644 index 0000000..7be19bc --- /dev/null +++ b/include/cpphibernate/driver/mariadb/classes/tables/tables.inl @@ -0,0 +1,66 @@ +#pragma once + +#include "tables.h" + +#include "table.inl" + +namespace cpphibernate { +namespace mariadb { + + namespace __impl + { + + /* tables_builder */ + + template + struct tables_builder + { + template + static constexpr decltype(auto) apply(T_args&&... args) + { static_assert(sizeof...(args) == -1, "Invalid parameters for mariadb::make_tables(...)!"); } + }; + + template< + typename T_owner, + typename T_schema> + struct tables_builder< + mp::list, + mp::enable_if_t< + mp::is_same_v> + && schema::is_schema_v>>> + { + template + static constexpr void emplace( + tables_t& result, + const schema_t& owner, + const T_schema& schema, + T_index&& index) + { + result.emplace_back(make_table(owner, schema, schema.tables[index])); + } + + template + static decltype(auto) helper( + const schema_t& owner, + const T_schema& schema, + std::index_sequence seq) + { + tables_t ret; + ret.reserve(sizeof...(I)); + int dummy[] = { 0, (emplace(ret, owner, schema, hana::size_c), void(), 0)... }; + (void) dummy; + return ret; + } + + static constexpr decltype(auto) apply( + const schema_t& owner, + const T_schema& schema) + { + using size = decltype(hana::size(schema.tables)); + return helper(owner, schema, std::make_index_sequence { }); + } + }; + + } + +} } diff --git a/include/cpphibernate/driver/mariadb/context.h b/include/cpphibernate/driver/mariadb/context.h new file mode 100644 index 0000000..f67177c --- /dev/null +++ b/include/cpphibernate/driver/mariadb/context.h @@ -0,0 +1,11 @@ +#pragma once + +#include "context/base_context.h" +#include "context/create_update_context.h" +#include "context/data_context.h" +#include "context/init_context.h" + +#include "context/base_context.inl" +#include "context/create_update_context.inl" +#include "context/data_context.inl" +#include "context/init_context.inl" diff --git a/include/cpphibernate/driver/mariadb/context/base_context.h b/include/cpphibernate/driver/mariadb/context/base_context.h new file mode 100644 index 0000000..48687ef --- /dev/null +++ b/include/cpphibernate/driver/mariadb/context/base_context.h @@ -0,0 +1,26 @@ +#pragma once + +#include + +namespace cpphibernate { +namespace mariadb { + + struct schema_t; + + /** + * @brief Base class for all mariadb driver context classes. + */ + struct base_context + { + const schema_t& schema; //!< schema to use for the operation + ::cppmariadb::connection& connection; //!< mariadb connection to use for executing queries + + /** + * @brief Constructor. + */ + inline base_context( + const schema_t& p_schema, + ::cppmariadb::connection& p_connection); + }; + +} } diff --git a/include/cpphibernate/driver/mariadb/context/base_context.inl b/include/cpphibernate/driver/mariadb/context/base_context.inl new file mode 100644 index 0000000..33aecda --- /dev/null +++ b/include/cpphibernate/driver/mariadb/context/base_context.inl @@ -0,0 +1,17 @@ +#pragma once + +#include "base_context.h" + +namespace cpphibernate { +namespace mariadb { + + /* base_context */ + + base_context::base_context( + const schema_t& p_schema, + ::cppmariadb::connection& p_connection) + : schema (p_schema) + , connection(p_connection) + { } + +} } diff --git a/include/cpphibernate/driver/mariadb/context/create_update_context.h b/include/cpphibernate/driver/mariadb/context/create_update_context.h new file mode 100644 index 0000000..9ff8b68 --- /dev/null +++ b/include/cpphibernate/driver/mariadb/context/create_update_context.h @@ -0,0 +1,71 @@ +#pragma once + +#include "data_context.h" +#include "../impl/filter.h" + +namespace cpphibernate { +namespace mariadb { + + /** + * @brief Context that is used for create or update operations. + */ + struct create_update_context + : public data_context + { + const filter_t * filter; //!< Filter that is used for update operation. + //!< Also indicated if this is an update or create operation. + const table_t * derived_table; //!< Derived table if the current context is executed in a base table. + const field_t * owner_field; //!< Field the current dataset is owned by (for foreign fields) + std::string owner_key; //!< Key of the dataset the current dataset is owned by. + ssize_t index; //!< Index of the current dataset in the container. + + /** + * @brief Constructor. Creates a create context (no filters are passed). + * + * @param[in] p_schema Mariadb driver schema to use for the operation. + * @param[in] p_connection Mariadb connection to execute queries with. + * @param[in] p_dataset Dataset to store in the context. + */ + template + inline create_update_context( + const schema_t& p_schema, + ::cppmariadb::connection& p_connection, + T_dataset& p_dataset); + + /** + * @brief Constructor. Create an update context. + * + * @param[in] p_schema Mariadb driver schema to use for the operation. + * @param[in] p_connection Mariadb connection to execute queries with. + * @param[in] p_dataset Dataset to store in the context. + * @param[in] p_filter Filters to use for the update operation. + */ + template + inline create_update_context( + const schema_t& p_schema, + ::cppmariadb::connection& p_connection, + T_dataset& p_dataset, + const filter_t& p_filter); + + /** + * @brief Create a create context from the current context. + */ + inline decltype(auto) make_create_context() const; + + /** + * @brief Create an update context from the current context. + */ + inline decltype(auto) make_update_context(const filter_t& p_filter) const; + + /** + * @brief Returns true if this is an create context, false otherwise. + */ + inline bool is_create() const; + + /** + * @brief Returns true if this is an update context, false otherwise. + */ + inline bool is_update() const; + }; + +} } diff --git a/include/cpphibernate/driver/mariadb/context/create_update_context.inl b/include/cpphibernate/driver/mariadb/context/create_update_context.inl new file mode 100644 index 0000000..b4cd1d7 --- /dev/null +++ b/include/cpphibernate/driver/mariadb/context/create_update_context.inl @@ -0,0 +1,57 @@ +#pragma once + +#include "create_update_context.h" + +namespace cpphibernate { +namespace mariadb { + + /* create_update_context */ + + template + create_update_context::create_update_context( + const schema_t& p_schema, + ::cppmariadb::connection& p_connection, + T_dataset& p_dataset) + : data_context (p_schema, p_connection, p_dataset) + , filter (nullptr) + , derived_table (nullptr) + , owner_field (nullptr) + , owner_key () + , index (0) + { } + + template + create_update_context::create_update_context( + const schema_t& p_schema, + ::cppmariadb::connection& p_connection, + T_dataset& p_dataset, + const filter_t& p_filter) + : data_context (p_schema, p_connection, p_dataset) + , filter (&p_filter) + , derived_table (nullptr) + , owner_field (nullptr) + , owner_key () + , index (0) + { } + + decltype(auto) create_update_context::make_create_context() const + { + auto ret = *this; + ret.filter = nullptr; + return ret; + } + + decltype(auto) create_update_context::make_update_context(const filter_t& p_filter) const + { + auto ret = *this; + ret.filter = &p_filter; + return ret; + } + + bool create_update_context::is_create() const + { return !static_cast(filter); } + + bool create_update_context::is_update() const + { return static_cast(filter); } + +} } diff --git a/include/cpphibernate/driver/mariadb/context/data_context.h b/include/cpphibernate/driver/mariadb/context/data_context.h new file mode 100644 index 0000000..13edca2 --- /dev/null +++ b/include/cpphibernate/driver/mariadb/context/data_context.h @@ -0,0 +1,92 @@ +#pragma once + +#include + +#include "base_context.h" + +namespace cpphibernate { +namespace mariadb { + + namespace __impl + { + + /** + * @brief Helper class to create the change_context predicate. + */ + template + struct change_context_builder; + + } + + struct table_t; + + /** + * @brief Predicate to change the stored dataset of any data_context. + */ + constexpr decltype(auto) change_context = cppmp::generic_predicate<__impl::change_context_builder> { }; + + /** + * @brief Mariadb driver context that helds a specific dataset. + */ + struct data_context + : public base_context + { + private: + mutable size_t _dataset_id; //!< Unique type id of the dataset that is stored in this context. + mutable void * _dataset; //!< Pointer to the stored dataset. + mutable const table_t * _table; //!< Table this context/dataset belongs to + + template + friend struct __impl::change_context_builder; + + protected: + /** + * @brief Constructor. + * + * @param[in] p_schema Mariadb driver schema to use for the operation. + * @param[in] p_connection Mariadb connection to execute queries with. + */ + inline data_context( + const schema_t& p_schema, + ::cppmariadb::connection& p_connection); + + public: + /** + * @brief Constructor. + * + * @param[in] p_schema Mariadb driver schema to use for the operation. + * @param[in] p_connection Mariadb connection to execute queries with. + * @param[in] p_dataset Dataset to store in the context. + */ + template + inline data_context( + const schema_t& p_schema, + ::cppmariadb::connection& p_connection, + T_dataset& p_dataset); + + /** + * @brief Get a reference to the stored dataset if the type matches. + * If an invalid type is requested an exception is thrown. + */ + template + inline decltype(auto) get() const; + + protected: + /** + * @brief Set the dataset that is stored in this context. + * + * @param[in] dataset Dataset to store in the context. + * @param[in] dataset_id Unique id of the dataset stored in the context. + * + * @return Pointer to the stored dataset. + */ + template + inline void * set(T_dataset& dataset, size_t dataset_id = 0) const; + + /** + * @brief Clear the context (set the stored dataset to nullptr). + */ + inline void clear() const; + }; + +} } diff --git a/include/cpphibernate/driver/mariadb/context/data_context.inl b/include/cpphibernate/driver/mariadb/context/data_context.inl new file mode 100644 index 0000000..22614b2 --- /dev/null +++ b/include/cpphibernate/driver/mariadb/context/data_context.inl @@ -0,0 +1,119 @@ +#pragma once + +#include "data_context.h" + +#include "base_context.inl" +#include "../classes/schema/schema.inl" + +namespace cpphibernate { +namespace mariadb { + + /* data_context */ + + data_context::data_context( + const schema_t& p_schema, + ::cppmariadb::connection& p_connection) + : base_context (p_schema, p_connection) + , _dataset_id (0) + , _dataset (nullptr) + , _table (nullptr) + { } + + template + data_context::data_context( + const schema_t& p_schema, + ::cppmariadb::connection& p_connection, + T_dataset& p_dataset) + : base_context (p_schema, p_connection) + , _dataset_id (0) + , _dataset (nullptr) + , _table (nullptr) + { set(p_dataset); } + + template + decltype(auto) data_context::get() const + { + using dataset_type = mp::decay_t; + + /* check if tha context has a dataset assigned (should never fail) */ + if (!_dataset) + throw exception("no data assigned!"); + + auto dataset_id = get_type_id(hana::type_c); + + /* if the dataset IDs does not match, search in the base tables for a matching ID */ + if (dataset_id != _dataset_id) + { + /* get the table of the stored dataset */ + if (!_table) + _table = &schema.table(_dataset_id); + + /* check if the table matches the stored dataset (should never happen) */ + else if (_table->dataset_id != _dataset_id) + throw exception("invalid table!"); + + /* walk through base tables until we have found a matching ID */ + auto table = _table; + while(table && table->dataset_id != dataset_id) + table = table->base_table; + + /* check if we have found a suitable table, if not throw an exception */ + if (!table) + { + throw exception(cppcore::type_helper::name() + + " is not a derived type of dataset with id " + std::to_string(_dataset_id)); + } + } + + /* return the dataset */ + return *static_cast(_dataset); + } + + template + void * data_context::set(T_dataset& dataset, size_t dataset_id) const + { + _dataset_id = dataset_id + ? dataset_id + : get_type_id(hana::type_c>); + _dataset = &dataset; + _table = nullptr; + return _dataset; + } + + void data_context::clear() const + { + _dataset_id = 0; + _dataset = nullptr; + _table = nullptr; + } + + namespace __impl + { + + /* change_context_builder */ + + template + struct change_context_builder + { + template + static constexpr decltype(auto) apply(T_args&&...) + { static_assert(sizeof...(T_args) == -1, "Invalid parameters for change_context(...)!"); } + }; + + template + struct change_context_builder< + mp::list, + mp::enable_if_t< + mp::is_base_of_v>>> + { + static constexpr decltype(auto) apply(const T_context& context, T_dataset& dataset) + { + auto new_context = context; + new_context.set(dataset); + return new_context; + } + }; + + } + +} } diff --git a/include/cpphibernate/driver/mariadb/context/destroy_context.h b/include/cpphibernate/driver/mariadb/context/destroy_context.h new file mode 100644 index 0000000..f2e8927 --- /dev/null +++ b/include/cpphibernate/driver/mariadb/context/destroy_context.h @@ -0,0 +1,23 @@ +#pragma once + +#include "data_context.h" + +namespace cpphibernate { +namespace mariadb { + + /** + * @brief Mariadb driver context for deleting datasets from the database. + */ + struct destroy_context + : public data_context + { + std::string where; //!< Where clause for deleting datasets. + + template + inline destroy_context( + const schema_t& p_schema, + ::cppmariadb::connection& p_connection, + T_dataset& p_dataset); + }; + +} } diff --git a/include/cpphibernate/driver/mariadb/context/destroy_context.inl b/include/cpphibernate/driver/mariadb/context/destroy_context.inl new file mode 100644 index 0000000..3fa7336 --- /dev/null +++ b/include/cpphibernate/driver/mariadb/context/destroy_context.inl @@ -0,0 +1,20 @@ +#pragma once + +#include "destroy_context.h" + +#include "data_context.inl" + +namespace cpphibernate { +namespace mariadb { + + /* destroy_context */ + + template + destroy_context::destroy_context( + const schema_t& p_schema, + ::cppmariadb::connection& p_connection, + T_dataset& p_dataset) + : data_context (p_schema, p_connection, p_dataset) + { } + +} } diff --git a/include/cpphibernate/driver/mariadb/context/init_context.h b/include/cpphibernate/driver/mariadb/context/init_context.h new file mode 100644 index 0000000..03ce5cd --- /dev/null +++ b/include/cpphibernate/driver/mariadb/context/init_context.h @@ -0,0 +1,22 @@ +#pragma once + +#include "base_context.h" + +namespace cpphibernate { +namespace mariadb { + + /** + * @brief Mariadb driver context for initializing the database. + */ + struct init_context + : public base_context + { + bool recreate; + + inline init_context( + const schema_t& p_schema, + ::cppmariadb::connection& p_connection, + bool p_recreate); + }; + +} } diff --git a/include/cpphibernate/driver/mariadb/context/init_context.inl b/include/cpphibernate/driver/mariadb/context/init_context.inl new file mode 100644 index 0000000..f9815ef --- /dev/null +++ b/include/cpphibernate/driver/mariadb/context/init_context.inl @@ -0,0 +1,20 @@ +#pragma once + +#include "init_context.h" + +#include "base_context.inl" + +namespace cpphibernate { +namespace mariadb { + + /* init_context */ + + init_context::init_context( + const schema_t& p_schema, + ::cppmariadb::connection& p_connection, + bool p_recreate) + : base_context (p_schema, p_connection) + , recreate (p_recreate) + { } + +} } diff --git a/include/cpphibernate/driver/mariadb/context/read_context.h b/include/cpphibernate/driver/mariadb/context/read_context.h new file mode 100644 index 0000000..308e771 --- /dev/null +++ b/include/cpphibernate/driver/mariadb/context/read_context.h @@ -0,0 +1,104 @@ +#pragma once + +#include "data_context.h" +#include "../impl/filter.h" + +namespace cpphibernate { +namespace mariadb { + + /** + * @brief Context that is used for read operations. + */ + struct read_context + : public data_context + { + protected: + const size_t real_dataset_id; //!< Unique id of thre real dataset the context was created with. + + public: + const filter_t& filter; //!< Filter that is used for read operation. + bool is_dynamic; //!< The dataset managed by this context is dynamic/polymorhp + std::string where; //!< Where statement to use for selection + std::string limit; //!< Limit statement to use for selection + std::string order_by; //!< Order statement to use for selection + + public: + /** + * @brief Destructor. + */ + virtual ~read_context() = default; + + /** + * @brief Emplace a new value in the dataset of the read context. + * + * @tparam T_dataset Type of the dataset to emplace. + * + * @param[in] table Table of the dataset to emplace. + * If the table is unknown, the table is searched within the schema. + * + * @return Reference to the emplaced dataset. + */ + template + inline T_dataset& emplace(const table_t * table = nullptr) const; + + /** + * @brief Emplace a new value in the dataset of the read context. + */ + inline void emplace() const; + + /** + * @brief Finish the emplacement of values in the dataset. + */ + inline void finish() const; + + private: + /** + * @brief Actual emplace method. This method should create a new value entry in the dataset of the read context. + * + * @param[in] data Actual data to emplace. If nullptr is passed a default value is created. + * @param[in] dataset_id Unique id of the dataset to emplace (must match the object pointed to by data). + * + * @return Pointer to the dataset that was emplaced. + */ + virtual void * emplace_intern(void * data, size_t dataset_id) const = 0; + + /** + * @brief Actual finish method. Should check how many element has been emplaced in the dataset. + */ + virtual void finish_intern () const = 0; + + protected: + /** + * @brief Constructor. Creates a create context (no filters are passed). + * + * @param[in] p_schema Mariadb driver schema to use for the operation. + * @param[in] p_connection Mariadb connection to execute queries with. + * @param[in] p_filter Filter to use for the read operation. + * @param[in] p_real_dataset_id Unique id the dataset that is stored in the context. + */ + inline read_context( + const schema_t& p_schema, + ::cppmariadb::connection& p_connection, + const filter_t& p_filter, + size_t p_real_dataset_id); + }; + + using read_context_ptr_u = std::unique_ptr; + + namespace __impl + { + + /** + * @brief Helper class to create read contexts. + */ + template + struct read_context_builder; + + } + + /** + * @brief Predicate to create read contexts. + */ + constexpr decltype(auto) make_read_context = mp::generic_predicate<__impl::read_context_builder> { }; + +} } diff --git a/include/cpphibernate/driver/mariadb/context/read_context.inl b/include/cpphibernate/driver/mariadb/context/read_context.inl new file mode 100644 index 0000000..b2adeb9 --- /dev/null +++ b/include/cpphibernate/driver/mariadb/context/read_context.inl @@ -0,0 +1,364 @@ +#pragma once + +#include "read_context.h" + +#include "data_context.inl" +#include "../helper/nullable_helper.inl" +#include "../helper/container_helper.inl" + +namespace cpphibernate { +namespace mariadb { + + read_context::read_context( + const schema_t& p_schema, + ::cppmariadb::connection& p_connection, + const filter_t& p_filter, + size_t p_real_dataset_id) + : data_context (p_schema, p_connection) + , filter (p_filter) + , real_dataset_id (p_real_dataset_id) + , is_dynamic (false) + { } + + template + T_dataset& read_context::emplace(const table_t * table) const + { + using dataset_type = mp::decay_t; + + /* check table */ + auto dataset_id = get_type_id(hana::type_c); + if (!table) + table = &schema.table(dataset_id); + else if (table->dataset_id != dataset_id) + throw exception("wrong table passed!"); + + /* check base */ + auto tbl = table; + while (tbl && tbl->dataset_id != real_dataset_id) + tbl = tbl->base_table; + if (!tbl) + { + throw exception(cppcore::type_helper::name() + + " is not a derived type of dataset with id " + std::to_string(real_dataset_id)); + } + + /* create dataset */ + auto ptr = std::make_unique(); + auto data = emplace_intern(ptr.get(), dataset_id); + if (!data) + throw exception("unable to store created dataset in context!"); + ptr.release(); + return *static_cast(data); + } + + void read_context::emplace() const + { emplace_intern(nullptr, 0); } + + void read_context::finish() const + { finish_intern(); } + + namespace __impl + { + + /* read_context_builder */ + + template + struct read_context_builder + { + template + static constexpr decltype(auto) apply(T_args&&... args) + { static_assert(sizeof...(args) == -1, "Invalid parameters for make_read_context(...)!"); } + }; + + /* read_context_builder - fake */ + + template< + typename T_schema, + typename T_connection, + typename T_dataset, + typename T_filter> + struct read_context_builder< + mp::list, + mp::enable_if_t< + mp::is_same_v>> + >> + { + using dataset_type = typename mp::decay_t::type; + + struct context_impl + : public read_context + { + public: + inline context_impl( + const schema_t& p_schema, + ::cppmariadb::connection& p_connection, + T_dataset& p_dataset, + const filter_t& p_filter) + : read_context (p_schema, p_connection, p_filter, + get_type_id(hana::type_c>>)) + { } + + private: + void * emplace_intern(void* data, size_t dataset_id) const override + { return nullptr; } + + void finish_intern() const override + { clear(); } + }; + + template + static constexpr decltype(auto) apply(X_args&&... args) + { return context_impl(std::forward(args)...); } + }; + + /* read_context_builder - normal types */ + + template< + typename T_schema, + typename T_connection, + typename T_dataset, + typename T_filter> + struct read_context_builder< + mp::list, + mp::enable_if_t< + !is_container_v> + && !is_nullable_v> + && !mp::is_same_v>> + >> + { + using dataset_type = mp::decay_t; + + struct context_impl + : public read_context + { + private: + mutable size_t _count; + dataset_type& _dataset; + + public: + inline context_impl( + const schema_t& p_schema, + ::cppmariadb::connection& p_connection, + T_dataset& p_dataset, + const filter_t& p_filter) + : read_context (p_schema, p_connection, p_filter, + get_type_id(hana::type_c>>)) + , _count (0) + , _dataset (p_dataset) + { } + + private: + void * emplace_intern(void* data, size_t dataset_id) const override + { + if (data || dataset_id != 0) + throw exception("Static datasets can not be assigned!"); + ++_count; + if (_count > 1) + throw exception("Expected exactly one dataset, but received more!"); + return set(_dataset); + } + + void finish_intern() const override + { + if (_count < 1) + throw exception("Expected exactly one dataset, but received none!"); + clear(); + } + }; + + template + static constexpr decltype(auto) apply(X_args&&... args) + { return context_impl(std::forward(args)...); } + }; + + /* read_context_builder - nullable */ + + template< + typename T_schema, + typename T_connection, + typename T_dataset, + typename T_filter> + struct read_context_builder< + mp::list, + mp::enable_if_t< + !is_container_v> + && is_nullable_v> + >> + { + using dataset_type = mp::decay_t; + using nullable_helper_type = nullable_helper; + using value_type = typename nullable_helper_type::value_type; + + struct context_impl + : public read_context + { + private: + dataset_type& _dataset; + mutable size_t _count; + + public: + inline context_impl( + const schema_t& p_schema, + ::cppmariadb::connection& p_connection, + T_dataset& p_dataset, + const filter_t& p_filter) + : read_context (p_schema, p_connection, p_filter, + get_type_id(hana::type_c>>)) + , _count (0) + , _dataset (p_dataset) + { + is_dynamic = is_pointer_v; + nullable_helper_type::clear(_dataset); + } + + private: + void * emplace_intern(void * data, size_t dataset_id) const override + { + if (data && !is_pointer_v) + throw exception("None pointer type can not be assigned!"); + ++_count; + if (_count > 1) + throw exception("Expected exactly one dataset, but received more!"); + + if (data) + { + auto* cast = static_cast(data); + auto& value = nullable_helper_type::set(_dataset, cast); + if (cast != &value) + throw exception("Nullable pointer value has changed!"); + return set(value, dataset_id); + } + else + { + auto& value = nullable_helper_type::set(_dataset, value_type { }); + return set(value); + } + } + + void finish_intern() const override + { clear(); } + }; + + template + static constexpr decltype(auto) apply(X_args&&... args) + { return context_impl(std::forward(args)...); } + }; + + /* container_emplace_helper */ + + template + struct container_emplace_helper; + + template + struct container_emplace_helper< + T_dataset, + mp::enable_if_t< + mp::is_valid_v::value_type> + && !is_pointer_v::value_type> + >> + { + static inline decltype(auto) emplace(T_dataset& dataset, void * data, size_t& data_id) + { + using container_helper_type = container_helper; + using value_type = typename container_helper_type::value_type; + + if (data || data_id != 0) + throw exception("Static datasets can not be assigned!"); + auto& value = container_helper_type::emplace(dataset); + data_id = get_type_id(hana::type_c); + + return value; + } + }; + + template + struct container_emplace_helper< + T_dataset, + mp::enable_if_t< + mp::is_valid_v::value_type> + && is_pointer_v::value_type> + >> + { + static inline decltype(auto) emplace(T_dataset& dataset, void * data, size_t& data_id) + { + using container_helper_type = container_helper; + using nullable_type = typename container_helper_type::value_type; + using nullable_helper_type = nullable_helper; + using inner_value_type = typename nullable_helper_type::value_type; + + if (!data) + throw exception("Expected dynamic data for pointer type!"); + if (data_id == 0) + throw exception("Expected dataset id for pointer type!"); + if (!is_pointer_v) + throw exception("None pointer type can not be assigned!"); + + auto& nullable = container_helper_type::emplace(dataset); + auto* cast = static_cast(data); + auto& value = nullable_helper_type::set(nullable, cast); + if (cast != &value) + throw exception("Nullable pointer value has changed!"); + + return value; + } + }; + + /* read_context_builder - container */ + + template< + typename T_schema, + typename T_connection, + typename T_dataset, + typename T_filter> + struct read_context_builder< + mp::list, + mp::enable_if_t< + is_container_v> + && !is_nullable_v> + >> + { + using dataset_type = mp::decay_t; + using real_dataset_type = real_dataset_t; + using container_helper_type = container_helper; + using value_type = typename container_helper_type::value_type; + using emplace_helper_type = container_emplace_helper; + + struct context_impl + : public read_context + { + private: + dataset_type& _dataset; + + public: + inline context_impl( + const schema_t& p_schema, + ::cppmariadb::connection& p_connection, + T_dataset& p_dataset, + const filter_t& p_filter) + : read_context (p_schema, p_connection, p_filter, + get_type_id(hana::type_c>>)) + , _dataset (p_dataset) + { + is_dynamic = is_pointer_v; + container_helper_type::clear(_dataset); + } + + private: + void * emplace_intern(void * data, size_t dataset_id) const override + { + auto& value = emplace_helper_type::emplace(_dataset, data, dataset_id); + return set(value, dataset_id); + } + + void finish_intern() const override + { clear(); } + }; + + template + static constexpr decltype(auto) apply(X_args&&... args) + { return context_impl(std::forward(args)...); } + }; + + } + +} } diff --git a/include/cpphibernate/driver/mariadb/driver.h b/include/cpphibernate/driver/mariadb/driver.h new file mode 100644 index 0000000..3c2e720 --- /dev/null +++ b/include/cpphibernate/driver/mariadb/driver.h @@ -0,0 +1,86 @@ +#pragma once + +#include "impl/driver_impl.h" + +namespace cpphibernate { +namespace mariadb { + + /** + * @brief Mariadb driver. + * + * The normal cpphibernate context will be inherited from this class. So all methods + * defined here will be part of the context class. + */ + struct driver_t + { + public: + friend struct driver_impl_t; + + private: + driver_impl_t _impl; //!< Driver implementation. + ::cppmariadb::connection * _connection; //!< Mariadb connection to use for queries. + filter_t _filter; //!< Filter to use for read and update operations. + + public: + /** + * @brief Constructor. Initializes the driver with the passed schema. + * + * @param[in] p_schema Schema to use with this driver. + */ + template + inline driver_t(T_schema&& p_schema); + + /** + * @brief Constructor. Initializes the driver with the passed schema. + * + * @param[in] p_schema Schema to use with this driver. + * @param[in] p_connection Connection to the mariadb server to use for queries. + */ + template + inline driver_t(T_schema&& p_schema, ::cppmariadb::connection& p_connection); + + /** + * @brief Print the schema of the driver to the passed stream. + */ + inline std::ostream& print(std::ostream& os) const; + + /** + * @brief Get the connection currently assigned to the driver. + */ + inline ::cppmariadb::connection * connection() const; + + /** + * @brief Set the connection to use for queries. + * This will invalidate all cached queries and strings. + */ + inline void connection(::cppmariadb::connection * p_connection); + + /** + * @brief Set the include filter to use for update and read operations. + * + * @param[in] args Fileds and tables to use as include filter. + */ + template + inline void set_filter_inclusive(T_args&&... args); + + /** + * @brief Set the exclude filter to use for update and read operations. + * + * @param[in] args Fileds and tables to use as exclude filter. + */ + template + inline void set_filter_exclusive(T_args&&... args); + + /** + * @brief Clear the filter. + */ + inline void clear_filter(); + + protected: + /** + * @brief Get the imlementation object of the driver. + */ + inline auto& impl() const; + }; + +} } diff --git a/include/cpphibernate/driver/mariadb/driver.inl b/include/cpphibernate/driver/mariadb/driver.inl new file mode 100644 index 0000000..993820b --- /dev/null +++ b/include/cpphibernate/driver/mariadb/driver.inl @@ -0,0 +1,44 @@ +#pragma once + + + + +namespace cpphibernate { +namespace mariadb { + + template + driver_t::driver_t(T_schema&& p_schema) + : _impl (*this, std::forward(p_schema)) + , _connection (nullptr) + { } + + template + driver_t::driver_t(T_schema&& p_schema, ::cppmariadb::connection& p_connection) + : _impl (*this, std::forward(p_schema)) + , _connection (&p_connection) + { } + + std::ostream& driver_t::print(std::ostream& os) const + { return _impl.schema->print(os); } + + ::cppmariadb::connection* driver_t::connection() const + { return _connection; } + + void driver_t::connection(::cppmariadb::connection * p_connection) + { _connection = p_connection; } + + template + void driver_t::set_filter_inclusive(T_args&&... args) + { _filter.set_inclusive(_impl.schema, std::forward(args)...); } + + template + void driver_t::set_filter_exclusive(T_args&&... args) + { _filter.set_exclusive(_impl.schema, std::forward(args)...); } + + void driver_t::clear_filter() + { _filter.clear(); } + + auto& driver_t::impl() const + { return _impl; } + +} } diff --git a/include/cpphibernate/driver/mariadb/helper.h b/include/cpphibernate/driver/mariadb/helper.h index 322dbe8..51c1c5a 100644 --- a/include/cpphibernate/driver/mariadb/helper.h +++ b/include/cpphibernate/driver/mariadb/helper.h @@ -1,6 +1,9 @@ #pragma once -#include -#include -#include -#include \ No newline at end of file +#include "helper/key_properties.h" +#include "helper/nullable_helper.h" +#include "helper/type_properties.h" + +#include "helper/key_properties.inl" +#include "helper/nullable_helper.inl" +#include "helper/type_properties.inl" diff --git a/include/cpphibernate/driver/mariadb/helper/container_helper.h b/include/cpphibernate/driver/mariadb/helper/container_helper.h new file mode 100644 index 0000000..e68b9d1 --- /dev/null +++ b/include/cpphibernate/driver/mariadb/helper/container_helper.h @@ -0,0 +1,27 @@ +#pragma once + +namespace cpphibernate { +namespace mariadb { + + /** + * @brief Helper class to manage operations on nullable types. + */ + template + struct container_helper + { + using container_type = T_container; + using value_type = typename container_type::value_type; + + /** + * @brief Emplace a new dataset in the container. + */ + template + static inline value_type& emplace(container_type& container, X_args&&... args) = delete; + + /** + * @brief Clear the whole container. + */ + static inline void clear(container_type& container) = delete; + }; + +} } diff --git a/include/cpphibernate/driver/mariadb/helper/container_helper.inl b/include/cpphibernate/driver/mariadb/helper/container_helper.inl new file mode 100644 index 0000000..22f1cc5 --- /dev/null +++ b/include/cpphibernate/driver/mariadb/helper/container_helper.inl @@ -0,0 +1,43 @@ +#pragma once + +#include +#include + +#include "container_helper.h" + +namespace cpphibernate { +namespace mariadb { + + /* container_helper */ + + template + struct container_helper< + T_container, + mp::enable_if_t< + mp::is_specialization_of_v, std::list> + || mp::is_specialization_of_v, std::vector> + >> + { + using container_type = T_container; + using value_type = typename container_type::value_type; + + /** + * @brief Emplace a new dataset in the container. + */ + template + static inline value_type& emplace(container_type& container, X_args&&... args) + { + container.emplace_back(std::forward(args)...); + return container.back(); + } + + /** + * @brief Clear the whole container. + */ + static inline void clear(container_type& container) + { + container.clear(); + } + }; + +} } diff --git a/include/cpphibernate/driver/mariadb/helper/context.h b/include/cpphibernate/driver/mariadb/helper/context.h deleted file mode 100644 index 1efb68a..0000000 --- a/include/cpphibernate/driver/mariadb/helper/context.h +++ /dev/null @@ -1,218 +0,0 @@ -#pragma once - -#include -#include -#include -#include -#include -#include -#include - -beg_namespace_cpphibernate_driver_mariadb -{ - - /* base_context */ - - struct base_context - { - const schema_t& schema; - ::cppmariadb::connection& connection; - - inline base_context( - const schema_t& p_schema, - ::cppmariadb::connection& p_connection) - : schema (p_schema) - , connection(p_connection) - { } - }; - - - /* init_context */ - - struct init_context - : public base_context - { - bool recreate; - - inline init_context( - const schema_t& p_schema, - ::cppmariadb::connection& p_connection, - bool p_recreate) - : base_context (p_schema, p_connection) - , recreate (p_recreate) - { } - }; - - namespace __impl - { - struct change_context_impl - { - template - constexpr decltype(auto) operator()(const T_context& context, T_data& data) const - { - auto new_context = context; - new_context.set(data); - return new_context; - } - }; - } - - constexpr decltype(auto) change_context = __impl::change_context_impl { }; - - /* data_context */ - - struct data_context - : public base_context - { - private: - friend __impl::change_context_impl; - - mutable size_t _dataset_id; - mutable void* _dataset; - mutable const table_t* _table; - - protected: - template - inline void* set(T_dataset& dataset, size_t dataset_id = 0) const; - - public: - template - inline decltype(auto) get() const; - - inline data_context( - const schema_t& p_schema, - ::cppmariadb::connection& p_connection) - : base_context (p_schema, p_connection) - , _dataset_id (0) - , _dataset (nullptr) - , _table (nullptr) - { } - - template - inline data_context( - T_data& p_data, - const schema_t& p_schema, - ::cppmariadb::connection& p_connection) - : base_context (p_schema, p_connection) - , _dataset_id (misc::get_type_id(hana::type_c>)) - , _dataset (&p_data) - , _table (nullptr) - { } - }; - - /* filter_context */ - - struct filter_context - : public data_context - { - const filter_t& filter; - - inline filter_context( - const schema_t& p_schema, - ::cppmariadb::connection& p_connection, - const filter_t& p_filter) - : data_context (p_schema, p_connection) - , filter (p_filter) - { } - - template - inline filter_context( - T_data& p_data, - const schema_t& p_schema, - ::cppmariadb::connection& p_connection, - const filter_t& p_filter) - : data_context (p_data, p_schema, p_connection) - , filter (p_filter) - { } - }; - - /* create_update_context */ - - struct create_update_context - : public filter_context - { - bool is_update; - const table_t* derived_table; - const field_t* owner_field; - std::string owner_key; - ssize_t index; - - template - inline create_update_context( - T_data& p_data, - const schema_t& p_schema, - ::cppmariadb::connection& p_connection, - const filter_t& p_filter, - bool p_is_update) - : filter_context(p_data, p_schema, p_connection, p_filter) - , is_update (p_is_update) - , derived_table (nullptr) - , owner_field (nullptr) - , index (-1) - { } - }; - - /* read_context */ - - struct read_context - : public filter_context - { - protected: - size_t base_dataset_id; - - public: - bool is_dynamic; - std::string where; - std::string limit; - std::string order_by; - - protected: - inline read_context( - const schema_t& p_schema, - ::cppmariadb::connection& p_connection, - const filter_t& p_filter) - : filter_context (p_schema, p_connection, p_filter) - , is_dynamic (false) - , base_dataset_id (0) - { } - - public: - virtual ~read_context() = default; - - template - inline T_dataset& emplace(const table_t* table = nullptr) const; - - void emplace() const - { emplace_intern(nullptr, 0); } - - void finish() const - { finish_intern(); } - - private: - virtual void* emplace_intern(void* data, size_t dataset_id) const = 0; - virtual void finish_intern () const = 0; - }; - - using read_context_ptr = std::unique_ptr; - - /* destroy_context */ - - struct destroy_context - : public data_context - { - std::string where; - - template - inline destroy_context( - T_data& p_data, - const schema_t& p_schema, - ::cppmariadb::connection& p_connection) - : data_context( - p_data, - p_schema, - p_connection) - { } - }; - -} -end_namespace_cpphibernate_driver_mariadb \ No newline at end of file diff --git a/include/cpphibernate/driver/mariadb/helper/context.inl b/include/cpphibernate/driver/mariadb/helper/context.inl deleted file mode 100644 index cbdb675..0000000 --- a/include/cpphibernate/driver/mariadb/helper/context.inl +++ /dev/null @@ -1,91 +0,0 @@ -#pragma once - -#include - -beg_namespace_cpphibernate_driver_mariadb -{ - - /* data_context */ - - template - inline void* data_context - ::set(T_dataset& dataset, size_t dataset_id) const - { - using dataset_type = mp::decay_t; - - _table = nullptr; - _dataset = &dataset; - _dataset_id = (dataset_id == 0) - ? misc::get_type_id(hana::type_c) - : dataset_id; - - return _dataset; - } - - template - inline decltype(auto) data_context - ::get() const - { - using dataset_type = mp::decay_t; - - if (!_dataset) - throw misc::hibernate_exception("no data assigned!"); - - auto dataset_id = misc::get_type_id(hana::type_c); - if (dataset_id != _dataset_id) - { - /* check table */ - if (!_table) - _table = &schema.table(_dataset_id); - else if (_table->dataset_id != _dataset_id) - throw misc::hibernate_exception("invalid table!"); - - auto table = _table; - while(table && table->dataset_id != dataset_id) - table = table->base_table; - - if (!table) - { - throw misc::hibernate_exception(utl::type_helper::name() + - " is not a derived type of dataset with id " + std::to_string(_dataset_id)); - } - } - return *static_cast(_dataset); - } - - /* read_context */ - - template - T_dataset& read_context - ::emplace(const table_t* table) const - { - using dataset_type = mp::decay_t; - - // check table - auto dataset_id = misc::get_type_id(hana::type_c); - if (!table) - table = &schema.table(dataset_id); - else if (table->dataset_id != dataset_id) - throw misc::hibernate_exception("wrong table passed!"); - - // check base - auto tbl = table; - while (tbl && tbl->dataset_id != base_dataset_id) - tbl = tbl->base_table; - if (!tbl) - { - throw misc::hibernate_exception(utl::type_helper::name() + - " is not a derived type of dataset with id " + std::to_string(base_dataset_id)); - } - - // create dataset - auto ptr = std::make_unique(); - auto data = emplace_intern(ptr.get(), dataset_id); - if (!data) - throw misc::hibernate_exception("unable to store created dataset in context!"); - ptr.release(); - return *static_cast(data); - } - -} -end_namespace_cpphibernate_driver_mariadb \ No newline at end of file diff --git a/include/cpphibernate/driver/mariadb/helper/key_properties.h b/include/cpphibernate/driver/mariadb/helper/key_properties.h index 6a800ea..05f37a0 100644 --- a/include/cpphibernate/driver/mariadb/helper/key_properties.h +++ b/include/cpphibernate/driver/mariadb/helper/key_properties.h @@ -1,42 +1,39 @@ #pragma once -#include -#include -#include -beg_namespace_cpphibernate_driver_mariadb -{ - /* key_properties */ +namespace cpphibernate { +namespace mariadb { + /** + * @brief Key type properties for the passed type. + * + * @tparam T_key Type to get key properties for. + */ template - struct key_properties; - - template<> - struct key_properties - { - using auto_generated = mp::c_false_t; - using key_type = uuid; - - static constexpr decltype(auto) create_table_argument = ""; - static constexpr decltype(auto) create_key_query = "SELECT Uuid()"; - - static bool is_default(const key_type& key) - { return key == key_type(); } - }; - - template - struct key_properties>> + struct key_properties { - using auto_generated = mp::c_true_t; - using key_type = T_key; - - static constexpr decltype(auto) create_table_argument = "AUTO_INCREMENT"; - static constexpr decltype(auto) create_key_query = ""; - - static bool is_default(const key_type& key) - { return key == key_type(); } + using key_type = T_key; + + /** + * @brief Check if the passed key value is the default value. + */ + static bool is_default(const key_type& key) = delete; + + /** + * @brief Returns true if the key type is auto generated, false otherwise. + */ + static constexpr bool is_auto_generated() = delete; + + /** + * @brief Returns extra arguments to use for creating the table. + */ + static constexpr const char * create_table_argument() = delete; + + /** + * @brief Return the query to create a new key value. + */ + static constexpr const char * create_key_query() = delete; }; -} -end_namespace_cpphibernate_driver_mariadb \ No newline at end of file +} } diff --git a/include/cpphibernate/driver/mariadb/helper/key_properties.inl b/include/cpphibernate/driver/mariadb/helper/key_properties.inl new file mode 100644 index 0000000..60a8abb --- /dev/null +++ b/include/cpphibernate/driver/mariadb/helper/key_properties.inl @@ -0,0 +1,48 @@ +#pragma once + +#include "key_properties.h" + +namespace cpphibernate { +namespace mariadb { + + /* key_properties */ + + template<> + struct key_properties + { + using key_type = uuid; + + static bool is_default(const key_type& key) + { return key == key_type(); } + + static constexpr bool is_auto_generated() + { return false; } + + static constexpr const char * create_table_argument() + { return nullptr; } + + static constexpr const char * create_key_query() + { return "SELECT Uuid()"; } + }; + + template + struct key_properties< + T_key, + mp::enable_if_t>>> + { + using key_type = mp::decay_t; + + static bool is_default(const key_type& key) + { return key == key_type(); } + + static constexpr bool is_auto_generated() + { return true; } + + static constexpr const char * create_table_argument() + { return "AUTO_INCREMENT"; } + + static constexpr const char * create_key_query() + { return nullptr; } + }; + +} } diff --git a/include/cpphibernate/driver/mariadb/helper/nullable_helper.h b/include/cpphibernate/driver/mariadb/helper/nullable_helper.h new file mode 100644 index 0000000..269b93b --- /dev/null +++ b/include/cpphibernate/driver/mariadb/helper/nullable_helper.h @@ -0,0 +1,31 @@ +#pragma once + +namespace cpphibernate { +namespace mariadb { + + /** + * @brief Helper class to manage operations on nullable types. + */ + template + struct nullable_helper + { + using nullable_type = T_nullable; + using value_type = real_dataset_t; + + /** + * @brief Get a pointer to the stored value of the nullable object. + */ + static value_type* get(const nullable_type&) = delete; + + /** + * @brief Set the new value of the nullable object. + */ + static value_type& set(nullable_type&, const value_type&) = delete; + + /** + * @brief Destroy the stored value of a nullable objec (clear the nullable object). + */ + static void clear(nullable_type&) = delete; + }; + +} } diff --git a/include/cpphibernate/driver/mariadb/helper/nullable_helper.inl b/include/cpphibernate/driver/mariadb/helper/nullable_helper.inl new file mode 100644 index 0000000..fcb7c1e --- /dev/null +++ b/include/cpphibernate/driver/mariadb/helper/nullable_helper.inl @@ -0,0 +1,73 @@ +#pragma once + +#include "nullable_helper.h" + +namespace cpphibernate { +namespace mariadb { + + + /* nullable_helper - cppcore::nullable */ + + template + struct nullable_helper, void> + { + using nullable_type = cppcore::nullable; + using value_type = T_value; + + static inline value_type* get(nullable_type& x) + { return x.has_value() ? &x.value() : nullptr; } + + static inline const value_type* get(const nullable_type& x) + { return x.has_value() ? &x.value() : nullptr; } + + static inline value_type& set(nullable_type& x, const value_type* value) + { return *(x = *value); } + + static inline value_type& set(nullable_type& x, const value_type& value) + { return *(x = value); } + + static inline value_type& set(nullable_type& x, value_type&& value) + { return *(x = std::move(value)); } + + static void clear(nullable_type& x) + { x.reset(); } + }; + + /* nullable_helper - std::unique_ptr/std::shared_ptr */ + + template + struct nullable_helper< + T, + mp::enable_if_t< + mp::is_specialization_of_v, std::unique_ptr> + || mp::is_specialization_of_v, std::shared_ptr>>> + { + using nullable_type = T; + using value_type = typename nullable_type::element_type; + + static inline value_type* get(const nullable_type& x) + { return x.get(); } + + static inline value_type& set(nullable_type& x, value_type* value) + { + x.reset(value); + return *x; + } + + static inline value_type& set(nullable_type& x, const value_type& value) + { + x.reset(new value_type(value)); + return *x; + } + + static inline value_type& set(nullable_type& x, value_type&& value) + { + x.reset(new value_type(std::move(value))); + return *x; + } + + static void clear(nullable_type& x) + { return x.reset(); } + }; + +} } diff --git a/include/cpphibernate/driver/mariadb/helper/transaction_lock.h b/include/cpphibernate/driver/mariadb/helper/transaction_lock.h deleted file mode 100644 index 750891f..0000000 --- a/include/cpphibernate/driver/mariadb/helper/transaction_lock.h +++ /dev/null @@ -1,93 +0,0 @@ -#pragma once - -#include -#include -#include -#include -#include - -beg_namespace_cpphibernate_driver_mariadb -{ - - struct transaction_lock final - { - public: - inline transaction_lock(::cppmariadb::connection& con) - { begin_transaction(*this, con); } - - inline ~transaction_lock() - { close_transaction(*this); } - - inline bool commit() - { return commit_transaction(*this); } - - private: - using transaction_ptr_type = std::unique_ptr<::cppmariadb::transaction>; - -#ifdef CPPHIBERNATE_DEBUG -# define debug_log(str) cpphibernate_debug_log( \ - "transaction (id=" << std::setw(8) << std::setfill(' ') << lock.id << \ - ", counter=" << std::setw(2) << std::setfill(' ') << counter << ") " str) - struct counter { }; - size_t id { utl::unique_counter::next() }; -#else -# define debug_log(str) do { } while(0) -#endif - - static size_t& ref_counter() - { - static size_t value = 0; - return value; - } - - static transaction_ptr_type& ref_transaction_ptr() - { - static transaction_ptr_type value; - return value; - } - - static void begin_transaction(const transaction_lock& lock, ::cppmariadb::connection& con) - { - auto& counter = ref_counter(); - ++counter; - debug_log("+++"); - if (counter == 1) - { - debug_log("begin"); - ref_transaction_ptr().reset(new ::cppmariadb::transaction(con)); - } - } - - static bool commit_transaction(const transaction_lock& lock) - { - auto& counter = ref_counter(); - if (counter == 1) - { - debug_log("commit"); - ref_transaction_ptr()->commit(); - return true; - } - return false; - } - - static void close_transaction(const transaction_lock& lock) - { - auto& counter = ref_counter(); - debug_log("---"); - if (counter <= 1) - { - debug_log("close"); - counter = 0; - ref_transaction_ptr().reset(); - } - else - { - --counter; - } - } - }; - -#undef debug_log - -} -end_namespace_cpphibernate_driver_mariadb \ No newline at end of file diff --git a/include/cpphibernate/driver/mariadb/helper/type_properties.h b/include/cpphibernate/driver/mariadb/helper/type_properties.h index ddb211a..af741ff 100644 --- a/include/cpphibernate/driver/mariadb/helper/type_properties.h +++ b/include/cpphibernate/driver/mariadb/helper/type_properties.h @@ -1,583 +1,52 @@ #pragma once -#include -#include -#include +#include -#include -#include -#include -#include - -beg_namespace_cpphibernate_driver_mariadb -{ - - /* value_t */ - - using value_t = utl::nullable; - - /* type_properties */ +namespace cpphibernate { +namespace mariadb { + /** + * @brief Type properties for the passed type. + * + * @tparam T Type to get properties for. + */ template struct type_properties { - static constexpr void type () = delete; - static T convert_to (const value_t&) = delete; - static value_t convert_from (const T&) = delete; - static constexpr const char* convert_to_open () = delete; - static constexpr const char* convert_to_close () = delete; - static constexpr const char* convert_from_open () = delete; - static constexpr const char* convert_from_close() = delete; - }; - - template<> - struct type_properties - { - static constexpr decltype(auto) type() - { return "BOOLEAN"; } - - static inline bool convert_to(const value_t& value) - { return utl::from_string(*value); } - - static inline value_t convert_from(const bool& value) - { return utl::to_string(static_cast(value)); } - - static constexpr const char* convert_to_open() - { return nullptr; } - - static constexpr const char* convert_to_close() - { return nullptr; } - - static constexpr const char* convert_from_open() - { return nullptr; } - - static constexpr const char* convert_from_close() - { return nullptr; } - }; - - template<> - struct type_properties - { - static constexpr decltype(auto) type() - { return "TINYINT UNSIGNED"; } - - static inline uint8_t convert_to(const value_t& value) - { return static_cast(utl::from_string(*value)); } - - static inline value_t convert_from(const uint8_t& value) - { return utl::to_string(static_cast(value)); } - - static constexpr const char* convert_to_open() - { return nullptr; } - - static constexpr const char* convert_to_close() - { return nullptr; } - - static constexpr const char* convert_from_open() - { return nullptr; } - - static constexpr const char* convert_from_close() - { return nullptr; } - }; - - template<> - struct type_properties - { - static constexpr decltype(auto) type() - { return "TINYINT"; } - - static inline int8_t convert_to(const value_t& value) - { return static_cast(utl::from_string(*value)); } - - static inline value_t convert_from(const int8_t& value) - { return utl::to_string(static_cast(value)); } - - static constexpr const char* convert_to_open() - { return nullptr; } - - static constexpr const char* convert_to_close() - { return nullptr; } - - static constexpr const char* convert_from_open() - { return nullptr; } - - static constexpr const char* convert_from_close() - { return nullptr; } - }; - - template<> - struct type_properties - { - static constexpr decltype(auto) type() - { return "SMALLINT UNSIGNED"; } - - static inline uint16_t convert_to(const value_t& value) - { return utl::from_string(*value); } - - static inline value_t convert_from(const uint16_t& value) - { return utl::to_string(value); } - - static constexpr const char* convert_to_open() - { return nullptr; } - - static constexpr const char* convert_to_close() - { return nullptr; } - - static constexpr const char* convert_from_open() - { return nullptr; } - - static constexpr const char* convert_from_close() - { return nullptr; } - }; - - template<> - struct type_properties - { - static constexpr decltype(auto) type() - { return "SMALLINT"; } - - static inline int16_t convert_to(const value_t& value) - { return utl::from_string(*value); } - - static inline value_t convert_from(const int16_t& value) - { return utl::to_string(value); } - - static constexpr const char* convert_to_open() - { return nullptr; } - - static constexpr const char* convert_to_close() - { return nullptr; } - - static constexpr const char* convert_from_open() - { return nullptr; } - - static constexpr const char* convert_from_close() - { return nullptr; } - }; - - template<> - struct type_properties - { - static constexpr decltype(auto) type() - { return "INT UNSIGNED"; } - - static inline uint32_t convert_to(const value_t& value) - { return utl::from_string(*value); } - - static inline value_t convert_from(const uint32_t& value) - { return utl::to_string(value); } - - static constexpr const char* convert_to_open() - { return nullptr; } - - static constexpr const char* convert_to_close() - { return nullptr; } - - static constexpr const char* convert_from_open() - { return nullptr; } - - static constexpr const char* convert_from_close() - { return nullptr; } - }; - - template<> - struct type_properties - { - static constexpr decltype(auto) type() - { return "INT"; } - - static inline int32_t convert_to(const value_t& value) - { return utl::from_string(*value); } - - static inline value_t convert_from(const int32_t& value) - { return utl::to_string(value); } - - static constexpr const char* convert_to_open() - { return nullptr; } - - static constexpr const char* convert_to_close() - { return nullptr; } - - static constexpr const char* convert_from_open() - { return nullptr; } - - static constexpr const char* convert_from_close() - { return nullptr; } - }; - - template<> - struct type_properties - { - static constexpr decltype(auto) type() - { return "BIGINT UNSIGNED"; } - - static inline uint64_t convert_to(const value_t& value) - { return utl::from_string(*value); } - - static inline value_t convert_from(const uint64_t& value) - { return utl::to_string(value); } - - static constexpr const char* convert_to_open() - { return nullptr; } - - static constexpr const char* convert_to_close() - { return nullptr; } - - static constexpr const char* convert_from_open() - { return nullptr; } - - static constexpr const char* convert_from_close() - { return nullptr; } - }; - - template<> - struct type_properties - { - static constexpr decltype(auto) type() - { return "BIGINT"; } - - static inline int64_t convert_to(const value_t& value) - { return utl::from_string(*value); } - - static inline value_t convert_from(const int64_t& value) - { return utl::to_string(value); } - - static constexpr const char* convert_to_open() - { return nullptr; } - - static constexpr const char* convert_to_close() - { return nullptr; } - - static constexpr const char* convert_from_open() - { return nullptr; } - - static constexpr const char* convert_from_close() - { return nullptr; } - }; - - template<> - struct type_properties - { - static constexpr decltype(auto) type() - { return "FLOAT"; } - - static inline float convert_to(const value_t& value) - { return utl::from_string(*value); } - - static inline value_t convert_from(const float& value) - { return utl::to_string(value); } - - static constexpr const char* convert_to_open() - { return nullptr; } - - static constexpr const char* convert_to_close() - { return nullptr; } - - static constexpr const char* convert_from_open() - { return nullptr; } - - static constexpr const char* convert_from_close() - { return nullptr; } - }; - - template<> - struct type_properties - { - static constexpr decltype(auto) type() - { return "DOUBLE"; } - - static inline double convert_to(const value_t& value) - { return utl::from_string(*value); } - - static inline value_t convert_from(const double& value) - { return utl::to_string(value); } - - static constexpr const char* convert_to_open() - { return nullptr; } - - static constexpr const char* convert_to_close() - { return nullptr; } - - static constexpr const char* convert_from_open() - { return nullptr; } - - static constexpr const char* convert_from_close() - { return nullptr; } - }; - - template<> - struct type_properties - { - static constexpr decltype(auto) type() - { return "BINARY(16)"; } - - static inline uuid convert_to(const value_t& value) - { return utl::from_string(*value); } - - static inline value_t convert_from(const uuid& value) - { return utl::to_string(value); } - - static constexpr const char* convert_to_open() - { return "UuidToBin("; } - - static constexpr const char* convert_to_close() - { return ")"; } - - static constexpr const char* convert_from_open() - { return "BinToUuid("; } - - static constexpr const char* convert_from_close() - { return ")"; } - }; - - template<> - struct type_properties - { - static constexpr decltype(auto) type() - { return "BIGINT"; } - - static inline timestamp convert_to(const value_t& value) - { return timestamp(utl::from_string(*value)); } - - static inline value_t convert_from(const timestamp& value) - { return utl::to_string(static_cast(value)); } - - static constexpr const char* convert_to_open() - { return nullptr; } - - static constexpr const char* convert_to_close() - { return nullptr; } - - static constexpr const char* convert_from_open() - { return nullptr; } - - static constexpr const char* convert_from_close() - { return nullptr; } - }; - - template<> - struct type_properties - { - static constexpr decltype(auto) type() - { return "VARCHAR(100)"; } - - static inline std::string convert_to(const value_t& value) - { return *value; } - - static inline value_t convert_from(const std::string& value) - { return value; } - - static constexpr const char* convert_to_open() - { return nullptr; } - - static constexpr const char* convert_to_close() - { return nullptr; } - - static constexpr const char* convert_from_open() - { return nullptr; } - - static constexpr const char* convert_from_close() - { return nullptr; } - }; - - template - struct type_properties, void> - { - static inline std::string make_type() - { return std::string("VARCHAR(") + utl::to_string(N) + ")"; } - - static inline decltype(auto) type() - { - static const std::string v = make_type(); - return v; - } - - static inline std::string convert_to(const value_t& value) - { return *value; } - - static inline value_t convert_from(const std::string& value) - { return value; } - - static constexpr const char* convert_to_open() - { return nullptr; } - - static constexpr const char* convert_to_close() - { return nullptr; } - - static constexpr const char* convert_from_open() - { return nullptr; } - - static constexpr const char* convert_from_close() - { return nullptr; } - }; - - template - struct type_properties>>> - { - using nullable_type = T; - using nullable_helper_type = misc::nullable_helper; - using value_type = typename nullable_helper_type::value_type; - using value_type_props = type_properties; - - static constexpr decltype(auto) type() - { return value_type_props::type(); } - - static inline nullable_type convert_to(const value_t& value) - { - nullable_type ret; - if (value.has_value()) - nullable_helper_type::set(ret, value_type_props::convert_to(value)); - return ret; - } - - static inline value_t convert_from(const nullable_type& value) - { - value_t ret; - auto v = nullable_helper_type::get(value); - if (v) - ret = value_type_props::convert_from(*v); - return ret; - } - - static constexpr const char* convert_to_open() - { return nullptr; } - - static constexpr const char* convert_to_close() - { return nullptr; } - - static constexpr const char* convert_from_open() - { return nullptr; } - - static constexpr const char* convert_from_close() - { return nullptr; } - }; - - template - struct type_properties>>> - { - using enum_type = T; - using base_type = typename std::underlying_type::type; - - static std::string make_type() - { - std::ostringstream os; - os << "ENUM ( "; - auto e = enum_type::first; - while (e <= enum_type::last) - { - if (e != enum_type::first) - os << ", "; - os << "'" << utl::enum_conversion::to_string(e, false) << "'"; - e = static_cast(static_cast(e) + 1); - } - os << " )"; - return os.str(); - } - - static inline decltype(auto) type() - { - static const std::string v = make_type(); - return v; - } - - static inline enum_type convert_to(const value_t& value) - { - enum_type ret; - if (!utl::enum_conversion::try_to_enum(*value, ret, false)) - throw misc::hibernate_exception("unable to convert enum value!"); - return ret; - } - - static inline value_t convert_from(const enum_type& value) - { return utl::enum_conversion::to_string(value, false); } - - static constexpr const char* convert_to_open() - { return nullptr; } - - static constexpr const char* convert_to_close() - { return nullptr; } - - static constexpr const char* convert_from_open() - { return nullptr; } - - static constexpr const char* convert_from_close() - { return nullptr; } - }; - - template - struct type_properties, utl::flags>>> - { - using flags_type = T; - using enum_type = typename flags_type::enum_type; - using base_type = typename std::underlying_type::type; - - static inline std::string make_type() - { - std::ostringstream os; - os << "SET ( "; - auto e = enum_type::first; - while (e <= enum_type::last) - { - if (e != enum_type::first) - os << ", "; - os << "'" << utl::to_string(e) << "'"; - e = static_cast(static_cast(e) + 1); - } - os << " )"; - return os.str(); - } - - static inline decltype(auto) type() - { - static const std::string v = make_type(); - return v; - } + /** + * @brief Get the mariadb type. + */ + static constexpr const char * type() = delete; - static inline flags_type convert_to(const value_t& value) - { - auto s = *value; - auto c = s.c_str(); - auto e = c + s.size(); - auto p = c; - flags_type ret; - while (c <= e) - { - if (c == e || *c == ',') - { - if (c - p > 0) - ret.set(utl::enum_conversion::to_enum(std::string(p, static_cast(c - p)), true)); - p = c + 1; - } - ++c; - } - return ret; - } + /** + * @brief Convert a value from the database to its actual type. + */ + static T convert_to(const value_t&) = delete; - static inline value_t convert_from(const flags_type& value) - { - std::ostringstream os; - bool first = true; - for (auto e : value) - { - if (first) first = false; - else os << ","; - os << utl::enum_conversion::to_string(e, false); - } - return os.str(); - } + /** + * @brief Convert the actual value to a database value. + */ + static value_t convert_from(const T&) = delete; - static constexpr const char* convert_to_open() - { return nullptr; } + /** + * @brief Get the string to start the "convert to" operation. + */ + static constexpr const char * convert_to_open() = delete; - static constexpr const char* convert_to_close() - { return nullptr; } + /** + * @brief Get the string to end the "convert to" operation. + */ + static constexpr const char * convert_to_close() = delete; - static constexpr const char* convert_from_open() - { return nullptr; } + /** + * @brief Get the string to start the "convert from" operation. + */ + static constexpr const char * convert_from_open() = delete; - static constexpr const char* convert_from_close() - { return nullptr; } + /** + * @brief Get the string to end the "convert from" operation. + */ + static constexpr const char * convert_from_close() = delete; }; -} -end_namespace_cpphibernate_driver_mariadb \ No newline at end of file +} } diff --git a/include/cpphibernate/driver/mariadb/helper/type_properties.inl b/include/cpphibernate/driver/mariadb/helper/type_properties.inl new file mode 100644 index 0000000..bd6fb94 --- /dev/null +++ b/include/cpphibernate/driver/mariadb/helper/type_properties.inl @@ -0,0 +1,559 @@ +#pragma once + +#include "type_properties.h" + +namespace cpphibernate { +namespace mariadb { + + template<> + struct type_properties + { + static constexpr decltype(auto) type() + { return "BOOLEAN"; } + + static inline bool convert_to(const value_t& value) + { return cppcore::from_string(*value); } + + static inline value_t convert_from(const bool& value) + { return cppcore::to_string(static_cast(value)); } + + static constexpr const char* convert_to_open() + { return nullptr; } + + static constexpr const char* convert_to_close() + { return nullptr; } + + static constexpr const char* convert_from_open() + { return nullptr; } + + static constexpr const char* convert_from_close() + { return nullptr; } + }; + + template<> + struct type_properties + { + static constexpr decltype(auto) type() + { return "TINYINT UNSIGNED"; } + + static inline uint8_t convert_to(const value_t& value) + { return static_cast(cppcore::from_string(*value)); } + + static inline value_t convert_from(const uint8_t& value) + { return cppcore::to_string(static_cast(value)); } + + static constexpr const char* convert_to_open() + { return nullptr; } + + static constexpr const char* convert_to_close() + { return nullptr; } + + static constexpr const char* convert_from_open() + { return nullptr; } + + static constexpr const char* convert_from_close() + { return nullptr; } + }; + + template<> + struct type_properties + { + static constexpr decltype(auto) type() + { return "TINYINT"; } + + static inline int8_t convert_to(const value_t& value) + { return static_cast(cppcore::from_string(*value)); } + + static inline value_t convert_from(const int8_t& value) + { return cppcore::to_string(static_cast(value)); } + + static constexpr const char* convert_to_open() + { return nullptr; } + + static constexpr const char* convert_to_close() + { return nullptr; } + + static constexpr const char* convert_from_open() + { return nullptr; } + + static constexpr const char* convert_from_close() + { return nullptr; } + }; + + template<> + struct type_properties + { + static constexpr decltype(auto) type() + { return "SMALLINT UNSIGNED"; } + + static inline uint16_t convert_to(const value_t& value) + { return cppcore::from_string(*value); } + + static inline value_t convert_from(const uint16_t& value) + { return cppcore::to_string(value); } + + static constexpr const char* convert_to_open() + { return nullptr; } + + static constexpr const char* convert_to_close() + { return nullptr; } + + static constexpr const char* convert_from_open() + { return nullptr; } + + static constexpr const char* convert_from_close() + { return nullptr; } + }; + + template<> + struct type_properties + { + static constexpr decltype(auto) type() + { return "SMALLINT"; } + + static inline int16_t convert_to(const value_t& value) + { return cppcore::from_string(*value); } + + static inline value_t convert_from(const int16_t& value) + { return cppcore::to_string(value); } + + static constexpr const char* convert_to_open() + { return nullptr; } + + static constexpr const char* convert_to_close() + { return nullptr; } + + static constexpr const char* convert_from_open() + { return nullptr; } + + static constexpr const char* convert_from_close() + { return nullptr; } + }; + + template<> + struct type_properties + { + static constexpr decltype(auto) type() + { return "INT UNSIGNED"; } + + static inline uint32_t convert_to(const value_t& value) + { return cppcore::from_string(*value); } + + static inline value_t convert_from(const uint32_t& value) + { return cppcore::to_string(value); } + + static constexpr const char* convert_to_open() + { return nullptr; } + + static constexpr const char* convert_to_close() + { return nullptr; } + + static constexpr const char* convert_from_open() + { return nullptr; } + + static constexpr const char* convert_from_close() + { return nullptr; } + }; + + template<> + struct type_properties + { + static constexpr decltype(auto) type() + { return "INT"; } + + static inline int32_t convert_to(const value_t& value) + { return cppcore::from_string(*value); } + + static inline value_t convert_from(const int32_t& value) + { return cppcore::to_string(value); } + + static constexpr const char* convert_to_open() + { return nullptr; } + + static constexpr const char* convert_to_close() + { return nullptr; } + + static constexpr const char* convert_from_open() + { return nullptr; } + + static constexpr const char* convert_from_close() + { return nullptr; } + }; + + template<> + struct type_properties + { + static constexpr decltype(auto) type() + { return "BIGINT UNSIGNED"; } + + static inline uint64_t convert_to(const value_t& value) + { return cppcore::from_string(*value); } + + static inline value_t convert_from(const uint64_t& value) + { return cppcore::to_string(value); } + + static constexpr const char* convert_to_open() + { return nullptr; } + + static constexpr const char* convert_to_close() + { return nullptr; } + + static constexpr const char* convert_from_open() + { return nullptr; } + + static constexpr const char* convert_from_close() + { return nullptr; } + }; + + template<> + struct type_properties + { + static constexpr decltype(auto) type() + { return "BIGINT"; } + + static inline int64_t convert_to(const value_t& value) + { return cppcore::from_string(*value); } + + static inline value_t convert_from(const int64_t& value) + { return cppcore::to_string(value); } + + static constexpr const char* convert_to_open() + { return nullptr; } + + static constexpr const char* convert_to_close() + { return nullptr; } + + static constexpr const char* convert_from_open() + { return nullptr; } + + static constexpr const char* convert_from_close() + { return nullptr; } + }; + + template<> + struct type_properties + { + static constexpr decltype(auto) type() + { return "FLOAT"; } + + static inline float convert_to(const value_t& value) + { return cppcore::from_string(*value); } + + static inline value_t convert_from(const float& value) + { return cppcore::to_string(value); } + + static constexpr const char* convert_to_open() + { return nullptr; } + + static constexpr const char* convert_to_close() + { return nullptr; } + + static constexpr const char* convert_from_open() + { return nullptr; } + + static constexpr const char* convert_from_close() + { return nullptr; } + }; + + template<> + struct type_properties + { + static constexpr decltype(auto) type() + { return "DOUBLE"; } + + static inline double convert_to(const value_t& value) + { return cppcore::from_string(*value); } + + static inline value_t convert_from(const double& value) + { return cppcore::to_string(value); } + + static constexpr const char* convert_to_open() + { return nullptr; } + + static constexpr const char* convert_to_close() + { return nullptr; } + + static constexpr const char* convert_from_open() + { return nullptr; } + + static constexpr const char* convert_from_close() + { return nullptr; } + }; + + template<> + struct type_properties + { + static constexpr decltype(auto) type() + { return "BINARY(16)"; } + + static inline uuid convert_to(const value_t& value) + { return cppcore::from_string(*value); } + + static inline value_t convert_from(const uuid& value) + { return cppcore::to_string(value); } + + static constexpr const char* convert_to_open() + { return "UuidToBin("; } + + static constexpr const char* convert_to_close() + { return ")"; } + + static constexpr const char* convert_from_open() + { return "BinToUuid("; } + + static constexpr const char* convert_from_close() + { return ")"; } + }; + + template<> + struct type_properties + { + static constexpr decltype(auto) type() + { return "BIGINT"; } + + static inline timestamp convert_to(const value_t& value) + { return timestamp(cppcore::from_string(*value)); } + + static inline value_t convert_from(const timestamp& value) + { return cppcore::to_string(static_cast(value)); } + + static constexpr const char* convert_to_open() + { return nullptr; } + + static constexpr const char* convert_to_close() + { return nullptr; } + + static constexpr const char* convert_from_open() + { return nullptr; } + + static constexpr const char* convert_from_close() + { return nullptr; } + }; + + template<> + struct type_properties + { + static constexpr decltype(auto) type() + { return "VARCHAR(100)"; } + + static inline std::string convert_to(const value_t& value) + { return *value; } + + static inline value_t convert_from(const std::string& value) + { return value; } + + static constexpr const char* convert_to_open() + { return nullptr; } + + static constexpr const char* convert_to_close() + { return nullptr; } + + static constexpr const char* convert_from_open() + { return nullptr; } + + static constexpr const char* convert_from_close() + { return nullptr; } + }; + + template + struct type_properties, void> + { + static inline std::string make_type() + { return std::string("VARCHAR(") + cppcore::to_string(N) + ")"; } + + static inline decltype(auto) type() + { + static const std::string v = make_type(); + return v; + } + + static inline std::string convert_to(const value_t& value) + { return *value; } + + static inline value_t convert_from(const std::string& value) + { return value; } + + static constexpr const char* convert_to_open() + { return nullptr; } + + static constexpr const char* convert_to_close() + { return nullptr; } + + static constexpr const char* convert_from_open() + { return nullptr; } + + static constexpr const char* convert_from_close() + { return nullptr; } + }; + + template + struct type_properties>>> + { + using nullable_type = T; + using nullable_helper_type = nullable_helper; + using value_type = typename nullable_helper_type::value_type; + + static constexpr decltype(auto) nullable = nullable_helper_type { }; + static constexpr decltype(auto) value_props = type_properties { }; + + static constexpr decltype(auto) type() + { return value_props.type(); } + + static inline decltype(auto) convert_to(const value_t& value) + { + nullable_type ret; + if (value.has_value()) + nullable.set(ret, value_props.convert_to(value)); + return ret; + } + + static inline value_t convert_from(const nullable_type& value) + { + value_t ret; + auto v = nullable.get(value); + if (v) + ret = value_props.convert_from(*v); + return ret; + } + + static constexpr const char* convert_to_open() + { return value_props.convert_to_open(); } + + static constexpr const char* convert_to_close() + { return value_props.convert_to_close(); } + + static constexpr const char* convert_from_open() + { return value_props.convert_from_open(); } + + static constexpr const char* convert_from_close() + { return value_props.convert_from_close(); } + }; + + template + struct type_properties>>> + { + using enum_type = T; + using base_type = typename std::underlying_type::type; + + static std::string make_type() + { + std::ostringstream os; + os << "ENUM ( "; + auto e = enum_type::first; + while (e <= enum_type::last) + { + if (e != enum_type::first) + os << ", "; + os << "'" << cppcore::enum_conversion::to_string(e, false) << "'"; + e = static_cast(static_cast(e) + 1); + } + os << " )"; + return os.str(); + } + + static inline decltype(auto) type() + { + static const std::string v = make_type(); + return v; + } + + static inline enum_type convert_to(const value_t& value) + { + enum_type ret; + if (!cppcore::enum_conversion::try_to_enum(*value, ret, false)) + throw exception("unable to convert enum value!"); + return ret; + } + + static inline value_t convert_from(const enum_type& value) + { return cppcore::enum_conversion::to_string(value, false); } + + static constexpr const char* convert_to_open() + { return nullptr; } + + static constexpr const char* convert_to_close() + { return nullptr; } + + static constexpr const char* convert_from_open() + { return nullptr; } + + static constexpr const char* convert_from_close() + { return nullptr; } + }; + + template + struct type_properties, cppcore::flags>>> + { + using flags_type = T; + using enum_type = typename flags_type::enum_type; + using base_type = typename std::underlying_type::type; + + static inline std::string make_type() + { + std::ostringstream os; + os << "SET ( "; + auto e = enum_type::first; + while (e <= enum_type::last) + { + if (e != enum_type::first) + os << ", "; + os << "'" << cppcore::to_string(e) << "'"; + e = static_cast(static_cast(e) + 1); + } + os << " )"; + return os.str(); + } + + static inline decltype(auto) type() + { + static const std::string v = make_type(); + return v; + } + + static inline flags_type convert_to(const value_t& value) + { + auto s = *value; + auto c = s.c_str(); + auto e = c + s.size(); + auto p = c; + flags_type ret; + while (c <= e) + { + if (c == e || *c == ',') + { + if (c - p > 0) + ret.set(cppcore::enum_conversion::to_enum(std::string(p, static_cast(c - p)), true)); + p = c + 1; + } + ++c; + } + return ret; + } + + static inline value_t convert_from(const flags_type& value) + { + std::ostringstream os; + bool first = true; + for (auto e : value) + { + if (first) first = false; + else os << ","; + os << cppcore::enum_conversion::to_string(e, false); + } + return os.str(); + } + + static constexpr const char* convert_to_open() + { return nullptr; } + + static constexpr const char* convert_to_close() + { return nullptr; } + + static constexpr const char* convert_from_open() + { return nullptr; } + + static constexpr const char* convert_from_close() + { return nullptr; } + }; + +} } diff --git a/include/cpphibernate/driver/mariadb/impl.h b/include/cpphibernate/driver/mariadb/impl.h index 4843de5..8a90f64 100644 --- a/include/cpphibernate/driver/mariadb/impl.h +++ b/include/cpphibernate/driver/mariadb/impl.h @@ -1,10 +1,11 @@ #pragma once -#include -#include -#include -#include -#include -#include +#include "impl/create_update.h" +#include "impl/destroy.h" +#include "impl/driver_impl.h" +#include "impl/filter.h" -#include \ No newline at end of file +#include "impl/create_update.inl" +#include "impl/destroy.inl" +#include "impl/driver_impl.inl" +#include "impl/filter.inl" diff --git a/include/cpphibernate/driver/mariadb/impl/create_update.h b/include/cpphibernate/driver/mariadb/impl/create_update.h index 4b2f964..dec90a0 100644 --- a/include/cpphibernate/driver/mariadb/impl/create_update.h +++ b/include/cpphibernate/driver/mariadb/impl/create_update.h @@ -1,117 +1,14 @@ #pragma once -#include -#include -#include -#include -#include - -beg_namespace_cpphibernate_driver_mariadb -{ - - /* create_update_impl_t */ - +namespace cpphibernate { +namespace mariadb { + + /** + * @brief Helper class to select the right implementation of the create/update operation for the passed datatype. + * + * @tparam T_dataset Dataset type to select implementation for. + */ template - struct create_update_impl_t - { - using dataset_type = T_dataset; - - static inline value_t apply(const create_update_context& context, bool strict = true) - { - value_t ret; - - auto dataset_id = misc::get_type_id(hana::type_c); - auto& connection = context.connection; - auto& schema = context.schema; - auto& table = schema.table(dataset_id); - - assert(table.primary_key_field); - transaction_lock trans(connection); - if (table.primary_key_field->is_default(context) == context.is_update) - { - if (!strict) - { - auto update_context = context; - update_context.is_update = !update_context.is_update; - ret = table.create_update(update_context); - } - else if (context.is_update) - { - throw misc::hibernate_exception("can not update dataset with no primary key assigned!"); - } - else - { - throw misc::hibernate_exception("can not create dataset with primary key assigned!"); - } - } - else - { - ret = table.create_update(context); - } - trans.commit(); - return ret; - } - }; - - /* create_update_impl_t - nullable */ - - template - struct create_update_impl_t< - T_dataset, - mp::enable_if>> - { - using dataset_type = T_dataset; - using nullable_helper_type = misc::nullable_helper; - - static inline value_t apply(const create_update_context& context, bool strict = true) - { - value_t ret; - auto& dataset = context.get(); - auto* value = nullable_helper_type::get(dataset); - - if (value) - { - using new_dataset_type = mp::decay_t; - using new_create_update_impl_type = create_update_impl_t; - ret = new_create_update_impl_type::apply(change_context(context, *value), strict); - } - else if (strict) - { - throw misc::hibernate_exception("can not create nullable type with no value!"); - } - return ret; - } - }; - - /* create_update_impl_t - container */ - - template - struct create_update_impl_t< - T_dataset, - mp::enable_if>> - { - using dataset_type = T_dataset; - - static inline value_t apply(const create_update_context& context, bool strict = true) - { - value_t ret; - auto& connection = context.connection; - auto& dataset = context.get(); - - transaction_lock trans(connection); - ssize_t index = 0; - for (auto& x : dataset) - { - using new_dataset_type = mp::decay_t; - using new_create_update_impl_type = create_update_impl_t; - auto new_context = change_context(context, x); - new_context.index = index++; - new_create_update_impl_type::apply(new_context, strict); - } - trans.commit(); - return ret; - } - }; + struct create_update_impl_t; -} -end_namespace_cpphibernate_driver_mariadb \ No newline at end of file +} } diff --git a/include/cpphibernate/driver/mariadb/impl/create_update.inl b/include/cpphibernate/driver/mariadb/impl/create_update.inl new file mode 100644 index 0000000..579b9b7 --- /dev/null +++ b/include/cpphibernate/driver/mariadb/impl/create_update.inl @@ -0,0 +1,130 @@ +#pragma once + +#include "create_update.h" + +#include "../classes/schema/schema.inl" +#include "../helper/nullable_helper.inl" +#include "../context/create_update_context.inl" + +namespace cpphibernate { +namespace mariadb { + + /* create_update_impl_t - default (normal dataset) */ + + template + struct create_update_impl_t + { + using dataset_type = T_dataset; + + static inline value_t apply(const create_update_context& context, bool strict = true) + { + using namespace ::cppmariadb; + + value_t ret; + + auto dataset_id = get_type_id(hana::type_c); + auto& connection = context.connection; + auto& schema = context.schema; + auto& table = schema.table(dataset_id); + + assert(table.primary_key_field); + transaction_lock trans(connection); + + /* if the value of primary key field does not match the operation of the context */ + if (table.primary_key_field->is_default(context) != context.is_create()) + { + /* if we are not in strict mode, change the context to an update context */ + if (!strict) + { + static const filter_t dummy; + ret = table.create_update(context.is_create() + ? context.make_update_context(dummy) + : context.make_create_context()); + } + + /* if we expect an update operation in strict mode throw an exception + * because an update operation needs an primary key assigned */ + else if (context.is_update()) + { + throw exception("can not update dataset with no primary key assigned!"); + } + + /* if we expect an create operation in strict mode throw an exception + because an create operation expects the primary key to not be assigned */ + else + { + throw exception("can not create dataset with primary key assigned!"); + } + } + else + { + ret = table.create_update(context); + } + trans.commit(); + return ret; + } + }; + + /* create_update_impl_t - nullable */ + + template + struct create_update_impl_t< + T_dataset, + mp::enable_if_t>> + { + using dataset_type = T_dataset; + using nullable_helper_type = nullable_helper; + + static inline value_t apply(const create_update_context& context, bool strict = true) + { + value_t ret; + auto& dataset = context.get(); + auto* value = nullable_helper_type::get(dataset); + + if (value) + { + using new_dataset_type = mp::decay_t; + using new_create_update_impl_type = create_update_impl_t; + ret = new_create_update_impl_type::apply(change_context(context, *value), strict); + } + else if (strict) + { + throw exception("can not create nullable type with no value!"); + } + return ret; + } + }; + + /* create_update_impl_t - container */ + + template + struct create_update_impl_t< + T_dataset, + mp::enable_if_t>> + { + using dataset_type = T_dataset; + + static inline value_t apply(const create_update_context& context, bool strict = true) + { + using namespace ::cppmariadb; + + value_t ret; + auto& connection = context.connection; + auto& dataset = context.get(); + + transaction_lock trans(connection); + ssize_t index = 0; + for (auto& x : dataset) + { + using new_dataset_type = mp::decay_t; + using new_create_update_impl_type = create_update_impl_t; + auto new_context = change_context(context, x); + new_context.index = index++; + new_create_update_impl_type::apply(new_context, strict); + } + trans.commit(); + return ret; + } + }; + +} } diff --git a/include/cpphibernate/driver/mariadb/impl/destroy.h b/include/cpphibernate/driver/mariadb/impl/destroy.h index add7eea..885ac68 100644 --- a/include/cpphibernate/driver/mariadb/impl/destroy.h +++ b/include/cpphibernate/driver/mariadb/impl/destroy.h @@ -1,96 +1,14 @@ #pragma once -#include -#include -#include -#include -#include - -beg_namespace_cpphibernate_driver_mariadb -{ - - /* destroy_impl_t */ - +namespace cpphibernate { +namespace mariadb { + + /** + * @brief Helper class to select the right implementation of the destroy operation for the passed datatype. + * + * @tparam T_dataset Dataset type to select implementation for. + */ template - struct destroy_impl_t - { - using dataset_type = T_dataset; - - static inline void apply(const destroy_context& context, bool strict = true) - { - auto dataset_id = misc::get_type_id(hana::type_c); - auto& connection = context.connection; - auto& schema = context.schema; - auto& table = schema.table(dataset_id); - - assert(table.primary_key_field); - transaction_lock trans(connection); - if (!table.primary_key_field->is_default(context)) - { - table.destroy(context); - } - else if (strict) - { - throw misc::hibernate_exception("can not destroy dataset with no primary key assigned!"); - } - trans.commit(); - } - }; - - /* destroy_impl_t - nullable */ - - template - struct destroy_impl_t< - T_dataset, - mp::enable_if>> - { - using dataset_type = T_dataset; - using nullable_helper_type = misc::nullable_helper; - - static inline void apply(const destroy_context& context, bool strict = true) - { - auto& dataset = context.get(); - auto* value = nullable_helper_type::get(dataset); - - if (value) - { - using new_dataset_type = mp::decay_t; - using new_destroy_impl_type = destroy_impl_t; - auto new_context = change_context(context, *value); - new_destroy_impl_type::apply(new_context, strict); - } - else if (strict) - { - throw misc::hibernate_exception("can not destroy nullable type with no value!"); - } - } - }; - - /* destroy_impl_t - container */ - - template - struct destroy_impl_t< - T_dataset, - mp::enable_if>> - { - using dataset_type = T_dataset; - - static inline void apply(const destroy_context& context, bool strict = true) - { - auto& connection = context.connection; - auto& dataset = context.get(); - - transaction_lock trans(connection); - for (auto& x : dataset) - { - using new_dataset_type = mp::decay_t; - using new_destroy_impl_type = destroy_impl_t; - auto new_context = change_context(context, x); - new_destroy_impl_type::apply(new_context, strict); - } - trans.commit(); - } - }; + struct destroy_impl_t; -} -end_namespace_cpphibernate_driver_mariadb \ No newline at end of file +} } diff --git a/include/cpphibernate/driver/mariadb/impl/destroy.inl b/include/cpphibernate/driver/mariadb/impl/destroy.inl new file mode 100644 index 0000000..5848bcd --- /dev/null +++ b/include/cpphibernate/driver/mariadb/impl/destroy.inl @@ -0,0 +1,112 @@ +#pragma once + +#include "destroy.h" + +#include "../context/destroy_context.inl" + +namespace cpphibernate { +namespace mariadb { + + /* destroy_impl_t - default */ + + template + struct destroy_impl_t + { + using dataset_type = T_dataset; + + static inline void apply(const destroy_context& context, bool strict = true) + { + auto dataset_id = get_type_id(hana::type_c); + auto& connection = context.connection; + auto& schema = context.schema; + auto& table = schema.table(dataset_id); + + assert(table.primary_key_field); + ::cppmariadb::transaction_lock trans(connection); + if (!table.primary_key_field->is_default(context)) + { + auto& field = *table.primary_key_field; + auto primary_key = field.get(context); + + assert(primary_key.has_value()); + + std::ostringstream os; + os << "WHERE `" + << field.table.name + << "`.`" + << field.name + << "`=" + << field.convert_to_open + << "'" + << connection.escape(*primary_key) + << "'" + << field.convert_to_close; + + auto new_context = context; + new_context.where = os.str(); + table.destroy(new_context); + } + else if (strict) + { + throw exception("can not destroy dataset with no primary key assigned!"); + } + trans.commit(); + } + }; + + /* destroy_impl_t - nullable */ + + template + struct destroy_impl_t< + T_dataset, + mp::enable_if_t>> + { + using dataset_type = T_dataset; + using nullable_helper_type = nullable_helper; + + static inline void apply(const destroy_context& context, bool strict = true) + { + auto& dataset = context.get(); + auto* value = nullable_helper_type::get(dataset); + + if (value) + { + using new_dataset_type = mp::decay_t; + using new_destroy_impl_type = destroy_impl_t; + auto new_context = change_context(context, *value); + new_destroy_impl_type::apply(new_context, strict); + } + else if (strict) + { + throw exception("can not destroy nullable type with no value!"); + } + } + }; + + /* destroy_impl_t - container */ + + template + struct destroy_impl_t< + T_dataset, + mp::enable_if_t>> + { + using dataset_type = T_dataset; + + static inline void apply(const destroy_context& context, bool strict = true) + { + auto& connection = context.connection; + auto& dataset = context.get(); + + ::cppmariadb::transaction_lock trans(connection); + for (auto& x : dataset) + { + using new_dataset_type = mp::decay_t; + using new_destroy_impl_type = destroy_impl_t; + auto new_context = change_context(context, x); + new_destroy_impl_type::apply(new_context, strict); + } + trans.commit(); + } + }; + +} } diff --git a/include/cpphibernate/driver/mariadb/impl/driver_impl.h b/include/cpphibernate/driver/mariadb/impl/driver_impl.h new file mode 100644 index 0000000..d618a42 --- /dev/null +++ b/include/cpphibernate/driver/mariadb/impl/driver_impl.h @@ -0,0 +1,72 @@ +#pragma once + +#include "../classes/schema/schema.h" + +namespace cpphibernate { +namespace mariadb { + + struct driver_t; + + /** + * @brief Actual implementation of the mariadb driver. + * + * This class is used/owner by the mariadb driver class (as a member). + * The public interface of this class will not be part of the cpphibernate context. + */ + struct driver_impl_t + { + driver_t& owner; + schema_ptr_u schema; + + /** + * @brief Constructor. + + * @param[in] p_owner Object the driver implementation is owned by. + * @param[in] p_schema Cpphibernate schema to use with the driver. + */ + template + inline driver_impl_t(driver_t& p_owner, T_schema&& p_schema); + + /** + * @brief Initialize the schema in the database. + * + * @param[in] recreate Recreate the whole schema (this will drop all existing data). + */ + inline void init(bool recreate) const; + + /** + * @brief Create a new dataset in the database. + * This will update the primary key field of the passed dataset. + * + * @param[in] dataset Dataset to create in the database. + */ + template + inline void create(T_dataset& dataset) const; + + /** + * @brief Read datasets from the database. + * + * @param[in] dataset Dataset to read from the database. + * @param[in] modifiers Modifiers to apply to the select query. + */ + template + inline void read(T_dataset& dataset, const T_modifiers& modifiers) const; + + /** + * @brief Update an exsisting dataset in the database. + * + * @param[in] dataset Dataset to update in the database. + */ + template + inline void update(T_dataset& dataset) const; + + /** + * @brief Destroy an exsisting dataset in the database. + * + * @param[in] dataset Dataset to destroy in the database. + */ + template + inline void destroy(T_dataset& dataset) const; + }; + +} } diff --git a/include/cpphibernate/driver/mariadb/impl/driver_impl.inl b/include/cpphibernate/driver/mariadb/impl/driver_impl.inl new file mode 100644 index 0000000..7164090 --- /dev/null +++ b/include/cpphibernate/driver/mariadb/impl/driver_impl.inl @@ -0,0 +1,86 @@ +#pragma once + +#include "../context/init_context.inl" +#include "../context/create_update_context.inl" +#include "../context/read_context.inl" +#include "../context/destroy_context.inl" + +#include "driver_impl/where.inl" +#include "driver_impl/limit.inl" +#include "driver_impl/order_by.inl" + +namespace cpphibernate { +namespace mariadb { + + /* driver_impl_t */ + + template + driver_impl_t::driver_impl_t(driver_t& p_owner, T_schema&& p_schema) + : owner (p_owner) + , schema(make_schema(std::forward(p_schema))) + { } + + void driver_impl_t::init(bool recreate) const + { + auto * connection = owner.connection(); + if (!connection) + throw exception("Cpphibernate mariadb driver is not connected to any database!"); + ::cppmariadb::transaction_lock trans(*connection); + schema->init(init_context(*schema, *connection, recreate)); + trans.commit(); + } + + template + void driver_impl_t::create(T_dataset& dataset) const + { + auto * connection = owner.connection(); + if (!connection) + throw exception("Cpphibernate mariadb driver is not connected to any database!"); + create_update_impl_t::apply( + create_update_context(*schema, *connection, dataset)); + } + + template + inline void driver_impl_t::read(T_dataset& dataset, const T_modifiers& modifiers) const + { + using dataset_type = mp::decay_t; + using real_dataset_type = real_dataset_t; + + auto * connection = owner.connection(); + if (!connection) + throw exception("Cpphibernate mariadb driver is not connected to any database!"); + + auto dataset_id = get_type_id(hana::type_c); + auto& table = schema->table(dataset_id); + auto context = make_read_context(*schema, *connection, dataset, owner._filter); + + context.where = build_where(*schema, modifiers).query(*connection); + context.limit = build_limit(modifiers).query(*connection); + context.order_by = build_order_by(*schema, modifiers).query(*connection); + + ::cppmariadb::transaction_lock trans(*connection); + table.read(context); + trans.commit(); + } + + template + void driver_impl_t::update(T_dataset& dataset) const + { + auto * connection = owner.connection(); + if (!connection) + throw exception("Cpphibernate mariadb driver is not connected to any database!"); + create_update_impl_t::apply( + create_update_context(*schema, *connection, dataset, owner._filter)); + } + + template + void driver_impl_t::destroy(T_dataset& dataset) const + { + auto * connection = owner.connection(); + if (!connection) + throw exception("Cpphibernate mariadb driver is not connected to any database!"); + destroy_impl_t::apply( + destroy_context(*schema, *connection, dataset)); + } + +} } diff --git a/include/cpphibernate/driver/mariadb/impl/limit.h b/include/cpphibernate/driver/mariadb/impl/driver_impl/limit.inl similarity index 79% rename from include/cpphibernate/driver/mariadb/impl/limit.h rename to include/cpphibernate/driver/mariadb/impl/driver_impl/limit.inl index 9076137..33de49a 100644 --- a/include/cpphibernate/driver/mariadb/impl/limit.h +++ b/include/cpphibernate/driver/mariadb/impl/driver_impl/limit.inl @@ -1,11 +1,10 @@ #pragma once -#include -#include -#include +#include +#include -beg_namespace_cpphibernate_driver_mariadb -{ +namespace cpphibernate { +namespace mariadb { /* limit_builder */ @@ -21,18 +20,18 @@ beg_namespace_cpphibernate_driver_mariadb hana::for_each(p_modifier, [&limit, &offset](auto& x_modifier){ using modifier_type = mp::decay_t; - using is_limit_type = modifier::is_limit_modifier; - using is_offset_type = modifier::is_offset; + using is_limit_type = is_limit_modifier; + using is_offset_type = is_offset_modifier; hana::eval_if( is_limit_type { }, [&limit, &x_modifier](auto _){ - limit = static_cast(hana::value(_(x_modifier).value)); + limit = static_cast(hana::value(_(x_modifier))); }, [&offset, &x_modifier](){ hana::eval_if( is_offset_type { }, [&offset, &x_modifier](auto _){ - offset = static_cast(hana::value(_(x_modifier).value)); + offset = static_cast(hana::value(_(x_modifier))); }, []{ /* no-op */ @@ -61,5 +60,4 @@ beg_namespace_cpphibernate_driver_mariadb return builder.statement; } -} -end_namespace_cpphibernate_driver_mariadb +} } diff --git a/include/cpphibernate/driver/mariadb/impl/order_by.h b/include/cpphibernate/driver/mariadb/impl/driver_impl/order_by.inl similarity index 73% rename from include/cpphibernate/driver/mariadb/impl/order_by.h rename to include/cpphibernate/driver/mariadb/impl/driver_impl/order_by.inl index cd85783..dc9c6ff 100644 --- a/include/cpphibernate/driver/mariadb/impl/order_by.h +++ b/include/cpphibernate/driver/mariadb/impl/driver_impl/order_by.inl @@ -1,12 +1,11 @@ #pragma once -#include -#include -#include -#include +#include +#include +#include -beg_namespace_cpphibernate_driver_mariadb -{ +namespace cpphibernate { +namespace mariadb { /* order_by_builder */ @@ -30,20 +29,20 @@ beg_namespace_cpphibernate_driver_mariadb size_t index = 0; hana::for_each(modifiers, [&](auto& modifier){ using modifier_type = mp::decay_t; - using is_order_by_type = modifier::is_order_by; + using is_order_by_type = is_order_by_modifier; hana::eval_if( is_order_by_type { }, [&](auto _){ - hana::for_each(_(modifier).fields, [&](auto& f){ - using field_type = mp::decay_t; - using is_ascencing_type = modifier::is_ascending; + hana::for_each(_(modifier).order_directions, [&](auto& dir){ + using order_direction_type = mp::decay_t; + using is_ascencing_type = is_ascending; if (index++ == 0) os << "ORDER_BY "; else os << ", "; - auto& field = schema.field(misc::get_type_id(f.wrapped_field)); + auto& field = schema.field(get_type_id(dir.wrapped_field)); os << "`" - << field.table_name + << field.table.name << "`.`" - << field.field_name + << field.name << "` " << (is_ascencing_type::value ? "ASC" @@ -79,5 +78,4 @@ beg_namespace_cpphibernate_driver_mariadb return builder.assign(schema, modifiers); } -} -end_namespace_cpphibernate_driver_mariadb +} } diff --git a/include/cpphibernate/driver/mariadb/impl/where.h b/include/cpphibernate/driver/mariadb/impl/driver_impl/where.inl similarity index 80% rename from include/cpphibernate/driver/mariadb/impl/where.h rename to include/cpphibernate/driver/mariadb/impl/driver_impl/where.inl index a8ef2bc..f3047ba 100644 --- a/include/cpphibernate/driver/mariadb/impl/where.h +++ b/include/cpphibernate/driver/mariadb/impl/driver_impl/where.inl @@ -1,12 +1,13 @@ #pragma once -#include -#include -#include -#include +#include +#include +#include +#include +#include -beg_namespace_cpphibernate_driver_mariadb -{ +namespace cpphibernate { +namespace mariadb { /* where_builder */ @@ -27,7 +28,7 @@ beg_namespace_cpphibernate_driver_mariadb template inline auto build_clause(T_clause&& p_clause) - -> mp::enable_if>> + -> mp::enable_if_t>> { os << "("; build_clause(p_clause.clauses[hana::size_c<0>]); @@ -43,7 +44,7 @@ beg_namespace_cpphibernate_driver_mariadb template inline auto build_clause(T_clause&& p_clause) - -> mp::enable_if>> + -> mp::enable_if_t>> { os << "("; build_clause(p_clause.clauses[hana::size_c<0>]); @@ -59,7 +60,7 @@ beg_namespace_cpphibernate_driver_mariadb template inline auto build_clause(T_clause&& p_clause) - -> mp::enable_if>> + -> mp::enable_if_t>> { os << "NOT ("; build_clause(p_clause.clause); @@ -68,14 +69,14 @@ beg_namespace_cpphibernate_driver_mariadb template inline auto build_clause(T_clause&& p_clause) - -> mp::enable_if>> + -> mp::enable_if_t>> { - auto field_id = misc::get_type_id(hana::type_c>); + auto field_id = get_type_id(hana::type_c>); auto& field = schema.field(field_id); os << "`" - << field.table_name + << field.table.name << "`.`" - << field.field_name + << field.name << "`=" << field.convert_to_open << "?\?" @@ -87,7 +88,7 @@ beg_namespace_cpphibernate_driver_mariadb size_t index = 0; hana::for_each(modifiers, [&](auto& modifier){ using modifier_type = mp::decay_t; - using is_where_type = modifier::is_where; + using is_where_type = is_where_modifier; hana::eval_if( is_where_type { }, [&](auto _){ @@ -115,9 +116,9 @@ beg_namespace_cpphibernate_driver_mariadb template inline auto assign_clause(T_clause&& p_clause) - -> mp::enable_if_c< - modifier::is_where_clause_and>::value - || modifier::is_where_clause_or >::value> + -> mp::enable_if_t< + is_and_v> + || is_or_v >> { hana::for_each(std::forward(p_clause).clauses, [&](auto& x_clause) { assign_clause(x_clause); @@ -126,14 +127,14 @@ beg_namespace_cpphibernate_driver_mariadb template inline auto assign_clause(T_clause&& p_clause) - -> mp::enable_if>> + -> mp::enable_if_t>> { assign_clause(std::forward(p_clause).clause); } template inline auto assign_clause(T_clause&& p_clause) - -> mp::enable_if>> + -> mp::enable_if_t>> { statement.set(index, std::forward(p_clause).value); ++index; @@ -143,7 +144,7 @@ beg_namespace_cpphibernate_driver_mariadb { hana::for_each(modifiers, [&](auto& modifier){ using modifier_type = mp::decay_t; - using is_where_type = modifier::is_where; + using is_where_type = is_where_modifier; hana::eval_if( is_where_type { }, [&](auto _){ @@ -178,5 +179,4 @@ beg_namespace_cpphibernate_driver_mariadb return builder.assign(schema, modifiers); } -} -end_namespace_cpphibernate_driver_mariadb +} } diff --git a/include/cpphibernate/driver/mariadb/impl/filter.h b/include/cpphibernate/driver/mariadb/impl/filter.h new file mode 100644 index 0000000..9c5b0c8 --- /dev/null +++ b/include/cpphibernate/driver/mariadb/impl/filter.h @@ -0,0 +1,60 @@ +#pragma once + +namespace cpphibernate { +namespace mariadb { + + struct field_t; + struct table_t; + struct schema_t; + + /** + * @brief Inclusive or exclusive field and table filter. + * Is used in update and read operations to select the fields that should be updated / fetched. + */ + struct filter_t + { + public: + using field_set_type = std::set; + using table_set_type = std::set; + + ssize_t cache_id { 0 }; //!< Unique ID that indicates the current filtered tables and fields + bool exclusive { true }; //!< True: Use exclusive filter. False: Use inclusive filter. + field_set_type fields; //!< Set of fields assigned to the filter. + table_set_type tables; //!< Set of tables assigned to the filter. + + public: + /** + * @brief Returns true if the passed table is excluded. + */ + inline bool is_excluded(const table_t& table) const; + + /** + * @brief Returns true if the passed field is excluded. + */ + inline bool is_excluded(const field_t& field) const; + + /** + * @brief Set included fields and tables. + * + * @param[in] schema Mariadb driver schame to use for looking up fields and tables. + * @param[in] args Fields and tables from the cpphibernate schema to include in this filter. + */ + template + inline void set_inclusive(const schema_t& schema, T_args&&... args); + + /** + * @brief Set excluded fields and tables. + * + * @param[in] schema Mariadb driver schame to use for looking up fields and tables. + * @param[in] args Fields and tables from the cpphibernate schema to exclude in this filter. + */ + template + inline void set_exclusive(const schema_t& schema, T_args&&... args); + + /** + * @brief Clear the filter. + */ + inline void clear(); + }; + +} } diff --git a/include/cpphibernate/driver/mariadb/schema/filter.inl b/include/cpphibernate/driver/mariadb/impl/filter.inl similarity index 73% rename from include/cpphibernate/driver/mariadb/schema/filter.inl rename to include/cpphibernate/driver/mariadb/impl/filter.inl index fd251bf..b70333d 100644 --- a/include/cpphibernate/driver/mariadb/schema/filter.inl +++ b/include/cpphibernate/driver/mariadb/impl/filter.inl @@ -1,33 +1,41 @@ #pragma once -#include -#include +#include -beg_namespace_cpphibernate_driver_mariadb -{ +#include "filter.h" +#include "../classes/fields/field.h" +#include "../classes/tables/table.h" +#include "../classes/schema/schema.h" + +#include +#include +#include + +namespace cpphibernate { +namespace mariadb { namespace __impl { /* filter_add_element_impl */ - template + template struct filter_add_element_impl { template static constexpr decltype(auto) apply(T_args&&... args) - { static_assert(sizeof...(args) == -1, "Invalid parameters for mariadb::filter_add_element(...)!"); } + { static_assert(sizeof...(args) == -1, "Invalid parameters for filter_add_element(...)!"); } }; template struct filter_add_element_impl< mp::list, - mp::enable_if_c< - schema::is_table>::value>> + mp::enable_if_t< + schema::is_table_v>>> { static constexpr decltype(auto) apply(filter_t& filter, const schema_t& schema, const T_table& table) { - auto dataset_id = misc::get_type_id(table.wrapped_dataset); + auto dataset_id = get_type_id(table.wrapped_dataset); auto& t = schema.table(dataset_id); filter.tables.emplace(&t); for (auto& ptr : t.fields) @@ -40,18 +48,20 @@ beg_namespace_cpphibernate_driver_mariadb template struct filter_add_element_impl< mp::list, - mp::enable_if_c< - schema::is_field>::value>> + mp::enable_if_t< + schema::is_field_v>>> { static constexpr decltype(auto) apply(filter_t& filter, const schema_t& schema, const T_field& field) { - auto field_id = misc::get_type_id(hana::type_c>); + auto field_id = get_type_id(hana::type_c>); auto& f = schema.field(field_id); filter.fields.emplace(&f); filter.tables.emplace(f.table); } }; + constexpr decltype(auto) filter_add_element = ::cppmp::generic_predicate<__impl::filter_add_element_impl> { }; + } /* filter_t */ @@ -77,7 +87,7 @@ beg_namespace_cpphibernate_driver_mariadb { clear(); exclusive = false; - cache_id = static_cast(utl::get_unique_id...>()); + cache_id = static_cast(cppcore::get_unique_id...>() + 1); int dummy[] = { 0, (__impl::filter_add_element(*this, schema, std::forward(args)), void(), 0)... }; (void)dummy; } @@ -87,7 +97,7 @@ beg_namespace_cpphibernate_driver_mariadb { clear(); exclusive = true; - cache_id = static_cast(utl::get_unique_id...>()); + cache_id = -static_cast(cppcore::get_unique_id...>() + 1); int dummy[] = { 0, (__impl::filter_add_element(*this, schema, std::forward(args)), void(), 0)... }; (void)dummy; @@ -118,5 +128,4 @@ beg_namespace_cpphibernate_driver_mariadb tables.clear(); } -} -end_namespace_cpphibernate_driver_mariadb \ No newline at end of file +} } diff --git a/include/cpphibernate/driver/mariadb/impl/modifier_tags.h b/include/cpphibernate/driver/mariadb/impl/modifier_tags.h deleted file mode 100644 index 020a4ff..0000000 --- a/include/cpphibernate/driver/mariadb/impl/modifier_tags.h +++ /dev/null @@ -1,69 +0,0 @@ -#pragma once - -#include -#include - -beg_namespace_cpphibernate_driver_mariadb -{ - - namespace __impl - { - - /* make_modifier_tags_impl */ - - template - struct make_modifier_tag_impl - { - template - static constexpr decltype(auto) apply(T_args&&... args) - { static_assert(sizeof...(args) == -1, "Invalid parameters for make_modifier_tag(...)!"); } - }; - - template - struct make_modifier_tag_impl< - mp::list, - mp::enable_if_c< - modifier::is_limit_modifier>::value - || modifier::is_offset>::value>> - { - static constexpr decltype(auto) apply(T_modifier&&) - { return T_modifier { }; } - }; - - } - - constexpr decltype(auto) make_modifier_tag = misc::make_generic_predicate<__impl::make_modifier_tag_impl> { }; - - namespace __impl - { - - /* make_modifier_tags_impl */ - - template - struct make_modifier_tags_impl - { - template - static constexpr decltype(auto) apply(T_args&&... args) - { static_assert(sizeof...(args) == -1, "Invalid parameters for make_modifier_tags(...)!"); } - }; - - template - struct make_modifier_tags_impl< - mp::list, - mp::enable_if_c< - modifier::is_modifiers>::value>> - { - static constexpr decltype(auto) apply(T_modifiers&& modifiers) - { - return hana::transform( - modifiers, - make_modifier_tag); - } - }; - - } - - constexpr decltype(auto) make_modifier_tags = misc::make_generic_predicate<__impl::make_modifier_tags_impl> { }; - -} -end_namespace_cpphibernate_driver_mariadb diff --git a/include/cpphibernate/driver/mariadb/impl/read.h b/include/cpphibernate/driver/mariadb/impl/read.h deleted file mode 100644 index 71cb715..0000000 --- a/include/cpphibernate/driver/mariadb/impl/read.h +++ /dev/null @@ -1,265 +0,0 @@ -#pragma once - -#include -#include -#include -#include -#include - -beg_namespace_cpphibernate_driver_mariadb -{ - - namespace __impl - { - - /* declaration */ - - template - struct make_read_context_impl - { - template - static constexpr decltype(auto) apply(T_args&&... args) - { static_assert(sizeof...(args) == -1, "Invalid parameters for mariadb::make_read_context(...)!"); } - }; - - /* normal datasets */ - - template - struct make_read_context_impl< - mp::list, - mp::enable_if_c< - !misc::is_container>::value - && !misc::is_nullable>::value>> - { - using dataset_type = mp::decay_t; - - struct context_impl - : public read_context - { - private: - mutable size_t _count; - dataset_type& _dataset; - - public: - template - context_impl(dataset_type& dataset, X_args&&... args) - : read_context (std::forward(args)...) - , _count (0) - , _dataset (dataset) - { - is_dynamic = false; - base_dataset_id = misc::get_type_id(hana::type_c); - } - - private: - virtual void* emplace_intern(void* data, size_t dataset_id) const override - { - if (data || dataset_id != 0) - throw misc::hibernate_exception("Static datasets can not be assigned!"); - ++_count; - if (_count > 1) - throw misc::hibernate_exception("Expected exactly one dataset, but received more!"); - return set(_dataset); - } - - virtual void finish_intern() const override - { - if (_count < 1) - throw misc::hibernate_exception("Expected exactly one dataset, but received none!"); - } - }; - - static constexpr decltype(auto) apply(dataset_type& dataset, T_args&&... args) - { return context_impl(dataset, std::forward(args)...); } - }; - - /* nullable datasets */ - - template - struct make_read_context_impl< - mp::list, - mp::enable_if_c< - !misc::is_container>::value - && misc::is_nullable>::value>> - { - using dataset_type = mp::decay_t; - using nullable_helper_type = misc::nullable_helper; - using value_type = typename nullable_helper_type::value_type; - - struct context_impl - : public read_context - { - private: - dataset_type& _dataset; - mutable size_t _count; - - public: - template - context_impl(dataset_type& dataset, X_args&&... args) - : read_context (std::forward(args)...) - , _dataset (dataset) - , _count (0) - { - is_dynamic = misc::is_pointer::value; - base_dataset_id = misc::get_type_id(hana::type_c); - nullable_helper_type::clear(_dataset); - } - - private: - virtual void* emplace_intern(void* data, size_t dataset_id) const override - { - if (data && !misc::is_pointer::value) - throw misc::hibernate_exception("None pointer type can not be assigned!"); - ++_count; - if (_count > 1) - throw misc::hibernate_exception("Expected exactly one dataset, but received more!"); - - if (data) - { - auto* cast = static_cast(data); - auto& value = nullable_helper_type::set(_dataset, cast); - if (cast != &value) - throw misc::hibernate_exception("Nullable pointer value has changed!"); - return set(value, dataset_id); - } - else - { - auto& value = nullable_helper_type::set(_dataset, value_type { }); - return set(value); - } - } - - virtual void finish_intern() const override - { } - }; - - static constexpr decltype(auto) apply(dataset_type& dataset, T_args&&... args) - { return context_impl(dataset, std::forward(args)...); } - }; - - /* container datasets */ - - template - struct make_read_context_impl< - mp::list, - mp::enable_if_c< - misc::is_container>::value - && !misc::is_nullable>::value>> - { - using dataset_type = mp::decay_t; - using real_dataset_type = misc::real_dataset_t; - using container_helper_type = misc::container_helper; - using value_type = typename container_helper_type::value_type; - - struct context_impl - : public read_context - { - private: - dataset_type& _dataset; - mutable size_t _count; - - public: - template - context_impl(dataset_type& dataset, X_args&&... args) - : read_context (std::forward(args)...) - , _dataset (dataset) - , _count (0) - { - is_dynamic = misc::is_pointer::value; - base_dataset_id = misc::get_type_id(hana::type_c); - container_helper_type::clear(_dataset); - } - - private: - virtual void* emplace_intern(void* data, size_t dataset_id) const override - { - return hana::eval_if( - misc::is_nullable { }, - [this, &data, &dataset_id](auto _){ - using nullable_type = typename mp::decay_t))>::type; - using nullable_helper_type = misc::nullable_helper; - using inner_value_type = typename nullable_helper_type::value_type; - - if (!data) - throw misc::hibernate_exception("Expected dynamic data for pointer type!"); - if (!misc::is_pointer::value) - throw misc::hibernate_exception("None pointer type can not be assigned!"); - - auto& nullable = container_helper_type::emplace(this->_dataset); - auto* cast = static_cast(data); - auto& value = nullable_helper_type::set(nullable, cast); - if (cast != &value) - throw misc::hibernate_exception("Nullable pointer value has changed!"); - return set(value, dataset_id); - }, - [this, &data, &dataset_id](){ - if (data || dataset_id != 0) - throw misc::hibernate_exception("Static datasets can not be assigned!"); - auto& value = container_helper_type::emplace(this->_dataset); - return this->set(value); - }); - } - - virtual void finish_intern() const override - { } - }; - - static constexpr decltype(auto) apply(dataset_type& dataset, T_args&&... args) - { return context_impl(dataset, std::forward(args)...); } - }; - - } - - constexpr decltype(auto) make_read_context = misc::make_generic_predicate<__impl::make_read_context_impl> { }; - - namespace __impl - { - - template - struct make_fake_context_impl - { - template - static constexpr decltype(auto) apply(T_args&&... args) - { static_assert(sizeof...(args) == -1, "Invalid parameters for mariadb::make_fake_context(...)!"); } - }; - - template - struct make_fake_context_impl< - mp::list, - mp::enable_if_c< - hana::is_a_t::value>> - { - using wrapped_dataset_type = mp::decay_t; - using dataset_type = misc::unwrap_t; - using real_dataset_type = misc::real_dataset_t; - - struct context_impl - : public read_context - { - public: - template - context_impl(X_args&&... args) - : read_context (std::forward(args)...) - { - is_dynamic = misc::is_pointer::value; - base_dataset_id = misc::get_type_id(hana::type_c); - } - - private: - virtual void* emplace_intern(void* data, size_t dataset_id) const override - { return nullptr; } - - virtual void finish_intern() const override - { } - }; - - static constexpr decltype(auto) apply(T_wrapped_dataset&&, T_args&&... args) - { return context_impl(std::forward(args)...); } - }; - - } - - constexpr decltype(auto) make_fake_context = misc::make_generic_predicate<__impl::make_fake_context_impl> { }; - -} -end_namespace_cpphibernate_driver_mariadb \ No newline at end of file diff --git a/include/cpphibernate/driver/mariadb/mariadb.h b/include/cpphibernate/driver/mariadb/mariadb.h deleted file mode 100644 index 8b95bd5..0000000 --- a/include/cpphibernate/driver/mariadb/mariadb.h +++ /dev/null @@ -1,120 +0,0 @@ -#pragma once - -#include -#include -#include -#include -#include - -beg_namespace_cpphibernate_driver_mariadb -{ - - struct mariadb_driver_t - { - public: - using lock_type = std::unique_ptr; - - private: - ::cppmariadb::connection* _connection; - schema_t _schema; - filter_t _filter; - - public: - template - mariadb_driver_t(T_schema&& p_schema, ::cppmariadb::connection& p_connection) - : _connection (&p_connection) - , _schema (make_schema(std::forward(p_schema))) - { } - - cpphibernate_copyable(mariadb_driver_t, delete); - cpphibernate_moveable(mariadb_driver_t, default); - - inline const ::cppmariadb::connection& connection() const - { return *_connection; } - - inline void connection(::cppmariadb::connection& p_connection) - { _connection = &p_connection; } - - template - inline void set_filter_inclusive(T_args&&... args) - { _filter.set_inclusive(_schema, std::forward(args)...); } - - template - inline void set_filter_exclusive(T_args&&... args) - { _filter.set_exclusive(_schema, std::forward(args)...); } - - inline void clear_filter() - { _filter.clear(); } - - inline lock_type lock() - { return std::make_unique(*_connection); } - - template - inline std::string build_query(const std::string& query, T_modifiers&& modifiers) const - { - auto where = build_where(_schema, modifiers).query(*_connection); - std::ostringstream os; - os << query; - if (!where.empty()) - os << " " << where; - return os.str(); - } - - protected: - inline void init_impl(bool recreate) const - { - transaction_lock trans(*_connection); - _schema.init(init_context(_schema, *_connection, recreate)); - trans.commit(); - } - - template - inline void create_impl(T_dataset& dataset) const - { - create_update_impl_t::apply( - create_update_context(dataset, _schema, *_connection, _filter, false)); - } - - template - inline void read_impl(T_dataset& dataset, T_modifiers&& modifiers) const - { - using dataset_type = mp::decay_t; - using real_dataset_type = misc::real_dataset_t; - - auto dataset_id = misc::get_type_id(hana::type_c); - auto& table = _schema.table(dataset_id); - auto context = make_read_context(dataset, _schema, *_connection, _filter); - context.where = build_where(_schema, modifiers).query(*_connection); - context.limit = build_limit(modifiers).query(*_connection); - context.order_by = build_order_by(_schema, modifiers).query(*_connection); - - transaction_lock trans(*_connection); - table.read(context); - trans.commit(); - } - - template - inline void update_impl(T_dataset& dataset) const - { - create_update_impl_t::apply( - create_update_context(dataset, _schema, *_connection, _filter, true)); - } - - template - inline void destroy_impl(T_dataset& dataset) const - { - using dataset_type = mp::decay_t; - using real_dataset_type = misc::real_dataset_t; - - auto dataset_id = misc::get_type_id(hana::type_c); - auto& table = _schema.table(dataset_id); - - destroy_context context(dataset, _schema, *_connection); - context.where = table.get_where_primary_key(context); - - destroy_impl_t::apply(context); - } - }; - -} -end_namespace_cpphibernate_driver_mariadb diff --git a/include/cpphibernate/driver/mariadb/schema.h b/include/cpphibernate/driver/mariadb/schema.h deleted file mode 100644 index f3fa522..0000000 --- a/include/cpphibernate/driver/mariadb/schema.h +++ /dev/null @@ -1,24 +0,0 @@ -#pragma once - -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include \ No newline at end of file diff --git a/include/cpphibernate/driver/mariadb/schema/attributes.fwd.h b/include/cpphibernate/driver/mariadb/schema/attributes.fwd.h deleted file mode 100644 index 259a091..0000000 --- a/include/cpphibernate/driver/mariadb/schema/attributes.fwd.h +++ /dev/null @@ -1,34 +0,0 @@ -#pragma once - -#include - -beg_namespace_cpphibernate_driver_mariadb -{ - - /* attribute_t */ - - enum class attribute_t - { - hex, - compress, - primary_key, - }; - - /* attributes_t */ - - struct attributes_t; - - /* make_attributes */ - - namespace __impl - { - - template - struct make_attributes_impl; - - } - - constexpr decltype(auto) make_attributes = misc::make_generic_predicate<__impl::make_attributes_impl> { }; - -} -end_namespace_cpphibernate_driver_mariadb \ No newline at end of file diff --git a/include/cpphibernate/driver/mariadb/schema/attributes.h b/include/cpphibernate/driver/mariadb/schema/attributes.h deleted file mode 100644 index 3b1ca22..0000000 --- a/include/cpphibernate/driver/mariadb/schema/attributes.h +++ /dev/null @@ -1,23 +0,0 @@ -#pragma once - -#include - -#include -#include -#include -#include - -beg_namespace_cpphibernate_driver_mariadb -{ - - /* attributes_t */ - - struct attributes_t : - public std::set - { - using base_type = std::set; - using base_type::base_type; - }; - -} -end_namespace_cpphibernate_driver_mariadb \ No newline at end of file diff --git a/include/cpphibernate/driver/mariadb/schema/field.fwd.h b/include/cpphibernate/driver/mariadb/schema/field.fwd.h deleted file mode 100644 index 275ffc8..0000000 --- a/include/cpphibernate/driver/mariadb/schema/field.fwd.h +++ /dev/null @@ -1,31 +0,0 @@ -#pragma once - -#include -#include -#include - -beg_namespace_cpphibernate_driver_mariadb -{ - - /* field_t */ - - struct field_t; - - /* field_ptr_t */ - - using field_ptr_t = std::unique_ptr; - - /* make_field */ - - namespace __impl - { - - template - struct make_field_impl; - - } - - constexpr decltype(auto) make_field = misc::make_generic_predicate<__impl::make_field_impl> { }; - -} -end_namespace_cpphibernate_driver_mariadb \ No newline at end of file diff --git a/include/cpphibernate/driver/mariadb/schema/field.h b/include/cpphibernate/driver/mariadb/schema/field.h deleted file mode 100644 index 8d037cf..0000000 --- a/include/cpphibernate/driver/mariadb/schema/field.h +++ /dev/null @@ -1,213 +0,0 @@ -#pragma once - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -beg_namespace_cpphibernate_driver_mariadb -{ - - /* field_t */ - - struct field_t - { - public: - size_t id { 0 }; // unique id of the field - size_t dataset_id { 0 }; // unique id of the dataset type - size_t real_dataset_id { 0 }; // unique id of the real/unwrapped dataset type - size_t value_id { 0 }; // unique id of the value type - size_t real_value_id { 0 }; // unique id of the real/unwrapped value type - - bool value_is_nullable { false }; // value is stored in a nullable container - bool value_is_pointer { false }; // value is stored in a pointer container - bool value_is_container { false }; // value is stored in a container - bool value_is_ordered { false }; // value is stored in a ordered container (vector, list, ...) - bool value_is_auto_incremented { false }; // value is a auto incremented field - - table_t* table { nullptr }; // table this field belongs to - table_t* referenced_table { nullptr }; // table that belongs to the value (if exists) - - std::string schema_name; // name of the SQL schema - std::string table_name; // name of the SQL table - std::string field_name; // name of the SQL field - std::string type; // SQL type name - std::string create_arguments; // additional arguments for CREATE TABLE command - - std::string convert_to_open; // SQL code to open the "convert to" operation - std::string convert_to_close; // SQL code to close the "convert to" operation - std::string convert_from_open; // SQL code to open the "convert from" operation - std::string convert_from_close; // SQL code to close the "convert from" operation - - attributes_t attributes; // attributes for the field - - inline field_t() = default; - inline field_t(const field_t&) = delete; - inline field_t(field_t&& other) - : id (std::move(other).id) - , dataset_id (std::move(other).dataset_id) - , real_dataset_id (std::move(other).real_dataset_id) - , value_id (std::move(other).value_id) - , real_value_id (std::move(other).real_value_id) - , value_is_nullable (std::move(other).value_is_nullable) - , value_is_pointer (std::move(other).value_is_pointer) - , value_is_container (std::move(other).value_is_container) - , value_is_auto_incremented (std::move(other).value_is_auto_incremented) - , table (nullptr) - , referenced_table (nullptr) - , schema_name (std::move(other).schema_name) - , table_name (std::move(other).table_name) - , field_name (std::move(other).field_name) - , type (std::move(other).type) - , create_arguments (std::move(other).create_arguments) - , convert_to_open (std::move(other).convert_to_open) - , convert_to_close (std::move(other).convert_to_close) - , convert_from_open (std::move(other).convert_from_open) - , convert_from_close (std::move(other).convert_from_close) - , attributes (std::move(other).attributes) - { } - virtual ~field_t() { }; - - void print (std::ostream& os) const; - virtual void update (); - - /* CRUD */ - using read_context_ptr = std::unique_ptr; - - virtual value_t foreign_create_update (const create_update_context& context) const; - virtual read_context_ptr foreign_read (const read_context& context, bool fake_context) const; - - /* properties */ - virtual value_t get (const data_context& context) const; - virtual void set (const data_context& context, const value_t&) const; - virtual bool is_default (const data_context& context) const; - virtual std::string generate_value (::cppmariadb::connection& connection) const; - - /* statements */ - virtual ::cppmariadb::statement& get_statement_foreign_one_delete(bool key_known) const; - virtual ::cppmariadb::statement& get_statement_foreign_many_update() const; - - protected: - using statement_ptr = std::unique_ptr<::cppmariadb::statement>; - - ::cppmariadb::statement& get_statement_foreign_one_delete_impl(bool key_known, statement_ptr& known, statement_ptr& unknown) const; - ::cppmariadb::statement& get_statement_foreign_many_update_impl(statement_ptr& statement) const; - }; - - /* simple_field_t */ - - template - struct simple_field_t - : public field_t - { - using base_type = field_t; - using field_type = mp::decay_t; - using getter_type = typename field_type::getter_type; - using dataset_type = mp::decay_t; - using real_dataset_type = misc::real_dataset_t; - using value_type = mp::decay_t; - using real_value_type = misc::real_dataset_t; - using type_props = type_properties; - - const field_type& field; - - inline simple_field_t(const field_type& p_field) - : field_t () - , field (p_field) - { } - - virtual void update() override; - }; - - /* value_field_t */ - - template - struct value_field_t - : public simple_field_t - { - using base_type = simple_field_t; - using field_type = typename base_type::field_type; - using getter_type = typename base_type::getter_type; - using dataset_type = typename base_type::dataset_type; - using real_dataset_type = typename base_type::dataset_type; - using value_type = typename base_type::value_type; - using real_value_type = typename base_type::real_value_type; - using type_props = typename base_type::type_props; - - using base_type::base_type; - - static_assert(mp::is_same::value, "internal error: dataset type mismatch!"); - - virtual void update () override; - virtual value_t get (const data_context& context) const override; - virtual void set (const data_context& context, const value_t& value) const override; - }; - - /* primary_key_field_t */ - - template - struct primary_key_field_t - : public value_field_t - { - using base_type = value_field_t; - using field_type = typename base_type::field_type; - using dataset_type = typename base_type::dataset_type; - using value_type = typename base_type::value_type; - using key_props = key_properties; - - using base_type::base_type; - - virtual void update () override; - virtual bool is_default (const data_context& context) const override; - virtual std::string generate_value(::cppmariadb::connection& connection) const override; - }; - - /* data_field_t */ - - template - struct data_field_t - : public value_field_t - { - using base_type = value_field_t; - - using base_type::base_type; - }; - - /* foreign_table_field_t */ - - template - struct foreign_table_field_t - : public simple_field_t - { - public: - using base_type = simple_field_t; - using value_type = typename base_type::value_type; - using real_value_type = typename base_type::real_value_type; - using dataset_type = typename base_type::dataset_type; - - using base_type::base_type; - - public: - /* CRUD */ - virtual value_t foreign_create_update(const create_update_context& context) const override; - virtual read_context_ptr foreign_read (const read_context& context, bool fake_context) const override; - - /* statements */ - virtual ::cppmariadb::statement& get_statement_foreign_one_delete(bool key_known) const override; - virtual ::cppmariadb::statement& get_statement_foreign_many_update() const override; - - private: - using statement_ptr = std::unique_ptr<::cppmariadb::statement>; - - mutable statement_ptr _statement_foreign_one_delete_key_known; - mutable statement_ptr _statement_foreign_one_delete_key_unknown; - mutable statement_ptr _statement_foreign_many_update; - }; - -} -end_namespace_cpphibernate_driver_mariadb \ No newline at end of file diff --git a/include/cpphibernate/driver/mariadb/schema/field.inl b/include/cpphibernate/driver/mariadb/schema/field.inl deleted file mode 100644 index fef4450..0000000 --- a/include/cpphibernate/driver/mariadb/schema/field.inl +++ /dev/null @@ -1,228 +0,0 @@ -#pragma once - -#include -#include - -beg_namespace_cpphibernate_driver_mariadb -{ - - /* simple_field_t */ - - template - void simple_field_t - ::update() - { - base_type::update(); - - id = misc::get_type_id(hana::type_c); - dataset_id = misc::get_type_id(hana::type_c); - real_dataset_id = misc::get_type_id(hana::type_c); - value_id = misc::get_type_id(hana::type_c); - real_value_id = misc::get_type_id(hana::type_c); - - value_is_nullable = misc::is_nullable::value; - value_is_pointer = misc::is_pointer::value; - value_is_container = misc::is_container::value; - value_is_ordered = misc::is_ordered::value; - } - - /* value_field_t */ - - template - void value_field_t - ::update() - { - base_type::update(); - - this->type = type_props::type(); - - if (type_props::convert_to_open()) - this->convert_to_open = this->convert_to_open + type_props::convert_to_open(); - if (type_props::convert_to_close()) - this->convert_to_close = type_props::convert_to_close() + this->convert_to_close; - if (type_props::convert_from_open()) - this->convert_from_open = this->convert_from_open + type_props::convert_from_open(); - if (type_props::convert_from_close()) - this->convert_from_close = type_props::convert_from_close() + this->convert_from_close; - } - - template - value_t value_field_t - ::get(const data_context& context) const - { - auto& dataset = context.get(); - return type_props::convert_from(this->field.getter(dataset)); - } - - template - void value_field_t - ::set(const data_context& context, const value_t& value) const - { - auto& dataset = context.get(); - this->field.setter(dataset, type_props::convert_to(value)); - } - - /* primary_key_field_t */ - - template - void primary_key_field_t - ::update() - { - base_type::update(); - - this->value_is_auto_incremented = key_props::auto_generated::value; - } - - template - bool primary_key_field_t - ::is_default(const data_context& context) const - { - auto& dataset = context.get(); - return key_props::is_default(this->field.getter(dataset)); - } - - template - std::string primary_key_field_t - ::generate_value(::cppmariadb::connection& connection) const - { - auto ret = connection.execute_used(key_props::create_key_query); - if (!ret || !ret->next()) - throw misc::hibernate_exception("unable to generate key value!"); - return ret->current()->at(0).template get(); - } - - /* foreign_table_field_t */ - - template - value_t foreign_table_field_t - ::foreign_create_update(const create_update_context& context) const - { - auto& dataset = context.get(); - auto& foreign = this->field.getter(dataset); - auto next_context = change_context(context, foreign); - - using foreign_dataset_type = mp::decay_t; - return create_update_impl_t::apply( - next_context, - false); - } - - template - read_context_ptr foreign_table_field_t - ::foreign_read(const read_context& context, bool fake_context) const - { - if (!fake_context) - { - auto& dataset = context.get(); - auto& member = this->field.getter(dataset); - auto new_context = make_read_context(member, context.schema, context.connection, context.filter); - using context_type = mp::decay_t; - return std::make_unique(new_context); - } - else - { - auto new_context = make_fake_context(hana::type_c, context.schema, context.connection, context.filter); - using context_type = mp::decay_t; - return std::make_unique(new_context); - } - } - - template - ::cppmariadb::statement& foreign_table_field_t - ::get_statement_foreign_one_delete(bool key_known) const - { - return base_type::get_statement_foreign_one_delete_impl( - key_known, - _statement_foreign_one_delete_key_known, - _statement_foreign_one_delete_key_unknown); - } - - template - ::cppmariadb::statement& foreign_table_field_t - ::get_statement_foreign_many_update() const - { return base_type::get_statement_foreign_many_update_impl(_statement_foreign_many_update); } - - namespace __impl - { - - /* is_primary_key_field */ - - template - struct is_primary_key_field - : public mp::decay_t().attributes, - schema::attribute::primary_key))> - { }; - - /* is_foreign_table_field */ - - template - struct is_foreign_table_field - : public mp::decay_t().tables, - schema::table::get_wrapped_dataset), - hana::type_c>))> - { }; - - /* field_type */ - - template - struct field_type - { using type = data_field_t; }; - - template - struct field_type>> - { using type = primary_key_field_t; }; - - template - struct field_type>> - { using type = foreign_table_field_t; }; - - template - using field_type_t = typename field_type::type; - - /* make_field_impl */ - - template - struct make_field_impl - { - template - static constexpr decltype(auto) apply(T_args&&... args) - { static_assert(sizeof...(args) == -1, "Invalid parameters for mariadb::make_field(...)!"); } - }; - - template - struct make_field_impl< - mp::list, - mp::enable_if_c< - schema::is_schema>::value - && schema::is_table >::value - && schema::is_field >::value>> - { - static constexpr decltype(auto) apply(const T_schema& schema, const T_table& table, const T_field& field) - { - using schema_type = mp::decay_t; - using field_type = mp::decay_t; - using return_type = field_type_t; - using primary_key_type = primary_key_field_t; - return_type ret(field); - ret.schema_name = schema.name; - ret.table_name = table.name; - ret.field_name = field.name; - ret.attributes = make_attributes(field.attributes); - hana::eval_if( - hana::equal(hana::type_c, hana::type_c), - [&ret](){ - ret.field_name = ret.table_name + "_" + ret.field_name; - }, [](){ }); - return ret; - } - }; - - } - -} -end_namespace_cpphibernate_driver_mariadb \ No newline at end of file diff --git a/include/cpphibernate/driver/mariadb/schema/fields.fwd.h b/include/cpphibernate/driver/mariadb/schema/fields.fwd.h deleted file mode 100644 index f2411a8..0000000 --- a/include/cpphibernate/driver/mariadb/schema/fields.fwd.h +++ /dev/null @@ -1,29 +0,0 @@ -#pragma once - -#include - -#include -#include -#include - -beg_namespace_cpphibernate_driver_mariadb -{ - - /* fields_t */ - - struct fields_t; - - /* make_fields */ - - namespace __impl - { - - template - struct make_fields_impl; - - } - - constexpr decltype(auto) make_fields = misc::make_generic_predicate<__impl::make_fields_impl> { }; - -} -end_namespace_cpphibernate_driver_mariadb \ No newline at end of file diff --git a/include/cpphibernate/driver/mariadb/schema/fields.h b/include/cpphibernate/driver/mariadb/schema/fields.h deleted file mode 100644 index 8eb5400..0000000 --- a/include/cpphibernate/driver/mariadb/schema/fields.h +++ /dev/null @@ -1,25 +0,0 @@ -#pragma once - -#include - -#include -#include -#include -#include -#include -#include - -beg_namespace_cpphibernate_driver_mariadb -{ - - /* fields_t */ - - struct fields_t - : public std::vector - { - using base_type = std::vector; - using base_type::base_type; - }; - -} -end_namespace_cpphibernate_driver_mariadb \ No newline at end of file diff --git a/include/cpphibernate/driver/mariadb/schema/fields.inl b/include/cpphibernate/driver/mariadb/schema/fields.inl deleted file mode 100644 index e5b044a..0000000 --- a/include/cpphibernate/driver/mariadb/schema/fields.inl +++ /dev/null @@ -1,55 +0,0 @@ -#pragma once - -#include - -beg_namespace_cpphibernate_driver_mariadb -{ - - namespace __impl - { - - /* make_fields_impl */ - - template - struct make_fields_impl - { - template - static constexpr decltype(auto) apply(T_args&&... args) - { static_assert(sizeof...(args) == -1, "Invalid parameters for mariadb::make_fields(...)!"); } - }; - - template - struct make_fields_impl< - mp::list, - mp::enable_if_c< - schema::is_schema>::value - && schema::is_table >::value>> - { - template - static constexpr void emplace(fields_t& fields, const T_schema& schema, const T_table& table, T_index&& index) - { - decltype(auto) field = make_field(schema, table, table.fields[index]); - using field_type = mp::decay_t; - fields.emplace_back(new field_type(std::move(field))); - } - - template - static auto helper(const T_schema& schema, const T_table& table, std::index_sequence&&) - { - fields_t fields; - int dummy[] = {0, (emplace(fields, schema, table, hana::size_c), void(), 0)...}; - (void) dummy; - return fields; - } - - static constexpr decltype(auto) apply(const T_schema& schema, const T_table& table) - { - using size = decltype(hana::size(table.fields)); - return helper(schema, table, std::make_index_sequence { }); - } - }; - - } - -} -end_namespace_cpphibernate_driver_mariadb \ No newline at end of file diff --git a/include/cpphibernate/driver/mariadb/schema/filter.fwd.h b/include/cpphibernate/driver/mariadb/schema/filter.fwd.h deleted file mode 100644 index 24e3510..0000000 --- a/include/cpphibernate/driver/mariadb/schema/filter.fwd.h +++ /dev/null @@ -1,13 +0,0 @@ -#pragma once - -#include - -beg_namespace_cpphibernate_driver_mariadb -{ - - /* filter_t */ - - struct filter_t; - -} -end_namespace_cpphibernate_driver_mariadb \ No newline at end of file diff --git a/include/cpphibernate/driver/mariadb/schema/filter.h b/include/cpphibernate/driver/mariadb/schema/filter.h deleted file mode 100644 index c1cc432..0000000 --- a/include/cpphibernate/driver/mariadb/schema/filter.h +++ /dev/null @@ -1,54 +0,0 @@ -#pragma once - -#include -#include -#include -#include -#include -#include - -beg_namespace_cpphibernate_driver_mariadb -{ - - namespace __impl - { - - /* filter_add_element */ - - template - struct filter_add_element_impl; - - constexpr decltype(auto) filter_add_element = misc::make_generic_predicate<__impl::filter_add_element_impl> { }; - - } - - /* filter_t */ - - struct filter_t - { - public: - using field_set_type = std::set; - using table_set_type = std::set; - - size_t cache_id { 0 }; - bool exclusive { true }; - field_set_type fields; - table_set_type tables; - - inline bool is_excluded(const table_t& table) const; - inline bool is_excluded(const field_t& field) const; - - template - inline void set_inclusive(const schema_t& schema, T_args&&... args); - - template - inline void set_exclusive(const schema_t& schema, T_args&&... args); - - inline void clear(); - - private: - void update_tables(); - }; - -} -end_namespace_cpphibernate_driver_mariadb \ No newline at end of file diff --git a/include/cpphibernate/driver/mariadb/schema/schema.fwd.h b/include/cpphibernate/driver/mariadb/schema/schema.fwd.h deleted file mode 100644 index 87217aa..0000000 --- a/include/cpphibernate/driver/mariadb/schema/schema.fwd.h +++ /dev/null @@ -1,27 +0,0 @@ -#pragma once - -#include -#include -#include - -beg_namespace_cpphibernate_driver_mariadb -{ - - /* schema_t */ - - struct schema_t; - - /* make_schema */ - - namespace __impl - { - - template - struct make_schema_impl; - - } - - constexpr decltype(auto) make_schema = misc::make_generic_predicate<__impl::make_schema_impl> { }; - -} -end_namespace_cpphibernate_driver_mariadb \ No newline at end of file diff --git a/include/cpphibernate/driver/mariadb/schema/schema.h b/include/cpphibernate/driver/mariadb/schema/schema.h deleted file mode 100644 index 2b3498b..0000000 --- a/include/cpphibernate/driver/mariadb/schema/schema.h +++ /dev/null @@ -1,37 +0,0 @@ -#pragma once - -#include -#include -#include -#include -#include - -beg_namespace_cpphibernate_driver_mariadb -{ - - /* schema_t */ - - struct schema_t - { - std::string schema_name; - tables_t tables; - - inline schema_t() = default; - inline schema_t(const schema_t&) = delete; - inline schema_t(schema_t&& other) - : schema_name(std::move(other).schema_name) - , tables (std::move(other).tables) - { update(); } - - void update (); - void print (std::ostream& os) const; - - const table_t& table(size_t dataset_id) const; - const field_t& field(size_t field_id) const; - - /* CRUD */ - void init(const init_context& context) const; - }; - -} -end_namespace_cpphibernate_driver_mariadb \ No newline at end of file diff --git a/include/cpphibernate/driver/mariadb/schema/schema.inl b/include/cpphibernate/driver/mariadb/schema/schema.inl deleted file mode 100644 index 8c1b6ed..0000000 --- a/include/cpphibernate/driver/mariadb/schema/schema.inl +++ /dev/null @@ -1,40 +0,0 @@ -#pragma once - -#include - -beg_namespace_cpphibernate_driver_mariadb -{ - - namespace __impl - { - - /* make_schema_impl */ - - template - struct make_schema_impl - { - template - static constexpr decltype(auto) apply(T_args&&... args) - { static_assert(sizeof...(args) == -1, "Invalid parameters for mariadb::make_schema(...)!"); } - }; - - template - struct make_schema_impl< - mp::list, - mp::enable_if_c< - schema::is_schema>::value>> - { - static decltype(auto) apply(const T_schema& schema) - { - schema_t ret; - ret.schema_name = schema.name; - ret.tables = make_tables(schema); - ret.update(); - return ret; - } - }; - - } - -} -end_namespace_cpphibernate_driver_mariadb \ No newline at end of file diff --git a/include/cpphibernate/driver/mariadb/schema/table.fwd.h b/include/cpphibernate/driver/mariadb/schema/table.fwd.h deleted file mode 100644 index d63f620..0000000 --- a/include/cpphibernate/driver/mariadb/schema/table.fwd.h +++ /dev/null @@ -1,31 +0,0 @@ -#pragma once - -#include -#include -#include - -beg_namespace_cpphibernate_driver_mariadb -{ - - /* table_t */ - - struct table_t; - - /* table_ptr_t */ - - using table_ptr_t = std::unique_ptr; - - /* make_table */ - - namespace __impl - { - - template - struct make_table_impl; - - } - - constexpr decltype(auto) make_table = misc::make_generic_predicate<__impl::make_table_impl> { }; - -} -end_namespace_cpphibernate_driver_mariadb \ No newline at end of file diff --git a/include/cpphibernate/driver/mariadb/schema/table.h b/include/cpphibernate/driver/mariadb/schema/table.h deleted file mode 100644 index 198077c..0000000 --- a/include/cpphibernate/driver/mariadb/schema/table.h +++ /dev/null @@ -1,208 +0,0 @@ -#pragma once - -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -beg_namespace_cpphibernate_driver_mariadb -{ - - /* table_t */ - - struct table_t - { - public: - size_t dataset_id { 0 }; - size_t base_dataset_id { 0 }; - size_t table_id { 0 }; - bool is_used_in_container { false }; - - std::vector derived_dataset_ids; - std::string table_name; - std::string schema_name; - - fields_t fields; - - const table_t* base_table { nullptr }; - std::vector derived_tables; - - const field_t* primary_key_field { nullptr }; - std::vector foreign_key_fields; - std::vector foreign_table_fields; - std::vector foreign_table_one_fields; - std::vector foreign_table_many_fields; - std::vector data_fields; - - inline table_t() = default; - inline table_t(const table_t&) = delete; - inline table_t(table_t&& other) - : dataset_id (std::move(other).dataset_id) - , base_dataset_id (std::move(other).base_dataset_id) - , table_id (std::move(other).table_id) - , derived_dataset_ids (std::move(other).derived_dataset_ids) - , table_name (std::move(other).table_name) - , schema_name (std::move(other).schema_name) - , fields (std::move(other).fields) - , base_table (nullptr) - , derived_tables () - , primary_key_field (nullptr) - , foreign_key_fields () - , foreign_table_fields () - , foreign_table_one_fields () - , foreign_table_many_fields () - , data_fields () - { } - virtual ~table_t() { }; - - void print(std::ostream& os) const; - - const table_t* get_derived_by_table_id(size_t id) const; - const table_t* get_derived_by_dataset_id(size_t id) const; - - virtual void emplace (const read_context& context) const; - std::string get_where_primary_key(const data_context& context) const; - std::string build_delete_query (const std::string* where) const; - - /* CRUD */ - inline void init_stage1(const init_context& context) const - { return init_stage1_exec(context); } - - inline void init_stage2(const init_context& context) const - { return init_stage2_exec(context); } - - inline decltype(auto) create_update(const create_update_context& context) const - { return create_update_intern(context); } - - inline void read(const read_context& context) const - { return read_exec(context); } - - inline void destroy(const destroy_context& context) const - { return destroy_intern(context); } - - private: - template - friend struct table_simple_t; - - template - friend struct table_polymorphic_t; - - using statement_ptr = std::unique_ptr<::cppmariadb::statement>; - using map_key = std::tuple; - using statement_map = std::map; - - mutable statement_ptr _statement_key_from_base; - mutable statement_ptr _statement_create_table; - mutable statement_ptr _statement_alter_table; - mutable statement_ptr _statement_insert_into; - mutable statement_map _statement_select_static; - mutable statement_map _statement_select_dynamic; - mutable statement_map _statement_update; - mutable statement_ptr _statement_foreign_many_delete; - mutable statement_ptr _statement_delete; - - ::cppmariadb::statement& get_statement_key_from_base() const; - ::cppmariadb::statement& get_statement_create_table() const; - ::cppmariadb::statement* get_statement_alter_table() const; - ::cppmariadb::statement* get_statement_insert_into() const; - ::cppmariadb::statement& get_statement_select(const read_context& context) const; - ::cppmariadb::statement* get_statement_update(const filter_t& filter, const field_t* owner) const; - ::cppmariadb::statement& get_statement_foreign_many_delete() const; - ::cppmariadb::statement& get_statement_delete() const; - - void execute_foreign_many_delete(const base_context& context) const; - - std::string execute_create_update( - const create_update_context& context, - ::cppmariadb::statement* statement) const; - - std::string get_primary_key(const data_context& context) const; - std::string get_key_from_base(const data_context& context) const; - - virtual std::string create_update_base(const create_update_context& context) const; - - protected: - using table_set = std::set; - - void init_stage1_exec (const init_context& context) const; - void init_stage2_exec (const init_context& context) const; - - virtual std::string create_update_intern(const create_update_context& context) const; - std::string create_update_exec (const create_update_context& context) const; - - void read_exec (const read_context& context) const; - - virtual void destroy_intern (const destroy_context& context) const; - void destroy_exec (const destroy_context& context) const; - void destroy_cleanup (const base_context& context, table_set& processed, bool check_derived, bool check_base) const; - }; - - /* table_simple_t */ - - template - struct table_simple_t - : public table_t - { - public: - using schema_type = T_schema; - using table_type = T_table; - using base_dataset_type = T_base_dataset; - using dataset_type = typename table_type::dataset_type; - - const schema_type& schema; - const table_type& table; - - inline table_simple_t(const schema_type& p_schema, const table_type& p_table) - : schema(p_schema) - , table (p_table) - { } - - inline table_simple_t(const table_simple_t&) = delete; - - inline table_simple_t(table_simple_t&& other) - : table_t(std::move(other)) - , schema (std::move(other).schema) - , table (std::move(other).table) - { } - }; - - /* table_polymorphic_t */ - - template - struct table_polymorphic_t - : public table_simple_t - { - public: - using base_type = table_simple_t; - using schema_type = typename base_type::schema_type; - using table_type = typename base_type::table_type; - using base_dataset_type = typename base_type::base_dataset_type; - using dataset_type = typename table_type::dataset_type; - using real_dataset_type = misc::real_dataset_t; - - using base_type::base_type; - - virtual void emplace(const read_context& context) const override; - - private: - template - constexpr void for_each_derived(T_dataset& dataset, const T_include_self& include_self, const T_pred& pred) const; - - protected: - virtual std::string create_update_intern(const create_update_context& context) const override; - virtual void destroy_intern (const destroy_context& context) const override; - - private: - virtual std::string create_update_base(const create_update_context& context) const override; - }; - -} -end_namespace_cpphibernate_driver_mariadb \ No newline at end of file diff --git a/include/cpphibernate/driver/mariadb/schema/table.inl b/include/cpphibernate/driver/mariadb/schema/table.inl deleted file mode 100644 index 9191096..0000000 --- a/include/cpphibernate/driver/mariadb/schema/table.inl +++ /dev/null @@ -1,218 +0,0 @@ -#pragma once - -#include -#include - -beg_namespace_cpphibernate_driver_mariadb -{ - - /* table_polymorphic_t */ - - template - void table_polymorphic_t - ::emplace(const read_context& context) const - { - hana::eval_if( - std::is_abstract { }, - [](){ - throw misc::hibernate_exception(std::string("can not create dataset of abstract type: ") - + utl::type_helper::name()); - }, - [&context, this](auto _){ - _(context).template emplace(this); - }); - } - - template - template - constexpr void table_polymorphic_t - ::for_each_derived(T_dataset& dataset, const T_include_self& include_self, const T_pred& pred) const - { - auto derived_types = hana::filter( - schema::get_all_derived_types(this->schema, hana::type_c), - [&](auto type){ - return hana::and_( - hana::not_(hana::trait(type)), - hana::or_( - type != hana::type_c, - include_self)); - }); - - hana::for_each(derived_types, [&](auto& type){ - using derived_type = misc::decay_unwrap_t; - auto* derived = dynamic_cast(&dataset); - if (derived) - pred(*derived); - }); - } - - template - std::string table_polymorphic_t - ::create_update_intern(const create_update_context& context) const - { - bool done = false; - auto& dataset = context.get(); - for_each_derived(dataset, hana::false_c, [&](auto& derived_dataset){ - if (!done) - { - using derived_dataset_type = mp::decay_t; - auto derived_dataset_id = misc::get_type_id(hana::type_c); - auto derived_table = this->get_derived_by_dataset_id(derived_dataset_id); - if (!derived_table) - { - throw misc::hibernate_exception(static_cast(std::ostringstream { } - << "unable to find derived table info for dataset '" - << utl::type_helper::name() << "'!").str()); - } - derived_table->create_update(change_context(context, derived_dataset)); - done = true; - } - }); - - return done - ? *this->primary_key_field->get(context) - : this->create_update_exec(context); - } - - template - void table_polymorphic_t - ::destroy_intern(const destroy_context& context) const - { - bool done = false; - auto& dataset = context.get(); - for_each_derived(dataset, hana::false_c, [&](auto& derived_dataset){ - if (!done) - { - using derived_dataset_type = mp::decay_t; - auto derived_dataset_id = misc::get_type_id(hana::type_c); - auto derived_table = this->get_derived_by_dataset_id(derived_dataset_id); - if (!derived_table) - { - throw misc::hibernate_exception(static_cast(std::ostringstream { } - << "unable to find derived table info for dataset '" - << utl::type_helper::name() << "'!").str()); - } - auto new_context = change_context(context, derived_dataset); - derived_table->destroy(new_context); - done = true; - } - }); - - if (!done) - { - this->destroy_exec(context); - } - } - - template - std::string table_polymorphic_t - ::create_update_base(const create_update_context& context) const - { - return hana::eval_if( - mp::is_same { }, - [this]()->std::string { - throw misc::hibernate_exception(static_cast(std::ostringstream { } - << "'" << this->table_name << "' does not have a base table").str()); - }, - [this, &context](auto _)->std::string { - using tmp_type = misc::decay_unwrap_t))>; - assert(this->base_table); - auto& dataset = context.get(); - auto& base = static_cast(dataset); - return this->base_table->create_update_exec(change_context(context, base)); - }); - } - - namespace __impl - { - - /* make_dataset_id_vector */ - - struct make_dataset_id_vector_impl - { - template - static constexpr decltype(auto) helper(const T_wrapped_datasets& wrapped_datasets, std::index_sequence) - { - return std::vector({ - misc::get_type_id(wrapped_datasets[hana::size_c])... - }); - } - - template - constexpr decltype(auto) operator()(T_wrapped_datasets&& wrapped_datasets) const - { - using size = mp::decay_t; - return helper(std::forward(wrapped_datasets), std::make_index_sequence { }); - } - }; - - static constexpr decltype(auto) make_dataset_id_vector = make_dataset_id_vector_impl { }; - - /* make_table_impl */ - - template - struct make_table_impl - { - template - static constexpr decltype(auto) apply(T_args&&... args) - { static_assert(sizeof...(args) == 0, "Invalid parameters for mariadb::make_table(...)!"); } - }; - - template - struct make_table_impl< - mp::list, - mp::enable_if_c< - schema::is_schema>::value - && schema::is_table >::value>> - { - - /* table_type */ - - template - struct table_type - { using type = table_simple_t, mp::decay_t, T_base_dataset>; }; - - template - struct table_type::value>> - { using type = table_polymorphic_t, mp::decay_t, T_base_dataset>; }; - - template - using table_type_t = typename table_type::type; - - /* apply */ - - static decltype(auto) apply(const T_schema& schema, const T_table& table) - { - using wrapped_base_type = mp::decay_t(), - std::declval().wrapped_dataset))>; - using base_type = misc::unwrap_t; - using derived_wrapped_types_type = mp::decay_t(), - std::declval().wrapped_dataset))>; - using wrapped_dataset_type = typename mp::decay_t::wrapped_dataset_type; - using dataset_type = misc::unwrap_t; - using real_dataset_type = misc::real_dataset_t; - using table_type = table_type_t; - - static_assert(mp::is_same::value, "table cn only be created for simple dataset types (not for containers)!"); - - table_type ret(schema, table); - ret.dataset_id = misc::get_type_id(table.wrapped_dataset); - ret.base_dataset_id = misc::get_type_id(wrapped_base_type { }); - ret.derived_dataset_ids = make_dataset_id_vector(derived_wrapped_types_type { }); - ret.table_id = hana::value(table.table_id); - ret.schema_name = schema.name; - ret.table_name = table.name; - ret.fields = make_fields(schema, table); - return ret; - } - }; - - } - -} -end_namespace_cpphibernate_driver_mariadb \ No newline at end of file diff --git a/include/cpphibernate/driver/mariadb/schema/tables.fwd.h b/include/cpphibernate/driver/mariadb/schema/tables.fwd.h deleted file mode 100644 index 78e0ebd..0000000 --- a/include/cpphibernate/driver/mariadb/schema/tables.fwd.h +++ /dev/null @@ -1,29 +0,0 @@ -#pragma once - -#include - -#include -#include -#include - -beg_namespace_cpphibernate_driver_mariadb -{ - - /* tables_t */ - - struct tables_t; - - /* make_tables */ - - namespace __impl - { - - template - struct make_tables_impl; - - } - - constexpr decltype(auto) make_tables = misc::make_generic_predicate<__impl::make_tables_impl> { }; - -} -end_namespace_cpphibernate_driver_mariadb \ No newline at end of file diff --git a/include/cpphibernate/driver/mariadb/schema/tables.h b/include/cpphibernate/driver/mariadb/schema/tables.h deleted file mode 100644 index c127cfb..0000000 --- a/include/cpphibernate/driver/mariadb/schema/tables.h +++ /dev/null @@ -1,23 +0,0 @@ -#pragma once - -#include - -#include -#include -#include -#include - -beg_namespace_cpphibernate_driver_mariadb -{ - - /* tables_t */ - - struct tables_t - : public std::map - { - using base_type = std::map; - using base_type::base_type; - }; - -} -end_namespace_cpphibernate_driver_mariadb \ No newline at end of file diff --git a/include/cpphibernate/driver/mariadb/schema/tables.inl b/include/cpphibernate/driver/mariadb/schema/tables.inl deleted file mode 100644 index acabb9e..0000000 --- a/include/cpphibernate/driver/mariadb/schema/tables.inl +++ /dev/null @@ -1,55 +0,0 @@ -#pragma once - -#include - -beg_namespace_cpphibernate_driver_mariadb -{ - - namespace __impl - { - - /* make_tables_impl */ - - template - struct make_tables_impl - { - template - static constexpr decltype(auto) apply(T_args&&... args) - { static_assert(sizeof...(args) == -1, "Invalid parameters for mariadb::make_tables(...)!"); } - }; - - template - struct make_tables_impl< - mp::list, - mp::enable_if_c< - schema::is_schema>::value>> - { - template - static constexpr void emplace(tables_t& tables, const T_schema& schema, T_index&& index) - { - decltype(auto) table = make_table(schema, schema.tables[index]); - using table_type = mp::clean_type; - auto key = table.dataset_id; - tables.emplace(key, std::make_unique(std::move(table))); - } - - template - static decltype(auto) helper(const T_schema& schema, std::index_sequence) - { - tables_t tables; - int dummy[] = {0, (emplace(tables, schema, hana::size_c), void(), 0)...}; - (void) dummy; - return tables; - } - - static constexpr decltype(auto) apply(const T_schema& schema) - { - using size = decltype(hana::size(schema.tables)); - return helper(schema, std::make_index_sequence { }); - } - }; - - } - -} -end_namespace_cpphibernate_driver_mariadb \ No newline at end of file diff --git a/include/cpphibernate/driver/mariadb/types.h b/include/cpphibernate/driver/mariadb/types.h new file mode 100644 index 0000000..61bac52 --- /dev/null +++ b/include/cpphibernate/driver/mariadb/types.h @@ -0,0 +1,13 @@ +#pragma once + +#include + +namespace cpphibernate { +namespace mariadb { + + /** + * @brief Value received from the database. + */ + using value_t = cppcore::nullable; + +} } diff --git a/include/cpphibernate/misc.h b/include/cpphibernate/misc.h index 04de4ef..1a581da 100644 --- a/include/cpphibernate/misc.h +++ b/include/cpphibernate/misc.h @@ -1,8 +1,9 @@ #pragma once -#include -#include -#include -#include -#include -#include \ No newline at end of file +#include "misc/equality_compare.h" +#include "misc/print_container.h" +#include "misc/printing.h" +#include "misc/type_helper.h" + +#include "misc/print_container.inl" +#include "misc/type_helper.inl" diff --git a/include/cpphibernate/misc/container_helper.h b/include/cpphibernate/misc/container_helper.h deleted file mode 100644 index 0adc64d..0000000 --- a/include/cpphibernate/misc/container_helper.h +++ /dev/null @@ -1,52 +0,0 @@ -#pragma once - -#include -#include -#include - -#include - -beg_namespace_cpphibernate_misc -{ - - /* container_helper */ - - template - struct container_helper; - - template - struct container_helper, void> - { - using container_type = std::vector; - using value_type = T_value; - - template - static inline value_type& emplace(container_type& container, T_xargs&&... args) - { - container.emplace_back(std::forward(args)...); - return container.back(); - } - - static inline void clear(container_type& container) - { container.clear(); } - }; - - template - struct container_helper, void> - { - using container_type = std::list; - using value_type = T_value; - - template - static inline value_type& emplace(container_type& container, T_xargs&&... args) - { - container.emplace_back(std::forward(args)...); - return container.back(); - } - - static inline void clear(container_type& container) - { container.clear(); } - }; - -} -end_namespace_cpphibernate_misc \ No newline at end of file diff --git a/include/cpphibernate/misc/equality_compare.h b/include/cpphibernate/misc/equality_compare.h new file mode 100644 index 0000000..033f005 --- /dev/null +++ b/include/cpphibernate/misc/equality_compare.h @@ -0,0 +1,62 @@ +#pragma once + +#include + +namespace cpphibernate +{ + + namespace __impl + { + + template + using equality_compare_equal = mp::bool_t>, + hana::type_c>))>; + + template + using equality_compare_not_equal = mp::bool_t>, + hana::type_c>))>; + + } + + /** + * @brief Tag all objects that are equality comparable to each other. + * + * @tparam T_group Type used for groupig different types. + */ + template + struct tag_equality_comparable + { + static constexpr decltype(auto) equality_compare_group = hana::type_c; + }; + + /** + * @brief Equality compare for all objects that has the quality compare tag + */ + template + constexpr auto operator == (T_lhs&&, T_rhs&&) + -> mp::enable_if_t< + mp::is_valid_v().equality_compare_group)> + && mp::is_valid_v().equality_compare_group)> + && mp::is_same_v< + typename decltype(+std::declval().equality_compare_group)::type, + typename decltype(+std::declval().equality_compare_group)::type>, + __impl::equality_compare_equal> + { return __impl::equality_compare_equal { }; } + + /** + * @brief Equality compare for all objects that has the quality compare tag + */ + template + constexpr auto operator != (T_lhs&&, T_rhs&&) + -> mp::enable_if_t< + mp::is_valid_v().equality_compare_group)> + && mp::is_valid_v().equality_compare_group)> + && mp::is_same_v< + typename decltype(+std::declval().equality_compare_group)::type, + typename decltype(+std::declval().equality_compare_group)::type>, + __impl::equality_compare_not_equal> + { return __impl::equality_compare_not_equal { }; } + +} diff --git a/include/cpphibernate/misc/general.h b/include/cpphibernate/misc/general.h deleted file mode 100644 index 037db6c..0000000 --- a/include/cpphibernate/misc/general.h +++ /dev/null @@ -1,32 +0,0 @@ -#pragma once - -#include -#include - -beg_namespace_cpphibernate_misc -{ - - /* make_generic_predicate */ - - template class T_builder> - struct make_generic_predicate - { - template - constexpr decltype(auto) operator()(T_args&&... args) const - { return T_builder>::apply(std::forward(args)...); } - }; - - /* get_dataset_id */ - - namespace __impl - { - struct counter_type_id - { }; - } - - template - constexpr decltype(auto) get_type_id(T_type&&) - { return utl::get_unique_id<__impl::counter_type_id, mp::decay_t>(); } - -} -end_namespace_cpphibernate_misc \ No newline at end of file diff --git a/include/cpphibernate/misc/meta.h b/include/cpphibernate/misc/meta.h deleted file mode 100644 index df78aff..0000000 --- a/include/cpphibernate/misc/meta.h +++ /dev/null @@ -1,152 +0,0 @@ -#pragma once - -#include -#include -#include - -#include - -#include -#include - -beg_namespace_cpphibernate_misc -{ - - namespace __impl - { - - /* is_container_impl */ - - template - struct is_container_impl - : public mp::c_false_t - { }; - - template - struct is_container_impl> - : public mp::c_true_t - { }; - - template - struct is_container_impl> - : public mp::c_true_t - { }; - - /* is_nullable_impl */ - - template - struct is_nullable_impl - : public mp::c_false_t - { }; - - template - struct is_nullable_impl> - : public mp::c_true_t - { }; - - template - struct is_nullable_impl> - : public mp::c_true_t - { }; - - template - struct is_nullable_impl> - : public mp::c_true_t - { }; - - /* is_pointer_impl */ - - template - struct is_pointer_impl - : public mp::c_false_t - { }; - - template - struct is_pointer_impl> - : public mp::c_true_t - { }; - - template - struct is_pointer_impl> - : public mp::c_true_t - { }; - - /* is_ordered_impl */ - - template - struct is_ordered_impl - : public mp::c_false_t - { }; - - template - struct is_ordered_impl> - : public mp::c_true_t - { }; - - template - struct is_ordered_impl> - : public mp::c_true_t - { }; - - /* real_dataset_impl */ - - template - struct real_dataset_impl - { using type = T; }; - - template - struct real_dataset_impl, void> - { using type = typename real_dataset_impl::type; }; - - template - struct real_dataset_impl, void> - { using type = typename real_dataset_impl::type; }; - - template - struct real_dataset_impl, void> - { using type = typename real_dataset_impl::type; }; - - template - struct real_dataset_impl, void> - { using type = typename real_dataset_impl::type; }; - - template - struct real_dataset_impl, void> - { using type = typename real_dataset_impl::type; }; - } - - /* meta */ - - template - struct is_container - : public __impl::is_container_impl - { }; - - template - struct is_nullable - : public __impl::is_nullable_impl - { }; - - template - struct is_ordered - : public __impl::is_ordered_impl - { }; - - template - struct is_pointer - : public __impl::is_pointer_impl - { }; - - template - using real_dataset_t = typename __impl::real_dataset_impl::type; - - /* hibernate_exception */ - - struct hibernate_exception - : public utl::exception - { - using utl::exception::exception; - }; - -} -end_namespace_cpphibernate_misc \ No newline at end of file diff --git a/include/cpphibernate/misc/nullable_helper.h b/include/cpphibernate/misc/nullable_helper.h deleted file mode 100644 index 5ed9ea9..0000000 --- a/include/cpphibernate/misc/nullable_helper.h +++ /dev/null @@ -1,120 +0,0 @@ -#pragma once - -#include - -#include -#include - -#include - -beg_namespace_cpphibernate_misc -{ - - /* nullable_helper */ - - template - struct nullable_helper - { - using nullable_type = T_nullable; - using value_type = real_dataset_t; - - static value_type* get (const nullable_type&) = delete; - static value_type& set (nullable_type&, const value_type&) = delete; - static void clear (nullable_type&) = delete; - }; - - /* nullable_helper - utl::nullable */ - - template - struct nullable_helper, void> - { - using nullable_type = utl::nullable; - using value_type = T_value; - - static inline value_type* get(nullable_type& x) - { return x.has_value() ? &x.value() : nullptr; } - - static inline const value_type* get(const nullable_type& x) - { return x.has_value() ? &x.value() : nullptr; } - - static inline value_type& set(nullable_type& x, const value_type* value) - { return *(x = *value); } - - static inline value_type& set(nullable_type& x, const value_type& value) - { return *(x = value); } - - static inline value_type& set(nullable_type& x, value_type&& value) - { return *(x = std::move(value)); } - - static void clear(nullable_type& x) - { x.reset(); } - }; - - /* nullable_helper - std::unique_ptr */ - - template - struct nullable_helper, void> - { - using nullable_type = std::unique_ptr; - using value_type = T_value; - - static inline value_type* get(const nullable_type& x) - { return x.get(); } - - static inline value_type& set(nullable_type& x, value_type* value) - { - x.reset(value); - return *x; - } - - static inline value_type& set(nullable_type& x, const value_type& value) - { - x.reset(new value_type(value)); - return *x; - } - - static inline value_type& set(nullable_type& x, value_type&& value) - { - x.reset(new value_type(std::move(value))); - return *x; - } - - static void clear(nullable_type& x) - { return x.reset(); } - }; - - /* nullable_helper - std::shared_ptr */ - - template - struct nullable_helper, void> - { - using nullable_type = std::shared_ptr; - using value_type = T_value; - - static inline value_type* get(const nullable_type& x) - { return x.get(); } - - static inline value_type& set(nullable_type& x, value_type* value) - { - x.reset(value); - return *x; - } - - static inline value_type& set(nullable_type& x, const value_type& value) - { - x.reset(new value_type(value)); - return *x; - } - - static inline value_type& set(nullable_type& x, value_type&& value) - { - x.reset(new value_type(std::move(value))); - return *x; - } - - static void clear(nullable_type& x) - { return x.reset(); } - }; - -} -end_namespace_cpphibernate_misc \ No newline at end of file diff --git a/include/cpphibernate/misc/print.h b/include/cpphibernate/misc/print.h deleted file mode 100644 index dcf7c06..0000000 --- a/include/cpphibernate/misc/print.h +++ /dev/null @@ -1,112 +0,0 @@ -#pragma once - -#include -#include -#include -#include - -#include -#include - -namespace std -{ - - template - inline auto operator <<(basic_ostream& os, X&& x) - -> ::utl::mp::enable_if< - utl::mp::is_valid(x).print(os))>, - basic_ostream&> - { - std::forward(x).print(os); - return os; - } - -} - -beg_namespace_cpphibernate_misc -{ - - template - struct container_printer - { - T_container container; - bool do_indent; - T_func func; - - inline void print(std::ostream& os) const - { - using namespace ::utl; - auto beg = std::begin(container); - auto end = std::end (container); - if (beg != end) - { - if (do_indent) - { - os << indent << "[" - << incindent; - } - else - { - os << "["; - } - - size_t index = 0; - for (auto it = beg; it != end; ++it) - { - if (index++) - os << ","; - if (!do_indent) - os << " "; - func(os, *it); - } - - if (do_indent) - { - os << decindent - << indent << "]"; - } - else - { - os << " ]"; - } - } - else - { - os << "[ ]"; - } - } - }; - - struct print_container_builder - { - template - constexpr decltype(auto) operator()(T_container&& container, bool do_indent, T_func&& func) const - { - return container_printer - { - std::forward(container), - do_indent, - std::forward(func) - }; - } - - template - constexpr decltype(auto) operator()(T_container&& container, bool do_indent) const - { - return this->operator()( - std::forward(container), - do_indent, - [do_indent](auto& os, auto& value) - { - using namespace ::utl; - if (do_indent) - os << indent; - os << utl::to_string(value); - }); - } - }; - - constexpr decltype(auto) print_container = print_container_builder { }; - -} -end_namespace_cpphibernate_misc \ No newline at end of file diff --git a/include/cpphibernate/misc/print_container.h b/include/cpphibernate/misc/print_container.h new file mode 100644 index 0000000..50df3a4 --- /dev/null +++ b/include/cpphibernate/misc/print_container.h @@ -0,0 +1,26 @@ +#pragma once + +#include + +namespace cpphibernate +{ + + namespace __impl + { + + /** + * @brief Helper type to build print container. + */ + template + struct print_container_builder; + + } + + /** + * @brief Predicate to create print container from the passed attributes. + */ + constexpr decltype(auto) make_print_container = mp::generic_predicate<__impl::print_container_builder> { }; + +} + +#include "print_container.inl" diff --git a/include/cpphibernate/misc/print_container.inl b/include/cpphibernate/misc/print_container.inl new file mode 100644 index 0000000..eef7839 --- /dev/null +++ b/include/cpphibernate/misc/print_container.inl @@ -0,0 +1,144 @@ +#pragma once + +#include +#include + +#include "print_container.h" + +namespace cpphibernate +{ + + namespace __impl + { + + /* print_container */ + + template + struct print_container + { + using container_type = T_container; + using func_type = T_func; + + container_type container; + bool do_indent; + func_type func; + + inline void print(std::ostream& os) const + { + using namespace ::cppcore; + + auto beg = std::begin(container); + auto end = std::end (container); + if (beg != end) + { + if (do_indent) + { + os << indent << "[" + << incindent; + } + else + { + os << "["; + } + + size_t index = 0; + for (auto it = beg; it != end; ++it) + { + if (index++) + os << ","; + if (!do_indent) + os << " "; + func(os, *it); + } + + if (do_indent) + { + os << decindent + << indent << "]"; + } + else + { + os << " ]"; + } + } + else + { + os << "[ ]"; + } + } + }; + + /* print_container_builder */ + + template + struct print_container_builder + { + template + static constexpr decltype(auto) apply(T_args&&...) + { static_assert(sizeof...(T_args) == -1, "Invalid parameters for make_print_container(...)!"); } + }; + + template + struct print_container_builder< + mp::list, + mp::enable_if_t>>> + { + static constexpr decltype(auto) apply(T_container&& container, bool do_indent, T_func&& func) + { + using print_container_type = print_container; + return print_container_type { + std::forward(container), + do_indent, + std::forward(func), + }; + } + }; + + template + struct print_container_builder< + mp::list, + mp::enable_if_t>>> + { + static constexpr decltype(auto) apply(T_container&& container, bool do_indent) + { + using namespace ::cppcore; + + return make_print_container( + std::forward(container), + do_indent, + [do_indent](auto& os, auto& value) + { + using namespace ::cppcore; + if (do_indent) + os << indent; + os << to_string(value); + }); + } + }; + + } + +} + +namespace std +{ + + template< + typename T_print_container, + typename T_char, + typename T_traits> + inline auto + operator << ( + basic_ostream& os, + const T_print_container& ctnr) + -> ::cppmp::enable_if_t< + ::cppmp::is_specialization_of_v< + ::cppmp::decay_t, + ::cpphibernate::__impl::print_container>, + basic_ostream&> + { + ctnr.print(os); + return os; + } + +} diff --git a/include/cpphibernate/misc/printing.h b/include/cpphibernate/misc/printing.h new file mode 100644 index 0000000..ab187f4 --- /dev/null +++ b/include/cpphibernate/misc/printing.h @@ -0,0 +1,20 @@ +#pragma once + +#include + +#include + +namespace std +{ + + template + inline auto operator <<(basic_ostream& os, X&& x) + -> ::cppmp::enable_if_t< + ::cppmp::is_valid_v(x).print(os))>, + basic_ostream&> + { + std::forward(x).print(os); + return os; + } + +} diff --git a/include/cpphibernate/misc/type_helper.h b/include/cpphibernate/misc/type_helper.h new file mode 100644 index 0000000..2f8a0b7 --- /dev/null +++ b/include/cpphibernate/misc/type_helper.h @@ -0,0 +1,95 @@ +#pragma once + +#include + +namespace cpphibernate +{ + + /** + * @brief Evaluates to the basic dataset type (containers are removed). + */ + template + struct real_dataset; + + /** + * @brief Basic dataset type (containers are removed). + */ + template + using real_dataset_t = typename real_dataset::type; + + /** + * @brief Extract the type stored in an type definition . + */ + template + using unwrap_t = typename T::type; + + /** + * @brief Extract the decayed type in an type definition. + */ + template + using decay_unwrap_t = typename mp::decay_t::type; + + /** + * @brief Unwrap the passed hana::type and return an object of the inner type. + */ + template + constexpr decltype(auto) unwrap(T&& t) + { return typename decltype(+t)::type { }; } + + /** + * @brief Get a unique ID for the passed type + */ + template + constexpr decltype(auto) get_type_id(T_type&&); + + /** + * @brief Evaluates to true_t if the passed type is a container, false_t otherwise. + */ + template + struct is_container; + + /** + * @brief Is true if the passed type is a container, false otherwise. + */ + template + constexpr decltype(auto) is_container_v = is_container::value; + + /** + * @brief Evaluates to true_t if the passed type is an ordered container type, false_t otherwise. + */ + template + struct is_ordered; + + /** + * @brief Is true if the passed type is an ordered container type, false otherwise. + */ + template + constexpr decltype(auto) is_ordered_v = is_ordered::value; + + /** + * @brief Evaluates to true_t if the passed type is a nullable type, false_t otherwise. + */ + template + struct is_pointer; + + /** + * @brief Is true if the passed type is a pointer type, false otherwise. + */ + template + constexpr decltype(auto) is_pointer_v = is_pointer::value; + + /** + * @brief Evaluates to true_t if the passed type is a pointer type, false_t otherwise. + */ + template + struct is_nullable; + + /** + * @brief Is true if the passed type is a nullable type, false otherwise. + */ + template + constexpr decltype(auto) is_nullable_v = is_nullable::value; + +} + +#include "type_helper.inl" diff --git a/include/cpphibernate/misc/type_helper.inl b/include/cpphibernate/misc/type_helper.inl new file mode 100644 index 0000000..a57541e --- /dev/null +++ b/include/cpphibernate/misc/type_helper.inl @@ -0,0 +1,129 @@ +#pragma once + +#include "type_helper.h" + +#include +#include +#include + +#include +#include +#include + +namespace cpphibernate +{ + + namespace __impl + { + + /* real_dataset_impl */ + + template + struct real_dataset_impl + { using type = T; }; + + template + struct real_dataset_impl, void> + { using type = typename real_dataset_impl::type; }; + + template + struct real_dataset_impl, void> + { using type = typename real_dataset_impl::type; }; + + template + struct real_dataset_impl, void> + { using type = typename real_dataset_impl::type; }; + + template + struct real_dataset_impl, void> + { using type = typename real_dataset_impl::type; }; + + template + struct real_dataset_impl, void> + { using type = typename real_dataset_impl::type; }; + + } + + /* real_dataset */ + + template + struct real_dataset + : public __impl::real_dataset_impl + { }; + + /* get_dataset_id */ + + namespace __impl + { + struct counter_type_id + { }; + } + + template + constexpr decltype(auto) get_type_id(T_type&&) + { return cppcore::get_unique_id<__impl::counter_type_id, mp::decay_t>(); } + + /* is_container */ + + template + struct is_container + : public mp::false_t + { }; + + template + struct is_container> + : public mp::true_t + { }; + + template + struct is_container> + : public mp::true_t + { }; + + /* is_ordered */ + + template + struct is_ordered + : public mp::false_t + { }; + + template + struct is_ordered> + : public mp::true_t + { }; + + template + struct is_ordered> + : public mp::true_t + { }; + + /* is_pointer */ + + template + struct is_pointer + : public mp::false_t + { }; + + template + struct is_pointer> + : public mp::true_t + { }; + + template + struct is_pointer> + : public mp::true_t + { }; + + /* is_nullable */ + + template + struct is_nullable + : public is_pointer + { }; + + template + struct is_nullable> + : public mp::true_t + { }; + +} diff --git a/include/cpphibernate/misc/wrap.h b/include/cpphibernate/misc/wrap.h deleted file mode 100644 index f5126f2..0000000 --- a/include/cpphibernate/misc/wrap.h +++ /dev/null @@ -1,44 +0,0 @@ -#pragma once - -#include -#include -#include - -#include - -#include - -beg_namespace_cpphibernate_misc -{ - - template - using unwrap_t = typename T::type; - - template - using decay_unwrap_t = typename mp::decay_t::type; - - namespace __impl - { - - struct wrap_t - { - template - constexpr decltype(auto) operator()(T) const noexcept - { return hana::type_c; } - }; - - struct unwrapped_t - { - template - constexpr decltype(auto) operator()(T) const noexcept - { return unwrap_t { }; } - }; - - } - - constexpr decltype(auto) wrap = __impl::wrap_t { }; - - constexpr decltype(auto) unwrap = __impl::unwrapped_t { }; - -} -end_namespace_cpphibernate_misc \ No newline at end of file diff --git a/include/cpphibernate/modifier.h b/include/cpphibernate/modifier.h index 36dfe11..e323e54 100644 --- a/include/cpphibernate/modifier.h +++ b/include/cpphibernate/modifier.h @@ -1,8 +1,39 @@ #pragma once -#include -#include -#include -#include -#include -#include \ No newline at end of file + + +#include "modifier/modifier.h" +#include "modifier/modifiers.h" + +#include "modifier/limit.h" +#include "modifier/offset.h" + +#include "modifier/order_by.h" +#include "modifier/order_by/ascending.h" +#include "modifier/order_by/descending.h" + +#include "modifier/where.h" +#include "modifier/where/where_clause.h" +#include "modifier/where/equal.h" +#include "modifier/where/negation.h" +#include "modifier/where/conjunction.h" +#include "modifier/where/disjunction.h" + + + +#include "modifier/modifier.inl" +#include "modifier/modifiers.inl" + +#include "modifier/limit.inl" +#include "modifier/offset.inl" + +#include "modifier/order_by.inl" +#include "modifier/order_by/ascending.inl" +#include "modifier/order_by/descending.inl" + +#include "modifier/where.inl" +#include "modifier/where/where_clause.inl" +#include "modifier/where/equal.inl" +#include "modifier/where/negation.inl" +#include "modifier/where/conjunction.inl" +#include "modifier/where/disjunction.inl" diff --git a/include/cpphibernate/modifier/limit.h b/include/cpphibernate/modifier/limit.h index 39c3256..80f8b21 100644 --- a/include/cpphibernate/modifier/limit.h +++ b/include/cpphibernate/modifier/limit.h @@ -1,39 +1,38 @@ #pragma once -#include #include -#include -beg_namespace_cpphibernate_modifier +namespace cpphibernate { namespace __impl { - /* limit_t */ - - template - struct limit_t - : public modifier_t - { - using value_type = T_value; - - value_type value; - }; + /** + * @brief Helper class to build limit objects. + */ + template + struct limit_builder; } - /* meta */ - + /** + * @brief Evaluates to true_t if the passed type is a limit modifier, false_t otherwise. + */ template - struct is_limit_modifier - : public mp::is_specialization_of - { }; + struct is_limit_modifier; - /* make */ + /** + * @brief Is true if the passed type is a limit modifier, false otherwise. + */ + template + constexpr decltype(auto) is_limit_modifier_v = is_limit_modifier::value; - template - constexpr decltype(auto) limit = __impl::limit_t> { }; + /** + * @brief Predicate to create new limit objects. + */ + constexpr decltype(auto) limit = mp::generic_predicate<__impl::limit_builder> { }; } -end_namespace_cpphibernate_modifier \ No newline at end of file + +#include "limit.inl" diff --git a/include/cpphibernate/modifier/limit.inl b/include/cpphibernate/modifier/limit.inl new file mode 100644 index 0000000..b54be4b --- /dev/null +++ b/include/cpphibernate/modifier/limit.inl @@ -0,0 +1,50 @@ +#pragma once + +#include "limit.h" + +namespace cpphibernate +{ + + namespace __impl + { + + /* limit_t */ + + template + struct limit_t + : public mp::size_t + , public tag_modifier + , public tag_equality_comparable + { }; + + /* limit_builder */ + + template + struct limit_builder + { + template + static constexpr decltype(auto) apply(T_args&&...) + { static_assert(sizeof...(T_args) == -1, "Invalid parameters for limit(...)!"); } + }; + + template + struct limit_builder< + mp::list, + mp::enable_if_t< + mp::is_valid_v + && mp::is_integral_v>>> + { + static constexpr decltype(auto) apply(T&&) + { return limit_t { }; } + }; + + } + + /* is_limit_modifier */ + + template + struct is_limit_modifier + : public mp::is_specialization_of + { }; + +} diff --git a/include/cpphibernate/modifier/modifier.h b/include/cpphibernate/modifier/modifier.h index 5e24e90..d047d02 100644 --- a/include/cpphibernate/modifier/modifier.h +++ b/include/cpphibernate/modifier/modifier.h @@ -2,30 +2,31 @@ #include -beg_namespace_cpphibernate_modifier +namespace cpphibernate { namespace __impl { - /* modifier_t */ - - struct modifier_t - { }; + /** + * @brief Tag class all modifiers are derived from. + */ + struct tag_modifier; } - /* meta */ - + /** + * @brief Evaluates to true_t if the passed type is a modifier, false_t otherwise. + */ template - struct is_modifier - : public mp::is_base_of<__impl::modifier_t, T> - { }; + struct is_modifier; - template - struct all_are_modifiers - : public mp::all_true::value...> - { }; + /** + * @brief Is true if the passed type is a modifier, false otherwise. + */ + template + constexpr decltype(auto) is_modifier_v = is_modifier::value; } -end_namespace_cpphibernate_modifier \ No newline at end of file + +#include "modifier.inl" diff --git a/include/cpphibernate/modifier/modifier.inl b/include/cpphibernate/modifier/modifier.inl new file mode 100644 index 0000000..cf3019e --- /dev/null +++ b/include/cpphibernate/modifier/modifier.inl @@ -0,0 +1,25 @@ +#pragma once + +#include "modifier.h" + +namespace cpphibernate +{ + + namespace __impl + { + + /* tag_modifier */ + + struct tag_modifier + { }; + + } + + /* is_modifier */ + + template + struct is_modifier + : public mp::is_base_of<__impl::tag_modifier, T> + { }; + +} diff --git a/include/cpphibernate/modifier/modifiers.h b/include/cpphibernate/modifier/modifiers.h index f1b5782..2488a32 100644 --- a/include/cpphibernate/modifier/modifiers.h +++ b/include/cpphibernate/modifier/modifiers.h @@ -1,65 +1,38 @@ #pragma once -#include #include -#include -beg_namespace_cpphibernate_modifier +namespace cpphibernate { namespace __impl { - /* is_modifiers_impl */ - + /** + * @brief Helper type to build modifiers. + */ template - struct is_modifiers_impl - : mp::c_false_t - { }; - - template - struct is_modifiers_impl, mp::enable_if>> - : mp::c_true_t - { }; + struct modifiers_builder; } - /* meta */ - + /** + * @brief Evaluates to true_t if the passed type is a modifiers type. + */ template - struct is_modifiers - : public __impl::is_modifiers_impl - { }; - - /* operations */ - - namespace __impl - { + struct is_modifiers; - /* make_modifiers_impl */ - - template - struct make_modifiers_impl - { - template - static constexpr decltype(auto) apply(T_args&&...) - { static_assert(sizeof...(T_args) == -1, "Invalid parameters for hibernate::schema::modifier::make(...)!"); } - }; - - template - struct make_modifiers_impl< - mp::list, - mp::enable_if_c< - all_are_modifiers...>::value>> - { - template - static constexpr decltype(auto) apply(T_args&&... args) - { return hana::make_basic_tuple(std::forward(args)...); } - }; - - } + /** + * @brief Is true if the passed type is an modifiers type, false otherwise. + */ + template + constexpr bool is_modifiers_v = is_modifiers::value; - constexpr decltype(auto) make_list = misc::make_generic_predicate<__impl::make_modifiers_impl> { }; + /** + * @brief Predicate to create an modifiers object from the passed modifiers. + */ + constexpr decltype(auto) make_modifiers = mp::generic_predicate<__impl::modifiers_builder> { }; } -end_namespace_cpphibernate_modifier \ No newline at end of file + +#include "modifiers.inl" diff --git a/include/cpphibernate/modifier/modifiers.inl b/include/cpphibernate/modifier/modifiers.inl new file mode 100644 index 0000000..bfcdb1e --- /dev/null +++ b/include/cpphibernate/modifier/modifiers.inl @@ -0,0 +1,63 @@ +#pragma once + +#include "modifier.h" +#include "modifiers.h" + +namespace cpphibernate +{ + + namespace __impl + { + + /* modifiers_t */ + + template + using modifiers_t = hana::basic_tuple; + + /* modifiers_builder */ + + template + struct modifiers_builder + { + template + static constexpr decltype(auto) apply(T_args&&...) + { static_assert(sizeof...(T_args) == -1, "Invalid parameters for make_modifiers(...)!"); } + }; + + template + struct modifiers_builder< + mp::list, + mp::enable_if_t< + mp::is_true_v>...>>> + { + static constexpr decltype(auto) apply(T_args&&... args) + { + using modifiers_type = modifiers_t; + return modifiers_type(std::forward(args)...); + } + }; + + /* is_modifiers_impl */ + + template + struct is_modifiers_impl + : mp::false_t + { }; + + template + struct is_modifiers_impl< + modifiers_t, + mp::enable_if_t>...>>> + : mp::true_t + { }; + + } + + /* is_modifiers */ + + template + struct is_modifiers + : public __impl::is_modifiers_impl + { }; + +} diff --git a/include/cpphibernate/modifier/offset.h b/include/cpphibernate/modifier/offset.h index 20bbcec..cbad170 100644 --- a/include/cpphibernate/modifier/offset.h +++ b/include/cpphibernate/modifier/offset.h @@ -1,39 +1,38 @@ #pragma once -#include #include -#include -beg_namespace_cpphibernate_modifier +namespace cpphibernate { namespace __impl { - /* offset_t */ - - template - struct offset_t - : public modifier_t - { - using value_type = T_value; - - value_type value; - }; + /** + * @brief Helper class to build offset objects. + */ + template + struct offset_builder; } - /* meta */ - + /** + * @brief Evaluates to true_t if the passed type is a offset modifier, false_t otherwise. + */ template - struct is_offset - : public mp::is_specialization_of - { }; + struct is_offset_modifier; - /* make */ + /** + * @brief Is true if the passed type is a offset modifier, false otherwise. + */ + template + constexpr decltype(auto) is_offset_modifier_v = is_offset_modifier::value; - template - constexpr decltype(auto) offset = __impl::offset_t> { }; + /** + * @brief Predicate to create new offset objects. + */ + constexpr decltype(auto) offset = mp::generic_predicate<__impl::offset_builder> { }; } -end_namespace_cpphibernate_modifier \ No newline at end of file + +#include "offset.inl" diff --git a/include/cpphibernate/modifier/offset.inl b/include/cpphibernate/modifier/offset.inl new file mode 100644 index 0000000..2e3f7ff --- /dev/null +++ b/include/cpphibernate/modifier/offset.inl @@ -0,0 +1,50 @@ +#pragma once + +#include "offset.h" + +namespace cpphibernate +{ + + namespace __impl + { + + /* offset_t */ + + template + struct offset_t + : public mp::size_t + , public tag_modifier + , public tag_equality_comparable + { }; + + /* offset_builder */ + + template + struct offset_builder + { + template + static constexpr decltype(auto) apply(T_args&&...) + { static_assert(sizeof...(T_args) == -1, "Invalid parameters for offset(...)!"); } + }; + + template + struct offset_builder< + mp::list, + mp::enable_if_t< + mp::is_valid_v + && mp::is_integral_v>>> + { + static constexpr decltype(auto) apply(T&&) + { return offset_t { }; } + }; + + } + + /* is_offset_modifier */ + + template + struct is_offset_modifier + : public mp::is_specialization_of + { }; + +} diff --git a/include/cpphibernate/modifier/order_by.h b/include/cpphibernate/modifier/order_by.h index 515c0b9..c4f580a 100644 --- a/include/cpphibernate/modifier/order_by.h +++ b/include/cpphibernate/modifier/order_by.h @@ -1,171 +1,38 @@ #pragma once -#include #include -#include -beg_namespace_cpphibernate_modifier +namespace cpphibernate { namespace __impl { - /* order_direction_tag_t */ - - struct order_direction_tag_t - { }; - - /* order_direction_t */ - - template - struct order_direction_t - : public order_direction_tag_t - { - using field_type = T_field; - using wrapped_field_type = hana::type>; - - wrapped_field_type wrapped_field; - }; + /** + * @brief Helper class to build order_by objects. + */ + template + struct order_by_builder; } - /* meta */ - + /** + * @brief Evaluates to true_t if the passed type is a offset modifier, false_t otherwise. + */ template - struct is_order_direction - : public mp::is_base_of<__impl::order_direction_tag_t, T> - { }; - - template - struct all_are_order_directions - : public mp::all_true::value...> - { }; - - namespace __impl - { - - /* order_ascending_t */ - - template - struct order_ascending_t - : public order_direction_t - { }; - - /* order_descending_t */ - - template - struct order_descending_t - : public order_direction_t - { }; - - /* order_by_t */ - - template - struct order_by_t - : public modifier_t - { - using fields_type = hana::basic_tuple; - - fields_type fields; - }; - - } - - /* meta */ + struct is_order_by_modifier; + /** + * @brief Is true if the passed type is a offset modifier, false otherwise. + */ template - struct is_order_by - : public mp::is_specialization_of - { }; + constexpr decltype(auto) is_order_by_modifier_v = is_order_by_modifier::value; - template - struct is_ascending - : public mp::is_specialization_of - { }; - - template - struct is_descending - : public mp::is_specialization_of - { }; - - namespace __impl - { - - /* ascending_builder */ - - template - struct ascending_builder - { - template - static constexpr decltype(auto) apply(T_args&&... args) - { static_assert(sizeof...(args) == -1, "Invalid parameters for hibernate::modifier::ascending(...)!"); } - }; - - template - struct ascending_builder< - mp::list, - mp::enable_if>>> - { - static constexpr decltype(auto) apply(T_field&&) - { - using field_type = mp::decay_t; - using ascending_type = order_ascending_t; - return ascending_type { }; - } - }; - - /* descending_builder */ - - template - struct descending_builder - { - template - static constexpr decltype(auto) apply(T_args&&... args) - { static_assert(sizeof...(args) == -1, "Invalid parameters for hibernate::modifier::descending(...)!"); } - }; - - template - struct descending_builder< - mp::list, - mp::enable_if>>> - { - static constexpr decltype(auto) apply(T_field&&) - { - using field_type = mp::decay_t; - using descending_type = order_descending_t; - return descending_type { }; - } - }; - - /* order_by_builder */ - - template - struct order_by_builder - { - template - static constexpr decltype(auto) apply(T_args&&... args) - { static_assert(sizeof...(args) == -1, "Invalid parameters for hibernate::modifier::order_by(...)!"); } - }; - - template - struct order_by_builder< - mp::list, - mp::enable_if...>>> - { - static constexpr decltype(auto) apply(T_order_directions&&...) - { - using order_by_type = order_by_t...>; - return order_by_type { }; - } - }; - - } - - /* make */ - - constexpr decltype(auto) ascending = misc::make_generic_predicate<__impl::ascending_builder> { }; - constexpr decltype(auto) descending = misc::make_generic_predicate<__impl::descending_builder> { }; - constexpr decltype(auto) order_by = misc::make_generic_predicate<__impl::order_by_builder> { }; + /** + * @brief Predicate to create new offset objects. + */ + constexpr decltype(auto) order_by = mp::generic_predicate<__impl::order_by_builder> { }; } -end_namespace_cpphibernate_modifier \ No newline at end of file + +#include "order_by.inl" diff --git a/include/cpphibernate/modifier/order_by.inl b/include/cpphibernate/modifier/order_by.inl new file mode 100644 index 0000000..0ed44f2 --- /dev/null +++ b/include/cpphibernate/modifier/order_by.inl @@ -0,0 +1,55 @@ +#pragma once + +#include "order_by.h" +#include "order_by/order_direction.h" + +namespace cpphibernate +{ + + namespace __impl + { + + /* order_by_t */ + + template + struct order_by_t + : public tag_modifier + , public tag_equality_comparable + { + using order_directions_type = hana::basic_tuple; + + order_directions_type order_directions; + }; + + /* order_by_builder */ + + template + struct order_by_builder + { + template + static constexpr decltype(auto) apply(T_args&&...) + { static_assert(sizeof...(T_args) == -1, "Invalid parameters for order_by(...)!"); } + }; + + template + struct order_by_builder< + mp::list, + mp::enable_if_t>...>>> + { + static constexpr decltype(auto) apply(T_order_directions&&...) + { + using order_by_type = order_by_t...>; + return order_by_type { }; + } + }; + + } + + /* is_order_by_modifier */ + + template + struct is_order_by_modifier + : public mp::is_specialization_of + { }; + +} diff --git a/include/cpphibernate/modifier/order_by/ascending.h b/include/cpphibernate/modifier/order_by/ascending.h new file mode 100644 index 0000000..3fa1f82 --- /dev/null +++ b/include/cpphibernate/modifier/order_by/ascending.h @@ -0,0 +1,38 @@ +#pragma once + +#include + +namespace cpphibernate +{ + + namespace __impl + { + + /** + * @brief Helper class to build ascending objects. + */ + template + struct ascending_builder; + + } + + /** + * @brief Evaluates to true_t if the passed type is a ascending order, false_t otherwise. + */ + template + struct is_ascending; + + /** + * @brief Is true if the passed type is a ascending order, false otherwise. + */ + template + constexpr decltype(auto) is_ascending_v = is_ascending::value; + + /** + * @brief Predicate to create new ascending order objects. + */ + constexpr decltype(auto) ascending = mp::generic_predicate<__impl::ascending_builder> { }; + +} + +#include "ascending.inl" diff --git a/include/cpphibernate/modifier/order_by/ascending.inl b/include/cpphibernate/modifier/order_by/ascending.inl new file mode 100644 index 0000000..434a266 --- /dev/null +++ b/include/cpphibernate/modifier/order_by/ascending.inl @@ -0,0 +1,49 @@ +#pragma once + +#include + +#include "ascending.h" +#include "order_direction.h" + +namespace cpphibernate +{ + + namespace __impl + { + + /* ascending_t */ + + template + struct ascending_t + : public order_direction_t + { }; + + /* ascending_builder */ + + template + struct ascending_builder + { + template + static constexpr decltype(auto) apply(T_args&&... args) + { static_assert(sizeof...(args) == -1, "Invalid parameters for hibernate::ascending(...)!"); } + }; + + template + struct ascending_builder< + mp::list, + mp::enable_if_t>>> + { + static constexpr decltype(auto) apply(T_field&&) + { return ascending_t { }; } + }; + + } + + /* is_ascending */ + + template + struct is_ascending + : public mp::is_specialization_of + { }; + +} diff --git a/include/cpphibernate/modifier/order_by/descending.h b/include/cpphibernate/modifier/order_by/descending.h new file mode 100644 index 0000000..e274edd --- /dev/null +++ b/include/cpphibernate/modifier/order_by/descending.h @@ -0,0 +1,38 @@ +#pragma once + +#include + +namespace cpphibernate +{ + + namespace __impl + { + + /** + * @brief Helper class to build descending objects. + */ + template + struct descending_builder; + + } + + /** + * @brief Evaluates to true_t if the passed type is a descending order, false_t otherwise. + */ + template + struct is_descending; + + /** + * @brief Is true if the passed type is a descending order, false otherwise. + */ + template + constexpr decltype(auto) is_descending_v = is_descending::value; + + /** + * @brief Predicate to create new descending order objects. + */ + constexpr decltype(auto) descending = mp::generic_predicate<__impl::descending_builder> { }; + +} + +#include "descending.inl" diff --git a/include/cpphibernate/modifier/order_by/descending.inl b/include/cpphibernate/modifier/order_by/descending.inl new file mode 100644 index 0000000..78fddf8 --- /dev/null +++ b/include/cpphibernate/modifier/order_by/descending.inl @@ -0,0 +1,47 @@ +#pragma once + +#include "descending.h" +#include "order_direction.h" + +namespace cpphibernate +{ + + namespace __impl + { + + /* descending_t */ + + template + struct descending_t + : public order_direction_t + { }; + + /* descending_builder */ + + template + struct descending_builder + { + template + static constexpr decltype(auto) apply(T_args&&... args) + { static_assert(sizeof...(args) == -1, "Invalid parameters for hibernate::descending(...)!"); } + }; + + template + struct descending_builder< + mp::list, + mp::enable_if_t>>> + { + static constexpr decltype(auto) apply(T_field&&) + { return descending_t { }; } + }; + + } + + /* is_descending */ + + template + struct is_descending + : public mp::is_specialization_of + { }; + +} diff --git a/include/cpphibernate/modifier/order_by/order_direction.h b/include/cpphibernate/modifier/order_by/order_direction.h new file mode 100644 index 0000000..2ce643b --- /dev/null +++ b/include/cpphibernate/modifier/order_by/order_direction.h @@ -0,0 +1,22 @@ +#pragma once + +#include + +namespace cpphibernate +{ + + /** + * @brief Evaluates to true_t if the passed type is a order_direction, false_t otherwise. + */ + template + struct is_order_direction; + + /** + * @brief Is true if the passed type is a order_direction, false otherwise. + */ + template + constexpr decltype(auto) is_order_direction_v = is_order_direction::value; + +} + +#include "order_direction.inl" diff --git a/include/cpphibernate/modifier/order_by/order_direction.inl b/include/cpphibernate/modifier/order_by/order_direction.inl new file mode 100644 index 0000000..58b24e2 --- /dev/null +++ b/include/cpphibernate/modifier/order_by/order_direction.inl @@ -0,0 +1,37 @@ +#pragma once + +#include "order_direction.h" + +namespace cpphibernate +{ + + namespace __impl + { + + /* tag_order_direction */ + + struct tag_order_direction + { }; + + /* order_direction_t */ + + template + struct order_direction_t + : public tag_order_direction + { + using field_type = T_field; + using wrapped_field_type = hana::type>; + + wrapped_field_type wrapped_field; + }; + + } + + /* is_order_direction */ + + template + struct is_order_direction + : public mp::is_base_of<__impl::tag_order_direction, T> + { }; + +} diff --git a/include/cpphibernate/modifier/where.h b/include/cpphibernate/modifier/where.h index e381b88..024f1a2 100644 --- a/include/cpphibernate/modifier/where.h +++ b/include/cpphibernate/modifier/where.h @@ -1,4 +1,38 @@ #pragma once -#include -#include \ No newline at end of file +#include + +namespace cpphibernate +{ + + namespace __impl + { + + /** + * @brief Helper class to build where objects. + */ + template + struct where_builder; + + } + + /** + * @brief Evaluates to true_t if the passed type is a where modifier, false_t otherwise. + */ + template + struct is_where_modifier; + + /** + * @brief Is true if the passed type is a where modifier, false otherwise. + */ + template + constexpr decltype(auto) is_where_modifier_v = is_where_modifier::value; + + /** + * @brief Predicate to create new where objects. + */ + constexpr decltype(auto) where = mp::generic_predicate<__impl::where_builder> { }; + +} + +#include "where.inl" diff --git a/include/cpphibernate/modifier/where.inl b/include/cpphibernate/modifier/where.inl new file mode 100644 index 0000000..47f2399 --- /dev/null +++ b/include/cpphibernate/modifier/where.inl @@ -0,0 +1,61 @@ +#pragma once + +#include "where.h" +#include "modifier.h" +#include "where/where_clause.h" + +namespace cpphibernate +{ + + namespace __impl + { + + /* where_t */ + + template + struct where_t + : public tag_modifier + , public tag_equality_comparable + { + using clause_type = T_where_clause; + + clause_type clause; + + constexpr where_t(clause_type&& p_clause) + : clause(p_clause) + { } + }; + + /* where_builder */ + + template + struct where_builder + { + template + static constexpr decltype(auto) apply(T_args&&...) + { static_assert(sizeof...(T_args) == -1, "Invalid parameters for where(...)!"); } + }; + + template + struct where_builder< + mp::list, + mp::enable_if_t>>>> + { + static constexpr decltype(auto) apply(T_where_clause&& clause) + { + using clause_type = T_where_clause; + using where_type = where_t; + return where_type(std::forward(clause)); + } + }; + + } + + /* is_where_modifier */ + + template + struct is_where_modifier + : public mp::is_specialization_of + { }; + +} diff --git a/include/cpphibernate/modifier/where/clauses.h b/include/cpphibernate/modifier/where/clauses.h deleted file mode 100644 index f716bce..0000000 --- a/include/cpphibernate/modifier/where/clauses.h +++ /dev/null @@ -1,7 +0,0 @@ -#pragma once - -#include -#include -#include -#include -#include \ No newline at end of file diff --git a/include/cpphibernate/modifier/where/clauses/and.h b/include/cpphibernate/modifier/where/clauses/and.h deleted file mode 100644 index 942fc3b..0000000 --- a/include/cpphibernate/modifier/where/clauses/and.h +++ /dev/null @@ -1,60 +0,0 @@ -#pragma once - -#include -#include - -beg_namespace_cpphibernate_modifier -{ - - namespace __impl - { - - /* where_clause_and_t */ - - template - struct where_clause_and_t - : public where_clause_t - { - using clauses_type = hana::tuple; - - clauses_type clauses; - - constexpr where_clause_and_t(T_clauses&&... p_clauses) - : clauses(std::forward(p_clauses)...) - { } - }; - - /* where_clause_and_builder */ - - template - struct where_clause_and_builder - { - template - static constexpr decltype(auto) apply(T_args&&... args) - { static_assert(sizeof...(args) == -1, "Invalid parameters for hibernate::modifier::and(...)!"); } - }; - - template - struct where_clause_and_builder< - mp::list, - mp::enable_if...>>> - { - static constexpr decltype(auto) apply(T_clauses&&... clauses) - { return where_clause_and_t(std::forward(clauses)...); } - }; - - } - - /* meta */ - - template - struct is_where_clause_and - : public mp::is_specialization_of - { }; - - /* make */ - - constexpr decltype(auto) and_ = misc::make_generic_predicate<__impl::where_clause_and_builder> { }; - -} -end_namespace_cpphibernate_modifier \ No newline at end of file diff --git a/include/cpphibernate/modifier/where/clauses/clause.h b/include/cpphibernate/modifier/where/clauses/clause.h deleted file mode 100644 index f1ed328..0000000 --- a/include/cpphibernate/modifier/where/clauses/clause.h +++ /dev/null @@ -1,31 +0,0 @@ -#pragma once - -#include - -beg_namespace_cpphibernate_modifier -{ - - namespace __impl - { - - /* where_clause_t */ - - struct where_clause_t - { }; - - } - - /* meta */ - - template - struct is_where_clause - : public mp::is_base_of<__impl::where_clause_t, T> - { }; - - template - struct all_are_where_clauses - : public mp::all_true::value...> - { }; - -} -end_namespace_cpphibernate_modifier \ No newline at end of file diff --git a/include/cpphibernate/modifier/where/clauses/equal.h b/include/cpphibernate/modifier/where/clauses/equal.h deleted file mode 100644 index b994fd8..0000000 --- a/include/cpphibernate/modifier/where/clauses/equal.h +++ /dev/null @@ -1,64 +0,0 @@ -#pragma once - -#include -#include -#include - -beg_namespace_cpphibernate_modifier -{ - - namespace __impl - { - - /* where_clause_equal_t */ - - template - struct where_clause_equal_t - : public where_clause_t - { - using field_type = T_field; - using value_type = T_value; - - field_type field; - value_type value; - - constexpr where_clause_equal_t(T_field&& p_field, T_value&& p_value) - : field(std::forward(p_field)) - , value(std::forward(p_value)) - { } - }; - - /* where_clause_equal_builder */ - - template - struct where_clause_equal_builder - { - template - static constexpr decltype(auto) apply(T_args&&... args) - { static_assert(sizeof...(args) == -1, "Invalid parameters for hibernate::modifier::equal(...)!"); } - }; - - template - struct where_clause_equal_builder< - mp::list, - mp::enable_if>>> - { - static constexpr decltype(auto) apply(T_field&& field, T_value&& value) - { return where_clause_equal_t(std::forward(field), std::forward(value)); } - }; - - } - - /* meta */ - - template - struct is_where_clause_equal - : public mp::is_specialization_of - { }; - - /* make */ - - constexpr decltype(auto) equal = misc::make_generic_predicate<__impl::where_clause_equal_builder> { }; - -} -end_namespace_cpphibernate_modifier \ No newline at end of file diff --git a/include/cpphibernate/modifier/where/clauses/not.h b/include/cpphibernate/modifier/where/clauses/not.h deleted file mode 100644 index e8cf36e..0000000 --- a/include/cpphibernate/modifier/where/clauses/not.h +++ /dev/null @@ -1,60 +0,0 @@ -#pragma once - -#include -#include - -beg_namespace_cpphibernate_modifier -{ - - namespace __impl - { - - /* where_clause_not_t */ - - template - struct where_clause_not_t - : public where_clause_t - { - using clause_type = T_clause; - - clause_type clause; - - constexpr where_clause_not_t(T_clause&& p_clause) - : clause(std::forward(p_clause)) - { } - }; - - /* where_clause_not_builder */ - - template - struct where_clause_not_builder - { - template - static constexpr decltype(auto) apply(T_args&&... args) - { static_assert(sizeof...(args) == -1, "Invalid parameters for hibernate::modifier::not(...)!"); } - }; - - template - struct where_clause_not_builder< - mp::list, - mp::enable_if>>> - { - static constexpr decltype(auto) apply(T_clause&& clause) - { return where_clause_not_t(std::forward(clause)); } - }; - - } - - /* meta */ - - template - struct is_where_clause_not - : public mp::is_specialization_of - { }; - - /* make */ - - constexpr decltype(auto) not_ = misc::make_generic_predicate<__impl::where_clause_not_builder> { }; - -} -end_namespace_cpphibernate_modifier \ No newline at end of file diff --git a/include/cpphibernate/modifier/where/clauses/or.h b/include/cpphibernate/modifier/where/clauses/or.h deleted file mode 100644 index 79dd497..0000000 --- a/include/cpphibernate/modifier/where/clauses/or.h +++ /dev/null @@ -1,60 +0,0 @@ -#pragma once - -#include -#include - -beg_namespace_cpphibernate_modifier -{ - - namespace __impl - { - - /* where_clause_or_t */ - - template - struct where_clause_or_t - : public where_clause_t - { - using clauses_type = hana::tuple; - - clauses_type clauses; - - constexpr where_clause_or_t(T_clauses&&... p_clauses) - : clauses(std::forward(p_clauses)...) - { } - }; - - /* where_clause_or_builder */ - - template - struct where_clause_or_builder - { - template - static constexpr decltype(auto) apply(T_args&&... args) - { static_assert(sizeof...(args) == -1, "Invalid parameters for hibernate::modifier::or(...)!"); } - }; - - template - struct where_clause_or_builder< - mp::list, - mp::enable_if...>>> - { - static constexpr decltype(auto) apply(T_clauses&&... clauses) - { return where_clause_or_t(std::forward(clauses)...); } - }; - - } - - /* meta */ - - template - struct is_where_clause_or - : public mp::is_specialization_of - { }; - - /* make */ - - constexpr decltype(auto) or_ = misc::make_generic_predicate<__impl::where_clause_or_builder> { }; - -} -end_namespace_cpphibernate_modifier \ No newline at end of file diff --git a/include/cpphibernate/modifier/where/conjunction.h b/include/cpphibernate/modifier/where/conjunction.h new file mode 100644 index 0000000..5f96115 --- /dev/null +++ b/include/cpphibernate/modifier/where/conjunction.h @@ -0,0 +1,59 @@ +#pragma once + +#include + +namespace cpphibernate +{ + + namespace __impl + { + + /** + * @brief Helper class to build conjunction objects. + */ + template + struct conjunction_builder; + + } + + + + /** + * @brief Evaluates to true_t if the passed type is a conjunction clause, false_t otherwise. + */ + template + struct is_conjunction; + + /** + * @brief Is true if the passed type is a conjunction clause, false otherwise. + */ + template + constexpr decltype(auto) is_conjunction_v = is_conjunction::value; + + /** + * @brief Predicate to create new conjunction clause objects. + */ + constexpr decltype(auto) conjunction = mp::generic_predicate<__impl::conjunction_builder> { }; + + + + /** + * @brief Evaluates to true_t if the passed type is a conjunction clause, false_t otherwise. + */ + template + struct is_and; + + /** + * @brief Is true if the passed type is a conjunction clause, false otherwise. + */ + template + constexpr decltype(auto) is_and_v = is_and::value; + + /** + * @brief Predicate to create new conjunction clause objects. + */ + constexpr decltype(auto) and_ = mp::generic_predicate<__impl::conjunction_builder> { }; + +} + +#include "conjunction.inl" diff --git a/include/cpphibernate/modifier/where/conjunction.inl b/include/cpphibernate/modifier/where/conjunction.inl new file mode 100644 index 0000000..f57bc35 --- /dev/null +++ b/include/cpphibernate/modifier/where/conjunction.inl @@ -0,0 +1,69 @@ +#pragma once + +#include + +#include "conjunction.h" + +namespace cpphibernate +{ + + namespace __impl + { + + /* where_clause_conjunction_t */ + + template + struct where_clause_conjunction_t + : public tag_where_clause + { + using clauses_type = hana::basic_tuple; + + clauses_type clauses; + + constexpr where_clause_conjunction_t( + T_clauses&&... p_clauses) + : clauses(std::forward(p_clauses)...) + { } + }; + + /* conjunction_builder */ + + template + struct conjunction_builder + { + template + static constexpr decltype(auto) apply(T_args&&... args) + { static_assert(sizeof...(args) == -1, "Invalid parameters for hibernate::conjunction(...)!"); } + }; + + template + struct conjunction_builder< + mp::list, + mp::enable_if_t< + mp::is_true_v>...> + && sizeof...(T_clauses) >= 2>> + { + static constexpr decltype(auto) apply(T_clauses&&... clauses) + { + using clause_type = where_clause_conjunction_t; + return clause_type(std::forward(clauses)...); + } + }; + + } + + /* is_conjunction */ + + template + struct is_conjunction + : public mp::is_specialization_of + { }; + + /* is_and */ + + template + struct is_and + : public mp::is_specialization_of + { }; + +} diff --git a/include/cpphibernate/modifier/where/disjunction.h b/include/cpphibernate/modifier/where/disjunction.h new file mode 100644 index 0000000..bc3e76e --- /dev/null +++ b/include/cpphibernate/modifier/where/disjunction.h @@ -0,0 +1,59 @@ +#pragma once + +#include + +namespace cpphibernate +{ + + namespace __impl + { + + /** + * @brief Helper class to build disjunction objects. + */ + template + struct disjunction_builder; + + } + + + + /** + * @brief Evaluates to true_t if the passed type is a disjunction clause, false_t otherwise. + */ + template + struct is_disjunction; + + /** + * @brief Is true if the passed type is a disjunction clause, false otherwise. + */ + template + constexpr decltype(auto) is_disjunction_v = is_disjunction::value; + + /** + * @brief Predicate to create new disjunction clause objects. + */ + constexpr decltype(auto) disjunction = mp::generic_predicate<__impl::disjunction_builder> { }; + + + + /** + * @brief Evaluates to true_t if the passed type is a disjunction clause, false_t otherwise. + */ + template + struct is_or; + + /** + * @brief Is true if the passed type is a disjunction clause, false otherwise. + */ + template + constexpr decltype(auto) is_or_v = is_or::value; + + /** + * @brief Predicate to create new disjunction clause objects. + */ + constexpr decltype(auto) or_ = mp::generic_predicate<__impl::disjunction_builder> { }; + +} + +#include "disjunction.inl" diff --git a/include/cpphibernate/modifier/where/disjunction.inl b/include/cpphibernate/modifier/where/disjunction.inl new file mode 100644 index 0000000..0e50791 --- /dev/null +++ b/include/cpphibernate/modifier/where/disjunction.inl @@ -0,0 +1,69 @@ +#pragma once + +#include + +#include "disjunction.h" + +namespace cpphibernate +{ + + namespace __impl + { + + /* where_clause_disjunction_t */ + + template + struct where_clause_disjunction_t + : public tag_where_clause + { + using clauses_type = hana::basic_tuple; + + clauses_type clauses; + + constexpr where_clause_disjunction_t( + T_clauses&&... p_clauses) + : clauses(std::forward(p_clauses)...) + { } + }; + + /* disjunction_builder */ + + template + struct disjunction_builder + { + template + static constexpr decltype(auto) apply(T_args&&... args) + { static_assert(sizeof...(args) == -1, "Invalid parameters for hibernate::disjunction(...)!"); } + }; + + template + struct disjunction_builder< + mp::list, + mp::enable_if_t< + mp::is_true_v>...> + && sizeof...(T_clauses) >= 2>> + { + static constexpr decltype(auto) apply(T_clauses&&... clauses) + { + using clause_type = where_clause_disjunction_t; + return clause_type(std::forward(clauses)...); + } + }; + + } + + /* is_disjunction */ + + template + struct is_disjunction + : public mp::is_specialization_of + { }; + + /* is_or */ + + template + struct is_or + : public mp::is_specialization_of + { }; + +} diff --git a/include/cpphibernate/modifier/where/equal.h b/include/cpphibernate/modifier/where/equal.h new file mode 100644 index 0000000..c35b6db --- /dev/null +++ b/include/cpphibernate/modifier/where/equal.h @@ -0,0 +1,38 @@ +#pragma once + +#include + +namespace cpphibernate +{ + + namespace __impl + { + + /** + * @brief Helper class to build equal objects. + */ + template + struct equal_builder; + + } + + /** + * @brief Evaluates to true_t if the passed type is a equal order, false_t otherwise. + */ + template + struct is_equal; + + /** + * @brief Is true if the passed type is a equal order, false otherwise. + */ + template + constexpr decltype(auto) is_equal_v = is_equal::value; + + /** + * @brief Predicate to create new equal order objects. + */ + constexpr decltype(auto) equal = mp::generic_predicate<__impl::equal_builder> { }; + +} + +#include "equal.inl" diff --git a/include/cpphibernate/modifier/where/equal.inl b/include/cpphibernate/modifier/where/equal.inl new file mode 100644 index 0000000..1a13346 --- /dev/null +++ b/include/cpphibernate/modifier/where/equal.inl @@ -0,0 +1,64 @@ +#pragma once + +#include + +#include "equal.h" + +namespace cpphibernate +{ + + namespace __impl + { + + /* where_clause_equal_t */ + + template + struct where_clause_equal_t + : public tag_where_clause + { + using field_type = T_field; + using value_type = T_value; + + field_type field; + value_type value; + + constexpr where_clause_equal_t( + field_type&& p_field, + value_type&& p_value) + : field(std::forward(p_field)) + , value(std::forward(p_value)) + { } + }; + + /* equal_builder */ + + template + struct equal_builder + { + template + static constexpr decltype(auto) apply(T_args&&... args) + { static_assert(sizeof...(args) == -1, "Invalid parameters for hibernate::equal(...)!"); } + }; + + template + struct equal_builder< + mp::list, + mp::enable_if_t>>> + { + static constexpr decltype(auto) apply(T_field&& field, T_value&& value) + { + using clause_type = where_clause_equal_t; + return clause_type(std::forward(field), std::forward(value)); + } + }; + + } + + /* is_equal */ + + template + struct is_equal + : public mp::is_specialization_of + { }; + +} diff --git a/include/cpphibernate/modifier/where/negation.h b/include/cpphibernate/modifier/where/negation.h new file mode 100644 index 0000000..ad49dd8 --- /dev/null +++ b/include/cpphibernate/modifier/where/negation.h @@ -0,0 +1,59 @@ +#pragma once + +#include + +namespace cpphibernate +{ + + namespace __impl + { + + /** + * @brief Helper class to build negation objects. + */ + template + struct negation_builder; + + } + + + + /** + * @brief Evaluates to true_t if the passed type is a negation clause, false_t otherwise. + */ + template + struct is_negation; + + /** + * @brief Is true if the passed type is a negation clause, false otherwise. + */ + template + constexpr decltype(auto) is_negation_v = is_negation::value; + + /** + * @brief Predicate to create new negation clause objects. + */ + constexpr decltype(auto) negation = mp::generic_predicate<__impl::negation_builder> { }; + + + + /** + * @brief Evaluates to true_t if the passed type is a negation clause, false_t otherwise. + */ + template + struct is_not; + + /** + * @brief Is true if the passed type is a negation clause, false otherwise. + */ + template + constexpr decltype(auto) is_not_v = is_not::value; + + /** + * @brief Predicate to create new negation clause objects. + */ + constexpr decltype(auto) not_ = mp::generic_predicate<__impl::negation_builder> { }; + +} + +#include "negation.inl" diff --git a/include/cpphibernate/modifier/where/negation.inl b/include/cpphibernate/modifier/where/negation.inl new file mode 100644 index 0000000..6ee91a5 --- /dev/null +++ b/include/cpphibernate/modifier/where/negation.inl @@ -0,0 +1,67 @@ +#pragma once + +#include + +#include "negation.h" + +namespace cpphibernate +{ + + namespace __impl + { + + /* where_clause_negation_t */ + + template + struct where_clause_negation_t + : public tag_where_clause + { + using clause_type = T_clause; + + clause_type clause; + + constexpr where_clause_negation_t( + clause_type&& p_clause) + : clause(std::forward(p_clause)) + { } + }; + + /* negation_builder */ + + template + struct negation_builder + { + template + static constexpr decltype(auto) apply(T_args&&... args) + { static_assert(sizeof...(args) == -1, "Invalid parameters for hibernate::negation(...)!"); } + }; + + template + struct negation_builder< + mp::list, + mp::enable_if_t>>> + { + static constexpr decltype(auto) apply(T_clause&& clause) + { + using clause_type = where_clause_negation_t; + return clause_type(std::forward(clause)); + } + }; + + } + + /* is_negation */ + + template + struct is_negation + : public mp::is_specialization_of + { }; + + /* is_not */ + + template + struct is_not + : public mp::is_specialization_of + { }; + +} diff --git a/include/cpphibernate/modifier/where/where.h b/include/cpphibernate/modifier/where/where.h deleted file mode 100644 index 685e24a..0000000 --- a/include/cpphibernate/modifier/where/where.h +++ /dev/null @@ -1,60 +0,0 @@ -#pragma once - -#include -#include -#include -#include - -beg_namespace_cpphibernate_modifier -{ - - namespace __impl - { - - /* where_t */ - - template - struct where_t - : public modifier_t - { - using clause_type = T_clause; - - clause_type clause; - - constexpr where_t(T_clause&& p_clause) - : clause(std::forward(p_clause)) - { } - }; - - /* where_builder */ - - template - struct where_builder - { - template - static constexpr decltype(auto) apply(T_args&&... args) - { static_assert(sizeof...(args) == -1, "Invalid parameters for hibernate::modifier::where(...)!"); } - }; - - template - struct where_builder, mp::enable_if>> - { - static constexpr decltype(auto) apply(T_clause&& clause) - { return where_t(std::forward(clause)); } - }; - - } - - /* meta */ - - template - struct is_where - : public mp::is_specialization_of - { }; - - /* make */ - - constexpr decltype(auto) where = misc::make_generic_predicate<__impl::where_builder> { }; - -} -end_namespace_cpphibernate_modifier \ No newline at end of file diff --git a/include/cpphibernate/modifier/where/where_clause.h b/include/cpphibernate/modifier/where/where_clause.h new file mode 100644 index 0000000..d4d774a --- /dev/null +++ b/include/cpphibernate/modifier/where/where_clause.h @@ -0,0 +1,22 @@ +#pragma once + +#include + +namespace cpphibernate +{ + + /** + * @brief Evaluates to true_t if the passed type is a where_clause, false_t otherwise. + */ + template + struct is_where_clause; + + /** + * @brief Is true if the passed type is a where_clause, false otherwise. + */ + template + constexpr decltype(auto) is_where_clause_v = is_where_clause::value; + +} + +#include "where_clause.inl" diff --git a/include/cpphibernate/modifier/where/where_clause.inl b/include/cpphibernate/modifier/where/where_clause.inl new file mode 100644 index 0000000..446a2e0 --- /dev/null +++ b/include/cpphibernate/modifier/where/where_clause.inl @@ -0,0 +1,25 @@ +#pragma once + +#include "where_clause.h" + +namespace cpphibernate +{ + + namespace __impl + { + + /* tag_where_clause */ + + struct tag_where_clause + { }; + + } + + /* is_where_clause */ + + template + struct is_where_clause + : public mp::is_base_of<__impl::tag_where_clause, T> + { }; + +} diff --git a/include/cpphibernate/schema.h b/include/cpphibernate/schema.h index cebb0f4..177403f 100644 --- a/include/cpphibernate/schema.h +++ b/include/cpphibernate/schema.h @@ -1,12 +1,19 @@ #pragma once -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include \ No newline at end of file +#include "schema/macros.h" + +#include "schema/attribute.h" +#include "schema/attributes.h" +#include "schema/field.h" +#include "schema/fields.h" +#include "schema/table.h" +#include "schema/tables.h" +#include "schema/schema.h" + +#include "schema/attribute.inl" +#include "schema/attributes.inl" +#include "schema/field.inl" +#include "schema/fields.inl" +#include "schema/table.inl" +#include "schema/tables.inl" +#include "schema/schema.inl" diff --git a/include/cpphibernate/schema/attribute.h b/include/cpphibernate/schema/attribute.h index b0df517..18b83d2 100644 --- a/include/cpphibernate/schema/attribute.h +++ b/include/cpphibernate/schema/attribute.h @@ -1,23 +1,47 @@ #pragma once #include +#include -beg_namespace_cpphibernate_schema -{ +namespace cpphibernate { +namespace schema { namespace __impl { - /* attribute_t */ + /** + * @brief Tag class to mark attribute types. + */ + struct tag_attribute + { }; + /** + * @brief Attribute implementation. + */ template struct attribute_t : public T_inner + , public tag_attribute + , public tag_equality_comparable { using inner_type = T_inner; using this_type = attribute_t; - cpphibernate_equality_comparable(); +/* + template + constexpr decltype(auto) operator==(T_other&&) const + { + return ::boost::hana::type> { } == + ::boost::hana::type> { }; + } + + template + constexpr decltype(auto) operator!=(T_other&&) const + { + return ::boost::hana::type> { } != + ::boost::hana::type> { }; + } +*/ }; struct hex_t { static constexpr decltype(auto) name = "hex"; }; @@ -29,32 +53,39 @@ beg_namespace_cpphibernate_schema namespace attribute { - using hex_type = __impl::attribute_t<__impl::hex_t>; - using compress_type = __impl::attribute_t<__impl::compress_t>; - using primary_key_type = __impl::attribute_t<__impl::primary_key_t>; + using hex_type = __impl::attribute_t<__impl::hex_t>; + using compress_type = __impl::attribute_t<__impl::compress_t>; + using primary_key_type = __impl::attribute_t<__impl::primary_key_t>; - /** value is stored as hexadecimal string, and will be converted to its binary form on read */ - constexpr hex_type hex { }; + /** + * @brief Attribute to indicate that the value is stored as hexadecimal string. + */ + constexpr hex_type hex { }; - /** value is stored as compressed binary, and will be uncompressed on read */ - constexpr compress_type compress { }; + /** + * @brief Attribute to indicate that the value is stored compressed. + */ + constexpr compress_type compress { }; - /** this value represents a primary key (it must be stored in the dataset to be able to do the operations on the database) */ - constexpr primary_key_type primary_key { }; + /** + * @brief Attribute to indicate that the value is the primary key field. + */ + constexpr primary_key_type primary_key { }; } - /* meta */ + /** + * @brief Evaluates to true_t if the passed type is an attribute type. + */ + template + struct is_attribute; + /** + * @brief Is true if the passed type is an attribute, false otherwise. + */ template - struct is_attribute - : public mp::is_specialization_of - { }; + constexpr bool is_attribute_v = is_attribute::value; - template - struct all_are_attribures - : public mp::all_true::value...> - { }; +} } -} -end_namespace_cpphibernate_schema \ No newline at end of file +#include "attribute.inl" diff --git a/include/cpphibernate/schema/attribute.inl b/include/cpphibernate/schema/attribute.inl new file mode 100644 index 0000000..0e8376f --- /dev/null +++ b/include/cpphibernate/schema/attribute.inl @@ -0,0 +1,15 @@ +#pragma once + +#include "attribute.h" + +namespace cpphibernate { +namespace schema { + + /* is_attribute */ + + template + struct is_attribute + : public mp::is_base_of<__impl::tag_attribute, T> + { }; + +} } diff --git a/include/cpphibernate/schema/attributes.h b/include/cpphibernate/schema/attributes.h index 1dcd0c0..59d1920 100644 --- a/include/cpphibernate/schema/attributes.h +++ b/include/cpphibernate/schema/attributes.h @@ -1,73 +1,39 @@ #pragma once #include -#include #include -beg_namespace_cpphibernate_schema -{ +namespace cpphibernate { +namespace schema { namespace __impl { - /* attributes_t */ - - template - using attributes_t = hana::tuple; - - /* is_attributes_impl */ - + /** + * @brief Helper type to build attributes. + */ template - struct is_attributes_impl - : mp::c_false_t - { }; - - template - struct is_attributes_impl, mp::enable_if>> - : mp::c_true_t - { }; + struct attributes_builder; } - /* meta */ - + /** + * @brief Evaluates to true_t if the passed type is a attributes type. + */ template - struct is_attributes - : public __impl::is_attributes_impl - { }; - - /* operations */ - - namespace __impl - { + struct is_attributes; - /* attributes_make_impl */ - - template - struct attributes_make_impl - { - template - static constexpr decltype(auto) apply(T_args&&...) - { static_assert(sizeof...(T_args) == -1, "Invalid parameters for hibernate::schema::attributes::make(...)!"); } - }; - - template - struct attributes_make_impl, mp::enable_if_c< - all_are_attribures...>::value>> - { - template - static constexpr decltype(auto) apply(T_args&&...) - { return attributes_t...> { }; } - }; - - } - - namespace attributes - { + /** + * @brief Is true if the passed type is an attributes type, false otherwise. + */ + template + constexpr bool is_attributes_v = is_attributes::value; - constexpr decltype(auto) make = misc::make_generic_predicate<__impl::attributes_make_impl> { }; + /** + * @brief Predicate to create an attributes object from the passed attributes. + */ + constexpr decltype(auto) make_attributes = mp::generic_predicate<__impl::attributes_builder> { }; - } +} } -} -end_namespace_cpphibernate_schema \ No newline at end of file +#include "attributes.inl" diff --git a/include/cpphibernate/schema/attributes.inl b/include/cpphibernate/schema/attributes.inl new file mode 100644 index 0000000..8d72793 --- /dev/null +++ b/include/cpphibernate/schema/attributes.inl @@ -0,0 +1,62 @@ +#pragma once + +#include "attributes.h" + +namespace cpphibernate { +namespace schema { + + namespace __impl + { + + /* attributes_t */ + + template + using attributes_t = hana::tuple; + + /* attributes_builder */ + + template + struct attributes_builder + { + template + static constexpr decltype(auto) apply(T_args&&...) + { static_assert(sizeof...(T_args) == -1, "Invalid parameters for make_attributes(...)!"); } + }; + + template + struct attributes_builder< + mp::list, + mp::enable_if_t< + mp::is_true_v>...>>> + { + static constexpr decltype(auto) apply(T&&...) + { + using attributes_type = attributes_t...>; + return attributes_type { }; + } + }; + + /* is_attributes_impl */ + + template + struct is_attributes_impl + : mp::false_t + { }; + + template + struct is_attributes_impl< + attributes_t, + mp::enable_if_t>...>>> + : mp::true_t + { }; + + } + + /* is_attributes */ + + template + struct is_attributes + : public __impl::is_attributes_impl + { }; + +} } diff --git a/include/cpphibernate/schema/field.h b/include/cpphibernate/schema/field.h index 2e994d2..01def1a 100644 --- a/include/cpphibernate/schema/field.h +++ b/include/cpphibernate/schema/field.h @@ -1,126 +1,119 @@ #pragma once -#include #include -#include -#include -#include +#include +#include -#include -#include - -beg_namespace_cpphibernate_schema -{ +namespace cpphibernate { +namespace schema { namespace __impl { - /* field_t */ - + /** + * @brief Tag class to mark field types. + */ + struct tag_field + { }; + + /** + * @brief Represents a data field in the schema. + * + * @tparam T_name Type of the name of the field. + * @tparam T_getter Type of the getter of the field. + * @tparam T_setter Type of the setter of the field. + * @tparam T_attributes Type of the attributes of the field. + */ template struct field_t + : public tag_field + , public tag_equality_comparable { using name_type = T_name; using getter_type = T_getter; using setter_type = T_setter; + using dataset_type = typename mp::decay_t::object_type; + using real_dataset_type = real_dataset_t; + using value_type = typename mp::decay_t::value_type; + using real_value_type = real_dataset_t; using attributes_type = T_attributes; using this_type = field_t; - name_type name; - getter_type getter; - setter_type setter; - attributes_type attributes; - + name_type name; //!< Name of the field. + getter_type getter; //!< Getter of the field, to get values from the dataset. + setter_type setter; //!< Setter of the field, to set values of the dataset. + attributes_type attributes; //!< List of attributes for this field. + + hana::type wrapped_value_type; //!< Type of the stoed value. + hana::type wrapped_dataset_type; //!< Type of the real stored value (containers are removed). + hana::type wrapped_real_value_type; //!< Type of the dataset this fields uses. + hana::type wrapped_real_dataset_type; //!< Type of the read dataset (contaiers are removed). + + /** + * @brief Constrcutor. + * + * @param[in] p_name Name of the field. + * @param[in] p_getter Getter to receive the represented value from the dataset. + * @param[in] p_setter Setter to set the represented value at the dataset. + * @param[in] p_attributes List of attributes of the field. + */ constexpr field_t( T_name&& p_name, T_getter&& p_getter, T_setter&& p_setter, - T_attributes&& p_attributes) - : name (std::forward (p_name)) - , getter (std::forward (p_getter)) - , setter (std::forward (p_setter)) - , attributes (std::forward(p_attributes)) - { } - - cpphibernate_copyable(field_t, delete); - cpphibernate_moveable(field_t, default); - - cpphibernate_equality_comparable(); - - inline void print(std::ostream& os) const - { - using namespace ::utl; - using value_type = typename mp::decay_t::value_type; - using dataset_type = misc::real_dataset_t; - size_t index = 0; - os << indent << '{' - << incindent - << indent << "\"name\": \"" << name << "\"," - << indent << "\"value_type\": \"" << utl::type_helper::name() << "\"" - << indent << "\"dataset_type\": \"" << utl::type_helper::name() << "\"" - << indent << "\"attributes\": " - << indent << '[' - << incindent; - hana::for_each(attributes, [&](auto& attrib){ - if (index++ > 0) os << ","; - os << indent << attrib.name; - }); - os << decindent - << indent << ']' - << decindent - << indent << '}'; - } + T_attributes&& p_attributes); + + /** + * @brief Move constructor. + */ + constexpr field_t(field_t&&) = default; + + /** + * @brief Copy constructor. + */ + constexpr field_t(const field_t&) = delete; + + /** + * @brief Move assignment constructor. + */ + constexpr field_t& operator = (field_t&&) = default; + + /** + * @brief Copy assignment constructor. + */ + constexpr field_t& operator = (const field_t&) = delete; + + /** + * @brief Print the field to the passed stream. + */ + inline void print(std::ostream& os) const; }; - } - - /* meta */ - - template - struct is_field - : public mp::is_specialization_of - { }; - - template - struct all_are_fields - : public mp::all_true::value...> - { }; - - /* operations */ - - namespace __impl - { - - /* field_make_impl */ - + /** + * @brief Helper type to build field objects. + */ template - struct field_make_impl - { - template - static constexpr decltype(auto) apply(T_args&&... args) - { static_assert(sizeof...(args) == -1, "Invalid parameters for hibernate::schema::field::make(...)!"); } - }; - - template - struct field_make_impl, mp::enable_if_c< - is_getter>::value - && is_setter>::value - && is_attributes>::value>> - { - using field_type = field_t; - - static constexpr decltype(auto) apply(T_name&& name, T_getter&& getter, T_setter&& setter, T_attributes&& attributes) - { return field_type(std::forward(name), std::forward(getter), std::forward(setter), std::forward(attributes)); } - }; + struct field_builder; } - namespace field - { + /** + * @brief Evaluates to true_t if the passed type is a field type. + */ + template + struct is_field; - constexpr decltype(auto) make = misc::make_generic_predicate<__impl::field_make_impl> { }; + /** + * @brief Is true if the passed type is an attributes type, false otherwise. + */ + template + constexpr bool is_field_v = is_field::value; - } + /** + * @brief Predicate to create an field object from the passed attributes. + */ + constexpr decltype(auto) make_field = mp::generic_predicate<__impl::field_builder> { }; + +} } -} -end_namespace_cpphibernate_schema \ No newline at end of file +#include "field.inl" diff --git a/include/cpphibernate/schema/field.inl b/include/cpphibernate/schema/field.inl new file mode 100644 index 0000000..32c76bc --- /dev/null +++ b/include/cpphibernate/schema/field.inl @@ -0,0 +1,103 @@ +#pragma once + +#include +#include + +#include "field.h" +#include "attributes.h" + +namespace cpphibernate { +namespace schema { + + namespace __impl + { + + /* field_t */ + + template + constexpr field_t + ::field_t( + T_name&& p_name, + T_getter&& p_getter, + T_setter&& p_setter, + T_attributes&& p_attributes) + : name (std::forward (p_name)) + , getter (std::forward (p_getter)) + , setter (std::forward (p_setter)) + , attributes (std::forward(p_attributes)) + { } + + template + void field_t + ::print(std::ostream& os) const + { + using namespace ::cppcore; + + size_t index = 0; + os << indent << '{' + << incindent + << indent << "\"name\": \"" << name << "\"," + << indent << "\"value_type\": \"" << type_helper::name() << "\"" + << indent << "\"dataset_type\": \"" << type_helper::name() << "\"" + << indent << "\"attributes\": " + << indent << '[' + << incindent; + hana::for_each(attributes, [&](auto& attrib){ + if (index++ > 0) os << ","; + os << indent << '"' << attrib.name << '"'; + }); + os << decindent + << indent << ']' + << decindent + << indent << '}'; + } + + /* field_builder */ + + template + struct field_builder + { + template + static constexpr decltype(auto) apply(T_args&&...) + { static_assert(sizeof...(T_args) == -1, "Invalid parameters for make_field(...)!"); } + }; + + template + struct field_builder< + mp::list, + mp::enable_if_t< + true + && mp::is_getter_v> + && mp::is_setter_v> + && is_attributes_v>>> + { + using name_type = T_name; + using getter_type = T_getter; + using setter_type = T_setter; + using attributes_type = T_attributes; + using field_type = field_t; + + static constexpr decltype(auto) apply( + name_type&& name, + getter_type&& getter, + setter_type&& setter, + attributes_type&& attributes) + { + return field_type( + std::forward (name), + std::forward (getter), + std::forward (setter), + std::forward (attributes)); + } + }; + + } + + /* is_field */ + + template + struct is_field + : public mp::is_base_of<__impl::tag_field, T> + { }; + +} } diff --git a/include/cpphibernate/schema/fields.h b/include/cpphibernate/schema/fields.h index 61e06a3..a497f12 100644 --- a/include/cpphibernate/schema/fields.h +++ b/include/cpphibernate/schema/fields.h @@ -1,72 +1,39 @@ #pragma once #include -#include #include -beg_namespace_cpphibernate_schema -{ +namespace cpphibernate { +namespace schema { namespace __impl { - /* fields_t */ - - template - using fields_t = hana::tuple; - - /* is_fields_impl */ - + /** + * @brief Helper type to build fields. + */ template - struct is_fields_impl - : public mp::c_false_t - { }; - - template - struct is_fields_impl, mp::enable_if>> - : public mp::c_true_t - { }; + struct fields_builder; } - /* meta */ - + /** + * @brief Evaluates to true_t if the passed type is a fields type. + */ template - struct is_fields : - public __impl::is_fields_impl - { }; - - /* operations */ - - namespace __impl - { + struct is_fields; - /* fields_make_impl */ - - template - struct fields_make_impl - { - template - static constexpr decltype(auto) apply(T_args&&... args) - { static_assert(sizeof...(args) == -1, "Invalid parameters for hibernate::schema::fields::make(...)!"); } - }; - - template - struct fields_make_impl, mp::enable_if>> - { - template - static constexpr decltype(auto) apply(T_args&&... args) - { return fields_t(std::forward(args)...); } - }; - - } - - namespace fields - { + /** + * @brief Is true if the passed type is an fields type, false otherwise. + */ + template + constexpr bool is_fields_v = is_fields::value; - constexpr decltype(auto) make = misc::make_generic_predicate<__impl::fields_make_impl> { }; + /** + * @brief Predicate to create an fields object from the passed fields. + */ + constexpr decltype(auto) make_fields = mp::generic_predicate<__impl::fields_builder> { }; - } +} } -} -end_namespace_cpphibernate_schema \ No newline at end of file +#include "fields.inl" diff --git a/include/cpphibernate/schema/fields.inl b/include/cpphibernate/schema/fields.inl new file mode 100644 index 0000000..8ed2800 --- /dev/null +++ b/include/cpphibernate/schema/fields.inl @@ -0,0 +1,63 @@ +#pragma once + +#include "fields.h" + +namespace cpphibernate { +namespace schema { + + namespace __impl + { + + /* fields_t */ + + template + using fields_t = hana::tuple; + + /* fields_builder */ + + template + struct fields_builder + { + template + static constexpr decltype(auto) apply(T_args&&...) + { static_assert(sizeof...(T_args) == -1, "Invalid parameters for make_fields(...)!"); } + }; + + template + struct fields_builder< + mp::list, + mp::enable_if_t>...>>> + { + template + static constexpr decltype(auto) apply(T_args&&... args) + { + using fields_type = fields_t; + return fields_type(std::forward(args)...); + } + }; + + /* is_fields_impl */ + + template + struct is_fields_impl + : mp::false_t + { }; + + template + struct is_fields_impl< + fields_t, + mp::enable_if_t>...>>> + : mp::true_t + { }; + + } + + /* is_fields */ + + template + struct is_fields + : public __impl::is_fields_impl + { }; + +} } diff --git a/include/cpphibernate/schema/getter.h b/include/cpphibernate/schema/getter.h deleted file mode 100644 index d67233c..0000000 --- a/include/cpphibernate/schema/getter.h +++ /dev/null @@ -1,192 +0,0 @@ -#pragma once - -#include -#include - -beg_namespace_cpphibernate_schema -{ - - namespace __impl - { - - /* getter_t */ - - template - struct getter_t - { - using dataset_type = T_dataset; - using value_type = T_value; - }; - - /* getter_member_var_t */ - - template - struct getter_member_var_t - : public getter_t - { - using base_type = getter_t; - using dataset_type = typename base_type::dataset_type; - using value_type = typename base_type::value_type; - using member_type = T_member; - - member_type member; - - template - constexpr getter_member_var_t(X_member&& p_member) - : member(std::forward(p_member)) - { } - - cpphibernate_copyable(getter_member_var_t, delete); - cpphibernate_moveable(getter_member_var_t, default); - - template - constexpr decltype(auto) operator()(X_dataset&& data) const - { return std::forward(data).*member; } - }; - - /* getter_member_func_t */ - - template - struct getter_member_func_t - : public getter_t - { - using base_type = getter_t; - using dataset_type = typename base_type::dataset_type; - using value_type = typename base_type::value_type; - using member_type = T_member; - - member_type member; - - template - constexpr getter_member_func_t(X_member&& p_member) - : member(std::forward(p_member)) - { }; - - cpphibernate_copyable(getter_member_func_t, delete); - cpphibernate_moveable(getter_member_func_t, default); - - template - constexpr decltype(auto) operator()(X_dataset&& data) const - { return (std::forward(data).*member)(); } - }; - - /* getter_lambda_t */ - - template - struct getter_lambda_t - : public getter_t - { - using base_type = getter_t; - using dataset_type = typename base_type::dataset_type; - using value_type = typename base_type::value_type; - using lambda_type = T_lambda; - - lambda_type lambda; - - template - constexpr getter_lambda_t(X_lambda&& p_lambda) - : lambda(std::forward(p_lambda)) - { } - - cpphibernate_copyable(getter_lambda_t, delete); - cpphibernate_moveable(getter_lambda_t, default); - - template - constexpr decltype(auto) operator()(X_dataset&& data) const - { return lambda(std::forward(data)); } - }; - - /* is_getter_impl */ - - template - struct is_getter_impl - : public mp::c_false_t - { }; - - template - struct is_getter_impl, T>>> - : public mp::c_true_t - { }; - - } - - /* meta */ - - template - struct is_getter : - public __impl::is_getter_impl - { }; - - /* make */ - - template - constexpr decltype(auto) make_getter_member_var(T_value T_dataset::* member) - { - using getter_type = __impl::getter_member_var_t; - return getter_type(member); - } - - template - constexpr decltype(auto) make_getter_member_func(T_value (T_dataset::*member)()) - { - using getter_type = __impl::getter_member_func_t; - return getter_type(member); - } - - template - constexpr decltype(auto) make_getter_member_func(T_value (T_dataset::*member)() const) - { - using getter_type = __impl::getter_member_func_t; - return getter_type(member); - } - - template - constexpr decltype(auto) make_getter_lambda(T_lambda&& lambda, T_wrapped_dataset&&, T_wrapped_value&&) - { - using getter_type = __impl::getter_lambda_t, misc::decay_unwrap_t>; - return getter_type(std::forward(lambda)); - } - - /* operations */ - - namespace __impl - { - - /* getter_make_impl */ - - struct getter_make_impl - { - template - constexpr decltype(auto) operator()(T_value T_dataset::*member) const - { return make_getter_member_var(member); } - - template - constexpr decltype(auto) operator()(T_value (T_dataset::*member)(void)) const - { return make_getter_member_func(member); } - - template - constexpr decltype(auto) operator()(T_value (T_dataset::*member)(void) const) const - { return make_getter_member_func(member); } - - template - constexpr decltype(auto) operator()(T_func&& func, hana::type&& wrapped_dataset, hana::type&& wrapped_value) const - { return make_getter_lambda(std::forward(func), std::forward>(wrapped_dataset), std::forward>(wrapped_value)); } - - template - constexpr auto operator()(T_getter&& getter) const - -> mp::enable_if, T_getter> - { return std::forward(getter); } - }; - - } - - namespace getter - { - - constexpr decltype(auto) make = __impl::getter_make_impl { }; - - } - -} -end_namespace_cpphibernate_schema \ No newline at end of file diff --git a/include/cpphibernate/schema/macros.h b/include/cpphibernate/schema/macros.h index f3759ed..a81edad 100644 --- a/include/cpphibernate/schema/macros.h +++ b/include/cpphibernate/schema/macros.h @@ -1,32 +1,31 @@ #pragma once #include -#include #define cpphibernate_make_string(str) \ #str #define cpphibernate_make_schema(p_name, ...) \ - cpphibernate::schema::make( \ + cpphibernate::schema::make_schema( \ cpphibernate_make_string(p_name), \ - cpphibernate::schema::tables::make(__VA_ARGS__)) + cpphibernate::schema::make_tables(__VA_ARGS__)) #define cpphibernate_make_table_name(p_name, p_type, p_id, ...) \ - cpphibernate::schema::table::make( \ + cpphibernate::schema::make_table( \ cpphibernate_make_string(p_name), \ boost::hana::type_c, \ boost::hana::size_c, \ - cpphibernate::schema::fields::make(__VA_ARGS__)) + cpphibernate::schema::make_fields(__VA_ARGS__)) #define cpphibernate_make_table(p_name, p_id, ...) \ cpphibernate_make_table_name(p_name, p_name, p_id, __VA_ARGS__) #define cpphibernate_make_field_custom(p_name, p_getter, p_setter, ...) \ - cpphibernate::schema::field::make( \ + cpphibernate::schema::make_field( \ cpphibernate_make_string(p_name), \ - cpphibernate::schema::getter::make(p_getter), \ - cpphibernate::schema::setter::make(p_setter), \ - cpphibernate::schema::attributes::make(__VA_ARGS__)) + cpphibernate::mp::make_getter(p_getter), \ + cpphibernate::mp::make_setter(p_setter), \ + cpphibernate::schema::make_attributes(__VA_ARGS__)) #define cpphibernate_make_field_name(p_name, p_member_ptr, ...) \ cpphibernate_make_field_custom( \ @@ -45,14 +44,10 @@ #define cpphibernate_make_temp_id(p_dataset, p_value) \ cpphibernate_make_field_custom( \ id, \ - ::cpphibernate::schema::make_getter_lambda( \ + ::cpphibernate::mp::make_getter( \ [](p_dataset&) { \ return p_value { }; \ - }, \ - ::boost::hana::type_c, \ - ::boost::hana::type_c), \ - ::cpphibernate::schema::make_setter_lambda( \ - [](p_dataset&, p_value&&) { }, \ - ::boost::hana::type_c, \ - ::boost::hana::type_c), \ + }), \ + ::cpphibernate::mp::make_setter( \ + [](p_dataset&, p_value&&) { }), \ cpphibernate::schema::attribute::primary_key) diff --git a/include/cpphibernate/schema/schema.h b/include/cpphibernate/schema/schema.h index 158f94a..9289ec9 100644 --- a/include/cpphibernate/schema/schema.h +++ b/include/cpphibernate/schema/schema.h @@ -1,252 +1,130 @@ #pragma once #include -#include -#include -beg_namespace_cpphibernate_schema -{ +namespace cpphibernate { +namespace schema { namespace __impl { - /* schema_t */ - + /** + * @brief Tag class to mark schema objects. + */ + struct tag_schema + { }; + + /** + * @brief Schema type with all tables and fields for a database layout. + * + * @tparam T_name Name of the schema. + * @tparam T_tables List of all tables that belong to this schema. + */ template struct schema_t + : public tag_schema + , public tag_equality_comparable { using name_type = T_name; using tables_type = T_tables; - name_type name; - tables_type tables; + name_type name; //!< Name of the schema. + tables_type tables; //!< List of all tables that belongs to the schema. + /** + * @brief Constructor. + * + * @param[in] p_name Name of the schema. + * @param[in] p_tables List of all tables that belongs to the schema. + */ constexpr schema_t( - T_name&& p_name, - T_tables&& p_tables) - : name (std::forward (p_name)) - , tables(std::forward(p_tables)) - { } - - cpphibernate_copyable(schema_t, delete); - cpphibernate_moveable(schema_t, default); - - inline void print(std::ostream& os) const - { - using namespace ::utl; - size_t index = 0; - os << indent << '{' - << incindent - << indent << "\"name\": \"" << name << "\"," - << indent << "\"tables\": " - << indent << '[' - << incindent; - hana::for_each(tables, [&](auto& table){ - if (index++ > 0) os << ","; - table.print(os); - }); - os << decindent - << indent << ']' - << decindent - << indent << '}'; - } - }; - - } - - /* meta */ - - template - struct is_schema : mp::is_specialization_of { }; - - /* schema::make */ - - namespace __impl - { - template - struct schema_make_impl - { - template - static constexpr decltype(auto) apply(T_args&&... args) - { static_assert(sizeof...(args) == -1, "Invalid parameters for hibernate::schema::make(...)!"); } - }; - - template - struct schema_make_impl, mp::enable_if_c< - is_tables>::value>> - { - static constexpr decltype(auto) apply(T_name&& name, T_tables&& tables) - { return schema_t(std::forward(name), std::forward(tables)); } - }; - } - - constexpr decltype(auto) make = misc::make_generic_predicate<__impl::schema_make_impl> { }; - - /* schema::get_base_type */ - - namespace __impl - { - struct schema_get_base_type_impl - { - static constexpr decltype(auto) is_base_of_pred = hana::integral(hana::metafunction); - - struct do_fold_impl - { - template - constexpr decltype(auto) operator()(T_type_1&& type_1, T_type_2&& type_2) const - { - auto check = hana::or_(is_base_of_pred(type_1, type_2), is_base_of_pred(type_2, type_1)); - static_assert(decltype(check)::value, "A dataset must not have more than one base class!"); - return hana::if_(is_base_of_pred(type_1, type_2), type_2, type_1); - } - }; - - static constexpr decltype(auto) do_fold = do_fold_impl { }; - - template - static constexpr decltype(auto) get_base_types(T_wrapped_datasets&& wrapped_datasets, T_wrapped_dataset&& wrapped_dataset) - { - return hana::filter( - std::forward(wrapped_datasets), - [&](auto type){ - return hana::and_( - is_base_of_pred(type, wrapped_dataset), - hana::not_equal(type, wrapped_dataset)); - }); - } - - template - constexpr decltype(auto) operator()(T_schema&& schema, T_wrapped_dataset&& wrapped_dataset) const - { - using wrapped_datasets_type = mp::decay_t().tables, - schema::table::get_wrapped_dataset))>; - auto base_types = get_base_types(wrapped_datasets_type { }, std::forward(wrapped_dataset)); - return hana::eval_if( - hana::size(base_types) <= hana::size_c<0>, - [&](auto _){ return hana::type_c; }, - [&](auto _){ return hana::fold(_(base_types), do_fold); }); - } + name_type&& p_name, + tables_type&& p_tables); + + /** + * @brief Move constructor. + */ + constexpr schema_t(schema_t&&) = default; + + /** + * @brief Copy constructor. + */ + constexpr schema_t(const schema_t&) = delete; + + /** + * @brief Move assignment constructor. + */ + constexpr schema_t& operator = (schema_t&&) = default; + + /** + * @brief Copy assignment constructor. + */ + constexpr schema_t& operator = (const schema_t&) = delete; + + /** + * @brief Print the whole schema to the passed stream. + */ + inline void print(std::ostream& os) const; }; - } - - constexpr decltype(auto) get_base_type = __impl::schema_get_base_type_impl { }; - /* schema::get_root_base_type */ + /** + * @brief Helper type to build schema objects. + */ + template + struct schema_builder; + + /** + * @brief Helper type to get the base type of the passed dataset type. + */ + template + struct get_base_type_builder; + + /** + * @brief Helper class to get a list of the directly derived types of the passed dataset type. + */ + template + struct get_derived_types_builder; + + /** + * @brief Helper class to get a list of all derived types (including the passed dataset type). + */ + template + struct get_all_derived_types_builder; - namespace __impl - { - struct schema_get_root_base_type_impl - { - template - struct is_base_pred - { - using dataset_type = misc::decay_unwrap_t; - - template - constexpr decltype(auto) operator()(T_type&&) const - { - using type = misc::unwrap_t; - return hana::bool_c< - std::is_base_of::value - && !std::is_same ::value>; - } - }; - - template - constexpr decltype(auto) operator()(T_schema&& schema, T_wrapped_dataset&& wrapped_dataset) const - { - auto all_types = hana::transform( - schema.tables, - table::get_wrapped_dataset); - auto base_type = hana::find_if( - all_types, - is_base_pred { }); - return hana::eval_if( - base_type != hana::nothing, - [&](auto _){ - return schema_get_root_base_type_impl { }(schema, _(base_type).value()); - }, - [&]{ - return wrapped_dataset; - }); - } - }; } - constexpr decltype(auto) get_root_base_type = __impl::schema_get_root_base_type_impl { }; + /** + * @brief Evaluates to true_t if the passed type is a schema type. + */ + template + struct is_schema; - /* schema::get_all_derived_types */ + /** + * @brief Is true if the passed type is an attributes type, false otherwise. + */ + template + constexpr bool is_schema_v = is_schema::value; - namespace __impl - { - struct schema_get_all_derived_types_impl - { - template - struct is_derived_pred - { - using dataset_type = misc::decay_unwrap_t; - - template - constexpr decltype(auto) operator()(T_type&&) const - { - return hana::bool_c< - std::is_base_of>::value>; - } - }; - - template - constexpr decltype(auto) operator()(T_schema&& schema, T_wrapped_dataset&&) const - { - auto all_types = hana::transform( - schema.tables, - table::get_wrapped_dataset); - auto derived_types = hana::filter( - all_types, - is_derived_pred { }); - return derived_types; - } - }; - } + /** + * @brief Predicate to create an schema object from the passed attributes. + */ + constexpr decltype(auto) make_schema = mp::generic_predicate<__impl::schema_builder> { }; - constexpr decltype(auto) get_all_derived_types = __impl::schema_get_all_derived_types_impl { }; + /** + * @brief Predicate to get the base type of the passed dataset type. + */ + constexpr decltype(auto) get_base_type = mp::generic_predicate<__impl::get_base_type_builder> { }; - /* schema::get_derived_types */ + /** + * @brief Predicate to get a list of the directly derived types of the passed dataset type. + */ + constexpr decltype(auto) get_derived_types = mp::generic_predicate<__impl::get_derived_types_builder> { }; - namespace __impl - { - struct schema_get_derived_types_impl - { - struct has_base_impl - { - template - constexpr decltype(auto) operator()(T_schema&& schema, T_base_type& base_type, T_type&& type) const - { return get_base_type(schema, type) == base_type; } - }; - - static constexpr decltype(auto) has_base = has_base_impl { }; - - template - constexpr decltype(auto) operator()(T_schema&& schema, T_wrapped_dataset&& wrapped_dataset) const - { - using wrapped_datasets_type = mp::decay_t().tables, - schema::table::get_wrapped_dataset))>; - using derived_wrapped_datasets_type = mp::decay_t(), - hana::partial( - has_base, - std::declval(), - wrapped_dataset)))>; - return derived_wrapped_datasets_type { }; - } - }; - } + /** + * @brief Predicate to get a list of all derived types (including the passed dataset type). + */ + constexpr decltype(auto) get_all_derived_types = mp::generic_predicate<__impl::get_all_derived_types_builder> { }; - constexpr decltype(auto) get_derived_types = __impl::schema_get_derived_types_impl { }; +} } -} -end_namespace_cpphibernate_schema \ No newline at end of file +#include "schema.inl" diff --git a/include/cpphibernate/schema/schema.inl b/include/cpphibernate/schema/schema.inl new file mode 100644 index 0000000..13185b1 --- /dev/null +++ b/include/cpphibernate/schema/schema.inl @@ -0,0 +1,238 @@ +#pragma once + +#include + +#include "tables.h" +#include "schema.h" + +namespace cpphibernate { +namespace schema { + + namespace __impl + { + + /* schema_t */ + + template + constexpr schema_t + ::schema_t( + name_type&& p_name, + tables_type&& p_tables) + : name (std::forward (p_name)) + , tables(std::forward(p_tables)) + { } + + template + void schema_t + ::print(std::ostream& os) const + { + using namespace ::cppcore; + + size_t index = 0; + os << indent << '{' + << incindent + << indent << "\"name\": \"" << name << "\"," + << indent << "\"tables\": " + << indent << '[' + << incindent; + hana::for_each(tables, [&](auto& table){ + if (index++ > 0) os << ","; + table.print(os); + }); + os << decindent + << indent << ']' + << decindent + << indent << '}'; + } + + /* schema_builder */ + + template + struct schema_builder + { + template + static constexpr decltype(auto) apply(T_args&&...) + { static_assert(sizeof...(T_args) == -1, "Invalid parameters for make_schema(...)!"); } + }; + + template + struct schema_builder< + mp::list, + mp::enable_if_t< + is_tables_v>>> + { + using name_type = T_name; + using tables_type = T_tables; + using schema_type = schema_t; + + static constexpr decltype(auto) apply( + name_type&& name, + tables_type&& tables) + { + return schema_type( + std::forward (name), + std::forward(tables)); + } + }; + + /* get_base_type_builder */ + + template + struct get_base_type_builder + { + template + static constexpr decltype(auto) apply(T_args&&...) + { static_assert(sizeof...(T_args) == -1, "Invalid parameters for get_base_type(...)!"); } + }; + + template + struct get_base_type_builder< + mp::list, + mp::enable_if_t>>> + { + + /* is_base_of */ + + static constexpr decltype(auto) is_base_of = hana::integral(hana::metafunction); + + /* do_fold */ + + struct do_fold_impl + { + template + constexpr decltype(auto) operator()(T_type_1&& type_1, T_type_2&& type_2) const + { + auto check = hana::or_(is_base_of(type_1, type_2), is_base_of(type_2, type_1)); + static_assert(decltype(check)::value, "A dataset must not have more than one base class!"); + return hana::if_(is_base_of(type_1, type_2), type_2, type_1); + } + }; + + static constexpr decltype(auto) do_fold = do_fold_impl { }; + + /* get_base_types */ + + template + static constexpr decltype(auto) get_base_types(T_datasets datasets, T_dataset dataset) + { + return hana::filter( + datasets, + [&](auto type){ + return hana::and_( + is_base_of(type, dataset), + hana::not_equal(type, dataset)); + }); + } + + /* apply */ + + static constexpr decltype(auto) apply( + const T_schema& schema, + const T_wrapped_dataset& wrapped_dataset) + { + decltype(auto) wrapped_datasets = hana::transform( + schema.tables, + schema::get_wrapped_dataset); + decltype(auto) base_types = get_base_types(wrapped_datasets, wrapped_dataset); + return hana::eval_if( + hana::size(base_types) <= hana::size_c<0>, + [&](auto _){ return hana::type_c; }, + [&](auto _){ return hana::fold(_(base_types), do_fold); }); + } + }; + + /* get_derived_types_builder */ + + template + struct get_derived_types_builder + { + template + static constexpr decltype(auto) apply(T_args&&...) + { static_assert(sizeof...(T_args) == -1, "Invalid parameters for get_derived_types(...)!"); } + }; + + template + struct get_derived_types_builder< + mp::list, + mp::enable_if_t< + schema::is_schema_v> + && mp::is_same_v> + >> + { + struct has_base_impl + { + template + constexpr decltype(auto) operator()(T_type&&) const + { + return decltype(hana::equal( + get_base_type( + std::declval(), + std::declval()), + std::declval())) { }; + } + }; + + static constexpr decltype(auto) has_base = has_base_impl { }; + + static constexpr decltype(auto) apply(T_schema&& schema, T_dataset&& dataset) + { + constexpr decltype(auto) dataset_types = + decltype(hana::transform(schema.tables, schema::get_wrapped_dataset)) { }; + constexpr decltype(auto) derived_types = + decltype(hana::filter(dataset_types, has_base)) { }; + return derived_types; + } + }; + + /* get_all_derived_types_builder */ + + template + struct get_all_derived_types_builder + { + template + static constexpr decltype(auto) apply(T_args&&...) + { static_assert(sizeof...(T_args) == -1, "Invalid parameters for get_all_derived_types(...)!"); } + }; + + template + struct get_all_derived_types_builder< + mp::list, + mp::enable_if_t< + schema::is_schema_v> + && mp::is_same_v> + >> + { + struct is_derived_impl + { + using dataset_type = decay_unwrap_t; + + template + constexpr decltype(auto) operator()(T_type&&) const + { + return hana::bool_c< + std::is_base_of_v>>; + } + }; + + static constexpr decltype(auto) is_derived = is_derived_impl { }; + + static constexpr decltype(auto) apply(T_schema&& schema, T_dataset&& dataset) + { + constexpr decltype(auto) dataset_types = + decltype(hana::transform(schema.tables, schema::get_wrapped_dataset)) { }; + constexpr decltype(auto) derived_types = + decltype(hana::filter(dataset_types, is_derived)) { }; + return derived_types; + } + }; + + } + + /* is_schema */ + + template + struct is_schema + : public mp::is_base_of<__impl::tag_schema, T> + { }; + +} } diff --git a/include/cpphibernate/schema/setter.h b/include/cpphibernate/schema/setter.h deleted file mode 100644 index 4c2be2c..0000000 --- a/include/cpphibernate/schema/setter.h +++ /dev/null @@ -1,216 +0,0 @@ -#pragma once - -#include -#include - -beg_namespace_cpphibernate_schema -{ - - namespace __impl - { - - /* setter_t */ - - template - struct setter_t - { - using dataset_type = T_dataset; - using value_type = T_value; - }; - - /* setter_none_t */ - - struct setter_none_t - : public setter_t - { - using base_type = setter_t; - using dataset_type = typename base_type::dataset_type; - using value_type = typename base_type::value_type; - - cpphibernate_constructable(setter_none_t, default); - cpphibernate_copyable (setter_none_t, delete); - cpphibernate_moveable (setter_none_t, default); - }; - - /* setter_member_var_t */ - - template - struct setter_member_var_t - : public setter_t - { - using base_type = setter_t; - using value_type = typename base_type::value_type; - using dataset_type = typename base_type::dataset_type; - using member_type = T_member; - - member_type member; - - template - constexpr setter_member_var_t(X_member&& p_member) - : member(std::forward(p_member)) - { } - - cpphibernate_copyable(setter_member_var_t, delete); - cpphibernate_moveable(setter_member_var_t, default); - - template - constexpr decltype(auto) operator()(X_dataset&& data, X_value&& value) const - { return std::forward(data).*member = std::forward(value); } - }; - - /* setter_member_func_t */ - - template - struct setter_member_func_t - : public setter_t - { - using base_type = setter_t; - using value_type = typename base_type::value_type; - using dataset_type = typename base_type::dataset_type; - using member_type = T_member; - - member_type member; - - template - constexpr setter_member_func_t(X_member&& p_member) - : member(std::forward(p_member)) - { }; - - cpphibernate_copyable(setter_member_func_t, delete); - cpphibernate_moveable(setter_member_func_t, default); - - template - constexpr decltype(auto) operator()(X_dataset&& data, X_value&& value) const - { return (std::forward(data).*member)(std::forward(value)); } - }; - - /* setter_lambda_t */ - - template - struct setter_lambda_t - : public setter_t - { - using base_type = setter_t; - using value_type = typename base_type::value_type; - using dataset_type = typename base_type::dataset_type; - using lambda_type = T_lambda; - - lambda_type lambda; - - template - constexpr setter_lambda_t(X_lambda&& p_lambda) - : lambda(std::forward(p_lambda)) - { } - - cpphibernate_copyable(setter_lambda_t, delete); - cpphibernate_moveable(setter_lambda_t, default); - - template - constexpr decltype(auto) operator()(X_dataset&& data, X_value&& value) const - { return lambda(std::forward(data), std::forward(value)); } - }; - - /* is_setter_impl */ - - template - struct is_setter_impl - : public mp::c_false_t - { }; - - template - struct is_setter_impl, T>>> - : public mp::c_true_t - { }; - - } - - /* meta */ - - template - struct is_setter : - public __impl::is_setter_impl - { }; - - /* make */ - - constexpr decltype(auto) make_setter_none() - { - using setter_type = __impl::setter_none_t; - return setter_type(); - } - - template - constexpr decltype(auto) make_setter_member_var(T_value T_dataset::* member) - { - using setter_type = __impl::setter_member_var_t; - return setter_type(member); - } - - template - constexpr decltype(auto) make_setter_member_func(T_return (T_dataset::*member)(T_value)) - { - using setter_type = __impl::setter_member_func_t; - return setter_type(member); - } - - template - constexpr decltype(auto) make_setter_member_func(T_return (T_dataset::*member)(T_value) const) - { - using setter_type = __impl::setter_member_func_t; - return setter_type(member); - } - - template - constexpr decltype(auto) make_setter_lambda(T_lambda&& lambda, T_wrapped_dataset&&, T_wrapped_value&&) - { - using setter_type = __impl::setter_lambda_t, misc::decay_unwrap_t>; - return setter_type(std::forward(lambda)); - } - - /* operations */ - - namespace __impl - { - - /* setter_make_impl */ - - struct setter_make_impl - { - template - constexpr decltype(auto) operator()(T_value T_dataset::*member) const - { return make_setter_member_var(member); } - - template - constexpr decltype(auto) operator()(void (T_dataset::*member)(T_value)) const - { return make_setter_member_func(member); } - - template - constexpr decltype(auto) operator()(void (T_dataset::*member)(T_value&)) const - { return make_setter_member_func(member); } - - template - constexpr decltype(auto) operator()(void (T_dataset::*member)(T_value&&)) const - { return make_setter_member_func(member); } - - template - constexpr decltype(auto) operator()(T_func&& func, T_wrapped_dataset&&, T_wrapped_value&&) const - { return make_setter_lambda(std::forward(func), T_wrapped_dataset { }, T_wrapped_value { }); } - - template - constexpr auto operator()(T_setter&& setter) const - -> mp::enable_if, T_setter> - { return std::forward(setter); } - }; - - } - - namespace setter - { - - constexpr decltype(auto) make = __impl::setter_make_impl { }; - - } - -} -end_namespace_cpphibernate_schema \ No newline at end of file diff --git a/include/cpphibernate/schema/table.h b/include/cpphibernate/schema/table.h index 589993c..f6c91fc 100644 --- a/include/cpphibernate/schema/table.h +++ b/include/cpphibernate/schema/table.h @@ -1,190 +1,137 @@ #pragma once #include -#include -#include -#include -#include +#include +#include -beg_namespace_cpphibernate_schema -{ +namespace cpphibernate { +namespace schema { namespace __impl { - /* table_t */ - - template + /** + * @brief Tag class to mark table objects. + */ + struct tag_table + { }; + + /** + * @brief Represents a data field in the schema. + * + * @tparam T_name Name of the table. + * @tparam T_wrapped_dataset Dataset type the table represents. + * @tparam T_table_id Unique ID assigned by the user. + * @tparam T_fields List of field that belongs to the table. + */ + template< + typename T_name, + typename T_wrapped_dataset, + typename T_table_id, + typename T_fields> struct table_t + : public tag_table + , public tag_equality_comparable { using name_type = T_name; using wrapped_dataset_type = mp::decay_t; - using dataset_type = misc::decay_unwrap_t; + using dataset_type = decay_unwrap_t; using table_id_type = T_table_id; using fields_type = T_fields; using this_type = table_t; - name_type name; - wrapped_dataset_type wrapped_dataset; - table_id_type table_id; - fields_type fields; - + name_type name; //!< Name of the table. + wrapped_dataset_type wrapped_dataset; //!< Dataset type this table represents. + table_id_type table_id; //!< Unique ID of the table, assigned by the user. + fields_type fields; //!< List of fields that belongs to the table. + + /** + * @brief Constructor. + * + * @param[in] p_name Name of the table. + * @param[in] p_wrapped_dataset Dataset type this table represents. + * @param[in] p_table_id Unique ID of the table assigned by the user. + * @param[in] p_fields List of fields that belongs to the table. + */ constexpr table_t( - T_name p_name, - T_wrapped_dataset p_wrapped_dataset, - T_table_id p_table_id, - T_fields p_fields) - : name (std::forward (p_name)) - , wrapped_dataset (std::forward(p_wrapped_dataset)) - , table_id (std::forward (p_table_id)) - , fields (std::forward (p_fields)) - { } - - cpphibernate_copyable(table_t, delete); - cpphibernate_moveable(table_t, default); - - cpphibernate_equality_comparable(); - - inline void print(std::ostream& os) const - { - using namespace ::utl; - size_t index = 0; - os << indent << '{' - << incindent - << indent << "\"name\": \"" << name << "\"," - << indent << "\"wrapped_dataset\": \"" << utl::type_helper>::name() << "\"" - << indent << "\"table_id\": \"" << hana::value(table_id) << "\"" - << indent << "\"fields\": " - << indent << '[' - << incindent; - hana::for_each(fields, [&](auto& field){ - if (index++ > 0) os << ","; - field.print(os); - }); - os << decindent - << indent << ']' - << decindent - << indent << '}'; - } - }; - - } - - /* meta */ - - template - struct is_table - : public mp::is_specialization_of - { }; - - template - struct all_are_tables - : mp::all_true::value...> - { }; - - /* operations */ - - namespace __impl - { - - /* table_make_impl */ - - template - struct table_make_impl - { - template - static constexpr decltype(auto) apply(T_args&&... args) - { static_assert(sizeof...(args) == -1, "Invalid parameters for hibernate::schema::table::make(...)!"); } - }; - - template - struct table_make_impl, mp::enable_if_c< - hana::is_a - && is_fields>::value>> - { - static constexpr decltype(auto) apply(T_name&& name, T_wrapped_dataset&& wrapped_dataset, T_id&& id, T_fields&& fields) - { - return table_t( - std::forward (name), - std::forward (wrapped_dataset), - std::forward (id), - std::forward (fields)); - } + name_type p_name, + wrapped_dataset_type p_wrapped_dataset, + table_id_type p_table_id, + fields_type p_fields); + + /** + * @brief Move constructor. + */ + constexpr table_t(table_t&&) = default; + + /** + * @brief Copy constructor. + */ + constexpr table_t(const table_t&) = delete; + + /** + * @brief Move assignment constructor. + */ + constexpr table_t& operator = (table_t&&) = default; + + /** + * @brief Copy assignment constructor. + */ + constexpr table_t& operator = (const table_t&) = delete; + + /** + * @brief Print the table to the passed stream. + */ + inline void print(std::ostream& os) const; }; - /* table_get_wrapped_dataset_impl */ - - struct table_get_wrapped_dataset_impl - { - template - constexpr decltype(auto) operator()(T_table&& table) const - { return table.wrapped_dataset; } - }; - - /* table_get_primary_key_field_impl */ + /** + * @brief Helper type to build table objects. + */ + template + struct table_builder; + /** + * @brief Helper type to get the wrapped dataset of the passed table. + */ template - struct table_get_primary_key_field_impl - { - template - static constexpr decltype(auto) apply(T_args&&... args) - { static_assert(sizeof...(args) == -1, "Invalid parameters for hibernate::schema::table::get_primary_key_field(...)!"); } - }; + struct get_wrapped_dataset_builder; - template - struct table_get_primary_key_field_impl< - mp::list, - mp::enable_if_c< - is_table>::value>> - { - template - using is_primary_key_field = decltype(hana::contains( - std::declval().fields[hana::size_c].attributes, - attribute::primary_key)); - - template - struct helper; - - template - struct helper - { static_assert(N != N, "Unable to find primary key field for table!"); }; - - template - struct helper::value>> - { - static constexpr decltype(auto) apply(T_table&& table) - { return helper::apply(std::forward(table)); } - }; - - template - struct helper::value>> - { - static constexpr decltype(auto) apply(T_table&& table) - { return std::forward(table).fields[hana::size_c]; } - }; - - static constexpr decltype(auto) apply(T_table&& table) - { - using count = mp::decay_t().fields))>; - return helper<0, count::value>::apply( - std::forward(table)); - } - }; + /** + * @brief Helper type to get the primary key field of the passed table. + */ + template + struct get_primary_key_field_builder; } - namespace table - { + /** + * @brief Evaluates to true_t if the passed type is a table type. + */ + template + struct is_table; + + /** + * @brief Is true if the passed type is an attributes type, false otherwise. + */ + template + constexpr bool is_table_v = is_table::value; - constexpr decltype(auto) make = misc::make_generic_predicate<__impl::table_make_impl> { }; + /** + * @brief Predicate to create an table object from the passed attributes. + */ + constexpr decltype(auto) make_table = mp::generic_predicate<__impl::table_builder> { }; - constexpr decltype(auto) get_wrapped_dataset = __impl::table_get_wrapped_dataset_impl { }; + /** + * @brief Predicate to get the wrapped dataset of the passed table. + */ + constexpr decltype(auto) get_wrapped_dataset = mp::generic_predicate<__impl::get_wrapped_dataset_builder> { }; - constexpr decltype(auto) get_primary_key_field = misc::make_generic_predicate<__impl::table_get_primary_key_field_impl> { }; + /** + * @brief Predicate to get the primary key field of the passed table. + */ + constexpr decltype(auto) get_primary_key_field = mp::generic_predicate<__impl::get_primary_key_field_builder> { }; - } +} } -} -end_namespace_cpphibernate_schema \ No newline at end of file +#include "table.inl" diff --git a/include/cpphibernate/schema/table.inl b/include/cpphibernate/schema/table.inl new file mode 100644 index 0000000..359c3ed --- /dev/null +++ b/include/cpphibernate/schema/table.inl @@ -0,0 +1,174 @@ +#pragma once + +#include + +#include "fields.h" +#include "table.h" + +namespace cpphibernate { +namespace schema { + + namespace __impl + { + + /* table_t */ + + template + constexpr table_t + ::table_t( + name_type p_name, + wrapped_dataset_type p_wrapped_dataset, + table_id_type p_table_id, + fields_type p_fields) + : name (std::forward (p_name)) + , wrapped_dataset (std::forward(p_wrapped_dataset)) + , table_id (std::forward (p_table_id)) + , fields (std::forward (p_fields)) + { } + + template + void table_t + ::print(std::ostream& os) const + { + using namespace ::cppcore; + size_t index = 0; + os << indent << '{' + << incindent + << indent << "\"name\": \"" << name << "\"," + << indent << "\"wrapped_dataset\": \"" << type_helper::name() << "\"" + << indent << "\"table_id\": \"" << hana::value(table_id) << "\"" + << indent << "\"fields\": " + << indent << '[' + << incindent; + hana::for_each(fields, [&](auto& table){ + if (index++ > 0) os << ","; + table.print(os); + }); + os << decindent + << indent << ']' + << decindent + << indent << '}'; + } + + /* table_builder */ + + template + struct table_builder + { + template + static constexpr decltype(auto) apply(T_args&&...) + { static_assert(sizeof...(T_args) == -1, "Invalid parameters for make_table(...)!"); } + }; + + template< + typename T_name, + typename T_wrapped_dataset, + typename T_table_id, + typename T_fields> + struct table_builder< + mp::list, + mp::enable_if_t< + mp::is_valid_v>> + && is_fields_v>>> + { + using name_type = T_name; + using wrapped_dataset_type = T_wrapped_dataset; + using table_id_type = T_table_id; + using fields_type = T_fields; + using table_type = table_t; + + static constexpr decltype(auto) apply( + name_type&& name, + wrapped_dataset_type&& wrapped_dataset, + table_id_type&& table_id, + fields_type&& fields) + { + return table_type( + std::forward (name), + std::forward (wrapped_dataset), + std::forward (table_id), + std::forward (fields)); + } + }; + + /* get_wrapped_dataset */ + + template + struct get_wrapped_dataset_builder + { + template + static constexpr decltype(auto) apply(T_args&&...) + { static_assert(sizeof...(T_args) == -1, "Invalid parameters for get_wrapped_dataset(...)!"); } + }; + + template + struct get_wrapped_dataset_builder< + mp::list, + mp::enable_if_t>>> + { + static constexpr decltype(auto) apply(T_table&& table) + { return std::forward(table).wrapped_dataset; } + }; + + /* get_primary_key_field_builder */ + + template + struct get_primary_key_field_builder + { + template + static constexpr decltype(auto) apply(T_args&&... args) + { static_assert(sizeof...(args) == -1, "Invalid parameters for get_primary_key_field(...)!"); } + }; + + template + struct get_primary_key_field_builder< + mp::list, + mp::enable_if_t< + is_table_v>>> + { + template + using is_primary_key_field = decltype(hana::contains( + std::declval().fields[hana::size_c].attributes, + attribute::primary_key)); + + template + struct helper; + + template + struct helper + { static_assert(N != N, "Unable to find primary key field for table!"); }; + + template + struct helper::value>> + { + static constexpr decltype(auto) apply(T_table&& table) + { return helper::apply(std::forward(table)); } + }; + + template + struct helper::value>> + { + static constexpr decltype(auto) apply(T_table&& table) + { return std::forward(table).fields[hana::size_c]; } + }; + + static constexpr decltype(auto) apply(T_table&& table) + { + using count = mp::decay_t().fields))>; + return helper<0, count::value>::apply( + std::forward(table)); + } + }; + + } + + /* is_table */ + + template + struct is_table + : public mp::is_specialization_of + { }; + +} } diff --git a/include/cpphibernate/schema/tables.h b/include/cpphibernate/schema/tables.h index 184fca2..5f8cd03 100644 --- a/include/cpphibernate/schema/tables.h +++ b/include/cpphibernate/schema/tables.h @@ -1,120 +1,50 @@ #pragma once #include -#include #include -beg_namespace_cpphibernate_schema -{ +namespace cpphibernate { +namespace schema { namespace __impl { - /* tables_t */ - - template - using tables_t = hana::tuple; - - /* is_tables_impl */ - + /** + * @brief Helper type to build tables. + */ template - struct is_tables_impl - : mp::c_false_t - { }; + struct tables_builder; - template - struct is_tables_impl, mp::enable_if>> - : mp::c_true_t - { }; + /** + * @brief Helper class to find a table by it's datatype. + */ + template + struct find_table_builder; } - /* meta */ - + /** + * @brief Evaluates to true_t if the passed type is a tables type. + */ template - struct is_tables - : public __impl::is_tables_impl - { }; - - /* operations */ - - namespace __impl - { - - /* tables_make_impl */ - - template - struct tables_make_impl - { - template - static constexpr decltype(auto) apply(T_args&&... args) - { static_assert(sizeof...(args) == -1, "Invalid parameters for hibernate::schema::tables::make(...)!"); } - }; - - template - struct tables_make_impl, mp::enable_if>> - { - template - static constexpr decltype(auto) apply(T_tables&&... tables) - { return tables_t(std::forward(tables)...); } - }; - - /* tables_find_impl */ + struct is_tables; - template - struct tables_find_impl - { - template - static constexpr decltype(auto) apply(T_args&&... args) - { static_assert(sizeof...(args) == -1, "Invalid parameters for hibernate::schema::tables::find(...)!"); } - }; - - template - struct tables_find_impl< - mp::list, - mp::enable_if_c< - is_tables>::value>> - { - template - struct helper; - - template - struct helper - { static_assert(N != N, "Table for given datatype does not exist!"); }; - - template - struct helper()[hana::size_c].wrapped_dataset, T_wrapped_dataset { }))::value>> - { - static constexpr decltype(auto) apply(T_tables&& tables) - { return helper::apply(std::forward(tables)); } - }; - - template - struct helper()[hana::size_c].wrapped_dataset, T_wrapped_dataset { }))::value>> - { - static constexpr decltype(auto) apply(T_tables&& tables) - { return std::forward(tables)[hana::size_c]; } - }; - - static constexpr decltype(auto) apply(T_tables&& tables, T_wrapped_dataset&&) - { - using count_type = mp::decay_t()))>; - return helper<0, count_type::value>::apply(std::forward(tables)); - } - }; - - } - - namespace tables - { + /** + * @brief Is true if the passed type is an tables type, false otherwise. + */ + template + constexpr bool is_tables_v = is_tables::value; - constexpr decltype(auto) make = misc::make_generic_predicate<__impl::tables_make_impl> { }; + /** + * @brief Predicate to create an tables object from the passed tables. + */ + constexpr decltype(auto) make_tables = mp::generic_predicate<__impl::tables_builder> { }; - constexpr decltype(auto) find = misc::make_generic_predicate<__impl::tables_find_impl> { }; + /** + * @brief Predicate to find a table by it's dataset type. + */ + constexpr decltype(auto) find_table = mp::generic_predicate<__impl::find_table_builder> { }; - } +} } -} -end_namespace_cpphibernate_schema \ No newline at end of file +#include "tables.inl" diff --git a/include/cpphibernate/schema/tables.inl b/include/cpphibernate/schema/tables.inl new file mode 100644 index 0000000..2988e18 --- /dev/null +++ b/include/cpphibernate/schema/tables.inl @@ -0,0 +1,111 @@ +#pragma once + +#include "tables.h" + +namespace cpphibernate { +namespace schema { + + namespace __impl + { + + /* tables_t */ + + template + using tables_t = hana::tuple; + + /* tables_builder */ + + template + struct tables_builder + { + template + static constexpr decltype(auto) apply(T_args&&...) + { static_assert(sizeof...(T_args) == -1, "Invalid parameters for make_tables(...)!"); } + }; + + template + struct tables_builder< + mp::list, + mp::enable_if_t>...>>> + { + template + static constexpr decltype(auto) apply(T_args&&... args) + { + using tables_type = tables_t; + return tables_type(std::forward(args)...); + } + }; + + /* find_table_builder */ + + template + struct find_table_builder + { + template + static constexpr decltype(auto) apply(T_args&&... args) + { static_assert(sizeof...(T_args) == -1, "Invalid parameters for find_table(...)!"); } + }; + + template + struct find_table_builder< + mp::list, + mp::enable_if_t< + is_tables_v> + && mp::is_same_v> + >> + { + template + struct helper; + + template + struct helper + { static_assert(N != N, "Table for given datatype does not exist!"); }; + + template + struct helper()[hana::size_c].wrapped_dataset, T_dataset { }))::value>> + { + static constexpr decltype(auto) apply(T_tables&& tables) + { return helper::apply(std::forward(tables)); } + }; + + template + struct helper()[hana::size_c].wrapped_dataset, T_dataset { }))::value>> + { + static constexpr decltype(auto) apply(T_tables&& tables) + { return std::forward(tables)[hana::size_c]; } + }; + + static constexpr decltype(auto) apply(T_tables&& tables, T_dataset&&) + { + using count_type = mp::decay_t()))>; + return helper<0, count_type::value>::apply(std::forward(tables)); + } + }; + + /* is_tables_impl */ + + template + struct is_tables_impl + : mp::false_t + { }; + + template + struct is_tables_impl< + tables_t, + mp::enable_if_t>...>>> + : mp::true_t + { }; + + } + + /* is_tables */ + + template + struct is_tables + : public __impl::is_tables_impl + { }; + +} } diff --git a/include/cpphibernate/types.h b/include/cpphibernate/types.h index 9f7d2d2..5cd92fc 100644 --- a/include/cpphibernate/types.h +++ b/include/cpphibernate/types.h @@ -1,75 +1,134 @@ #pragma once #include +#include #include #include +#include -#include - -beg_namespace_cpphibernate +namespace cpphibernate { - /* string */ + /** + * @brief Hibernate exception class. + */ + struct exception + : public cppcore::exception + { + using cppcore::exception::exception; + }; + /** + * @brief Represents a string with an fixed length. + */ template struct string : public std::string { static constexpr decltype(auto) max_size = N; + using std::string::string; using std::string::operator=; }; - /* timestamp */ - + /** + * @brief Timestamp class. + */ struct timestamp { uint64_t value; + /** + * @brief Default constructor. + */ inline timestamp() = default; - inline timestamp(uint64_t v) - : value(v) - { } + /** + * @brief Value constructor. + */ + inline timestamp(uint64_t v); - inline timestamp& operator=(const uint64_t& v) - { - value = v; - return *this; - } + /** + * @brief Value assignment constructor. + */ + inline timestamp& operator=(const uint64_t& v); - inline operator uint64_t() const - { return value; } + /** + * @brief Impicit type conversion to base type. + */ + inline operator uint64_t() const; }; - /* uuid */ + /** + * @brief A typical UUID implementation. + */ struct uuid : public std::array { public: - inline uuid() - : std::array::array({ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } }) - { } - - inline uuid(const std::string& str) - : std::array::array({ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } }) - { - if (!from_string(str, *this)) - throw utl::argument_exception("str", "invalid uuid"); - } - - cpphibernate_copyable(uuid, default); - cpphibernate_moveable(uuid, default); - + /** + * @brief Default constructor. + */ + inline uuid(); + + /** + * @brief Construtor to create a UUID from a string. + * + * @param[in] str String to create UUID from. + */ + inline uuid(const std::string& str); + + /** + * @brief Move constructor. + */ + inline uuid(uuid&&) = default; + + /** + * @brief Copy constructor. + */ + inline uuid(const uuid&) = default; + + /** + * @brief Move assignment constructor. + */ + inline uuid& operator = (uuid&&) = default; + + /** + * @brief Copy assignment constructor. + */ + inline uuid& operator = (const uuid&) = default; + + /** + * @brief Write the UUID to passed stream. + * + * @param[in] os Stream to write UUID to. + */ void to_string(std::ostream& os) const; + /** + * @brief Write the UUID to passed stream. + * + * @param[in] os Stream to write UUID to. + * + * @return Stream passed as parameter. + */ std::ostream& operator<<(std::ostream& os) const; public: + /** + * @brief Create an UUID object from an string. + * + * @param[in] str String to create UUID from. + * @param[in] val Parameter to store converted object in + * + * @retval true If the convertion was successful. + * @retval false If the string could not be converted. + */ static bool from_string(const std::string& str, uuid& val); }; } -end_namespace_cpphibernate + +#include "types.inl" diff --git a/include/cpphibernate/types.inl b/include/cpphibernate/types.inl new file mode 100644 index 0000000..7416f3c --- /dev/null +++ b/include/cpphibernate/types.inl @@ -0,0 +1,38 @@ +#pragma once + +#include + +#include "types.h" + +namespace cpphibernate +{ + + /* timestamp */ + + timestamp::timestamp(uint64_t v) + : value(v) + { } + + timestamp& timestamp::operator=(const uint64_t& v) + { + value = v; + return *this; + } + + timestamp::operator uint64_t() const + { return value; } + + /* uuid */ + + uuid::uuid() + : std::array::array({ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } }) + { } + + uuid::uuid(const std::string& str) + : std::array::array({ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } }) + { + if (!from_string(str, *this)) + throw ::cppcore::argument_exception("str", "invalid uuid"); + } + +} diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index b39bec3..d69771b 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,53 +1,151 @@ # Initialize ###################################################################################### -Include ( cotire OPTIONAL ) -Include ( pedantic OPTIONAL ) -Include ( strip_symbols OPTIONAL ) +Include ( cotire OPTIONAL RESULT_VARIABLE HAS_COTIRE ) +Include ( pedantic OPTIONAL RESULT_VARIABLE HAS_PEDANTIC ) +Include ( strip_symbols OPTIONAL RESULT_VARIABLE HAS_STRIP_SYMBOLS ) -Include ( ${CMAKE_CURRENT_SOURCE_DIR}/../cmake/options.cmake ) +Find_Package ( Hana REQUIRED ) +Find_Package ( cppmp REQUIRED ) +Find_Package ( cppcore REQUIRED ) +Find_Package ( cppmariadb REQUIRED ) +If ( cppmariadb_FOUND ) + Set ( CPPHIBERNATE_HAS_CPPMARIADB true ) +EndIf ( ) +If ( CPPHIBERNATE_USE_CPPLOGGING ) + Find_Package ( cpplogging ) + If ( cpplogging_FOUND ) + Set ( CPPHIBERNATE_HAS_CPPLOGGING true ) + EndIf ( ) +EndIf ( ) -Set ( BUILD_SHARED_LIBS ${CPPHIBERNATE_BUILD_SHARED} ) -Set ( CMAKE_CXX_STANDARD 17 ) -Set ( CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${PEDANTIC_C_FLAGS}" ) -Set ( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${PEDANTIC_CXX_FLAGS}" ) +# Object Library ################################################################################## -# Dependencies #################################################################################### +Set ( CMAKE_POSITION_INDEPENDENT_CODE ON ) +Set ( CPPHIBERNATE_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/../include ) +Set ( CPPHIBERNATE_GENERATED_INCLUDE_DIR ${CMAKE_CURRENT_BINARY_DIR}/generated/include ) +Configure_File ( ${CMAKE_CURRENT_SOURCE_DIR}/../cmake/config.h.in + ${CPPHIBERNATE_GENERATED_INCLUDE_DIR}/cpphibernate/config.h ) +File ( GLOB_RECURSE CPPHIBERNATE_HEADER_FILES ${CPPHIBERNATE_INCLUDE_DIR}/*.h ) +File ( GLOB_RECURSE CPPHIBERNATE_INLINE_FILES ${CPPHIBERNATE_INCLUDE_DIR}/*.inl ) +File ( GLOB_RECURSE CPPHIBERNATE_SOURCE_FILES ${CMAKE_CURRENT_SOURCE_DIR}/*.cpp ) +Add_Library ( cpphibernate-objects + OBJECT + ${CPPHIBERNATE_HEADER_FILES} + ${CPPHIBERNATE_INLINE_FILES} + ${CPPHIBERNATE_SOURCE_FILES} ) +Target_Include_Directories ( cpphibernate-objects + PUBLIC + $ + $ + $ ) +Target_Link_Libraries ( cpphibernate-objects + PUBLIC + hana + cppmp::cppmp + cppcore::cppcore ) +If ( CPPHIBERNATE_HAS_CPPMARIADB ) + Target_Link_Libraries ( cpphibernate-objects + PUBLIC + cppmariadb::cppmariadb-shared ) +EndIf ( ) +If ( CPPHIBERNATE_HAS_CPPLOGGING ) + Target_Link_Libraries ( cpphibernate-objects + PUBLIC + cpplogging::cpplogging-shared ) +EndIf ( ) -Find_Package ( cpputils REQUIRED ) -Find_Package ( cppmariadb REQUIRED ) +# Static Library ################################################################################## -# Project: cpphibernate ############################################################################### +Add_Library ( cpphibernate-static STATIC $ ) +Set_Target_Properties ( cpphibernate-static + PROPERTIES + OUTPUT_NAME "${CPPHIBERNATE_OUTPUTNAME}" + VERSION ${CPPHIBERNATE_VERSION} ) +Target_Include_Directories ( cpphibernate-static + PUBLIC + $ + $ + $ ) +If ( CPPHIBERNATE_HAS_CPPMARIADB ) + Target_Link_Libraries ( cpphibernate-static + PUBLIC + cppmariadb::cppmariadb-static ) +EndIf ( ) +If ( CPPHIBERNATE_HAS_CPPLOGGING ) + Target_Link_Libraries ( cpphibernate-objects + PUBLIC + cpplogging::cpplogging-static ) +EndIf ( ) -# Build -Project ( cpphibernate VERSION 1.0.0.0 LANGUAGES CXX ) -Set ( CPPHIBERNATE_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/../include ) -Set ( CPPHIBERNATE_GENERATE_DIR ${CMAKE_CURRENT_BINARY_DIR}/generated ) -File ( GLOB_RECURSE SOURCE_FILES ${CMAKE_CURRENT_SOURCE_DIR}/*.cpp ) -Add_Library ( cpphibernate ${SOURCE_FILES} ) -Configure_File ( ${CMAKE_CURRENT_SOURCE_DIR}/../cmake/options.h.in - ${CPPHIBERNATE_GENERATE_DIR}/cpphibernate/options.h ) -Target_Include_Directories ( cpphibernate - PUBLIC ${CPPHIBERNATE_INCLUDE_DIR} - ${CPPHIBERNATE_GENERATE_DIR} ) -Target_Link_Libraries ( cpphibernate - cpputils - cppmariadb ) - -# Install -If ( BUILD_SHARED_LIBS OR CPPHIBERNATE_INSTALL_DEV_FILES ) - Install ( TARGETS cpphibernate DESTINATION lib ) -EndIf ( ) - -If ( CPPHIBERNATE_INSTALL_DEV_FILES ) - Install ( FILES ${CPPHIBERNATE_INCLUDE_DIR}/cpphibernate.h DESTINATION include ) - Install ( DIRECTORY ${CPPHIBERNATE_INCLUDE_DIR}/cpphibernate DESTINATION include ) - Install ( DIRECTORY ${CPPHIBERNATE_GENERATE_DIR}/cpphibernate DESTINATION include ) -EndIf ( ) - -# Optimize -If ( __COTIRE_INCLUDED ) - Cotire ( cpphibernate ) -EndIf ( ) -If ( __STRIP_SYMBOLS_INCLUDED AND BUILD_SHARED_LIBS ) - Strip_Symbols ( cpphibernate DBG_FILE ) -EndIf () +# Shared Library ################################################################################## + +Add_Library ( cpphibernate-shared SHARED $ ) +Set_Target_Properties ( cpphibernate-shared + PROPERTIES + OUTPUT_NAME "${CPPHIBERNATE_OUTPUTNAME}" + VERSION ${CPPHIBERNATE_VERSION} + SOVERSION ${CPPHIBERNATE_VERSION_SHORT} ) +Target_Include_Directories ( cpphibernate-shared + PUBLIC + $ + $ + $ ) +If ( CPPHIBERNATE_HAS_CPPMARIADB ) + Target_Link_Libraries ( cpphibernate-shared + PUBLIC + cppmariadb::cppmariadb-shared ) +EndIf ( ) +If ( CPPHIBERNATE_HAS_CPPLOGGING ) + Target_Link_Libraries ( cpphibernate-objects + PUBLIC + cpplogging::cpplogging-shared ) +EndIf ( ) + +# Optimization #################################################################################### + +# pedantic +If ( HAS_PEDANTIC ) + Pedantic_Apply_Flags_Target ( cpphibernate-objects ALL ) + Pedantic_Apply_Flags_Target ( cpphibernate-static ALL ) + Pedantic_Apply_Flags_Target ( cpphibernate-shared ALL ) +EndIf ( ) + +# cotire +If ( HAS_COTIRE ) + Cotire ( cpphibernate-objects ) + Cotire ( cpphibernate-static ) + Cotire ( cpphibernate-shared ) +EndIf ( ) + +# Install ######################################################################################### + +# Header +If ( CPPHIBERNATE_INSTALL_HEADER ) + Install ( FILES ${CPPHIBERNATE_INCLUDE_DIR}/cpphibernate.h + DESTINATION ${CPPHIBERNATE_INSTALL_DIR_INCLUDE} ) + Install ( DIRECTORY ${CPPHIBERNATE_INCLUDE_DIR}/cpphibernate + DESTINATION ${CPPHIBERNATE_INSTALL_DIR_INCLUDE} ) +EndIf ( ) + +# Static +If ( CPPHIBERNATE_INSTALL_STATIC ) + Install ( TARGETS cpphibernate-static + EXPORT cpphibernate + DESTINATION ${CPPHIBERNATE_INSTALL_DIR_LIB} ) +EndIf ( ) + +# Shared +If ( CPPHIBERNATE_INSTALL_SHARED ) + Install ( TARGETS cpphibernate-shared + EXPORT cpphibernate + DESTINATION ${CPPHIBERNATE_INSTALL_DIR_LIB} ) +EndIf ( ) + +# Debug +If ( HAS_STRIP_SYMBOLS AND NOT CPPHIBERNATE_NO_STRIP ) + Strip_Symbols ( cpphibernate-shared CPPHIBERNATE_DBG_FILE ) + If ( CPPHIBERNATE_INSTALL_DEBUG ) + Install ( FILES ${CPPHIBERNATE_DBG_FILE} + DESTINATION ${CPPHIBERNATE_INSTALL_DIR_LIB} ) + EndIf ( ) +EndIf ( ) diff --git a/src/cpphibernate/driver/mariadb/classes/fields/create_update.cpp b/src/cpphibernate/driver/mariadb/classes/fields/create_update.cpp new file mode 100644 index 0000000..f2c4aba --- /dev/null +++ b/src/cpphibernate/driver/mariadb/classes/fields/create_update.cpp @@ -0,0 +1,66 @@ +#include +#include +#include + +#include + +using namespace ::cpphibernate; +using namespace ::cpphibernate::mariadb; + +static std::string build_foreign_many_update_query(const field_t& field); + +void field_t::foreign_many_update( + const create_update_context& context, + const std::string& primary_key) const +{ + auto& statement = get_statement_foreign_many_update(); + auto& connection = context.connection; + statement.set(0, primary_key); + cpphibernate_log_debug("execute UPDATE old foreign many query: " << std::endl << update_statement.query(connection) << std::endl); + connection.execute(statement); +} + +::cppmariadb::statement& field_t::get_statement_foreign_many_update() const +{ + if (!_statement_foreign_many_update) + { + auto query = build_foreign_many_update_query(*this); + _statement_foreign_many_update.reset(new ::cppmariadb::statement(std::move(query))); + } + return *_statement_foreign_many_update; +} + +std::string build_foreign_many_update_query(const field_t& field) +{ + assert(field.referenced_table); + assert(field.referenced_table->primary_key_field); + + auto& ref_key_info = *field.referenced_table->primary_key_field; + + std::ostringstream os; + os << "UPDATE `" + << ref_key_info.table.name + << "` SET `" + << field.table.name + << "_id_" + << field.name + << "`=NULL"; + if (field.value_is_container) + { + os << ", `" + << field.table.name + << "_index_" + << field.name + << "`=0"; + } + os << " WHERE `" + << field.table.name + << "_id_" + << field.name + << "`=" + << ref_key_info.convert_to_open + << "?\?" + << ref_key_info.convert_to_close; + + return os.str(); +} diff --git a/src/cpphibernate/driver/mariadb/classes/fields/destroy.cpp b/src/cpphibernate/driver/mariadb/classes/fields/destroy.cpp new file mode 100644 index 0000000..073708c --- /dev/null +++ b/src/cpphibernate/driver/mariadb/classes/fields/destroy.cpp @@ -0,0 +1,125 @@ +#include +#include +#include + +using namespace ::cpphibernate; +using namespace ::cpphibernate::mariadb; + +static std::string build_foreign_one_delete_query(const field_t& field, bool key_known); + +void field_t::foreign_one_delete( + const create_update_context& context, + const std::string& primary_key, + const value_t& foreign_key) const +{ + /* get statement */ + auto& statement = foreign_key.has_value() + ? get_statement_foreign_one_delete_key_known() + : get_statement_foreign_one_delete_key_unknown(); + + /* update statement values */ + statement.set(0, primary_key); + if (foreign_key.has_value()) + statement.set(1, *foreign_key); + + /* execute query */ + auto& connection = context.connection; + cpphibernate_log_debug("execute DELETE old foreign one query: " << std::endl << statement.query(connection) << std::endl); + connection.execute(statement); + + /* cleanup tables */ + assert(referenced_table); + table_t::table_set processed; + referenced_table->cleanup(context, processed, true, true); +} + + +::cppmariadb::statement& field_t::get_statement_foreign_one_delete_key_known() const +{ + if (!_statement_foreign_one_delete_key_known) + { + auto query = build_foreign_one_delete_query(*this, true); + _statement_foreign_one_delete_key_known.reset(new ::cppmariadb::statement(std::move(query))); + } + return *_statement_foreign_one_delete_key_known; +} + +cppmariadb::statement& field_t::get_statement_foreign_one_delete_key_unknown() const +{ + if (!_statement_foreign_one_delete_key_unknown) + { + auto query = build_foreign_one_delete_query(*this, false); + _statement_foreign_one_delete_key_unknown.reset(new ::cppmariadb::statement(std::move(query))); + } + return *_statement_foreign_one_delete_key_unknown; +} + +std::string build_foreign_one_delete_query(const field_t& field, bool key_known) +{ + assert(field.table.primary_key_field); + assert(field.referenced_table); + assert(field.referenced_table->primary_key_field); + + if (key_known) + { + auto& ref_table = *field.referenced_table; + auto& key_info = *field.table.primary_key_field; + auto& ref_key_info = *ref_table.primary_key_field; + + std::ostringstream os; + os << "WHERE `" + << ref_key_info.name + << "` IN (SELECT `" + << ref_key_info.table.name + << "_id_" + << field.name + << "` FROM `" + << key_info.table.name + << "` WHERE `" + << key_info.name + << "`=" + << key_info.convert_to_open + << "?\?" + << key_info.convert_to_close + << " AND `" + << ref_key_info.table.name + << "_id_" + << field.name + << "`!=" + << ref_key_info.convert_to_open + << "?\?" + << ref_key_info.convert_to_close + << ")"; + + auto where = os.str(); + auto query = ref_table.build_delete_query(&where); + return query; + } + else + { + auto& ref_table = *field.referenced_table; + auto& key_info = *field.table.primary_key_field; + auto& ref_key_info = *ref_table.primary_key_field; + + std::ostringstream os; + os << "WHERE `" + << ref_key_info.name + << "` IN (SELECT `" + << ref_key_info.table.name + << "_id_" + << field.name + << "` FROM `" + << key_info.table.name + << "` WHERE `" + << key_info.name + << "`=" + << key_info.convert_to_open + << "?\?" + << key_info.convert_to_close + << ")"; + + auto where = os.str(); + auto query = ref_table.build_delete_query(&where); + return query; + } +} diff --git a/src/cpphibernate/driver/mariadb/classes/fields/misc.cpp b/src/cpphibernate/driver/mariadb/classes/fields/misc.cpp new file mode 100644 index 0000000..f2be7ee --- /dev/null +++ b/src/cpphibernate/driver/mariadb/classes/fields/misc.cpp @@ -0,0 +1,143 @@ +#include +#include +#include +#include + +#include + +using namespace ::cpphibernate; +using namespace ::cpphibernate::mariadb; + +/* field_t */ + +field_t::~field_t() + { } + +std::ostream& field_t::print(std::ostream& os) const +{ + using namespace ::cppcore; + + os << indent << '{' + << incindent + + << indent << "\"id\": " << id << "," + << indent << "\"value_id\": " << value_id << "," + << indent << "\"real_value_id\": " << real_value_id << "," + << indent << "\"value_is_nullable\": " << (value_is_nullable ? "true" : "false") << "," + << indent << "\"value_is_pointer\": " << (value_is_pointer ? "true" : "false") << "," + << indent << "\"value_is_container\": " << (value_is_container ? "true" : "false") << "," + << indent << "\"value_is_auto_incremented\": " << (value_is_auto_incremented ? "true" : "false") << "," + << indent << "\"table\": " << '"' << table.name << "\"," + << indent << "\"referenced_table\": " << (referenced_table + ? std::string("\"") + referenced_table->name + "\"" + : "null") << "," + << indent << "\"name\": \"" << name << "\"," + << indent << "\"type\": \"" << type << "\"," + << indent << "\"create_arguments\": \"" << create_arguments << "\"," + + << indent << "\"convert_to_open\": \"" << convert_to_open << "\"," + << indent << "\"convert_to_close\": \"" << convert_to_close << "\"," + << indent << "\"convert_from_open\": \"" << convert_from_open << "\"," + << indent << "\"convert_from_close\": \"" << convert_from_close << "\"," + + << indent << "\"attributes\": " << make_print_container(attributes, false) + + << decindent + << indent << '}'; + + return os; +} + +void field_t::init() +{ + /* conver_to_open */ + { + std::ostringstream ss; + for (auto& attrib : attributes) + { + switch(attrib) + { + case attribute_t::hex: + ss << "HEX("; + break; + case attribute_t::compress: + ss << "COMPRESS("; + break; + default: + break; + } + } + convert_to_open = ss.str(); + } + + /* convert_to_close */ + { + std::ostringstream ss; + for (auto& attrib : attributes) + { + switch(attrib) + { + case attribute_t::hex: + case attribute_t::compress: + ss << ')'; + break; + default: + break; + } + } + convert_to_close = ss.str(); + } + + /* convert_from_open */ + { + std::ostringstream ss; + for (auto& attrib : attributes) + { + switch(attrib) + { + case attribute_t::hex: + ss << "UNHEX("; + break; + case attribute_t::compress: + ss << "UNCOMPRESS("; + break; + default: + break; + } + } + convert_from_open = ss.str(); + } + + /* convert_from_close */ + { + std::ostringstream ss; + for (auto& attrib : attributes) + { + switch(attrib) + { + case attribute_t::hex: + case attribute_t::compress: + ss << ')'; + break; + default: + break; + } + } + convert_from_close = ss.str(); + } +} + +#define throw_not_implemented(p_ret, p_name, ...) \ + p_ret field_t::p_name(__VA_ARGS__) const \ + { \ + throw ::cpphibernate::exception( \ + std::string("'") + table.name + "." + name + \ + "' does not implement the " #p_name "() method!"); \ + } + +throw_not_implemented(value_t, get, const data_context& context) +throw_not_implemented(void, set, const data_context& context, const value_t& value) +throw_not_implemented(bool, is_default, const data_context& context) +throw_not_implemented(std::string, generate_value, ::cppmariadb::connection& connection) +throw_not_implemented(value_t, foreign_create_update, const create_update_context& context) +throw_not_implemented(read_context_ptr_u, foreign_read, const read_context& context, bool create_fake) diff --git a/src/cpphibernate/driver/mariadb/classes/schema/init.cpp b/src/cpphibernate/driver/mariadb/classes/schema/init.cpp new file mode 100644 index 0000000..e0605c0 --- /dev/null +++ b/src/cpphibernate/driver/mariadb/classes/schema/init.cpp @@ -0,0 +1,90 @@ +#include +#include + +using namespace ::cpphibernate; +using namespace ::cpphibernate::mariadb; + +#define exec_query() \ + do { \ + connection.execute(ss.str()); \ + ss.str(std::string()); \ + ss.clear(); \ + } while(0) + +void schema_t::init(const init_context& context) const +{ + std::ostringstream ss; + auto& connection = context.connection; + + if (context.recreate) + { + ss << "DROP DATABASE IF EXISTS `" + << name + << "`"; + exec_query(); + } + + /* create schema */ + ss << "CREATE SCHEMA IF NOT EXISTS `" + << name + << "` DEFAULT CHARACTER SET utf8"; + exec_query(); + + /* use schema */ + ss << "USE `" + << name + << "`"; + exec_query(); + + /* UuidToBin */ + ss << "CREATE FUNCTION IF NOT EXISTS UuidToBin(_uuid CHAR(36))\n" + " RETURNS BINARY(16)\n" + " LANGUAGE SQL\n" + " DETERMINISTIC\n" + " CONTAINS SQL\n" + " SQL SECURITY INVOKER\n" + "RETURN\n" + " UNHEX(CONCAT(\n" + " SUBSTR(_uuid, 25, 12),\n" // node id + " SUBSTR(_uuid, 20, 4),\n" // clock sequence + " SUBSTR(_uuid, 15, 4),\n" // time high and version + " SUBSTR(_uuid, 10, 4),\n" // time mid + " SUBSTR(_uuid, 1, 8)\n" // time low + " )\n" + ")"; + exec_query(); + + /* BinToUuid */ + ss << "CREATE FUNCTION IF NOT EXISTS BinToUuid(_bin BINARY(16))\n" + " RETURNS CHAR(36)\n" + " LANGUAGE SQL\n" + " DETERMINISTIC\n" + " CONTAINS SQL\n" + " SQL SECURITY INVOKER\n" + "RETURN\n" + " IF(\n" + " _bin IS NULL,\n" + " NULL,\n" + " LCASE(CONCAT_WS('-',\n" + " HEX(SUBSTR(_bin, 13, 4)),\n" // time low + " HEX(SUBSTR(_bin, 11, 2)),\n" // time mid + " HEX(SUBSTR(_bin, 9, 2)),\n" // time high and version + " HEX(SUBSTR(_bin, 7, 2)),\n" // clock sequence + " HEX(SUBSTR(_bin, 1, 6))\n" // node id + " )\n" + " )\n" + ")"; + exec_query(); + + /* initialize tables */ + for (auto& table : tables) + { + assert(static_cast(table)); + table->init(context, init_stage::stage1); + } + for (auto& table : tables) + { + assert(static_cast(table)); + table->init(context, init_stage::stage2); + } +} diff --git a/src/cpphibernate/driver/mariadb/classes/schema/misc.cpp b/src/cpphibernate/driver/mariadb/classes/schema/misc.cpp new file mode 100644 index 0000000..5fd7e60 --- /dev/null +++ b/src/cpphibernate/driver/mariadb/classes/schema/misc.cpp @@ -0,0 +1,152 @@ +#include + +#include +#include +#include + +using namespace ::cpphibernate; +using namespace ::cpphibernate::mariadb; + +/* schema_t */ + +std::ostream& schema_t::print(std::ostream& os) const +{ + using namespace ::cppcore; + + os << indent << '{' + << incindent + << indent << "\"schema_name\": \"" << name << "\"," + << indent << "\"tables\": " << make_print_container(tables, true, [](auto& s, auto& table) { + table->print(s); + }) + << decindent + << indent << '}'; + + return os; +} + +void schema_t::init() +{ + /* build lookup tables */ + for (auto& t : tables) + { + assert(static_cast(t)); + _table_lookup.emplace(t->dataset_id, t.get()); + + for (auto& f : t->fields) + { + assert(static_cast(f)); + _field_lookup.emplace(f->id, f.get()); + } + } + + /* update table referencs */ + for (auto& t : tables) + { + assert(static_cast(t)); + auto& table = const_cast(*t); + + /* get base table */ + auto it = _table_lookup.find(table.base_dataset_id); + table.base_table = (it != _table_lookup.end() + ? it->second + : nullptr); + + /* dereived tables */ + for (auto& id : table.derived_dataset_ids) + { + it = _table_lookup.find(id); + if (it == _table_lookup.end()) + throw exception(std::string("unable to find derived table for dataset id ") + std::to_string(id)); + table.derived_tables.emplace_back(it->second); + } + + /* update fields */ + for (auto& f : table.fields) + { + assert(static_cast(f)); + auto& field = const_cast(*f); + + /* referenced_table */ + it = _table_lookup.find(field.real_value_id); + auto * referenced_table = (it != _table_lookup.end() + ? const_cast(it->second) + : nullptr); + field.referenced_table = referenced_table; + + /* primary key field */ + if (field.attributes.count(attribute_t::primary_key)) + { + if (static_cast(table.primary_key_field)) + throw exception(std::string("Table '") + table.name + "' can not have more then one primary key!"); + table.primary_key_field = &field; + } + + /* foreign table field */ + else if (static_cast(referenced_table)) + { + table.foreign_table_fields.emplace_back(&field); + if (field.value_is_container) + { + referenced_table->is_used_in_container = true; + } + } + + /* normal data field */ + else + { + table.data_fields.emplace_back(&field); + } + } + if (!static_cast(table.primary_key_field)) + throw exception(std::string("Table '") + table.name + "' does not have a primary key!"); + } + + /* update foreign fields (one, many, key) */ + for (auto& t : tables) + { + assert(static_cast(t)); + auto& table = const_cast(*t); + + for (auto& f : table.foreign_table_fields) + { + assert(static_cast(f)); + assert(static_cast(f->referenced_table)); + + auto& field = *f; + auto& referenced_table = const_cast(*field.referenced_table); + + if (field.value_is_container) + { + table.foreign_table_many_fields.emplace_back(&field); + referenced_table.foreign_key_fields.emplace_back(&field); + } + else + { + table.foreign_table_one_fields.emplace_back(&field); + if (referenced_table.is_used_in_container) + { + referenced_table.foreign_key_fields.push_back(&field); + } + } + } + } +} + +const table_t& schema_t::table(size_t dataset_id) const +{ + auto it = _table_lookup.find(dataset_id); + if (it == _table_lookup.end()) + throw exception(std::string("unable to find table for dataset with id ") + std::to_string(dataset_id)); + assert(static_cast(it->second)); + return *it->second; +} + +const field_t& schema_t::field(size_t field_id) const +{ + auto it = _field_lookup.find(field_id); + if (it == _field_lookup.end()) + throw exception(std::string("unable to find field for dataset with id ") + std::to_string(field_id)); + assert(static_cast(it->second)); + return *it->second; +} diff --git a/src/cpphibernate/driver/mariadb/classes/tables/create_update.cpp b/src/cpphibernate/driver/mariadb/classes/tables/create_update.cpp new file mode 100644 index 0000000..1edb06f --- /dev/null +++ b/src/cpphibernate/driver/mariadb/classes/tables/create_update.cpp @@ -0,0 +1,465 @@ +#include + +#include +#include +#include +#include + +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(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(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(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(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; +} diff --git a/src/cpphibernate/driver/mariadb/classes/tables/destroy.cpp b/src/cpphibernate/driver/mariadb/classes/tables/destroy.cpp new file mode 100644 index 0000000..1ae2440 --- /dev/null +++ b/src/cpphibernate/driver/mariadb/classes/tables/destroy.cpp @@ -0,0 +1,303 @@ +#include +#include +#include +#include + +using namespace ::cpphibernate; +using namespace ::cpphibernate::mariadb; + +struct delete_query_builder_t +{ + const table_t& this_table; + + size_t alias_id { 0 }; + std::ostringstream os; + std::list names; + + delete_query_builder_t( + const table_t& p_this_table) + : this_table(p_this_table) + { } + + inline std::string make_alias() + { return std::string("T") + std::to_string(alias_id++); } + + inline void add_table(const table_t& table, const std::string& alias, bool add_base, bool add_derived) + { + auto has_alias = !alias.empty(); + auto real_alias = has_alias ? alias : table.name; + + if (table.base_table && add_base) + { + assert(table.base_table->primary_key_field); + + auto& base_table = *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; + + os << " LEFT JOIN `" + << base_table.name; + if (has_alias) + { + os << "` AS `" + << base_alias; + } + os << "` ON `" + << real_alias + << "`.`" + << base_key.name + << "`=`" + << real_base_alias + << "`.`" + << base_key.name + << "`"; + + add_table(base_table, base_alias, true, false); + } + + names.emplace_back(real_alias); + + /* foreign table one */ + for (auto& ptr : 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& own_key = *table.primary_key_field; + auto& ref_table = *field.referenced_table; + auto& ref_key = *ref_table.primary_key_field; + auto new_alias = make_alias(); + + if (ref_table.is_used_in_container) + { + os << " LEFT JOIN `" + << ref_table.name + << "` AS `" + << new_alias + << "` ON `" + << real_alias + << "`.`" + << own_key.name + << "`=`" + << new_alias + << "`.`" + << table.name + << "_id_" + << field.name + << "`"; + } + else + { + os << " LEFT JOIN `" + << ref_table.name + << "` AS `" + << new_alias + << "` ON `" + << real_alias + << "`.`" + << ref_key.table.name + << "_id_" + << field.name + << "`=`" + << new_alias + << "`.`" + << ref_key.name + << "`"; + } + + add_table(ref_table, new_alias, true, true); + } + + /* derived tables */ + if (add_derived) + { + for (auto& ptr : table.derived_tables) + { + assert(ptr); + assert(ptr->primary_key_field); + + auto& derived_table = *ptr; + auto& primary_key = *table.primary_key_field; + auto derived_alias = has_alias ? make_alias() : std::string(); + auto real_derived_alias = has_alias ? derived_alias : derived_table.name; + + os << " LEFT JOIN `" + << derived_table.name; + if (has_alias) + { + os << "` AS `" + << derived_alias; + } + os << "` ON `" + << real_alias + << "`.`" + << primary_key.name + << "`=`" + << real_derived_alias + << "`.`" + << primary_key.name + << "`"; + + add_table(derived_table, derived_alias, false, true); + } + } + } + + inline std::string operator()(const std::string* where) + { + os << " FROM `" + << this_table.name + << "`"; + add_table(this_table, "", true, true); + if (where) + { + os << " " << *where; + } + else + { + os << " ?where!"; + } + + std::ostringstream ss; + ss << "DELETE "; + bool first = true; + for (auto& name : names) + { + if (first) + first = false; + else + ss << ", "; + ss << "`" + << name + << "`"; + } + ss << os.str(); + + return ss.str(); + } +}; + +static std::string build_foreign_many_delete_query(const table_t& table); + +void table_t::destroy(const destroy_context& context) const + { return destroy_exec(context); } + +void table_t::cleanup(const base_context& context, table_set& processed, bool check_derived, bool check_base) const +{ + /* check and add the table to the set of processed tables */ + if (processed.count(this)) + return; + processed.emplace(this); + + /* execute the cleanup query */ + foreign_many_delete_exec(context); + + /* cleanup all foreign fields */ + for (auto ptr : foreign_table_fields) + { + assert(ptr); + assert(ptr->referenced_table); + auto& ref_table = *ptr->referenced_table; + if (ref_table.is_used_in_container) + ref_table.cleanup(context, processed, true, true); + } + + /* cleanup the base table */ + if (check_base && base_table) + { + base_table->cleanup(context, processed, false, true); + } + + /* cleanup the dereived tables */ + if (check_derived) + { + for (auto ptr : derived_tables) + { + assert(ptr); + ptr->cleanup(context, processed, true, false); + } + } +} + +void table_t::destroy_exec(const destroy_context& context) const +{ + assert(primary_key_field); + + auto& connection = context.connection; + auto& statement = get_statement_delete(); + + statement.set(0, context.where); + cpphibernate_log_debug("execute DELETE query: " << std::endl << statement.query(connection) << std::endl); + connection.execute(statement); + + table_set processed; + for (auto& ptr : foreign_table_fields) + { + assert(ptr); + assert(ptr->referenced_table); + auto& ref_table = *ptr->referenced_table; + if (ref_table.is_used_in_container) + ref_table.cleanup(context, processed, true, true); + } +} + +void table_t::foreign_many_delete_exec(const base_context& context) const +{ + if (foreign_key_fields.empty()) + return; + auto& connection = context.connection; + auto& statement = get_statement_foreign_many_delete(); + cpphibernate_log_debug("execute DELETE old foreign many query: " << std::endl << statement.query(connection) << std::endl); + connection.execute(statement); +} + +std::string table_t::build_delete_query(const std::string* where) const + { return delete_query_builder_t(*this)(where); } + +::cppmariadb::statement& table_t::get_statement_foreign_many_delete() const +{ + if (!_statement_foreign_many_delete) + { + auto query = build_foreign_many_delete_query(*this); + _statement_foreign_many_delete.reset(new ::cppmariadb::statement(std::move(query))); + } + return *_statement_foreign_many_delete; +} + +::cppmariadb::statement& table_t::get_statement_delete() const +{ + if (!_statement_delete) + { + auto query = delete_query_builder_t(*this)(nullptr); + _statement_delete.reset(new ::cppmariadb::statement(std::move(query))); + } + return *_statement_delete; +} + +std::string build_foreign_many_delete_query(const table_t& table) +{ + std::ostringstream os; + auto first = true; + os << "WHERE"; + for (auto ptr : table.foreign_key_fields) + { + assert(ptr); + auto& field = *ptr; + if (first) + first = false; + else + os << " AND"; + os << " (`" + << field.table.name + << "_id_" + << field.name + << "` IS NULL)"; + } + std::string where = os.str(); + + return delete_query_builder_t(table)(&where); +} diff --git a/src/cpphibernate/driver/mariadb/classes/tables/init.cpp b/src/cpphibernate/driver/mariadb/classes/tables/init.cpp new file mode 100644 index 0000000..9df25bc --- /dev/null +++ b/src/cpphibernate/driver/mariadb/classes/tables/init.cpp @@ -0,0 +1,424 @@ +#include + +#include +#include +#include +#include + +using namespace ::cpphibernate; +using namespace ::cpphibernate::mariadb; + +static std::string build_init_stage1_query(const table_t& table); +static std::string build_init_stage2_query(const table_t& table); + +void table_t::init(const init_context& context, init_stage stage) const +{ + switch (stage) + { + case init_stage::stage1: + { + auto& statement = get_statement_init_stage1(); + auto& connection = context.connection; + cpphibernate_log_debug("execute CREATE TABLE query: " << std::endl << statement.query(connection) << std::endl); + connection.execute(statement); + } + break; + + case init_stage::stage2: + { + auto* statement = get_statement_init_stage2(); + auto& connection = context.connection; + if (!statement) + return; + cpphibernate_log_debug("execute ALTER TABLE query: " << std::endl << statement->query(connection) << std::endl); + connection.execute(*statement); + } + break; + + case init_stage::unknown: + throw exception("Unkown or invalid init stage!"); + break; + } +} + +::cppmariadb::statement& table_t::get_statement_init_stage1() const +{ + if (!_statement_init_stage1) + _statement_init_stage1.reset(new ::cppmariadb::statement(build_init_stage1_query(*this))); + return *_statement_init_stage1; +} + +::cppmariadb::statement* table_t::get_statement_init_stage2() const +{ + if (!_statement_init_stage2) + _statement_init_stage2.reset(new ::cppmariadb::statement(build_init_stage2_query(*this))); + + return _statement_init_stage2->empty() + ? nullptr + : _statement_init_stage2.get(); +} + +std::string build_init_stage1_query(const table_t& table) +{ + using namespace ::cppcore; + + std::ostringstream os; + + /* CREATE TABLE */ + os << "CREATE TABLE IF NOT EXISTS `" + << table.name + << "`" + << indent + << "(" + << incindent; + + /* primary key */ + { + assert(table.primary_key_field); + auto& key_info = *table.primary_key_field; + auto args = key_info.create_arguments; + + os << indent + << "`" + << key_info.name + << "` " + << key_info.type + << " NOT NULL" + << (args.empty() ? "" : " ") + << args + << ","; + } + + /* base table key fields */ + if (static_cast(table.base_table)) + { + auto& base_table_info = *table.base_table; + + assert(base_table_info.primary_key_field); + auto& key_info = *base_table_info.primary_key_field; + + os << indent + << "`" + << key_info.name + << "` " + << key_info.type + << " NOT NULL,"; + } + + /* foreign table one fields */ + for (auto& ptr : table.foreign_table_one_fields) + { + assert(static_cast(ptr)); + auto& field_info = *ptr; + + assert(field_info.referenced_table); + auto& referenced_table = *field_info.referenced_table; + if (referenced_table.is_used_in_container) + continue; + + assert(referenced_table.primary_key_field); + auto& ref_key_info = *referenced_table.primary_key_field; + + os << indent + << "`" + << ref_key_info.table.name + << "_id_" + << field_info.name + << "` " + << ref_key_info.type + << (field_info.value_is_nullable + ? " NULL DEFAULT NULL," + : " NOT NULL,"); + } + + /* foreign fields */ + for (auto& ptr : table.foreign_key_fields) + { + assert(static_cast(ptr)); + auto& field_info = *ptr; + + assert(field_info.table.primary_key_field); + auto& ref_key_info = *field_info.table.primary_key_field; + + os << indent + << "`" + << field_info.table.name + << "_id_" + << field_info.name + << "` " + << ref_key_info.type + << " NULL DEFAULT NULL,"; + if (field_info.value_is_ordered) + { + os << indent + << "`" + << field_info.table.name + << "_index_" + << field_info.name + << "` INT UNSIGNED NOT NULL,"; + } + } + + /* data fields */ + for (auto& ptr : table.data_fields) + { + assert(static_cast(ptr)); + auto& field_info = *ptr; + + os << indent + << "`" + << field_info.name + << "` " + << field_info.type + << (field_info.value_is_nullable + ? " NULL DEFAULT NULL," + : " NOT NULL,"); + } + + /* type field for derived tables */ + if (!table.derived_tables.empty() && + !table.base_table) + { + os << indent + << "`__type` INT UNSIGNED NOT NULL,"; + } + + /* PRIMARY KEY */ + { + assert(table.primary_key_field); + auto& key_info = *table.primary_key_field; + + os << indent + << "PRIMARY KEY ( `" + << key_info.name + << "` )"; + } + + /* UNIQUE INDEX primary key */ + { + assert(table.primary_key_field); + auto& key_info = *table.primary_key_field; + + os << ',' + << indent + << "UNIQUE INDEX `index_" + << key_info.name + << "` ( `" + << key_info.name + << "` ASC )"; + } + + /* UNIQUE INDEX base table keys */ + if (table.base_table) + { + auto& table_info = *table.base_table; + + assert(table_info.primary_key_field); + auto& key_info = *table_info.primary_key_field; + + os << ',' + << indent + << "UNIQUE INDEX `index_" + << key_info.name + << "` ( `" + << key_info.name + << "` ASC )"; + } + + /* INDEX foreign table one fields */ + for (auto& ptr : table.foreign_table_one_fields) + { + assert(static_cast(ptr)); + auto& field_info = *ptr; + + assert(field_info.referenced_table); + auto& referenced_table = *field_info.referenced_table; + if (referenced_table.is_used_in_container) + continue; + + assert(referenced_table.primary_key_field); + auto& ref_key_info = *referenced_table.primary_key_field; + + os << "," + << indent + << "INDEX `index_" + << ref_key_info.table.name + << "_id_" + << field_info.name + << "` ( `" + << ref_key_info.table.name + << "_id_" + << field_info.name + << "` ASC )"; + } + + /* INDEX foreign fields */ + for (auto& ptr : table.foreign_key_fields) + { + assert(static_cast(ptr)); + auto& field_info = *ptr; + + os << "," + << indent + << "INDEX `index_" + << field_info.table.name + << "_id_" + << field_info.name + << "` ( `" + << field_info.table.name + << "_id_" + << field_info.name + << "` ASC )"; + } + + /* CREATE TABLE end */ + os << decindent + << indent + << ")" + << indent + << "ENGINE = InnoDB" + << indent + << "DEFAULT CHARACTER SET = utf8"; + + return os.str(); +} + +std::string build_init_stage2_query(const table_t& table) +{ + using namespace ::cppcore; + + std::ostringstream os; + + /* ALTER TABLE */ + os << "ALTER TABLE `" + << table.name + << "`" + << incindent; + + size_t index = 0; + + /* CONSTRAINT base table */ + if (table.base_table) + { + assert(table.base_table->primary_key_field); + auto& ref_key_info = *table.base_table->primary_key_field; + + if (index++) os << ","; + os << indent + << "ADD CONSTRAINT `fk_" + << table.name + << "_" + << ref_key_info.name + << "`" + << incindent + << indent + << "FOREIGN KEY IF NOT EXISTS (`" + << ref_key_info.name + << "`)" + << indent + << "REFERENCES `" + << ref_key_info.table.schema.name + << "`.`" + << ref_key_info.table.name + << "` (`" + << ref_key_info.name + << "`)" + << indent + << "ON DELETE CASCADE" + << indent + << "ON UPDATE CASCADE" + << decindent; + } + + /* CONSTRAINT foreign table one fields */ + for (auto& ptr : table.foreign_table_one_fields) + { + assert(static_cast(ptr)); + auto& field_info = *ptr; + + assert(field_info.referenced_table); + auto& referenced_table = *field_info.referenced_table; + if (referenced_table.is_used_in_container) + continue; + + assert(referenced_table.primary_key_field); + auto& ref_key_info = *referenced_table.primary_key_field; + + if (index++) os << ","; + os << indent + << "ADD CONSTRAINT `fk_" + << table.name + << "_" + << ref_key_info.table.name + << "_" + << field_info.name + << "`" + << incindent + << indent + << "FOREIGN KEY IF NOT EXISTS (`" + << ref_key_info.table.name + << "_id_" + << field_info.name + << "`)" + << indent + << "REFERENCES `" + << ref_key_info.table.schema.name + << "`.`" + << ref_key_info.table.name + << "` (`" + << ref_key_info.name + << "`)" + << indent; + if (field_info.value_is_nullable) + os << "ON DELETE SET NULL"; + else + os << "ON DELETE CASCADE"; + os << indent + << "ON UPDATE NO ACTION" + << decindent; + } + + /* CONSTRAINT foreign fields */ + for (auto& ptr : table.foreign_key_fields) + { + assert(static_cast(ptr)); + auto& field_info = *ptr; + + assert(field_info.table.primary_key_field); + auto& ref_key_info = *field_info.table.primary_key_field; + + if (index++) os << ","; + os << indent + << "ADD CONSTRAINT `fk_" + << table.name + << "_" + << field_info.table.name + << "_" + << field_info.name + << "`" + << incindent + << indent + << "FOREIGN KEY IF NOT EXISTS (`" + << field_info.table.name + << "_id_" + << field_info.name + << "`)" + << indent + << "REFERENCES `" + << ref_key_info.table.schema.name + << "`.`" + << ref_key_info.table.name + << "` (`" + << ref_key_info.name + << "`)" + << indent + << "ON DELETE SET NULL" + << indent + << "ON UPDATE NO ACTION" + << decindent; + } + + return index == 0 + ? std::string { } + : os.str(); +} diff --git a/src/cpphibernate/driver/mariadb/classes/tables/misc.cpp b/src/cpphibernate/driver/mariadb/classes/tables/misc.cpp new file mode 100644 index 0000000..0957d3f --- /dev/null +++ b/src/cpphibernate/driver/mariadb/classes/tables/misc.cpp @@ -0,0 +1,113 @@ +#include +#include + +#include + +#include +#include + +using namespace ::cpphibernate; +using namespace ::cpphibernate::mariadb; + +/* table_t */ + +table_t::~table_t() + { } + +std::ostream& table_t::print(std::ostream& os) const +{ + using namespace ::cppcore; + + os << indent << '{' + << incindent + << indent << "\"id\": " << id << "," + << indent << "\"dataset_id\": " << dataset_id << "," + << indent << "\"base_dataset_id\": " << base_dataset_id << "," + << indent << "\"derived_dataset_ids\": " << make_print_container(derived_dataset_ids, false) << "," + << indent << "\"name\": \"" << name << "\"," + << indent << "\"base_table\": " << (base_table ? std::string("\"") + base_table->name + "\"" : "null") << "," + << indent << "\"derived_tables\":" << make_print_container(derived_tables, true, [](auto& s, auto& ptr){ + s << indent << '"' << ptr->name << '"'; + }) << "," + << indent << "\"primary_key_field\": " << (primary_key_field ? std::string("\"") + primary_key_field->name + "\"" : "null") << "," + << indent << "\"foreign_key_fields\": " << make_print_container(foreign_key_fields, true, [](auto& s, auto& ptr){ + s << indent << '"' << ptr->name << '.' << ptr->name << '"'; + }) << "," + << indent << "\"foreign_table_fields\": " << make_print_container(foreign_table_fields, true, [](auto& s, auto& ptr){ + s << indent << '"' << ptr->name << '"'; + }) << "," + << indent << "\"foreign_table_one_fields\": " << make_print_container(foreign_table_one_fields, true, [](auto& s, auto& ptr){ + s << indent << '"' << ptr->name << '"'; + }) << "," + << indent << "\"foreign_table_many_fields\": " << make_print_container(foreign_table_many_fields, true, [](auto& s, auto& ptr){ + s << indent << '"' << ptr->name << '"'; + }) << "," + << indent << "\"data_fields\": " << make_print_container(data_fields, true, [](auto& s, auto& ptr){ + s << indent << '"' << ptr->name << '"'; + }) << "," + << indent << "\"fields\":" << make_print_container(fields, true, [](auto& s, auto& field) { + field->print(s); + }) + << decindent + << indent << '}'; + + return os; +} + +std::string table_t::get_primary_key(const data_context& context) const +{ + assert(primary_key_field); + if ( primary_key_field->is_default(context) + && base_table) + { + auto key = get_key_from_base(context); + primary_key_field->set(context, key); + return key; + } + else + { + return *primary_key_field->get(context); + } +} + +std::string table_t::get_key_from_base(const data_context& context) const +{ + if (!base_table) + { + throw exception(std::string("table has no base table: ") + name); + } + auto& statement = get_statement_key_from_base(); + auto base_key = base_table->get_primary_key(context); + statement.set(0, base_key); + auto result = context.connection.execute_stored(statement); + if (!result) + throw exception("unable to fetch key from database: unable to execute query!"); + auto row = result->next(); + if (!row) + throw exception("unable to fetch key from database: result set is empty!"); + return row->at(0).get(); +} + +::cppmariadb::statement& table_t::get_statement_key_from_base() const +{ + if (!_statement_key_from_base) + { + if (!base_table) + throw exception(std::string("table has no base table: ") + name); + assert(primary_key_field); + assert(base_table); + assert(base_table->primary_key_field); + auto& key_info = *primary_key_field; + auto& base_key = *base_table->primary_key_field; + std::ostringstream os; + os << "SELECT `" + << key_info.name + << "` FROM `" + << key_info.table.name + << "` WHERE `" + << base_key.table.name + << "_id`=?\?"; + _statement_key_from_base.reset(new ::cppmariadb::statement(os.str())); + } + return *_statement_key_from_base; +} diff --git a/src/cpphibernate/driver/mariadb/classes/tables/read.cpp b/src/cpphibernate/driver/mariadb/classes/tables/read.cpp new file mode 100644 index 0000000..a16f803 --- /dev/null +++ b/src/cpphibernate/driver/mariadb/classes/tables/read.cpp @@ -0,0 +1,541 @@ +#include +#include +#include + +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 + { }; + +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(); + 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(value) && !skip) + { + auto type = cppcore::from_string(*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(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 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(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; +} diff --git a/src/cpphibernate/driver/mariadb/schema/field.cpp b/src/cpphibernate/driver/mariadb/schema/field.cpp deleted file mode 100644 index f11e29f..0000000 --- a/src/cpphibernate/driver/mariadb/schema/field.cpp +++ /dev/null @@ -1,282 +0,0 @@ -#include -#include - -#include -#include -#include - -#include -#include -#include - -using namespace ::std; -using namespace ::utl; -using namespace ::cpphibernate::driver::mariadb_impl; - -void field_t::print(std::ostream& os) const -{ - os << indent << '{' - << incindent - - << indent << "\"id\": " << id << "," - << indent << "\"dataset_id\": " << dataset_id << "," - << indent << "\"real_dataset_id\": " << real_dataset_id << "," - << indent << "\"value_id\": " << value_id << "," - << indent << "\"real_value_id\": " << real_value_id << "," - << indent << "\"value_is_nullable\": " << (value_is_nullable ? "true" : "false") << "," - << indent << "\"value_is_pointer\": " << (value_is_pointer ? "true" : "false") << "," - << indent << "\"value_is_container\": " << (value_is_container ? "true" : "false") << "," - << indent << "\"value_is_auto_incremented\": " << (value_is_auto_incremented ? "true" : "false") << "," - << indent << "\"table\": " << (table ? std::string("\"") + table->table_name + "\"" : "null") << "," - << indent << "\"referenced_table\": " << (referenced_table ? std::string("\"") + referenced_table->table_name + "\"" : "null") << "," - - << indent << "\"schema_name\": \"" << schema_name << "\"," - << indent << "\"table_name\": \"" << table_name << "\"," - << indent << "\"field_name\": \"" << field_name << "\"," - << indent << "\"type\": \"" << type << "\"," - << indent << "\"create_arguments\": \"" << create_arguments << "\"," - - << indent << "\"convert_to_open\": \"" << convert_to_open << "\"," - << indent << "\"convert_to_close\": \"" << convert_to_close << "\"," - << indent << "\"convert_from_open\": \"" << convert_from_open << "\"," - << indent << "\"convert_from_close\": \"" << convert_from_close << "\"," - - << indent << "\"attributes\": " << misc::print_container(attributes, false) - - << decindent - << indent << '}'; -} - -void field_t::update() -{ - id = 0; - dataset_id = 0; - real_dataset_id = 0; - value_id = 0; - real_value_id = 0; - value_is_nullable = false; - value_is_container = false; - value_is_auto_incremented = false; - table = nullptr; - referenced_table = nullptr; - - type.clear(); - create_arguments.clear(); - - /* conver_to_open */ - { - std::ostringstream ss; - for (auto it = this->attributes.begin(); it != this->attributes.end(); ++it) - { - switch(*it) - { - case attribute_t::hex: - ss << "HEX("; - break; - case attribute_t::compress: - ss << "COMPRESS("; - break; - default: - break; - } - } - convert_to_open = ss.str(); - } - - /* convert_to_close */ - { - std::ostringstream ss; - for (auto it = this->attributes.begin(); it != this->attributes.end(); ++it) - { - switch(*it) - { - case attribute_t::hex: - case attribute_t::compress: - ss << ')'; - break; - default: - break; - } - } - convert_to_close = ss.str(); - } - - /* convert_from_open */ - { - std::ostringstream ss; - for (auto it = this->attributes.rbegin(); it != this->attributes.rend(); ++it) - { - switch(*it) - { - case attribute_t::hex: - ss << "UNHEX("; - break; - case attribute_t::compress: - ss << "UNCOMPRESS("; - break; - default: - break; - } - } - convert_from_open = ss.str(); - } - - /* convert_from_close */ - { - std::ostringstream ss; - for (auto it = this->attributes.rbegin(); it != this->attributes.rend(); ++it) - { - switch(*it) - { - case attribute_t::hex: - case attribute_t::compress: - ss << ')'; - break; - default: - break; - } - } - convert_from_close = ss.str(); - } -} - -#define throw_not_implemented(p_ret, p_name, ...) \ - p_ret field_t::p_name(__VA_ARGS__) const \ - { \ - throw misc::hibernate_exception( \ - std::string("'") + table_name + "." + field_name + \ - "' does not implement the " #p_name "() method!"); \ - } - -/* CRUD */ - -throw_not_implemented(value_t, foreign_create_update, const create_update_context&) -throw_not_implemented(read_context_ptr, foreign_read, const read_context&, bool fake_context) - -/* properties */ - -throw_not_implemented(bool, is_default, const data_context& context) -throw_not_implemented(string, generate_value, ::cppmariadb::connection&) -throw_not_implemented(value_t, get, const data_context& context) -throw_not_implemented(void, set, const data_context& context, const value_t&) - -/* statements */ - -throw_not_implemented(::cppmariadb::statement&, get_statement_foreign_one_delete, bool) -throw_not_implemented(::cppmariadb::statement&, get_statement_foreign_many_update) - -::cppmariadb::statement& field_t::get_statement_foreign_one_delete_impl(bool key_known, statement_ptr& known, statement_ptr& unknown) const -{ - assert(table); - assert(table->primary_key_field); - assert(referenced_table); - assert(referenced_table->primary_key_field); - - if (key_known) - { - if (!known) - { - auto& ref_table = *referenced_table; - auto& key_info = *table->primary_key_field; - auto& ref_key_info = *ref_table.primary_key_field; - - std::ostringstream os; - os << "WHERE `" - << ref_key_info.field_name - << "` IN (SELECT `" - << ref_key_info.table_name - << "_id_" - << field_name - << "` FROM `" - << key_info.table_name - << "` WHERE `" - << key_info.field_name - << "`=" - << key_info.convert_to_open - << "?\?" - << key_info.convert_to_close - << " AND `" - << ref_key_info.table_name - << "_id_" - << field_name - << "`!=" - << ref_key_info.convert_to_open - << "?\?" - << ref_key_info.convert_to_close - << ")"; - - auto where = os.str(); - auto query = ref_table.build_delete_query(&where); - known.reset(new ::cppmariadb::statement(query)); - } - return *known; - } - else - { - if (!unknown) - { - auto& ref_table = *referenced_table; - auto& key_info = *table->primary_key_field; - auto& ref_key_info = *ref_table.primary_key_field; - - std::ostringstream os; - os << "WHERE `" - << ref_key_info.field_name - << "` IN (SELECT `" - << ref_key_info.table_name - << "_id_" - << field_name - << "` FROM `" - << key_info.table_name - << "` WHERE `" - << key_info.field_name - << "`=" - << key_info.convert_to_open - << "?\?" - << key_info.convert_to_close - << ")"; - - auto where = os.str(); - auto query = ref_table.build_delete_query(&where); - unknown.reset(new ::cppmariadb::statement(query)); - } - return *unknown; - } -} - -::cppmariadb::statement& field_t::get_statement_foreign_many_update_impl(statement_ptr& statement) const -{ - assert(referenced_table); - assert(referenced_table->primary_key_field); - if (!statement) - { - auto& ref_key_info = *referenced_table->primary_key_field; - - std::ostringstream os; - os << "UPDATE `" - << ref_key_info.table_name - << "` SET `" - << table_name - << "_id_" - << field_name - << "`=NULL"; - if (value_is_container) - { - os << ", `" - << table_name - << "_index_" - << field_name - << "`=0"; - } - os << " WHERE `" - << table_name - << "_id_" - << field_name - << "`=" - << ref_key_info.convert_to_open - << "?\?" - << ref_key_info.convert_to_close; - statement.reset(new ::cppmariadb::statement(os.str())); - } - return *statement; -} \ No newline at end of file diff --git a/src/cpphibernate/driver/mariadb/schema/schema.cpp b/src/cpphibernate/driver/mariadb/schema/schema.cpp deleted file mode 100644 index 4c321c6..0000000 --- a/src/cpphibernate/driver/mariadb/schema/schema.cpp +++ /dev/null @@ -1,259 +0,0 @@ -#include -#include - -#include -#include -#include - -#include -#include - -using namespace ::utl; -using namespace ::cpphibernate::driver::mariadb_impl; - -void schema_t::update() -{ - // clear everything - for (auto& kvp : tables) - { - assert(static_cast(kvp.second)); - auto& table = *kvp.second; - table.primary_key_field = nullptr; - table.derived_tables.clear(); - table.foreign_key_fields.clear(); - table.foreign_table_fields.clear(); - table.foreign_table_one_fields.clear(); - table.foreign_table_many_fields.clear(); - table.data_fields.clear(); - table.is_used_in_container = false; - - for (auto& ptr : table.fields) - { - assert(ptr); - auto& field = *ptr; - field.update(); - } - } - - // update references - for (auto& kvp : tables) - { - assert(static_cast(kvp.second)); - auto& table = *kvp.second; - - // base table - auto it = tables.find(table.base_dataset_id); - table.base_table = (it != tables.end() - ? it->second.get() - : nullptr); - - // derived tables - for (auto& id : table.derived_dataset_ids) - { - it = tables.find(id); - if (it == tables.end()) - throw misc::hibernate_exception(std::string("unable to find derived table for dataset id ") + std::to_string(id)); - table.derived_tables.emplace_back(it->second.get()); - } - - // update fields - for (auto& ptr : table.fields) - { - assert(ptr); - auto& field = *ptr; - - // table - if (table.dataset_id != field.dataset_id) - throw misc::hibernate_exception(std::string("dataset id of field '") + field.table_name + '.' + field.field_name + "' does not match!"); - field.table = &table; - - // referenced table - it = tables.find(field.real_value_id); - auto referenced_table = (it != tables.end() - ? it->second.get() - : nullptr); - field.referenced_table = referenced_table; - - // is primary key field - if (field.attributes.count(attribute_t::primary_key)) - { - if (static_cast(table.primary_key_field)) - throw misc::hibernate_exception(std::string("Table '") + table.table_name + "' can not have more then one primary key!"); - table.primary_key_field = &field; - } - - // is foreign table field - else if (static_cast(referenced_table)) - { - table.foreign_table_fields.emplace_back(&field); - if (field.value_is_container) - { - referenced_table->is_used_in_container = true; - } - } - - // is data field - else - { - table.data_fields.emplace_back(&field); - } - } - if (!static_cast(table.primary_key_field)) - throw misc::hibernate_exception(std::string("Table '") + table.table_name + "' does not have a primary key!"); - } - - // update foreign fields (one, many, key) - for (auto& kvp : tables) - { - assert(static_cast(kvp.second)); - auto& table = *kvp.second; - - for (auto& ptr : table.foreign_table_fields) - { - assert(ptr); - assert(ptr->referenced_table); - auto& field = *ptr; - auto& referenced_table = *field.referenced_table; - - if (field.value_is_container) - { - table.foreign_table_many_fields.emplace_back(&field); - referenced_table.foreign_key_fields.push_back(&field); - } - else - { - table.foreign_table_one_fields.emplace_back(&field); - if (referenced_table.is_used_in_container) - { - referenced_table.foreign_key_fields.push_back(&field); - } - } - } - } -} - -void schema_t::print(std::ostream& os) const -{ - os << indent << '{' - << incindent - << indent << "\"schema_name\": \"" << schema_name << "\"," - << indent << "\"tables\": " << misc::print_container(tables, true, [](auto& s, auto& kvp) { - kvp.second->print(s); - }) - << decindent - << indent << '}'; -} - -const table_t& schema_t::table(size_t dataset_id) const -{ - auto it = tables.find(dataset_id); - if (it == tables.end()) - throw misc::hibernate_exception(std::string("unable to find table for dataset with id ") + std::to_string(dataset_id)); - assert(static_cast(it->second)); - return *it->second; -} - -const field_t& schema_t::field(size_t field_id) const -{ - for (auto& kvp : tables) - { - assert(kvp.second); - auto& table = *kvp.second; - for (auto& ptr : table.fields) - { - assert(ptr); - auto& field = *ptr; - if (field.id == field_id) - return field; - } - } - throw misc::hibernate_exception(std::string("unable to find field with id ") + std::to_string(field_id)); -} - -#define exec_query() \ - do { \ - cpphibernate_debug_log("execute init query: " << ss.str()); \ - connection.execute(ss.str()); \ - ss.str(std::string()); \ - ss.clear(); \ - } while(0) - -void schema_t::init(const init_context& context) const -{ - std::ostringstream ss; - auto& connection = context.connection; - - if (context.recreate) - { - ss << "DROP DATABASE IF EXISTS `" - << schema_name - << "`"; - exec_query(); - } - - /* create schema */ - ss << "CREATE SCHEMA IF NOT EXISTS `" - << schema_name - << "` DEFAULT CHARACTER SET utf8"; - exec_query(); - - /* use schema */ - ss << "USE `" - << schema_name - << "`"; - exec_query(); - - /* UuidToBin */ - ss << "CREATE FUNCTION IF NOT EXISTS UuidToBin(_uuid CHAR(36))\n" - " RETURNS BINARY(16)\n" - " LANGUAGE SQL\n" - " DETERMINISTIC\n" - " CONTAINS SQL\n" - " SQL SECURITY INVOKER\n" - "RETURN\n" - " UNHEX(CONCAT(\n" - " SUBSTR(_uuid, 25, 12),\n" // node id - " SUBSTR(_uuid, 20, 4),\n" // clock sequence - " SUBSTR(_uuid, 15, 4),\n" // time high and version - " SUBSTR(_uuid, 10, 4),\n" // time mid - " SUBSTR(_uuid, 1, 8)\n" // time low - " )\n" - ")"; - exec_query(); - - /* BinToUuid */ - ss << "CREATE FUNCTION IF NOT EXISTS BinToUuid(_bin BINARY(16))\n" - " RETURNS CHAR(36)\n" - " LANGUAGE SQL\n" - " DETERMINISTIC\n" - " CONTAINS SQL\n" - " SQL SECURITY INVOKER\n" - "RETURN\n" - " IF(\n" - " _bin IS NULL,\n" - " NULL,\n" - " LCASE(CONCAT_WS('-',\n" - " HEX(SUBSTR(_bin, 13, 4)),\n" // time low - " HEX(SUBSTR(_bin, 11, 2)),\n" // time mid - " HEX(SUBSTR(_bin, 9, 2)),\n" // time high and version - " HEX(SUBSTR(_bin, 7, 2)),\n" // clock sequence - " HEX(SUBSTR(_bin, 1, 6))\n" // node id - " )\n" - " )\n" - ")"; - exec_query(); - - /* initialize tables */ - for (auto& kvp : tables) - { - assert(kvp.second); - kvp.second->init_stage1(context); - } - for (auto& kvp : tables) - { - assert(kvp.second); - kvp.second->init_stage2(context); - } -} - -#undef exec_query diff --git a/src/cpphibernate/driver/mariadb/schema/table.cpp b/src/cpphibernate/driver/mariadb/schema/table.cpp deleted file mode 100644 index 7b0cf28..0000000 --- a/src/cpphibernate/driver/mariadb/schema/table.cpp +++ /dev/null @@ -1,1827 +0,0 @@ -#include -#include - -#include -#include -#include - -#include -#include -#include - -using namespace ::utl; -using namespace ::cpphibernate; -using namespace ::cpphibernate::driver::mariadb_impl; - -/* data_extractor_t */ - -struct foreign_many_tuple_t -{ - const field_t& field; - read_context_ptr context; - std::string owner; -}; - -struct foreign_many_list_t : - public std::list - { }; - -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(); - 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(value) && !skip) - { - auto type = utl::from_string(*value); - auto derived = _table.get_derived_by_table_id(type); - if (!derived) - throw misc::hibernate_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(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 misc::hibernate_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 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.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.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.table_name; - - std::ostringstream ss; - ss << " JOIN `" - << base_table.table_name; - if (has_alias) - { - ss << "` AS `" - << base_alias; - } - ss << "` ON `" - << real_alias - << "`.`" - << base_key.field_name - << "`=`" - << real_base_alias - << "`.`" - << base_key.field_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.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); - 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.table_name - << "` AS `" - << new_alias - << "` ON `" - << real_alias - << "`.`" - << own_key.field_name - << "`=`" - << new_alias - << "`.`" - << table.table_name - << "_id_" - << field.field_name - << "`"; - } - else - { - ss << " LEFT JOIN `" - << ref_table.table_name - << "` AS `" - << new_alias - << "` ON `" - << real_alias - << "`.`" - << ref_key.table_name - << "_id_" - << field.field_name - << "`=`" - << new_alias - << "`.`" - << ref_key.field_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.table_name; - - std::ostringstream ss; - ss << " LEFT JOIN `" - << derived_table.table_name; - if (has_alias) - { - ss << "` AS `" - << derived_alias; - } - ss << "` ON `" - << real_alias - << "`.`" - << primary_key.field_name - << "`=`" - << real_derived_alias - << "`.`" - << primary_key.field_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.table_name - << "`"; - - for (auto& join : joins) - os << join; - - os << " ?where! ?order! ?limit!"; - return os.str(); - } -}; - -/* delete_query_builder_t */ - -struct delete_query_builder_t -{ - const table_t& _table; - - size_t alias_id { 0 }; - std::ostringstream os; - std::list names; - - delete_query_builder_t( - const table_t& p_table) - : _table (p_table) - { } - - inline std::string make_alias() - { return std::string("T") + std::to_string(alias_id++); } - - inline void add_table(const table_t& table, const std::string& alias, bool add_base, bool add_derived) - { - auto has_alias = !alias.empty(); - auto real_alias = has_alias ? alias : table.table_name; - - if (table.base_table && add_base) - { - assert(table.base_table->primary_key_field); - - auto& base_table = *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.table_name; - - os << " LEFT JOIN `" - << base_table.table_name; - if (has_alias) - { - os << "` AS `" - << base_alias; - } - os << "` ON `" - << real_alias - << "`.`" - << base_key.field_name - << "`=`" - << real_base_alias - << "`.`" - << base_key.field_name - << "`"; - - add_table(base_table, base_alias, true, false); - } - - names.emplace_back(real_alias); - - /* foreign table one */ - for (auto& ptr : table.foreign_table_one_fields) - { - assert(ptr); - assert(ptr->table); - assert(ptr->table->primary_key_field); - assert(ptr->referenced_table); - assert(ptr->referenced_table->primary_key_field); - - auto& field = *ptr; - auto& own_key = *table.primary_key_field; - auto& ref_table = *field.referenced_table; - auto& ref_key = *ref_table.primary_key_field; - auto new_alias = make_alias(); - - if (ref_table.is_used_in_container) - { - os << " LEFT JOIN `" - << ref_table.table_name - << "` AS `" - << new_alias - << "` ON `" - << real_alias - << "`.`" - << own_key.field_name - << "`=`" - << new_alias - << "`.`" - << table.table_name - << "_id_" - << field.field_name - << "`"; - } - else - { - os << " LEFT JOIN `" - << ref_table.table_name - << "` AS `" - << new_alias - << "` ON `" - << real_alias - << "`.`" - << ref_key.table_name - << "_id_" - << field.field_name - << "`=`" - << new_alias - << "`.`" - << ref_key.field_name - << "`"; - } - - add_table(ref_table, new_alias, true, true); - } - - /* derived tables */ - if (add_derived) - { - for (auto& ptr : table.derived_tables) - { - assert(ptr); - assert(ptr->primary_key_field); - - auto& derived_table = *ptr; - auto& primary_key = *table.primary_key_field; - auto derived_alias = has_alias ? make_alias() : std::string(); - auto real_derived_alias = has_alias ? derived_alias : derived_table.table_name; - - os << " LEFT JOIN `" - << derived_table.table_name; - if (has_alias) - { - os << "` AS `" - << derived_alias; - } - os << "` ON `" - << real_alias - << "`.`" - << primary_key.field_name - << "`=`" - << real_derived_alias - << "`.`" - << primary_key.field_name - << "`"; - - add_table(derived_table, derived_alias, false, true); - } - } - } - - inline std::string operator()(const std::string* where) - { - os << " FROM `" - << _table.table_name - << "`"; - add_table(_table, "", true, true); - if (where) - { - os << " " << *where; - } - else - { - os << " ?where!"; - } - - std::ostringstream ss; - ss << "DELETE "; - bool first = true; - for (auto& name : names) - { - if (first) - first = false; - else - ss << ", "; - ss << "`" - << name - << "`"; - } - ss << os.str(); - - return ss.str(); - } -}; - -/* build queries */ - -std::string build_init_stage1_query(const table_t& table) -{ - std::ostringstream os; - - /* CREATE TABLE */ - os << "CREATE TABLE IF NOT EXISTS `" - << table.table_name - << "`" - << indent - << "(" - << incindent; - - /* primary key */ - { - assert(table.primary_key_field); - auto& key_info = *table.primary_key_field; - auto args = key_info.create_arguments; - os << indent - << "`" - << key_info.field_name - << "` " - << key_info.type - << " NOT NULL" - << (args.empty() ? "" : " ") - << args - << ","; - } - - /* base table key fields */ - if (static_cast(table.base_table)) - { - auto& base_table_info = *table.base_table; - assert(base_table_info.primary_key_field); - auto& key_info = *base_table_info.primary_key_field; - os << indent - << "`" - << key_info.field_name - << "` " - << key_info.type - << " NOT NULL,"; - } - - /* foreign table one fields */ - for (auto& ptr : table.foreign_table_one_fields) - { - assert(static_cast(ptr)); - auto& field_info = *ptr; - assert(field_info.referenced_table); - assert(field_info.referenced_table->primary_key_field); - if (field_info.referenced_table->is_used_in_container) - continue; - auto& ref_key_info = *field_info.referenced_table->primary_key_field; - os << indent - << "`" - << ref_key_info.table_name - << "_id_" - << field_info.field_name - << "` " - << ref_key_info.type - << (field_info.value_is_nullable - ? " NULL DEFAULT NULL," - : " NOT NULL,"); - } - - /* foreign fields */ - for (auto& ptr : table.foreign_key_fields) - { - assert(static_cast(ptr)); - auto& field_info = *ptr; - assert(field_info.table); - assert(field_info.table->primary_key_field); - auto& ref_key_info = *field_info.table->primary_key_field; - os << indent - << "`" - << field_info.table_name - << "_id_" - << field_info.field_name - << "` " - << ref_key_info.type - << " NULL DEFAULT NULL,"; - if (field_info.value_is_ordered) - { - os << indent - << "`" - << field_info.table_name - << "_index_" - << field_info.field_name - << "` INT UNSIGNED NOT NULL,"; - } - } - - /* data fields */ - for (auto& ptr : table.data_fields) - { - assert(static_cast(ptr)); - auto& field_info = *ptr; - os << indent - << "`" - << field_info.field_name - << "` " - << field_info.type - << (field_info.value_is_nullable - ? " NULL DEFAULT NULL," - : " NOT NULL,"); - } - - /* type field for derived tables */ - if (!table.derived_tables.empty() && - !table.base_table) - { - os << indent - << "`__type` INT UNSIGNED NOT NULL,"; - } - - /* PRIMARY KEY */ - { - assert(table.primary_key_field); - auto& key_info = *table.primary_key_field; - os << indent - << "PRIMARY KEY ( `" - << key_info.field_name - << "` )"; - } - - /* UNIQUE INDEX primary key */ - { - assert(table.primary_key_field); - auto& key_info = *table.primary_key_field; - os << ',' - << indent - << "UNIQUE INDEX `index_" - << key_info.field_name - << "` ( `" - << key_info.field_name - << "` ASC )"; - } - - /* UNIQUE INDEX base table keys */ - if (table.base_table) - { - auto& table_info = *table.base_table; - assert(table_info.primary_key_field); - auto& key_info = *table_info.primary_key_field; - os << ',' - << indent - << "UNIQUE INDEX `index_" - << key_info.field_name - << "` ( `" - << key_info.field_name - << "` ASC )"; - } - - /* INDEX foreign table one fields */ - for (auto& ptr : table.foreign_table_one_fields) - { - assert(static_cast(ptr)); - auto& field_info = *ptr; - assert(field_info.referenced_table); - assert(field_info.referenced_table->primary_key_field); - if (field_info.referenced_table->is_used_in_container) - continue; - auto& ref_key_info = *field_info.referenced_table->primary_key_field; - os << "," - << indent - << "INDEX `index_" - << ref_key_info.table_name - << "_id_" - << field_info.field_name - << "` ( `" - << ref_key_info.table_name - << "_id_" - << field_info.field_name - << "` ASC )"; - } - - /* INDEX foreign fields */ - for (auto& ptr : table.foreign_key_fields) - { - assert(static_cast(ptr)); - auto& field_info = *ptr; - os << "," - << indent - << "INDEX `index_" - << field_info.table_name - << "_id_" - << field_info.field_name - << "` ( `" - << field_info.table_name - << "_id_" - << field_info.field_name - << "` ASC )"; - } - - /* CREATE TABLE end */ - os << decindent - << indent - << ")" - << indent - << "ENGINE = InnoDB" - << indent - << "DEFAULT CHARACTER SET = utf8"; - - return os.str(); -} - -std::string build_init_stage2_query(const table_t& table) -{ - std::ostringstream os; - - /* ALTER TABLE */ - os << "ALTER TABLE `" - << table.table_name - << "`" - << incindent; - - size_t index = 0; - - /* CONSTRAINT base table */ - if (table.base_table) - { - assert(table.base_table->primary_key_field); - auto& ref_key_info = *table.base_table->primary_key_field; - if (index++) os << ","; - os << indent - << "ADD CONSTRAINT `fk_" - << table.table_name - << "_" - << ref_key_info.field_name - << "`" - << incindent - << indent - << "FOREIGN KEY IF NOT EXISTS (`" - << ref_key_info.field_name - << "`)" - << indent - << "REFERENCES `" - << ref_key_info.schema_name - << "`.`" - << ref_key_info.table_name - << "` (`" - << ref_key_info.field_name - << "`)" - << indent - << "ON DELETE CASCADE" - << indent - << "ON UPDATE CASCADE" - << decindent; - } - - /* CONSTRAINT foreign table one fields */ - for (auto& ptr : table.foreign_table_one_fields) - { - assert(static_cast(ptr)); - auto& field_info = *ptr; - assert(field_info.referenced_table); - assert(field_info.referenced_table->primary_key_field); - if (field_info.referenced_table->is_used_in_container) - continue; - auto& ref_key_info = *field_info.referenced_table->primary_key_field; - if (index++) os << ","; - os << indent - << "ADD CONSTRAINT `fk_" - << table.table_name - << "_" - << ref_key_info.table_name - << "_" - << field_info.field_name - << "`" - << incindent - << indent - << "FOREIGN KEY IF NOT EXISTS (`" - << ref_key_info.table_name - << "_id_" - << field_info.field_name - << "`)" - << indent - << "REFERENCES `" - << ref_key_info.schema_name - << "`.`" - << ref_key_info.table_name - << "` (`" - << ref_key_info.field_name - << "`)" - << indent; - if (field_info.value_is_nullable) - os << "ON DELETE SET NULL"; - else - os << "ON DELETE CASCADE"; - os << indent - << "ON UPDATE NO ACTION" - << decindent; - } - - /* CONSTRAINT foreign fields */ - for (auto& ptr : table.foreign_key_fields) - { - assert(static_cast(ptr)); - auto& field_info = *ptr; - assert(field_info.table); - assert(field_info.table->primary_key_field); - auto& ref_key_info = *field_info.table->primary_key_field; - if (index++) os << ","; - os << indent - << "ADD CONSTRAINT `fk_" - << table.table_name - << "_" - << field_info.table_name - << "_" - << field_info.field_name - << "`" - << incindent - << indent - << "FOREIGN KEY IF NOT EXISTS (`" - << field_info.table_name - << "_id_" - << field_info.field_name - << "`)" - << indent - << "REFERENCES `" - << ref_key_info.schema_name - << "`.`" - << ref_key_info.table_name - << "` (`" - << ref_key_info.field_name - << "`)" - << indent - << "ON DELETE SET NULL" - << indent - << "ON UPDATE NO ACTION" - << decindent; - } - - return index == 0 - ? std::string { } - : os.str(); -} - -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(filter); - - /* INSER INTO / UPDATE */ - os << (is_update - ? "UPDATE" - : "INSERT INTO") - << " `" - << table.table_name - << "`"; - - /* primary key */ - if (!is_update) - { - 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.field_name - << "`=" - << key_info.convert_to_open - << "?" - << key_info.field_name - << "?" - << key_info.convert_to_close; - } - } - - /* base table key fields */ - if ( static_cast(table.base_table) - && ( !is_update - || !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.field_name - << "`=" - << key_info.convert_to_open - << "?" - << key_info.field_name - << "?" - << key_info.convert_to_close; - } - - /* foreign table one fields */ - for (auto& ptr : table.foreign_table_one_fields) - { - assert(static_cast(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.field_name - << "`=" - << key_info.convert_to_open - << "?" - << key_info.table_name - << "_id_" - << field_info.field_name - << "?" - << key_info.convert_to_close; - } - - /* foreign fields */ - for (auto& ptr : table.foreign_key_fields) - { - assert(static_cast(ptr)); - if (is_update && ptr != owner) - continue; - if (index++) - os << ", "; - else - os << " SET "; - auto& field_info = *ptr; - assert(field_info.table); - assert(field_info.table->primary_key_field); - auto& key_info = *field_info.table->primary_key_field; - os << "`" - << field_info.table_name - << "_id_" - << field_info.field_name - << "`=" - << key_info.convert_to_open - << "?" - << field_info.table_name - << "_id_" - << field_info.field_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.field_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.field_name - << "`=" - << field_info.convert_to_open - << "?" - << field_info.field_name - << "?" - << field_info.convert_to_close; - } - - /* type field for derived tables */ - if ( !table.derived_tables.empty() - && !table.base_table - && !is_update) - { - 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.field_name - << "`=" - << key_info.convert_to_open - << "?" - << key_info.field_name - << "?" - << key_info.convert_to_close; - } - - return index == 0 && !(table.primary_key_field->value_is_auto_incremented && !is_update) - ? std::string() - : os.str(); -} - -std::string build_select_query( - const table_t& table, - const filter_t& filter, - bool is_dynamic) -{ - return select_query_builder_t(table, filter, is_dynamic)(); -} - -/* execute_create_update */ - -std::string table_t::execute_create_update( - const create_update_context& context, - ::cppmariadb::statement* statement) const -{ - auto& connection = context.connection; - auto& filter = context.filter; - - size_t index = 0; - bool is_update = context.is_update; - - std::string primary_key; - if (statement) statement->clear(); - - /* primary key */ - assert(primary_key_field); - if (is_update) - { - primary_key = get_primary_key(context); - } - else if (!primary_key_field->value_is_auto_incremented) - { - primary_key = primary_key_field->generate_value(context.connection); - if (statement) statement->set(index, primary_key); - ++index; - } - - /* base_key */ - if ( base_table - && ( !is_update - || !filter.is_excluded(*base_table))) - { - auto new_context = context; - if (!new_context.derived_table) - new_context.derived_table = this; - std::string key = create_update_base(new_context); - if (statement) statement->set(index, std::move(key)); - ++index; - } - - if (is_update && filter.is_excluded(*this)) - return primary_key; - - /* foreign table one fields */ - for (auto& ptr : 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, std::move(key)); - } - else if (field.value_is_nullable) - { - if (statement) statement->set_null(index); - } - else - { - throw misc::hibernate_exception("Received null key for non nullable foreign dataset!"); - } - ++index; - - /* cleanup old dataset (if new one was created) */ - if (context.is_update) - { - auto& delete_statement = field.get_statement_foreign_one_delete(key.has_value()); - delete_statement.set(0, primary_key); - if (key.has_value()) - delete_statement.set(1, *key); - cpphibernate_debug_log("execute DELETE old foreign one query: " << delete_statement.query(connection)); - connection.execute(delete_statement); - - table_set processed; - field.referenced_table->destroy_cleanup(context, processed, true, true); - } - } - - /* foreign fields */ - for (auto& ptr : 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 : 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 ( !derived_tables.empty() - && !base_table - && !is_update) - { - if (statement) statement->set(index, context.derived_table - ? context.derived_table->table_id - : table_id); - ++index; - } - - /* where primary key (for update) */ - if (is_update) - { - assert(primary_key_field); - if (statement) statement->set(index, *primary_key_field->get(context)); - ++index; - } - - /* execute */ - if (statement) - { - if (!is_update) - { - cpphibernate_debug_log("execute INSERT query: " << statement->query(connection)); - } - else - { - cpphibernate_debug_log("execute UPDATE query: " << statement->query(connection)); - } - - if ( primary_key_field->value_is_auto_incremented - && !is_update) - { - auto id = connection.execute_id(*statement); - primary_key = utl::to_string(id); - } - else - { - auto count = connection.execute_rows(*statement); - if (count > 1) - throw misc::hibernate_exception("Expected one/ row to be inserted/updated!"); - cpphibernate_debug_log(count << " rows inserted/updated"); - } - primary_key_field->set(context, primary_key); - } - - /* foreign table many fields */ - for (auto& ptr : 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) - { - auto& update_statement = field.get_statement_foreign_many_update(); - update_statement.set(0, primary_key); - cpphibernate_debug_log("execute UPDATE old foreign many query: " << update_statement.query(connection)); - connection.execute(update_statement); - } - - /* 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_set processed; - ref_table.destroy_cleanup(context, processed, true, true); - } - } - - return primary_key; -} - -/* table_t */ - -void table_t::print(std::ostream& os) const -{ - os << indent << '{' - << incindent - << indent << "\"dataset_id\": " << dataset_id << "," - << indent << "\"base_dataset_id\": " << base_dataset_id << "," - << indent << "\"table_id\": " << table_id << "," - << indent << "\"derived_dataset_ids\": " << misc::print_container(derived_dataset_ids, false) << "," - << indent << "\"schema_name\": \"" << schema_name << "\"," - << indent << "\"table_name\": \"" << table_name << "\"," - << indent << "\"fields\":" << misc::print_container(fields, true, [](auto& s, auto& field) { - field->print(s); - }) << "," - << indent << "\"base_table\": " << (base_table ? std::string("\"") + base_table->table_name + "\"" : "null") << "," - << indent << "\"derived_tables\":" << misc::print_container(derived_tables, true, [](auto& s, auto& ptr){ - s << indent << '"' << ptr->table_name << '"'; - }) << "," - << indent << "\"primary_key_field\": " << (primary_key_field ? std::string("\"") + primary_key_field->field_name + "\"" : "null") << "," - << indent << "\"foreign_key_fields\": " << misc::print_container(foreign_key_fields, true, [](auto& s, auto& ptr){ - s << indent << '"' << ptr->table_name << '.' << ptr->field_name << '"'; - }) << "," - << indent << "\"foreign_table_fields\": " << misc::print_container(foreign_table_fields, true, [](auto& s, auto& ptr){ - s << indent << '"' << ptr->field_name << '"'; - }) << "," - << indent << "\"foreign_table_one_fields\": " << misc::print_container(foreign_table_one_fields, true, [](auto& s, auto& ptr){ - s << indent << '"' << ptr->field_name << '"'; - }) << "," - << indent << "\"foreign_table_many_fields\": " << misc::print_container(foreign_table_many_fields, true, [](auto& s, auto& ptr){ - s << indent << '"' << ptr->field_name << '"'; - }) << "," - << indent << "\"data_fields\": " << misc::print_container(data_fields, true, [](auto& s, auto& ptr){ - s << indent << '"' << ptr->field_name << '"'; - }) - << decindent - << indent << '}'; -} - -const table_t* table_t::get_derived_by_table_id(size_t id) const -{ - if (table_id == id) - return this; - for (auto ptr : derived_tables) - { - assert(ptr); - auto ret = ptr->get_derived_by_table_id(id); - if (ret) return ret; - } - return nullptr; -} - -const table_t* table_t::get_derived_by_dataset_id(size_t id) const -{ - if (dataset_id == id) - return this; - for (auto ptr : derived_tables) - { - assert(ptr); - auto ret = ptr->get_derived_by_dataset_id(id); - if (ret) return ret; - } - return nullptr; -} - -void table_t::emplace(const read_context& context) const - { throw misc::hibernate_exception(std::string("'") + table_name + "' does not implement the emplace() method!"); } - -std::string table_t::get_where_primary_key(const data_context& context) const -{ - assert(primary_key_field); - - auto& field = *primary_key_field; - auto primary_key = *field.get(context); - - std::ostringstream os; - os << "WHERE `" - << field.table_name - << "`.`" - << field.field_name - << "`=" - << field.convert_to_open - << "'" - << context.connection.escape(primary_key) - << "'" - << field.convert_to_close; - - return os.str(); -} - -std::string table_t::build_delete_query(const std::string* where) const - { return delete_query_builder_t(*this)(where); } - -::cppmariadb::statement& table_t::get_statement_key_from_base() const -{ - if (!_statement_key_from_base) - { - if (!base_table) - throw exception(std::string("table has no base table: ") + table_name); - assert(primary_key_field); - assert(base_table); - assert(base_table->primary_key_field); - auto& key_info = *primary_key_field; - auto& base_key = *base_table->primary_key_field; - std::ostringstream os; - os << "SELECT `" - << key_info.field_name - << "` FROM `" - << key_info.table_name - << "` WHERE `" - << base_key.table_name - << "_id`=?\?"; - _statement_key_from_base.reset(new ::cppmariadb::statement(os.str())); - } - return *_statement_key_from_base; -} - -::cppmariadb::statement& table_t::get_statement_create_table() const -{ - if (_statement_create_table) - return *_statement_create_table; - auto query = build_init_stage1_query(*this); - _statement_create_table.reset(new ::cppmariadb::statement(query)); - return *_statement_create_table; -} - -::cppmariadb::statement* table_t::get_statement_alter_table() const -{ - if (!_statement_alter_table) - { - auto query = build_init_stage2_query(*this); - _statement_alter_table.reset(new ::cppmariadb::statement(query)); - } - return _statement_alter_table->empty() - ? nullptr - : _statement_alter_table.get(); -} - -::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_select(const read_context& context) const -{ - auto& map = context.is_dynamic - ? _statement_select_dynamic - : _statement_select_static; - auto key = std::make_tuple(context.filter.cache_id, static_cast(nullptr)); - auto it = map.find(key); - if (it == map.end()) - { - auto query = build_select_query(*this, context.filter, context.is_dynamic); - it = map.emplace(key, ::cppmariadb::statement(query)).first; - } - return it->second; -} - -::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; -} - -::cppmariadb::statement& table_t::get_statement_foreign_many_delete() const -{ - if (!_statement_foreign_many_delete) - { - std::ostringstream os; - auto first = true; - os << "WHERE"; - for (auto ptr : foreign_key_fields) - { - assert(ptr); - auto& field = *ptr; - if (first) - first = false; - else - os << " AND"; - os << " (`" - << field.table_name - << "_id_" - << field.field_name - << "` IS NULL)"; - } - std::string where = os.str(); - - auto query = delete_query_builder_t(*this)(&where); - _statement_foreign_many_delete.reset(new ::cppmariadb::statement(query)); - } - return *_statement_foreign_many_delete; -} - -::cppmariadb::statement& table_t::get_statement_delete() const -{ - if (!_statement_delete) - { - auto query = delete_query_builder_t(*this)(nullptr); - _statement_delete.reset(new ::cppmariadb::statement(query)); - } - return *_statement_delete; -} - -void table_t::execute_foreign_many_delete(const base_context& context) const -{ - if (foreign_key_fields.empty()) - return; - auto& connection = context.connection; - auto& statement = get_statement_foreign_many_delete(); - cpphibernate_debug_log("execute DELETE old foreign many query: " << statement.query(connection)); - connection.execute(statement); -} - -std::string table_t::get_primary_key(const data_context& context) const -{ - assert(primary_key_field); - if ( primary_key_field->is_default(context) - && base_table) - { - auto key = get_key_from_base(context); - primary_key_field->set(context, key); - return key; - } - else - { - return *primary_key_field->get(context); - } -} - -std::string table_t::get_key_from_base(const data_context& context) const -{ - if (!base_table) - { - throw exception(std::string("table has no base table: ") + table_name); - } - auto& statement = get_statement_key_from_base(); - auto base_key = base_table->get_primary_key(context); - statement.set(0, base_key); - auto result = context.connection.execute_stored(statement); - if (!result) - throw exception("unable to fetch key from database: unable to execute query!"); - auto row = result->next(); - if (!row) - throw exception("unable to fetch key from database: result set is empty!"); - return row->at(0).get(); -} - -std::string table_t::create_update_base(const create_update_context& context) const -{ - throw misc::hibernate_exception(static_cast(std::ostringstream { } - << "'" << this->table_name << "' does not implement create_update_base!").str()); -} - -void table_t::init_stage1_exec(const init_context& context) const -{ - auto& statement = get_statement_create_table(); - auto& connection = context.connection; - cpphibernate_debug_log("execute CREATE TABLE query: " << statement.query(connection)); - connection.execute(statement); -} - -void table_t::init_stage2_exec(const init_context& context) const -{ - auto* statement = get_statement_alter_table(); - auto& connection = context.connection; - if (!statement) return; - cpphibernate_debug_log("execute ALTER TABLE query: " << statement->query(connection)); - connection.execute(*statement); -} - -std::string table_t::create_update_intern(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(context, statement); -} - -void table_t::read_exec(const read_context& context) const -{ - auto& statement = get_statement_select(context); - auto& connection = context.connection; - - statement.set(0, context.where); - statement.set(1, context.order_by); - statement.set(2, context.limit); - - cpphibernate_debug_log("execute SELECT query: " << statement.query(connection)); - auto result = connection.execute_used(statement); - if (!result) - throw misc::hibernate_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.table); - 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.table_name - << "`.`" - << field.table_name - << "_id_" - << field.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.table_name - << "`.`" - << field.table_name - << "_index_" - << field.field_name - << "` ASC"; - next_context.order_by = ss.str(); - } - - ref_table.read(next_context); - } -} - -void table_t::destroy_intern(const destroy_context& context) const - { return destroy_exec(context); } - -void table_t::destroy_exec(const destroy_context& context) const -{ - assert(primary_key_field); - - auto& connection = context.connection; - auto& statement = get_statement_delete(); - - statement.set(0, context.where); - cpphibernate_debug_log("execute DELETE query: " << statement.query(connection)); - connection.execute(statement); - - table_set processed; - for (auto& ptr : foreign_table_fields) - { - assert(ptr); - assert(ptr->referenced_table); - auto& ref_table = *ptr->referenced_table; - if (ref_table.is_used_in_container) - ref_table.destroy_cleanup(context, processed, true, true); - } -} - -void table_t::destroy_cleanup(const base_context& context, table_set& processed, bool check_derived, bool check_base) const -{ - if (processed.count(this)) - return; - - processed.emplace(this); - - execute_foreign_many_delete(context); - - for (auto ptr : foreign_table_fields) - { - assert(ptr); - assert(ptr->referenced_table); - auto& ref_table = *ptr->referenced_table; - if (ref_table.is_used_in_container) - ref_table.destroy_cleanup(context, processed, true, true); - } - - if (check_base && base_table) - { - base_table->destroy_cleanup(context, processed, false, true); - } - - if (check_derived) - { - for (auto ptr : derived_tables) - { - assert(ptr); - ptr->destroy_cleanup(context, processed, true, false); - } - } -} diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index de3903d..ef708be 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -1,31 +1,43 @@ # Initialize ###################################################################################### -Include ( cotire OPTIONAL ) -Include ( pedantic OPTIONAL ) -Include ( cmake_tests OPTIONAL ) - -Include ( ${CMAKE_CURRENT_SOURCE_DIR}/../cmake/options.cmake ) - -Set ( CMAKE_CXX_STANDARD 17 ) -Set ( CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${PEDANTIC_C_FLAGS}" ) -Set ( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${PEDANTIC_CXX_FLAGS}" ) - -# Project: test_cpphibernate ########################################################################## - -Project ( test_cpphibernate ) -File ( GLOB_RECURSE SOURCE_FILES ${CMAKE_CURRENT_SOURCE_DIR}/*.cpp ) -List ( FILTER SOURCE_FILES EXCLUDE REGEX "/_[A-Za-z0-9_-]*\.cpp$" ) -Add_Executable ( test_cpphibernate EXCLUDE_FROM_ALL ${SOURCE_FILES} ) -Target_Link_Libraries ( test_cpphibernate - cpphibernate - gmock_main - gmock - gtest - pthread ) -If ( __COTIRE_INCLUDED ) - Cotire ( test_cpphibernate ) +Include ( cotire OPTIONAL RESULT_VARIABLE HAS_COTIRE ) +Include ( pedantic OPTIONAL RESULT_VARIABLE HAS_PEDANTIC ) +Include ( cmake_tests OPTIONAL RESULT_VARIABLE HAS_CMAKE_TESTS ) + +# Test ############################################################################################ + +Find_Package ( GTest ) +If ( NOT "${GTest_FOUND}" ) + Return ( ) EndIf ( ) -If ( __CMAKE_TESTS_INCLUDED ) - Add_CMake_Test ( NAME cpphibernate - TARGET test_cpphibernate ) + +File ( GLOB_RECURSE CPPHIBERNATE_TEST_HEADER_FILES ${CMAKE_CURRENT_SOURCE_DIR}/*.h ) +File ( GLOB_RECURSE CPPHIBERNATE_TEST_INLINE_FILES ${CMAKE_CURRENT_SOURCE_DIR}/*.inl ) +File ( GLOB_RECURSE CPPHIBERNATE_TEST_SOURCE_FILES ${CMAKE_CURRENT_SOURCE_DIR}/*.cpp ) + +Add_Executable ( cpphibernate-test + EXCLUDE_FROM_ALL + ${CPPHIBERNATE_TEST_HEADER_FILES} + ${CPPHIBERNATE_TEST_INLINE_FILES} + ${CPPHIBERNATE_TEST_SOURCE_FILES} ) +Target_Link_Libraries ( cpphibernate-test + PUBLIC + cpphibernate-objects + GMock::Main ) + +# pedantic +If ( HAS_PEDANTIC ) + Pedantic_Apply_Flags_Target ( cpphibernate-test ALL ) +EndIf ( ) + +# optimization +If ( HAS_COTIRE ) + Cotire ( cpphibernate-test ) +EndIf ( ) + +# test +If ( HAS_CMAKE_TESTS ) + Add_CMake_Test ( NAME cpphibernate TARGET cpphibernate-test ) +Else ( ) + Add_Test ( NAME cpphibernate COMMAND cpphibernate-test ) EndIf ( ) diff --git a/test/cpphibernate_create.cpp b/test/cpphibernate/cpphibernate_create.cpp similarity index 97% rename from test/cpphibernate_create.cpp rename to test/cpphibernate/cpphibernate_create.cpp index 37b74da..bd85333 100644 --- a/test/cpphibernate_create.cpp +++ b/test/cpphibernate/cpphibernate_create.cpp @@ -45,7 +45,7 @@ TEST(CppHibernateTests, create_test1) t1.u32_ptr_s = std::make_shared(789); ::cppmariadb::connection connection(reinterpret_cast(0x1111)); - auto context = make_context(test_schema, connection); + auto context = make_context(test_schema, connection); context.create(t1); } @@ -86,7 +86,7 @@ TEST(CppHibernateTests, create_test2) t2.i16_data = 4; ::cppmariadb::connection connection(reinterpret_cast(0x1111)); - auto context = make_context(test_schema, connection); + auto context = make_context(test_schema, connection); context.create(t2); } @@ -131,7 +131,7 @@ TEST(CppHibernateTests, create_test3) t3.i64_data = 8; ::cppmariadb::connection connection(reinterpret_cast(0x1111)); - auto context = make_context(test_schema, connection); + auto context = make_context(test_schema, connection); context.create(t3); } @@ -196,7 +196,7 @@ TEST(CppHibernateTests, create_derived1) d1.test1_data.u32_ptr_s = std::make_shared(789); ::cppmariadb::connection connection(reinterpret_cast(0x1111)); - auto context = make_context(test_schema, connection); + auto context = make_context(test_schema, connection); context.create(static_cast(d1)); } @@ -278,7 +278,7 @@ TEST(CppHibernateTests, create_derived2) d2.test2_ptr_u->i16_data = 23; ::cppmariadb::connection connection(reinterpret_cast(0x1111)); - auto context = make_context(test_schema, connection); + auto context = make_context(test_schema, connection); context.create(static_cast(d2)); } @@ -446,7 +446,7 @@ TEST(CppHibernateTests, create_derived3) d3.test3_vector.back().i64_data = 223; ::cppmariadb::connection connection(reinterpret_cast(0x1111)); - auto context = make_context(test_schema, connection); + auto context = make_context(test_schema, connection); context.create(static_cast(d3)); } @@ -515,7 +515,7 @@ TEST(CppHibernateTests, create_dummy_owner) d.dummies.emplace_back(789); ::cppmariadb::connection connection(reinterpret_cast(0x1111)); - auto context = make_context(test_schema, connection); + auto context = make_context(test_schema, connection); context.create(d); } @@ -569,11 +569,11 @@ TEST(CppHibernateTests, create_double_usage) d.multiple_items.emplace_back(789); ::cppmariadb::connection connection(reinterpret_cast(0x1111)); - auto context = make_context(test_schema, connection); + auto context = make_context(test_schema, connection); context.create(d); EXPECT_EQ(d.id, 1); EXPECT_EQ(d.single_item->id, 1001); EXPECT_EQ(d.multiple_items[0].id, 1002); EXPECT_EQ(d.multiple_items[1].id, 1003); -} \ No newline at end of file +} diff --git a/test/cpphibernate_destroy.cpp b/test/cpphibernate/cpphibernate_destroy.cpp similarity index 94% rename from test/cpphibernate_destroy.cpp rename to test/cpphibernate/cpphibernate_destroy.cpp index 013c9c1..e59818e 100644 --- a/test/cpphibernate_destroy.cpp +++ b/test/cpphibernate/cpphibernate_destroy.cpp @@ -35,7 +35,7 @@ TEST(CppHibernateTests, destroy_test1) t1.id = uuid("3d12697a-abb9-11e8-98d0-529269fb1459"); ::cppmariadb::connection connection(reinterpret_cast(0x1111)); - auto context = make_context(test_schema, connection); + auto context = make_context(test_schema, connection); context.destroy(t1); } @@ -67,7 +67,7 @@ TEST(CppHibernateTests, destroy_test2) t2.id = uuid("3d1270dc-abb9-11e8-98d0-529269fb1459"); ::cppmariadb::connection connection(reinterpret_cast(0x1111)); - auto context = make_context(test_schema, connection); + auto context = make_context(test_schema, connection); context.destroy(t2); } @@ -99,7 +99,7 @@ TEST(CppHibernateTests, destroy_test3) t3.id = uuid("3d12737a-abb9-11e8-98d0-529269fb1459"); ::cppmariadb::connection connection(reinterpret_cast(0x1111)); - auto context = make_context(test_schema, connection); + auto context = make_context(test_schema, connection); context.destroy(t3); } @@ -137,7 +137,7 @@ TEST(CppHibernateTests, destroy_derived1) d1.id = uuid("3d12778a-abb9-11e8-98d0-529269fb1459"); ::cppmariadb::connection connection(reinterpret_cast(0x1111)); - auto context = make_context(test_schema, connection); + auto context = make_context(test_schema, connection); context.destroy(static_cast(d1)); } @@ -184,7 +184,7 @@ TEST(CppHibernateTests, destroy_derived2) d2.id = uuid("3d127db6-abb9-11e8-98d0-529269fb1459"); ::cppmariadb::connection connection(reinterpret_cast(0x1111)); - auto context = make_context(test_schema, connection); + auto context = make_context(test_schema, connection); context.destroy(static_cast(d2)); } @@ -238,7 +238,7 @@ TEST(CppHibernateTests, destroy_derived3) d3.derived2_id = uuid("3d1287a2-abb9-11e8-98d0-529269fb1459"); ::cppmariadb::connection connection(reinterpret_cast(0x1111)); - auto context = make_context(test_schema, connection); + auto context = make_context(test_schema, connection); context.destroy(static_cast(d3)); } @@ -280,7 +280,6 @@ TEST(CppHibernateTests, destroy_double_usage) d.id = 1; ::cppmariadb::connection connection(reinterpret_cast(0x1111)); - auto context = make_context(test_schema, connection); + auto context = make_context(test_schema, connection); context.destroy(d); } - diff --git a/test/cpphibernate_init.cpp b/test/cpphibernate/cpphibernate_init.cpp similarity index 98% rename from test/cpphibernate_init.cpp rename to test/cpphibernate/cpphibernate_init.cpp index 72c88a0..8d92f1a 100644 --- a/test/cpphibernate_init.cpp +++ b/test/cpphibernate/cpphibernate_init.cpp @@ -7,6 +7,8 @@ using namespace ::testing; using namespace ::cpphibernate; +namespace hana = ::boost::hana; + TEST(CppHibernateTests, init) { StrictMock mock; @@ -175,7 +177,7 @@ TEST(CppHibernateTests, init) expect_query(mock, "CREATE TABLE IF NOT EXISTS `tbl_double_usage_item`\n" "(\n" - " `tbl_double_usage_item_id` INT UNSIGNED NOT NULL,\n" + " `tbl_double_usage_item_id` INT UNSIGNED NOT NULL AUTO_INCREMENT,\n" " `tbl_double_usage_id_single_item` INT NULL DEFAULT NULL,\n" " `tbl_double_usage_id_multiple_items` INT NULL DEFAULT NULL,\n" " `tbl_double_usage_index_multiple_items` INT UNSIGNED NOT NULL,\n" @@ -190,7 +192,7 @@ TEST(CppHibernateTests, init) expect_query(mock, "CREATE TABLE IF NOT EXISTS `tbl_double_usage`\n" "(\n" - " `tbl_double_usage_id` INT NOT NULL,\n" + " `tbl_double_usage_id` INT NOT NULL AUTO_INCREMENT,\n" " PRIMARY KEY ( `tbl_double_usage_id` ),\n" " UNIQUE INDEX `index_tbl_double_usage_id` ( `tbl_double_usage_id` ASC )\n" ")\n" @@ -199,7 +201,7 @@ TEST(CppHibernateTests, init) expect_query(mock, "CREATE TABLE IF NOT EXISTS `tbl_double_usage_wrapper`\n" "(\n" - " `tbl_double_usage_wrapper_id` INT NOT NULL,\n" + " `tbl_double_usage_wrapper_id` INT NOT NULL AUTO_INCREMENT,\n" " `tbl_double_usage_id_double_usage` INT NULL DEFAULT NULL,\n" " PRIMARY KEY ( `tbl_double_usage_wrapper_id` ),\n" " UNIQUE INDEX `index_tbl_double_usage_wrapper_id` ( `tbl_double_usage_wrapper_id` ASC ),\n" @@ -295,6 +297,7 @@ TEST(CppHibernateTests, init) reinterpret_cast(0x1111))); ::cppmariadb::connection connection(reinterpret_cast(0x1111)); - auto context = make_context(test_schema, connection); + auto context = make_context(test_schema); + context.connection(&connection); context.init(true); } diff --git a/test/cpphibernate_read.cpp b/test/cpphibernate/cpphibernate_read.cpp similarity index 92% rename from test/cpphibernate_read.cpp rename to test/cpphibernate/cpphibernate_read.cpp index bac682a..b77d707 100644 --- a/test/cpphibernate_read.cpp +++ b/test/cpphibernate/cpphibernate_read.cpp @@ -6,7 +6,6 @@ using namespace ::testing; using namespace ::cpphibernate; -using namespace ::cpphibernate::modifier; using namespace ::boost::hana::literals; TEST(CppHibernateTests, read_test1) @@ -45,9 +44,8 @@ TEST(CppHibernateTests, read_test1) t1.id = uuid("3d12697a-abb9-11e8-98d0-529269fb1459"); ::cppmariadb::connection connection(reinterpret_cast(0x1111)); - auto context = make_context(test_schema, connection); + auto context = make_context(test_schema, connection); - using namespace modifier; context.read(t1); EXPECT_EQ (t1.id, uuid("3d12697a-abb9-11e8-98d0-529269fb1459")); @@ -92,7 +90,7 @@ TEST(CppHibernateTests, read_test2) reinterpret_cast(0x1111))); ::cppmariadb::connection connection(reinterpret_cast(0x1111)); - auto context = make_context(test_schema, connection); + auto context = make_context(test_schema, connection); constexpr decltype(auto) test2_key_field = test_schema.tables[1_c].fields[0_c]; @@ -138,7 +136,7 @@ TEST(CppHibernateTests, read_test3) reinterpret_cast(0x1111))); ::cppmariadb::connection connection(reinterpret_cast(0x1111)); - auto context = make_context(test_schema, connection); + auto context = make_context(test_schema, connection); test3 t3; t3.id = uuid("3d12737a-abb9-11e8-98d0-529269fb1459"); @@ -193,7 +191,7 @@ TEST(CppHibernateTests, read_derived1_static) reinterpret_cast(0x1111))); ::cppmariadb::connection connection(reinterpret_cast(0x1111)); - auto context = make_context(test_schema, connection); + auto context = make_context(test_schema, connection); derived1 d1; d1.derived1_id = uuid("3d12758c-abb9-11e8-98d0-529269fb1459"); @@ -266,7 +264,7 @@ TEST(CppHibernateTests, read_derived2_static) reinterpret_cast(0x1111))); ::cppmariadb::connection connection(reinterpret_cast(0x1111)); - auto context = make_context(test_schema, connection); + auto context = make_context(test_schema, connection); derived2 d2; d2.derived2_id = uuid("3d127bcc-abb9-11e8-98d0-529269fb1459"); @@ -378,7 +376,7 @@ TEST(CppHibernateTests, read_derived3_static) reinterpret_cast(0x1111))); ::cppmariadb::connection connection(reinterpret_cast(0x1111)); - auto context = make_context(test_schema, connection); + auto context = make_context(test_schema, connection); derived3 d3; d3.derived3_id = uuid("3d12866c-abb9-11e8-98d0-529269fb1459"); @@ -541,7 +539,7 @@ TEST(CppHibernateTests, read_derived2_ptr_dynamic) reinterpret_cast(0x1111))); ::cppmariadb::connection connection(reinterpret_cast(0x1111)); - auto context = make_context(test_schema, connection); + auto context = make_context(test_schema, connection); constexpr decltype(auto) d2_key_field = test_schema.tables[5_c].fields[0_c]; @@ -738,7 +736,7 @@ TEST(CppHibernateTests, read_base_ptr_vector_dynamic) reinterpret_cast(0x1111))); ::cppmariadb::connection connection(reinterpret_cast(0x1111)); - auto context = make_context(test_schema, connection); + auto context = make_context(test_schema, connection); using base_ptr_type = std::unique_ptr; using base_vec_type = std::vector; @@ -908,7 +906,7 @@ TEST(CppHibernateTests, read_dummy_owner) d.id = uuid("00000000-0000-0000-0000-000000000001"); ::cppmariadb::connection connection(reinterpret_cast(0x1111)); - auto context = make_context(test_schema, connection); + auto context = make_context(test_schema, connection); context.read(d); @@ -967,7 +965,7 @@ TEST(CppHibernateTests, read_double_usage) d.id = 1; ::cppmariadb::connection connection(reinterpret_cast(0x1111)); - auto context = make_context(test_schema, connection); + auto context = make_context(test_schema, connection); context.read(d); @@ -980,4 +978,67 @@ TEST(CppHibernateTests, read_double_usage) EXPECT_EQ (d.multiple_items[0].data, 456); EXPECT_EQ (d.multiple_items[1].id, 1003); EXPECT_EQ (d.multiple_items[1].data, 789); -} \ No newline at end of file +} + +TEST(CppHibernateTests, read_with_modifiers) +{ + StrictMock mock; + + expect_query(mock, "START TRANSACTION"); + expect_query(mock, "SELECT " + "BinToUuid(`tbl_test1`.`tbl_test1_id`), " + "`tbl_test1`.`str_data`, " + "`tbl_test1`.`str64_data`, " + "`tbl_test1`.`u32_nullable`, " + "`tbl_test1`.`u32_ptr_u`, " + "`tbl_test1`.`u32_ptr_s` " + "FROM " + "`tbl_test1` " + "WHERE (" + "`tbl_test1`.`tbl_test1_id`=UuidToBin('X3d1270dc-abb9-11e8-98d0-529269fb1459X')) " + "ORDER_BY " + "`tbl_test1`.`str_data` ASC, " + "`tbl_test1`.`str64_data` DESC " + "LIMIT 1 " + "OFFSET 4", + result_used({ + { "3d12697a-abb9-11e8-98d0-529269fb1459", "str_data of class `test1` object `t1`", "str64_data of class `test1` object `t1`", nullptr, "123", "456" } + })); + expect_query(mock, "COMMIT"); + + EXPECT_CALL( + mock, + mysql_close( + reinterpret_cast(0x1111))); + + EXPECT_CALL( + mock, + mysql_real_escape_string(reinterpret_cast(0x1111), _, _, _)) + .Times(AnyNumber()) + .WillRepeatedly(WithArgs<1, 2, 3>(EscapeString())); + + test1 t1; + ::cppmariadb::connection connection(reinterpret_cast(0x1111)); + auto context = make_context(test_schema, connection); + + constexpr decltype(auto) test1_key_field = test_schema.tables[0_c].fields[0_c]; + constexpr decltype(auto) test1_tmp_field_0 = test_schema.tables[0_c].fields[1_c]; + constexpr decltype(auto) test1_tmp_field_1 = test_schema.tables[0_c].fields[2_c]; + + context.read(t1, + where(equal(test1_key_field, "3d1270dc-abb9-11e8-98d0-529269fb1459")), + limit(1_c), + offset(4_c), + order_by( + ascending(test1_tmp_field_0), + descending(test1_tmp_field_1))); + + EXPECT_EQ (t1.id, uuid("3d12697a-abb9-11e8-98d0-529269fb1459")); + EXPECT_EQ (t1.str_data, "str_data of class `test1` object `t1`"); + EXPECT_EQ (t1.str64_data, "str64_data of class `test1` object `t1`"); + EXPECT_FALSE(static_cast(t1.u32_nullable)); + ASSERT_TRUE (static_cast(t1.u32_ptr_u)); + EXPECT_EQ (*t1.u32_ptr_u, 123); + ASSERT_TRUE (static_cast(t1.u32_ptr_s)); + EXPECT_EQ (*t1.u32_ptr_s, 456); +} diff --git a/test/cpphibernate_update.cpp b/test/cpphibernate/cpphibernate_update.cpp similarity index 98% rename from test/cpphibernate_update.cpp rename to test/cpphibernate/cpphibernate_update.cpp index 3307c12..469fad9 100644 --- a/test/cpphibernate_update.cpp +++ b/test/cpphibernate/cpphibernate_update.cpp @@ -44,7 +44,7 @@ TEST(CppHibernateTests, update_test1) t1.u32_ptr_s = std::make_shared(789); ::cppmariadb::connection connection(reinterpret_cast(0x1111)); - auto context = make_context(test_schema, connection); + auto context = make_context(test_schema, connection); context.update(t1); } @@ -84,7 +84,7 @@ TEST(CppHibernateTests, update_test2) t2.i16_data = 4; ::cppmariadb::connection connection(reinterpret_cast(0x1111)); - auto context = make_context(test_schema, connection); + auto context = make_context(test_schema, connection); context.update(t2); } @@ -124,7 +124,7 @@ TEST(CppHibernateTests, update_test3) t3.i64_data = 8; ::cppmariadb::connection connection(reinterpret_cast(0x1111)); - auto context = make_context(test_schema, connection); + auto context = make_context(test_schema, connection); context.update(t3); } @@ -200,7 +200,7 @@ TEST(CppHibernateTests, update_derived1) d1.test1_data.u32_ptr_s = std::make_shared(789); ::cppmariadb::connection connection(reinterpret_cast(0x1111)); - auto context = make_context(test_schema, connection); + auto context = make_context(test_schema, connection); context.update(static_cast(d1)); } @@ -320,7 +320,7 @@ TEST(CppHibernateTests, update_derived2) d2.test2_ptr_u->i16_data = 23; ::cppmariadb::connection connection(reinterpret_cast(0x1111)); - auto context = make_context(test_schema, connection); + auto context = make_context(test_schema, connection); context.update(static_cast(d2)); } @@ -538,7 +538,7 @@ TEST(CppHibernateTests, update_derived3) d3.test3_vector.back().i64_data = 223; ::cppmariadb::connection connection(reinterpret_cast(0x1111)); - auto context = make_context(test_schema, connection); + auto context = make_context(test_schema, connection); context.update(static_cast(d3)); } @@ -632,7 +632,7 @@ TEST(CppHibernateTests, update_dynamic_base) d2.name = "dynamic derived 1"; ::cppmariadb::connection connection(reinterpret_cast(0x1111)); - auto context = make_context(test_schema, connection); + auto context = make_context(test_schema, connection); context.update(b); } @@ -707,7 +707,7 @@ TEST(CppHibernateTests, update_dummy_owner) d.dummies.emplace_back(789); ::cppmariadb::connection connection(reinterpret_cast(0x1111)); - auto context = make_context(test_schema, connection); + auto context = make_context(test_schema, connection); context.update(d); } @@ -796,7 +796,7 @@ TEST(CppHibernateTests, update_double_usage) d.multiple_items.back().data = 789; ::cppmariadb::connection connection(reinterpret_cast(0x1111)); - auto context = make_context(test_schema, connection); + auto context = make_context(test_schema, connection); context.update(d); } @@ -885,6 +885,6 @@ TEST(CppHibernateTests, update_double_usage_wrapper) d.double_usage->multiple_items.back().data = 789; ::cppmariadb::connection connection(reinterpret_cast(0x1111)); - auto context = make_context(test_schema, connection); + auto context = make_context(test_schema, connection); context.update(d); -} \ No newline at end of file +} diff --git a/test/mariadb_mock.cpp b/test/cpphibernate/mariadb_mock.cpp similarity index 100% rename from test/mariadb_mock.cpp rename to test/cpphibernate/mariadb_mock.cpp diff --git a/test/mariadb_mock.h b/test/cpphibernate/mariadb_mock.h similarity index 100% rename from test/mariadb_mock.h rename to test/cpphibernate/mariadb_mock.h diff --git a/test/test_helper.h b/test/cpphibernate/test_helper.h similarity index 99% rename from test/test_helper.h rename to test/cpphibernate/test_helper.h index 18b2360..2e38c5e 100644 --- a/test/test_helper.h +++ b/test/cpphibernate/test_helper.h @@ -180,4 +180,4 @@ inline void expect_query(T_mock& mock, const std::string& query, T_result&& resu template inline void expect_query(T_mock& mock, const std::string& query) - { expect_query(mock, query, result_stored()); } \ No newline at end of file + { expect_query(mock, query, result_stored()); } diff --git a/test/test_schema.h b/test/cpphibernate/test_schema.h similarity index 94% rename from test/test_schema.h rename to test/cpphibernate/test_schema.h index 0437940..a61fb70 100644 --- a/test/test_schema.h +++ b/test/cpphibernate/test_schema.h @@ -1,7 +1,7 @@ #pragma once #include -#include +#include enum class test_enum { @@ -14,7 +14,7 @@ enum class test_enum last = test3, }; -DEFINE_ENUM_TO_STRING_MAP( +cppcore_define_enum_value_traits( test_enum, { test_enum::test0, "test0" }, { test_enum::test1, "test1" }, @@ -22,22 +22,13 @@ DEFINE_ENUM_TO_STRING_MAP( { test_enum::test3, "test3" } ); -DEFINE_STRING_TO_ENUM_MAP( - test_enum, - invariant_string_less, - { "test0", test_enum::test0 }, - { "test1", test_enum::test1 }, - { "test2", test_enum::test2 }, - { "test3", test_enum::test3 } -); - struct test1 { ::cpphibernate::uuid id; std::string str_data; ::cpphibernate::string<64> str64_data; - utl::nullable u32_nullable; + cppcore::nullable u32_nullable; std::unique_ptr u32_ptr_u; std::shared_ptr u32_ptr_s; }; @@ -80,7 +71,7 @@ struct derived2 : public base { ::cpphibernate::uuid derived2_id; - utl::nullable test2_nullable; + cppcore::nullable test2_nullable; std::unique_ptr test2_ptr_u; std::shared_ptr test2_ptr_s; }; @@ -233,4 +224,4 @@ constexpr decltype(auto) test_schema = cpphibernate_make_schema( cpphibernate_make_id (&double_usage_wrapper::id), cpphibernate_make_field (double_usage_wrapper, double_usage) ) -); \ No newline at end of file +);