added column generator and tests

This commit is contained in:
Sascha Kuehl 2023-11-11 08:29:22 +01:00
parent ae236746ad
commit 30f2126225
20 changed files with 917 additions and 19 deletions

View File

@ -4,13 +4,20 @@ project(query)
set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD 17)
set(CMAKE_POSITION_INDEPENDENT_CODE ON) set(CMAKE_POSITION_INDEPENDENT_CODE ON)
list(APPEND CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake)
find_package(ODBC REQUIRED) find_package(ODBC REQUIRED)
find_package(SQLite3 REQUIRED) find_package(SQLite3 REQUIRED)
find_package(PostgreSQL REQUIRED)
find_package(MySQL REQUIRED)
message(STATUS "Found SQLite3 ${SQLite3_VERSION}") message(STATUS "Found SQLite3 ${SQLite3_VERSION}")
message(STATUS "Adding SQLite3 include directory: ${SQLite3_INCLUDE_DIRS}") message(STATUS "Adding SQLite3 include directory: ${SQLite3_INCLUDE_DIRS}")
message(STATUS "Adding SQLite3 libs: ${SQLite3_LIBRARIES}") 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 "Common flags ${CMAKE_CXX_FLAGS}")
message(STATUS "Debug flags ${CMAKE_CXX_FLAGS_DEBUG}") message(STATUS "Debug flags ${CMAKE_CXX_FLAGS_DEBUG}")
message(STATUS "Relase flags ${CMAKE_CXX_FLAGS_RELEASE}") message(STATUS "Relase flags ${CMAKE_CXX_FLAGS_RELEASE}")

129
cmake/FindMySQL.cmake Normal file
View File

@ -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)

View File

@ -17,7 +17,11 @@ public:
template<typename Type> template<typename Type>
explicit column(std::string name, utils::field_attributes attr = utils::null_attributes) explicit column(std::string name, utils::field_attributes attr = utils::null_attributes)
: column(std::move(name), data_type_traits<Type>::data_type(), attr) : column(std::move(name), data_type_traits<Type>::builtin_type(attr.size()), attr)
{}
template<typename Type>
column(std::string name, const Type &, utils::field_attributes attr = utils::null_attributes)
: column(std::move(name), data_type_traits<Type>::builtin_type(attr.size()), attr)
{} {}
column(std::string name, data_type_t type, utils::field_attributes attr = utils::null_attributes); column(std::string name, data_type_t type, utils::field_attributes attr = utils::null_attributes);

View File

@ -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 <vector>
namespace matador::utils {
class identifiable;
enum class cascade_type;
}
namespace matador::sql {
class column_generator
{
private:
explicit column_generator(std::vector<column> &columns);
public:
~column_generator() = default;
template < class Type >
static std::vector<column> generate()
{
std::vector<column> 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<std::is_integral<V>::value && !std::is_same<bool, V>::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<typename Type>
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<column> &columns_;
// typed_column_identifier_serializer column_identifier_serializer_;
};
template<typename V>
void column_generator::on_primary_key(const char *id, V &x, typename std::enable_if<std::is_integral<V>::value && !std::is_same<bool, V>::value>::type*)
{
on_attribute(id, x, { utils::constraints::PRIMARY_KEY });
}
template<typename Type>
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

View File

@ -100,6 +100,7 @@ public:
query_builder& remove(); query_builder& remove();
query_builder& table(const std::string &table, std::initializer_list<column> columns); query_builder& table(const std::string &table, std::initializer_list<column> columns);
query_builder& table(const std::string &table, const std::vector<column> &columns);
query_builder& table(const std::string &table); query_builder& table(const std::string &table);
query_builder& into(const std::string &table, std::initializer_list<std::string> column_names); query_builder& into(const std::string &table, std::initializer_list<std::string> column_names);
query_builder& values(std::initializer_list<any_type> values); query_builder& values(std::initializer_list<any_type> values);

View File

@ -2,6 +2,7 @@
#define QUERY_QUERY_INTERMEDIATES_HPP #define QUERY_QUERY_INTERMEDIATES_HPP
#include "matador/sql/column.hpp" #include "matador/sql/column.hpp"
#include "matador/sql/column_generator.hpp"
#include "matador/sql/key_value_pair.hpp" #include "matador/sql/key_value_pair.hpp"
#include "matador/sql/query_result.hpp" #include "matador/sql/query_result.hpp"
#include "matador/sql/record.hpp" #include "matador/sql/record.hpp"
@ -136,6 +137,11 @@ public:
using query_intermediate::query_intermediate; using query_intermediate::query_intermediate;
query_execute_finish table(const std::string &table, std::initializer_list<column> columns); query_execute_finish table(const std::string &table, std::initializer_list<column> columns);
template<class Type>
query_execute_finish table(const std::string &table)
{
return {db(), query().table(table, column_generator::generate<Type>())};
}
}; };
class query_drop_intermediate : query_intermediate class query_drop_intermediate : query_intermediate

