From 30f21262250f302e22d716dab915f2344e6ef0d9 Mon Sep 17 00:00:00 2001 From: Sascha Kuehl Date: Sat, 11 Nov 2023 08:29:22 +0100 Subject: [PATCH] added column generator and tests --- CMakeLists.txt | 7 + cmake/FindMySQL.cmake | 129 +++++++++++ include/matador/sql/column.hpp | 6 +- include/matador/sql/column_generator.hpp | 70 ++++++ include/matador/sql/query_builder.hpp | 1 + include/matador/sql/query_intermediates.hpp | 6 + include/matador/utils/access.hpp | 75 +++++++ include/matador/utils/cascade_type.hpp | 30 +++ include/matador/utils/identifiable.hpp | 50 +++++ include/matador/utils/identifier.hpp | 223 ++++++++++++++++++++ src/CMakeLists.txt | 15 +- src/sql/column_generator.cpp | 28 +++ src/sql/query_builder.cpp | 25 ++- src/utils/identifier.cpp | 171 +++++++++++++++ test/CMakeLists.txt | 5 +- test/column_generator.cpp | 30 +++ test/connection.cpp | 16 ++ test/product.hpp | 40 ++++ test/query.cpp | 2 +- test/session.cpp | 7 +- 20 files changed, 917 insertions(+), 19 deletions(-) create mode 100644 cmake/FindMySQL.cmake create mode 100644 include/matador/sql/column_generator.hpp create mode 100644 include/matador/utils/access.hpp create mode 100644 include/matador/utils/cascade_type.hpp create mode 100644 include/matador/utils/identifiable.hpp create mode 100644 include/matador/utils/identifier.hpp create mode 100644 src/sql/column_generator.cpp create mode 100644 src/utils/identifier.cpp create mode 100644 test/column_generator.cpp create mode 100644 test/connection.cpp create mode 100644 test/product.hpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 6afa9e4..6e65498 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -4,13 +4,20 @@ project(query) set(CMAKE_CXX_STANDARD 17) set(CMAKE_POSITION_INDEPENDENT_CODE ON) +list(APPEND CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake) + find_package(ODBC REQUIRED) find_package(SQLite3 REQUIRED) +find_package(PostgreSQL REQUIRED) +find_package(MySQL REQUIRED) message(STATUS "Found SQLite3 ${SQLite3_VERSION}") message(STATUS "Adding SQLite3 include directory: ${SQLite3_INCLUDE_DIRS}") message(STATUS "Adding SQLite3 libs: ${SQLite3_LIBRARIES}") +message(STATUS "Adding MySQL include directory: ${MYSQL_INCLUDE_DIR}") +message(STATUS "Adding MySQL libs: ${MYSQL_LIBRARY}") + message(STATUS "Common flags ${CMAKE_CXX_FLAGS}") message(STATUS "Debug flags ${CMAKE_CXX_FLAGS_DEBUG}") message(STATUS "Relase flags ${CMAKE_CXX_FLAGS_RELEASE}") diff --git a/cmake/FindMySQL.cmake b/cmake/FindMySQL.cmake new file mode 100644 index 0000000..c482816 --- /dev/null +++ b/cmake/FindMySQL.cmake @@ -0,0 +1,129 @@ +# - Find mysqlclient +# Find the native MySQL includes and library +# +# MYSQL_INCLUDE_DIR - where to find mysql.h, etc. +# MYSQL_LIBRARY - List of libraries when using MySQL. +# MYSQL_FOUND - True if MySQL found. + +IF (MYSQL_INCLUDE_DIR) + # Already in cache, be silent + SET(MYSQL_FIND_QUIETLY TRUE) +ENDIF (MYSQL_INCLUDE_DIR) + +if(WIN32) + find_path(MYSQL_INCLUDE_DIR mysql.h + PATHS + $ENV{MYSQL_INCLUDE_DIR} + $ENV{MYSQL_DIR}/include + $ENV{ProgramFiles}/MySQL/*/include + $ENV{SystemDrive}/MySQL/*/include + $ENV{ProgramW6432}/MySQL/*/include + $ENV{ProgramFiles}/MariaDB/*/include + $ENV{SystemDrive}/MariaDB/*/include + $ENV{ProgramW6432}/MariaDB/*/include + ) +else(WIN32) + find_path(MYSQL_INCLUDE_DIR mysql.h + PATHS + $ENV{MYSQL_DIR}/include + $ENV{MYSQL_INCLUDE_DIR} + /usr/local/mysql/include + /opt/mysql/mysql/include + PATH_SUFFIXES + mysql + ) +endif(WIN32) + +if(WIN32) + if (${CMAKE_BUILD_TYPE}) + STRING(TOLOWER ${CMAKE_BUILD_TYPE} CMAKE_BUILD_TYPE_TOLOWER) + endif() + + # path suffix for debug/release mode + # binary_dist: mysql binary distribution + # build_dist: custom build + if(CMAKE_BUILD_TYPE_TOLOWER MATCHES "debug") + SET(binary_dist debug) + SET(build_dist Debug) + else(CMAKE_BUILD_TYPE_TOLOWER MATCHES "debug") + ADD_DEFINITIONS(-DDBUG_OFF) + SET(binary_dist opt) + SET(build_dist Release) + endif(CMAKE_BUILD_TYPE_TOLOWER MATCHES "debug") + + FIND_LIBRARY(MYSQL_LIBRARY NAMES libmysql libmariadb + PATHS + $ENV{MYSQL_DIR}/lib + # $ENV{MYSQL_DIR}/lib/${binary_dist} + # $ENV{MYSQL_DIR}/libmysql/${build_dist} + # $ENV{MYSQL_DIR}/client/${build_dist} + # $ENV{ProgramFiles}/MySQL/*/lib/${binary_dist} + $ENV{ProgramFiles}/MySQL/*/lib + $ENV{SystemDrive}/MySQL/*/lib + $ENV{ProgramW6432}/MySQL/*/lib + $ENV{ProgramFiles}/MariaDB/*/lib + $ENV{SystemDrive}/MariaDB/*/lib + $ENV{ProgramW6432}/MariaDB/*/lib + # $ENV{SystemDrive}/MySQL/*/lib/${binary_dist} + # $ENV{ProgramFiles}/MariaDB/*/lib/${binary_dist} + # $ENV{SystemDrive}/MariaDB/*/lib/${binary_dist} + # $ENV{MYSQL_DIR}/lib/opt + # $ENV{MYSQL_DIR}/client/release + # $ENV{ProgramFiles}/MySQL/*/lib/opt + # $ENV{SystemDrive}/MySQL/*/lib/opt + # $ENV{ProgramW6432}/MySQL/*/lib + # $ENV{ProgramFiles}/MariaDB/*/lib/opt + # $ENV{SystemDrive}/MariaDB/*/lib/opt + # $ENV{ProgramW6432}/MariaDB/*/lib + ) +else(WIN32) + find_library(MYSQL_LIBRARY NAMES libmysql + PATHS + $ENV{MYSQL_DIR}/libmysql_r/.libs + $ENV{MYSQL_DIR}/lib + $ENV{MYSQL_DIR}/lib/mysql + /usr/local/mysql/lib + /opt/mysql/mysql/lib + PATH_SUFFIXES + mysql + ) +endif(WIN32) + +if(WIN32) +else(WIN32) + set(MYSQL_LIB_PATHS + $ENV{MYSQL_DIR}/libmysql_r/.libs + $ENV{MYSQL_DIR}/lib + $ENV{MYSQL_DIR}/lib/mysql + /usr/local/mysql/lib + /opt/mysql/mysql/lib + PATH_SUFFIXES + mysql + ) + find_library(MYSQL_LIBRARY NAMES mysqlclient + PATHS + ${MYSQL_LIB_PATHS} + ) +endif(WIN32) + +IF (MYSQL_INCLUDE_DIR) + MESSAGE(STATUS "MariaDB include ${MYSQL_INCLUDE_DIR}") +ELSE (MYSQL_INCLUDE_DIR) + MESSAGE(STATUS "MariaDB include dir not found") +ENDIF (MYSQL_INCLUDE_DIR) + +IF (MYSQL_LIBRARY) + MESSAGE(STATUS "MariaDB libs ${MYSQL_LIBRARY}") +ELSE (MYSQL_LIBRARY) + MESSAGE(STATUS "MariaDB libs dir not found") +ENDIF (MYSQL_LIBRARY) + + +IF (MYSQL_INCLUDE_DIR AND MYSQL_LIBRARY) + SET(MYSQL_FOUND TRUE) +ELSE (MYSQL_INCLUDE_DIR AND MYSQL_LIBRARY) + SET(MYSQL_FOUND FALSE) + SET(MYSQL_LIBRARY) +ENDIF (MYSQL_INCLUDE_DIR AND MYSQL_LIBRARY) + +MARK_AS_ADVANCED(MYSQL_LIBRARY MYSQL_INCLUDE_DIR) diff --git a/include/matador/sql/column.hpp b/include/matador/sql/column.hpp index 391a1ab..3118d32 100644 --- a/include/matador/sql/column.hpp +++ b/include/matador/sql/column.hpp @@ -17,7 +17,11 @@ public: template explicit column(std::string name, utils::field_attributes attr = utils::null_attributes) - : column(std::move(name), data_type_traits::data_type(), attr) + : column(std::move(name), data_type_traits::builtin_type(attr.size()), attr) + {} + template + column(std::string name, const Type &, utils::field_attributes attr = utils::null_attributes) + : column(std::move(name), data_type_traits::builtin_type(attr.size()), attr) {} column(std::string name, data_type_t type, utils::field_attributes attr = utils::null_attributes); diff --git a/include/matador/sql/column_generator.hpp b/include/matador/sql/column_generator.hpp new file mode 100644 index 0000000..4361d60 --- /dev/null +++ b/include/matador/sql/column_generator.hpp @@ -0,0 +1,70 @@ +#ifndef QUERY_COLUMN_GENERATOR_HPP +#define QUERY_COLUMN_GENERATOR_HPP + +#include "matador/sql/column.hpp" +#include "matador/sql/types.hpp" + +#include "matador/utils/access.hpp" +#include "matador/utils/field_attributes.hpp" + +#include + +namespace matador::utils { +class identifiable; +enum class cascade_type; +} + +namespace matador::sql { + +class column_generator +{ +private: + explicit column_generator(std::vector &columns); + +public: + ~column_generator() = default; + + template < class Type > + static std::vector generate() + { + std::vector columns; + column_generator gen(columns); + Type obj; + matador::utils::access::process(gen, obj); + return std::move(columns); + } + + template < class V > + void on_primary_key(const char *, V &x, typename std::enable_if::value && !std::is_same::value>::type* = 0); + void on_primary_key(const char *id, std::string &pk, size_t size); + void on_revision(const char *id, unsigned long long &/*rev*/); + + template + void on_attribute(const char *id, Type &x, const utils::field_attributes &attr = utils::null_attributes); + + void on_belongs_to(const char *id, utils::identifiable &x, utils::cascade_type); + void on_has_one(const char *id, utils::identifiable &x, utils::cascade_type); +// void on_has_many(const char *, abstract_container &, const char *, const char *, cascade_type) {} +// void on_has_many(const char *, abstract_container &, cascade_type) {} + +private: + size_t index_ = 0; + std::vector &columns_; + +// typed_column_identifier_serializer column_identifier_serializer_; +}; + +template +void column_generator::on_primary_key(const char *id, V &x, typename std::enable_if::value && !std::is_same::value>::type*) +{ + on_attribute(id, x, { utils::constraints::PRIMARY_KEY }); +} + +template +void column_generator::on_attribute(const char *id, Type &x, const utils::field_attributes &attr) +{ + columns_.emplace_back(id, x, attr); +} + +} +#endif //QUERY_COLUMN_GENERATOR_HPP diff --git a/include/matador/sql/query_builder.hpp b/include/matador/sql/query_builder.hpp index ea40b98..3728745 100644 --- a/include/matador/sql/query_builder.hpp +++ b/include/matador/sql/query_builder.hpp @@ -100,6 +100,7 @@ public: query_builder& remove(); query_builder& table(const std::string &table, std::initializer_list columns); + query_builder& table(const std::string &table, const std::vector &columns); query_builder& table(const std::string &table); query_builder& into(const std::string &table, std::initializer_list column_names); query_builder& values(std::initializer_list values); diff --git a/include/matador/sql/query_intermediates.hpp b/include/matador/sql/query_intermediates.hpp index 46ec5c9..c06ba03 100644 --- a/include/matador/sql/query_intermediates.hpp +++ b/include/matador/sql/query_intermediates.hpp @@ -2,6 +2,7 @@ #define QUERY_QUERY_INTERMEDIATES_HPP #include "matador/sql/column.hpp" +#include "matador/sql/column_generator.hpp" #include "matador/sql/key_value_pair.hpp" #include "matador/sql/query_result.hpp" #include "matador/sql/record.hpp" @@ -136,6 +137,11 @@ public: using query_intermediate::query_intermediate; query_execute_finish table(const std::string &table, std::initializer_list columns); + template + query_execute_finish table(const std::string &table) + { + return {db(), query().table(table, column_generator::generate())}; + } }; class query_drop_intermediate : query_intermediate diff --git a/include/matador/utils/access.hpp b/include/matador/utils/access.hpp new file mode 100644 index 0000000..5c8a536 --- /dev/null +++ b/include/matador/utils/access.hpp @@ -0,0 +1,75 @@ +#ifndef QUERY_ACCESS_HPP +#define QUERY_ACCESS_HPP + +#include + +namespace matador::utils { + +enum class cascade_type; + +template < class Type, template < class ... > class ContainerType > +class container; + +class field_attributes; + +namespace access { +template +void process(Operator &op, Type &object) { + object.process(op); +} + +template +void process(Operator &op, const Type &object) { + const_cast(object).process(op); +} + +template< class Operator, class Type > +void primary_key(Operator &op, const char *id, Type &value) { + op.on_primary_key(id, value); +} + +template< class Operator > +void primary_key(Operator &op, const char *id, std::string &value, size_t size ) { + op.on_primary_key(id, value, size); +} + +template +void revision(Operator &op, const char *id, unsigned long long &value) { + op.on_revision(id, value); +} + +template +void attribute(Operator &op, const char *id, Type &value, const field_attributes &attr) { + op.on_attribute(id, value, attr); +} + +template +void attribute(Operator &op, const char *id, Type &value) { + op.on_attribute(id, value); +} + +template +void has_one(Operator &op, const char *id, Type &value, cascade_type cascade) { + op.on_has_one(id, value, cascade); +} + +template +void belongs_to(Operator &op, const char *id, Type &value, cascade_type cascade) { + op.on_belongs_to(id, value, cascade); +} + +template class ContainerType> +void has_many(Operator &op, const char *id, container &container, cascade_type cascade) { + op.on_has_many(id, container, cascade); +} + +template class ContainerType> +void has_many(Operator &op, const char *id, container &container, const char *left_column, const char *right_column, cascade_type cascade) { + op.on_has_many(id, container, left_column, right_column, cascade); +} + +} + +} + +#endif //QUERY_ACCESS_HPP diff --git a/include/matador/utils/cascade_type.hpp b/include/matador/utils/cascade_type.hpp new file mode 100644 index 0000000..2132cb2 --- /dev/null +++ b/include/matador/utils/cascade_type.hpp @@ -0,0 +1,30 @@ +#ifndef QUERY_CASCADE_TYPE_HPP +#define QUERY_CASCADE_TYPE_HPP + +#include + +namespace matador::utils { + +/** + * @brief Cascade types for database actions + */ +enum class cascade_type : uint8_t +{ + NONE = 0, /**< Cascade type none */ + REMOVE = 1, /**< Cascade type remove */ + UPDATE = 2, /**< Cascade type update */ + INSERT = 4, /**< Cascade type insert */ + ALL = REMOVE | UPDATE | INSERT /**< Cascade type all */ +}; + +inline cascade_type operator~ (cascade_type a) { return (cascade_type)~(int)a; } +inline cascade_type operator| (cascade_type a, cascade_type b) { return (cascade_type)((int)a | (int)b); } +inline cascade_type operator& (cascade_type a, cascade_type b) { return (cascade_type)((int)a & (int)b); } +inline cascade_type operator^ (cascade_type a, cascade_type b) { return (cascade_type)((int)a ^ (int)b); } +inline cascade_type& operator|= (cascade_type& a, cascade_type b) { return (cascade_type&)((int&)a |= (int)b); } +inline cascade_type& operator&= (cascade_type& a, cascade_type b) { return (cascade_type&)((int&)a &= (int)b); } +inline cascade_type& operator^= (cascade_type& a, cascade_type b) { return (cascade_type&)((int&)a ^= (int)b); } + +} + +#endif //QUERY_CASCADE_TYPE_HPP diff --git a/include/matador/utils/identifiable.hpp b/include/matador/utils/identifiable.hpp new file mode 100644 index 0000000..f28a10f --- /dev/null +++ b/include/matador/utils/identifiable.hpp @@ -0,0 +1,50 @@ +#ifndef QUERY_IDENTIFIABLE_HPP +#define QUERY_IDENTIFIABLE_HPP + +#include "matador/utils/identifier.hpp" + +namespace matador::utils { + +/** + * Base class for all pointer object + * which can contain an identifiable + */ +class identifiable +{ +public: + virtual ~identifiable() = default; + + /** + * Resets the object_holder with the given + * identifier. If the type of identifier differs + * from internal type an exception is thrown + * + * @param id The identifier to set + */ + virtual void reset(const identifier &id) = 0; + + /** + * Returns true if serializable has a primary key + * + * @return true if serializable has a primary key + */ + [[nodiscard]] virtual bool has_primary_key() const = 0; + + /** + * Gets the primary key of the foreign serializable + * + * @return The primary key of the foreign serializable + */ + [[nodiscard]] virtual const identifier& primary_key() const = 0; + virtual identifier& primary_key() = 0; + + /** + * Creates a new identifier object. + * + * @return Returns a new identifier object. + */ + [[nodiscard]] virtual identifier create_identifier() const = 0; +}; + +} +#endif //QUERY_IDENTIFIABLE_HPP diff --git a/include/matador/utils/identifier.hpp b/include/matador/utils/identifier.hpp new file mode 100644 index 0000000..990e580 --- /dev/null +++ b/include/matador/utils/identifier.hpp @@ -0,0 +1,223 @@ +#ifndef QUERY_IDENTIFIER_HPP +#define QUERY_IDENTIFIER_HPP + +#include "matador/utils/field_attributes.hpp" + +#include +#include +#include +#include + +namespace matador::utils { + +struct null_type_t {}; + +namespace detail { + +enum class identifier_type_t : unsigned int { + INTEGRAL_TYPE, + STRING_TYPE, + NULL_TYPE +}; + +template +struct identifier_type_traits; + +template +struct identifier_type_traits::value>::type> { + static identifier_type_t type() { return identifier_type_t::INTEGRAL_TYPE; } + static std::string type_string() { return "integral"; } + static bool is_valid(Type value) { return value > 0; } + static std::string to_string(Type value) { return std::to_string(value); } +}; + +template +struct identifier_type_traits::value>::type> { + static identifier_type_t type() { return identifier_type_t::STRING_TYPE; } + static std::string type_string() { return "string"; } + static bool is_valid(const Type &value) { return !value.empty(); } + static std::string to_string(Type value) { return value; } +}; + +template +struct identifier_type_traits::value>::type> { + static identifier_type_t type() { return identifier_type_t::NULL_TYPE; } + static std::string type_string() { return "null"; } + static bool is_valid() { return false; } + static std::string to_string() { return "null_pk"; } +}; + +} + +class identifier_serializer +{ +public: + virtual ~identifier_serializer() = default; + + virtual void serialize(short &, const field_attributes &) = 0; + virtual void serialize(int &, const field_attributes &) = 0; + virtual void serialize(long &, const field_attributes &) = 0; + virtual void serialize(long long &, const field_attributes &) = 0; + virtual void serialize(unsigned short &, const field_attributes &) = 0; + virtual void serialize(unsigned int &, const field_attributes &) = 0; + virtual void serialize(unsigned long &, const field_attributes &) = 0; + virtual void serialize(unsigned long long &, const field_attributes &) = 0; + virtual void serialize(std::string &, const field_attributes &) = 0; + virtual void serialize(null_type_t &, const field_attributes &) = 0; +}; + +class identifier +{ +private: + struct base + { + explicit base(const std::type_index &ti, detail::identifier_type_t id_type); + base(const base &x) = delete; + base &operator=(const base &x) = delete; + base(base &&x) = delete; + base &operator=(base &&x) = delete; + virtual ~base() = default; + + template + bool is_similar_type() const + { + return identifier_type_ == detail::identifier_type_traits::type(); + } + + bool is_similar_type(const base &x) const; + detail::identifier_type_t type() const; + + virtual base *copy() const = 0; + virtual bool equal_to(const base &x) const = 0; + virtual bool less(const base &x) const = 0; + virtual bool is_valid() const = 0; + virtual void serialize(identifier_serializer &s) = 0; + virtual std::string str() const = 0; + virtual size_t hash() const = 0; + + std::type_index type_index_; + detail::identifier_type_t identifier_type_; + }; + + template + struct pk : public base + { + using self = pk; + + explicit pk(const IdType &id, size_t size = 0) : base(std::type_index(typeid(IdType)), detail::identifier_type_traits::type()) + , id_(id) + , size_(size) {} + + base *copy() const final { + return new self(id_, size_); + } + + bool equal_to(const base &x) const final { + return static_cast &>(x).id_ == id_; + } + + bool less(const base &x) const final { + return static_cast &>(x).id_ < id_; + } + + bool is_valid() const final + { + return detail::identifier_type_traits::is_valid(id_); + } + + std::string str() const final + { + return detail::identifier_type_traits::to_string(id_); + } + + void serialize(identifier_serializer &s) final { + s.serialize(id_, size_); + } + + size_t hash() const final { + std::hash hash_func; + return hash_func(id_); + } + + IdType id_; + size_t size_{}; + }; + + struct null_pk : public base + { + null_pk(); + base *copy() const final; + bool equal_to(const base &x) const final; + bool less(const base &x) const final; + bool is_valid() const final; + void serialize(identifier_serializer &s) final; + std::string str() const final; + size_t hash() const final; + null_type_t null_; + }; + +public: + identifier(); + template + explicit identifier(const Type &id, long size = -1) + : id_(std::make_shared>(id, size)) {} + identifier(const identifier &x); + identifier &operator=(const identifier &x); + identifier(identifier &&x) noexcept ; + identifier &operator=(identifier &&x) noexcept; + + template + identifier &operator=(const Type &value) + { + id_ = std::make_shared>(value); + return *this; + } + + ~identifier() = default; + + bool operator==(const identifier &x) const; + bool operator!=(const identifier &x) const; + bool operator<(const identifier &x) const; + bool operator<=(const identifier &x) const; + bool operator>(const identifier &x) const; + bool operator>=(const identifier &x) const; + + bool is_similar_type(const identifier &x) const; + template + bool is_similar_type() const + { + return id_->is_similar_type(); + } + + std::string str() const; + const std::type_index &type_index() const; + + identifier share() const; + size_t use_count() const; + + bool is_null() const; + bool is_valid() const; + void clear(); + + void serialize(identifier_serializer &s); + + size_t hash() const; + + friend std::ostream &operator<<(std::ostream &out, const identifier &id); + +private: + explicit identifier(const std::shared_ptr& id); + +private: + std::shared_ptr id_; +}; + +static identifier null_identifier{}; + +struct id_pk_hash +{ + size_t operator()(const identifier &id) const; +}; + +} +#endif //QUERY_IDENTIFIER_HPP diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index e97a852..5c485b2 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -11,7 +11,8 @@ set(SQL_SOURCES sql/connection_impl.cpp sql/session.cpp sql/backend_provider.cpp - sql/query_result_impl.cpp) + sql/query_result_impl.cpp + sql/column_generator.cpp) set(SQL_HEADER ../include/matador/sql/dialect.hpp @@ -31,21 +32,27 @@ set(SQL_HEADER ../include/matador/sql/connection_pool.hpp ../include/matador/sql/session.hpp ../include/matador/sql/backend_provider.hpp - ../include/matador/sql/query_result_impl.hpp) + ../include/matador/sql/query_result_impl.hpp + ../include/matador/sql/column_generator.hpp) set(UTILS_HEADER ../include/matador/utils/field_attributes.hpp ../include/matador/utils/string.hpp ../include/matador/utils/constraints.hpp ../include/matador/utils/library.hpp - ../include/matador/utils/os.hpp) + ../include/matador/utils/os.hpp + ../include/matador/utils/access.hpp + ../include/matador/utils/identifiable.hpp + ../include/matador/utils/identifier.hpp + ../include/matador/utils/cascade_type.hpp) set(UTILS_SOURCES utils/field_attributes.cpp utils/string.cpp sql/condition.cpp utils/library.cpp - utils/os.cpp) + utils/os.cpp + utils/identifier.cpp) add_library(matador STATIC ${SQL_SOURCES} ${SQL_HEADER} ${UTILS_SOURCES} ${UTILS_HEADER}) target_include_directories(matador PUBLIC ${PROJECT_SOURCE_DIR}/include) diff --git a/src/sql/column_generator.cpp b/src/sql/column_generator.cpp new file mode 100644 index 0000000..7599f7d --- /dev/null +++ b/src/sql/column_generator.cpp @@ -0,0 +1,28 @@ +#include "matador/sql/column_generator.hpp" + +namespace matador::sql { + +column_generator::column_generator(std::vector &columns) +: columns_(columns) {} + +void column_generator::on_primary_key(const char *id, std::string &pk, size_t size) +{ + on_attribute(id, pk, { size, utils::constraints::PRIMARY_KEY }); +} + +void column_generator::on_revision(const char *id, unsigned long long int &x) +{ + on_attribute(id, x); +} + +void column_generator::on_belongs_to(const char *id, utils::identifiable &x, utils::cascade_type) +{ + +} + +void column_generator::on_has_one(const char *id, utils::identifiable &x, utils::cascade_type) +{ + +} + +} \ No newline at end of file diff --git a/src/sql/query_builder.cpp b/src/sql/query_builder.cpp index 549c43f..6305dad 100644 --- a/src/sql/query_builder.cpp +++ b/src/sql/query_builder.cpp @@ -143,7 +143,12 @@ query_builder& query_builder::remove() { return *this; } -query_builder& query_builder::table(const std::string &table, std::initializer_list columns) { +query_builder& query_builder::table(const std::string &table, std::initializer_list columns) +{ + return this->table(table, std::vector{columns}); +} + +query_builder &query_builder::table(const std::string &table, const std::vector &columns) { transition_to(state_t::QUERY_TABLE_CREATE); query_parts_.emplace_back(" " + dialect_.token_at(dialect::token_t::TABLE) + " " + dialect_.prepare_identifier(table) + " "); @@ -151,16 +156,16 @@ query_builder& query_builder::table(const std::string &table, std::initializer_l std::string result = "("; if (columns.size() < 2) { - for (const auto &col : columns) { - result.append(build_create_column(col, dialect_)); - } + for (const auto &col : columns) { + result.append(build_create_column(col, dialect_)); + } } else { - auto it = columns.begin(); - result.append(build_create_column(*it++, dialect_)); - for (; it != columns.end(); ++it) { - result.append(", "); - result.append(build_create_column(*it, dialect_)); - } + auto it = columns.begin(); + result.append(build_create_column(*it++, dialect_)); + for (; it != columns.end(); ++it) { + result.append(", "); + result.append(build_create_column(*it, dialect_)); + } } result += ")"; diff --git a/src/utils/identifier.cpp b/src/utils/identifier.cpp new file mode 100644 index 0000000..05051f6 --- /dev/null +++ b/src/utils/identifier.cpp @@ -0,0 +1,171 @@ +#include "matador/utils/identifier.hpp" + +#include +#include + +namespace matador::utils { + +identifier::base::base(const std::type_index &ti, detail::identifier_type_t id_type) + : type_index_(ti) + , identifier_type_(id_type) +{} + +bool identifier::base::is_similar_type(const identifier::base &x) const +{ + return identifier_type_ == x.type(); +} + +detail::identifier_type_t identifier::base::type() const +{ + return identifier_type_; +} + +identifier::null_pk::null_pk() + : base(std::type_index(typeid(null_type_t)), detail::identifier_type_traits::type()) +{} + +identifier::base* identifier::null_pk::copy() const +{ + return new null_pk; +} + +bool identifier::null_pk::equal_to(const identifier::base &x) const +{ + return type_index_ == x.type_index_; +} + +bool identifier::null_pk::less(const identifier::base &x) const +{ + return type_index_ == x.type_index_; +} + +bool identifier::null_pk::is_valid() const +{ + return detail::identifier_type_traits::is_valid(); +} + +std::string identifier::null_pk::str() const +{ + return detail::identifier_type_traits::to_string(); +} + +void identifier::null_pk::serialize(identifier_serializer &s) +{ + s.serialize(null_, {}); +} + +size_t identifier::null_pk::hash() const +{ + throw std::runtime_error("hash for null_pk not allowed"); +} + +identifier::identifier() + : id_(std::make_shared()) {} + +identifier::identifier(const identifier &x) + : id_(x.id_->copy()) {} + +identifier &identifier::operator=(const identifier &x) { + if (this == &x) { + return *this; + } + id_.reset(x.id_->copy()); + return *this; +} + +identifier::identifier(identifier &&x) noexcept + : id_(std::move(x.id_)) {} + +identifier &identifier::operator=(identifier &&x) noexcept { + id_ = std::move(x.id_); + return *this; +} + +bool identifier::operator==(const identifier &x) const { + return id_->equal_to(*x.id_); +} + +bool identifier::operator!=(const identifier &x) const { + return !operator==(x); +} + +bool identifier::operator<(const identifier &x) const { + return id_->less(*x.id_); +} + +bool identifier::operator<=(const identifier &x) const { + return operator==(x) || operator<(x); +} + +bool identifier::operator>(const identifier &x) const { + return !operator<=(x); +} + +bool identifier::operator>=(const identifier &x) const { + return !operator<(x); +} + +bool identifier::is_similar_type(const identifier &x) const +{ + return id_->is_similar_type(*x.id_); +} + +std::string identifier::str() const { + return id_->str(); +} + +const std::type_index &identifier::type_index() const { + return id_->type_index_; +} + +identifier identifier::share() const +{ + return identifier(id_); +} + +size_t identifier::use_count() const +{ + return id_.use_count(); +} + +bool identifier::is_null() const +{ + return type_index() == null_identifier.type_index(); +} + +bool identifier::is_valid() const +{ + return id_->is_valid(); +} + +void identifier::clear() +{ + id_ = std::make_unique(); +} + +void identifier::serialize(identifier_serializer &s) +{ + id_->serialize(s); +} + +size_t identifier::hash() const +{ + return id_->hash(); +} + +identifier::identifier(const std::shared_ptr &id) + : id_(id) +{} + +std::ostream &operator<<(std::ostream &out, const identifier &id) +{ + out << id.str(); + return out; +} + +size_t id_pk_hash::operator()(const identifier &id) const +{ + return id.hash(); +} + +} \ No newline at end of file diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index b7610dc..d6a7734 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -13,7 +13,10 @@ add_executable(tests builder.cpp record.cpp connection_pool.cpp query.cpp - backend_provider.cpp) + backend_provider.cpp + connection.cpp + product.hpp + column_generator.cpp) target_link_libraries(tests PRIVATE Catch2::Catch2WithMain matador diff --git a/test/column_generator.cpp b/test/column_generator.cpp new file mode 100644 index 0000000..3719971 --- /dev/null +++ b/test/column_generator.cpp @@ -0,0 +1,30 @@ +#include + +#include "matador/sql/column_generator.hpp" + +#include "product.hpp" + +using namespace matador::sql; + +TEST_CASE("Generate columns from object", "[column generator]") { + + auto columns = column_generator::generate(); + + const std::vector expected_columns = { + "product_name", + "supplier_id", + "category_id", + "quantity_per_unit", + "unit_price", + "units_in_stock", + "units_in_order", + "reorder_level", + "discontinued" + }; + REQUIRE(!columns.empty()); + REQUIRE(columns.size() == expected_columns.size()); + + for (size_t i = 0; i != expected_columns.size(); ++i) { + REQUIRE(expected_columns[i] == columns[i].name()); + } +} diff --git a/test/connection.cpp b/test/connection.cpp new file mode 100644 index 0000000..c89e626 --- /dev/null +++ b/test/connection.cpp @@ -0,0 +1,16 @@ +#include + +#include "matador/sql/connection.hpp" + +using namespace matador::sql; + +TEST_CASE("Create connection", "[connection]") { + connection c("sqlite://test.db"); + REQUIRE(!c.is_open()); + + c.open(); + REQUIRE(c.is_open()); + + c.close(); + REQUIRE(!c.is_open()); +} \ No newline at end of file diff --git a/test/product.hpp b/test/product.hpp new file mode 100644 index 0000000..161e647 --- /dev/null +++ b/test/product.hpp @@ -0,0 +1,40 @@ +#ifndef QUERY_PRODUCT_HPP +#define QUERY_PRODUCT_HPP + +#include "matador/utils/access.hpp" +#include "matador/utils/field_attributes.hpp" + +#include + +namespace matador::test { + +struct product +{ + std::string product_name; + unsigned long supplier_id; + unsigned long category_id; + std::string quantity_per_unit; + unsigned int unit_price; + unsigned int units_in_stock; + unsigned int units_in_order; + unsigned int reorder_level; + bool discontinued; + + template + void process(Operator &op) { + namespace field = matador::utils::access; + using namespace matador::utils; + field::primary_key(op, "product_name", product_name, 255); + field::attribute(op, "supplier_id", supplier_id, { constraints::FOREIGN_KEY | constraints::NOT_NULL }); + field::attribute(op, "category_id", category_id, { constraints::FOREIGN_KEY | constraints::NOT_NULL }); + field::attribute(op, "quantity_per_unit", quantity_per_unit, 255); + field::attribute(op, "unit_price", unit_price); + field::attribute(op, "units_in_stock", units_in_stock); + field::attribute(op, "units_in_order", units_in_order); + field::attribute(op, "reorder_level", reorder_level); + field::attribute(op, "discontinued", discontinued); + } +}; +} + +#endif //QUERY_PRODUCT_HPP diff --git a/test/query.cpp b/test/query.cpp index 0def8a3..52cbd95 100644 --- a/test/query.cpp +++ b/test/query.cpp @@ -1,5 +1,5 @@ #include -#include "catch2/generators/catch_generators.hpp" +#include TEST_CASE("Query test", "[query]") { auto dns = GENERATE(as{}, "mssql", "sqlite", "postgres" ); diff --git a/test/session.cpp b/test/session.cpp index 5e236bd..37bc385 100644 --- a/test/session.cpp +++ b/test/session.cpp @@ -4,21 +4,24 @@ #include #include +#include "product.hpp" + using namespace matador::sql; TEST_CASE("Execute create table statement", "[connection]") { connection_pool pool("sqlite://sqlite.db", 4); session s(pool); - const auto res = s.create() + auto res = s.create() .table("person", { make_pk_column("id"), make_column("name", 255), make_column("age") }).execute(); - REQUIRE(true); REQUIRE(res.second == R"(CREATE TABLE "person" ("id" BIGINT NOT NULL PRIMARY KEY, "name" VARCHAR(255), "age" INTEGER))"); + + res = s.create().table("product").execute(); } TEST_CASE("Execute drop table statement", "[connection]") {