View File

@ -0,0 +1,75 @@
#ifndef QUERY_ACCESS_HPP
#define QUERY_ACCESS_HPP
#include <string>
namespace matador::utils {
enum class cascade_type;
template < class Type, template < class ... > class ContainerType >
class container;
class field_attributes;
namespace access {
template<class Operator, class Type>
void process(Operator &op, Type &object) {
object.process(op);
}
template<class Operator, class Type>
void process(Operator &op, const Type &object) {
const_cast<Type &>(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<class Operator>
void revision(Operator &op, const char *id, unsigned long long &value) {
op.on_revision(id, value);
}
template<class Operator, class Type>
void attribute(Operator &op, const char *id, Type &value, const field_attributes &attr) {
op.on_attribute(id, value, attr);
}
template<class Operator, class Type>
void attribute(Operator &op, const char *id, Type &value) {
op.on_attribute(id, value);
}
template<class Operator, class Type>
void has_one(Operator &op, const char *id, Type &value, cascade_type cascade) {
op.on_has_one(id, value, cascade);
}
template<class Operator, class Type>
void belongs_to(Operator &op, const char *id, Type &value, cascade_type cascade) {
op.on_belongs_to(id, value, cascade);
}
template<class Operator, class Type, template<class ...> class ContainerType>
void has_many(Operator &op, const char *id, container<Type, ContainerType> &container, cascade_type cascade) {
op.on_has_many(id, container, cascade);
}
template<class Operator, class Type, template<class ...> class ContainerType>
void has_many(Operator &op, const char *id, container<Type, ContainerType> &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

View File

@ -0,0 +1,30 @@
#ifndef QUERY_CASCADE_TYPE_HPP
#define QUERY_CASCADE_TYPE_HPP
#include <cstdint>
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

View File

@ -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

View File

@ -0,0 +1,223 @@
#ifndef QUERY_IDENTIFIER_HPP
#define QUERY_IDENTIFIER_HPP
#include "matador/utils/field_attributes.hpp"
#include <memory>
#include <string>
#include <typeindex>
#include <type_traits>
namespace matador::utils {
struct null_type_t {};
namespace detail {
enum class identifier_type_t : unsigned int {
INTEGRAL_TYPE,
STRING_TYPE,
NULL_TYPE
};
template<typename Type, class Enabled = void>
struct identifier_type_traits;
template<typename Type>
struct identifier_type_traits<Type, typename std::enable_if<std::is_integral<Type>::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<typename Type>
struct identifier_type_traits<Type, typename std::enable_if<std::is_same<Type, std::string>::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<typename Type>
struct identifier_type_traits<Type, typename std::enable_if<std::is_same<Type, null_type_t>::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<typename Type>
bool is_similar_type() const
{
return identifier_type_ == detail::identifier_type_traits<Type>::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<class IdType>
struct pk : public base
{
using self = pk<IdType>;
explicit pk(const IdType &id, size_t size = 0) : base(std::type_index(typeid(IdType)), detail::identifier_type_traits<IdType>::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<const pk<IdType> &>(x).id_ == id_;
}
bool less(const base &x) const final {
return static_cast<const pk<IdType> &>(x).id_ < id_;
}
bool is_valid() const final
{
return detail::identifier_type_traits<IdType>::is_valid(id_);
}
std::string str() const final
{
return detail::identifier_type_traits<IdType>::to_string(id_);
}
void serialize(identifier_serializer &s) final {
s.serialize(id_, size_);
}
size_t hash() const final {
std::hash<IdType> 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<typename Type>
explicit identifier(const Type &id, long size = -1)
: id_(std::make_shared<pk<Type>>(id, size)) {}
identifier(const identifier &x);
identifier &operator=(const identifier &x);
identifier(identifier &&x) noexcept ;
identifier &operator=(identifier &&x) noexcept;
template<typename Type>
identifier &operator=(const Type &value)
{
id_ = std::make_shared<pk<Type>>(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<typename Type>
bool is_similar_type() const
{
return id_->is_similar_type<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<base>& id);
private:
std::shared_ptr<base> id_;
};
static identifier null_identifier{};
struct id_pk_hash
{
size_t operator()(const identifier &id) const;
};
}
#endif //QUERY_IDENTIFIER_HPP

View File

@ -11,7 +11,8 @@ set(SQL_SOURCES
sql/connection_impl.cpp sql/connection_impl.cpp
sql/session.cpp sql/session.cpp
sql/backend_provider.cpp sql/backend_provider.cpp
sql/query_result_impl.cpp) sql/query_result_impl.cpp
sql/column_generator.cpp)
set(SQL_HEADER set(SQL_HEADER
../include/matador/sql/dialect.hpp ../include/matador/sql/dialect.hpp
@ -31,21 +32,27 @@ set(SQL_HEADER
../include/matador/sql/connection_pool.hpp ../include/matador/sql/connection_pool.hpp
../include/matador/sql/session.hpp ../include/matador/sql/session.hpp
../include/matador/sql/backend_provider.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 set(UTILS_HEADER
../include/matador/utils/field_attributes.hpp ../include/matador/utils/field_attributes.hpp
../include/matador/utils/string.hpp ../include/matador/utils/string.hpp
../include/matador/utils/constraints.hpp ../include/matador/utils/constraints.hpp
../include/matador/utils/library.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 set(UTILS_SOURCES
utils/field_attributes.cpp utils/field_attributes.cpp
utils/string.cpp utils/string.cpp
sql/condition.cpp sql/condition.cpp
utils/library.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}) add_library(matador STATIC ${SQL_SOURCES} ${SQL_HEADER} ${UTILS_SOURCES} ${UTILS_HEADER})
target_include_directories(matador PUBLIC ${PROJECT_SOURCE_DIR}/include) target_include_directories(matador PUBLIC ${PROJECT_SOURCE_DIR}/include)

View File

@ -0,0 +1,28 @@
#include "matador/sql/column_generator.hpp"
namespace matador::sql {
column_generator::column_generator(std::vector<column> &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)
{
}
}

View File

@ -143,7 +143,12 @@ query_builder& query_builder::remove() {
return *this; return *this;
} }
query_builder& query_builder::table(const std::string &table, std::initializer_list<column> columns) { query_builder& query_builder::table(const std::string &table, std::initializer_list<column> columns)
{
return this->table(table, std::vector<column>{columns});
}
query_builder &query_builder::table(const std::string &table, const std::vector<column> &columns) {
transition_to(state_t::QUERY_TABLE_CREATE); transition_to(state_t::QUERY_TABLE_CREATE);
query_parts_.emplace_back(" " + dialect_.token_at(dialect::token_t::TABLE) + " " + dialect_.prepare_identifier(table) + " "); 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 = "("; std::string result = "(";
if (columns.size() < 2) { if (columns.size() < 2) {
for (const auto &col : columns) { for (const auto &col : columns) {
result.append(build_create_column(col, dialect_)); result.append(build_create_column(col, dialect_));
} }
} else { } else {
auto it = columns.begin(); auto it = columns.begin();
result.append(build_create_column(*it++, dialect_)); result.append(build_create_column(*it++, dialect_));
for (; it != columns.end(); ++it) { for (; it != columns.end(); ++it) {
result.append(", "); result.append(", ");
result.append(build_create_column(*it, dialect_)); result.append(build_create_column(*it, dialect_));
} }
} }
result += ")"; result += ")";

171
src/utils/identifier.cpp Normal file
View File

@ -0,0 +1,171 @@
#include "matador/utils/identifier.hpp"
#include <stdexcept>
#include <ostream>
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<null_type_t>::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<null_type_t>::is_valid();
}
std::string identifier::null_pk::str() const
{
return detail::identifier_type_traits<null_type_t>::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<null_pk>()) {}
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<null_pk>();
}
void identifier::serialize(identifier_serializer &s)
{
id_->serialize(s);
}
size_t identifier::hash() const
{
return id_->hash();
}
identifier::identifier(const std::shared_ptr<base> &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();
}
}

View File

@ -13,7 +13,10 @@ add_executable(tests builder.cpp
record.cpp record.cpp
connection_pool.cpp connection_pool.cpp
query.cpp query.cpp
backend_provider.cpp) backend_provider.cpp
connection.cpp
product.hpp
column_generator.cpp)
target_link_libraries(tests PRIVATE target_link_libraries(tests PRIVATE
Catch2::Catch2WithMain Catch2::Catch2WithMain
matador matador

30
test/column_generator.cpp Normal file
View File

@ -0,0 +1,30 @@
#include <catch2/catch_test_macros.hpp>
#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<matador::test::product>();
const std::vector<std::string> 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());
}
}

16
test/connection.cpp Normal file
View File

@ -0,0 +1,16 @@
#include <catch2/catch_test_macros.hpp>
#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());
}

40
test/product.hpp Normal file
View File

@ -0,0 +1,40 @@
#ifndef QUERY_PRODUCT_HPP
#define QUERY_PRODUCT_HPP
#include "matador/utils/access.hpp"
#include "matador/utils/field_attributes.hpp"
#include <string>
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<class Operator>
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

View File

@ -1,5 +1,5 @@
#include <catch2/catch_test_macros.hpp> #include <catch2/catch_test_macros.hpp>
#include "catch2/generators/catch_generators.hpp" #include <catch2/generators/catch_generators.hpp>
TEST_CASE("Query test", "[query]") { TEST_CASE("Query test", "[query]") {
auto dns = GENERATE(as<std::string>{}, "mssql", "sqlite", "postgres" ); auto dns = GENERATE(as<std::string>{}, "mssql", "sqlite", "postgres" );

View File

@ -4,21 +4,24 @@
#include <matador/sql/condition.hpp> #include <matador/sql/condition.hpp>
#include <matador/sql/session.hpp> #include <matador/sql/session.hpp>
#include "product.hpp"
using namespace matador::sql; using namespace matador::sql;
TEST_CASE("Execute create table statement", "[connection]") { TEST_CASE("Execute create table statement", "[connection]") {
connection_pool<connection> pool("sqlite://sqlite.db", 4); connection_pool<connection> pool("sqlite://sqlite.db", 4);
session s(pool); session s(pool);
const auto res = s.create() auto res = s.create()
.table("person", { .table("person", {
make_pk_column<unsigned long>("id"), make_pk_column<unsigned long>("id"),
make_column<std::string>("name", 255), make_column<std::string>("name", 255),
make_column<unsigned short>("age") make_column<unsigned short>("age")
}).execute(); }).execute();
REQUIRE(true);
REQUIRE(res.second == R"(CREATE TABLE "person" ("id" BIGINT NOT NULL PRIMARY KEY, "name" VARCHAR(255), "age" INTEGER))"); REQUIRE(res.second == R"(CREATE TABLE "person" ("id" BIGINT NOT NULL PRIMARY KEY, "name" VARCHAR(255), "age" INTEGER))");
res = s.create().table<matador::test::product>("product").execute();
} }
TEST_CASE("Execute drop table statement", "[connection]") { TEST_CASE("Execute drop table statement", "[connection]") {