added any type visitor and converter

This commit is contained in:
Sascha Kuehl 2023-11-21 20:07:07 +01:00
parent 171e8d36ce
commit 145c4dc0a3
28 changed files with 730 additions and 163 deletions

View File

@ -4,6 +4,8 @@ project(query)
set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD 17)
set(CMAKE_POSITION_INDEPENDENT_CODE ON) set(CMAKE_POSITION_INDEPENDENT_CODE ON)
set(GCC_CLANG_COMMON_FLAGS "-Wall -Wconversion -Wextra -pedantic -ftemplate-backtrace-limit=0")
list(APPEND CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake) list(APPEND CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake)
find_package(ODBC REQUIRED) find_package(ODBC REQUIRED)

View File

@ -1,3 +1,45 @@
# query # query
A fluent sql query builder A fluent sql query builder
Object definition
```cpp
struct airplane
{
unsigned long id;
std::string brand;
std::string model;
template<class Operator>
void process(Operator &op) {
namespace field = matador::utils::access;
field::primary_key(op, "id", id);
field::attribute(op, "brand", brand, 255);
field::attribute(op, "model", model, 255);
}
};
```
```cpp
connection_pool<connection> pool("sqlite://sqlite.db", 4);
session s(pool);
// register entity
s.attach<airplane>("airplane")
// create all tables
s.create();
// insert
s.insert<airplane>(1, "Airbus", "A380");
s.insert<airplane>(2, "Boeing", "748");
s.insert<airplane>(3, "Boeing", "707");
s.update<airplane>().set({"model", "747"}).where("id"_col == 2);
auto result = s.select<airplane>().where("brand"_col == "Boeing");
for (const auto &plane : result) {
std::cout << "airplane: " << plane->brand << " - " << plane->model << "\n";
}
```

8
Todo.md Normal file
View File

@ -0,0 +1,8 @@
# Todo
- Add is_valid() method to connection & connection_impl
- Read in foreign fields
- Add special handling for update in backends
- Add PostgreSQL backend
- Add MySQL/MariaDB backend
- Add ODBC/SQL Server backend

View File

@ -33,6 +33,8 @@ public:
sql::record describe(const std::string& table) override; sql::record describe(const std::string& table) override;
bool exists(const std::string &table_name) override;
private: private:
static int parse_result(void* param, int column_count, char** values, char** columns); static int parse_result(void* param, int column_count, char** values, char** columns);

View File

@ -153,14 +153,27 @@ sql::record sqlite_connection::describe(const std::string& table)
options = utils::constraints::NOT_NULL; options = utils::constraints::NOT_NULL;
} }
// f.default_value(res->column(4)); // f.default_value(res->column(4));
// end = nullptr;
// f.is_primary_key(strtoul(res->column(3), &end, 10) == 0);
prototype.append({name, type, {options}}); prototype.append({name, type, {options}});
} }
return std::move(prototype); return std::move(prototype);
} }
bool sqlite_connection::exists(const std::string &table_name)
{
const auto result = fetch("SELECT COUNT(*) FROM sqlite_master WHERE type='table' AND tbl_name='" + table_name + "' LIMIT 1");
if (!result->fetch()) {
// Todo: throw an exception?
return false;
}
int v{};
result->read_value(nullptr, 0, v);
return v == 1;
}
} }
extern "C" extern "C"

View File

@ -0,0 +1,131 @@
#ifndef QUERY_ANY_TYPE_TO_VISITOR_HPP
#define QUERY_ANY_TYPE_TO_VISITOR_HPP
#include <array>
#include <charconv>
#include <cstring>
#include <stdexcept>
#include <string>
#include <type_traits>
namespace matador::sql {
template < typename DestType, typename SourceType >
void convert(DestType &dest, SourceType source, typename std::enable_if<std::is_same<DestType, SourceType>::value>::type* = nullptr)
{
dest = source;
}
template < typename DestType, typename SourceType >
void convert(DestType &dest, SourceType source, typename std::enable_if<std::is_integral<DestType>::value && std::is_arithmetic<SourceType>::value && !std::is_same<DestType, SourceType>::value>::type* = nullptr)
{
dest = static_cast<DestType>(source);
}
template < typename DestType, typename SourceType >
void convert(DestType &dest, SourceType source, typename std::enable_if<std::is_floating_point<DestType>::value && std::is_arithmetic<SourceType>::value && !std::is_same<DestType, SourceType>::value>::type* = nullptr)
{
dest = static_cast<DestType>(source);
}
void convert(std::string &dest, bool source);
template < typename SourceType >
void convert(std::string &dest, SourceType source, typename std::enable_if<std::is_integral<SourceType>::value && !std::is_same<bool, SourceType>::value>::type* = nullptr)
{
std::array<char, 128> buffer{};
auto [ptr, ec] = std::to_chars(buffer.data(), buffer.data() + buffer.size(), source, 10);
if (ec == std::errc{}) {
dest.assign(buffer.data(), ptr);
} else {
throw std::logic_error("couldn't convert value to std::string");
}
}
template < typename SourceType >
void convert(std::string &dest, SourceType source, typename std::enable_if<std::is_floating_point<SourceType>::value>::type* = nullptr)
{
std::array<char, 128> buffer{};
auto [ptr, ec] = std::to_chars(buffer.data(), buffer.data() + buffer.size(), source);
if (ec == std::errc{}) {
dest.assign(buffer.data(), ptr);
} else {
throw std::logic_error("couldn't convert value to std::string");
}
}
void convert(std::string &dest, const char* source);
unsigned long long to_unsigned_long_long(const char *source);
template < typename DestType >
void convert(DestType &dest, const std::string &source, typename std::enable_if<std::is_integral<DestType>::value && std::is_unsigned<DestType>::value>::type* = nullptr)
{
dest = to_unsigned_long_long(source.c_str());
}
template < typename DestType >
void convert(DestType &dest, const char *source, typename std::enable_if<std::is_integral<DestType>::value && std::is_unsigned<DestType>::value>::type* = nullptr)
{
dest = to_unsigned_long_long(source);
}
long long to_long_long(const char *source);
template < typename DestType >
void convert(DestType &dest, const std::string &source, typename std::enable_if<std::is_integral<DestType>::value && std::is_signed<DestType>::value>::type* = nullptr)
{
dest = to_long_long(source.c_str());
}
template < typename DestType >
void convert(DestType &dest, const char *source, typename std::enable_if<std::is_integral<DestType>::value && std::is_signed<DestType>::value>::type* = nullptr)
{
dest = to_long_long(source);
}
long double to_double(const char *source);
template < typename DestType >
void convert(DestType &dest, const std::string &source, typename std::enable_if<std::is_floating_point<DestType>::value>::type* = nullptr)
{
dest = to_double(source.c_str());
}
template < typename DestType >
void convert(DestType &dest, const char *source, typename std::enable_if<std::is_floating_point<DestType>::value>::type* = nullptr)
{
dest = to_double(source);
}
template < typename DestType >
void convert(DestType &dest, bool source, typename std::enable_if<std::is_floating_point<DestType>::value>::type* = nullptr)
{
dest = static_cast<DestType>(source);
}
template < typename Type >
struct any_type_to_visitor
{
void operator()(char &x) { convert(result, x); }
void operator()(short &x) { convert(result, x); }
void operator()(int &x) { convert(result, x); }
void operator()(long &x) { convert(result, x); }
void operator()(long long &x) { convert(result, x); }
void operator()(unsigned char &x) { convert(result, x); }
void operator()(unsigned short &x) { convert(result, x); }
void operator()(unsigned int &x) { convert(result, x); }
void operator()(unsigned long &x) { convert(result, x); }
void operator()(unsigned long long &x) { convert(result, x); }
void operator()(bool &x) { convert(result, x); }
void operator()(float &x) { convert(result, x); }
void operator()(double &x) { convert(result, x); }
void operator()(const char *x) { convert(result, x); }
void operator()(std::string &x) { convert(result, x); }
Type result{};
};
}
#endif //QUERY_ANY_TYPE_TO_VISITOR_HPP

View File

@ -2,6 +2,7 @@
#define QUERY_COLUMN_HPP #define QUERY_COLUMN_HPP
#include "matador/sql/any_type.hpp" #include "matador/sql/any_type.hpp"
#include "matador/sql/any_type_to_visitor.hpp"
#include "matador/sql/types.hpp" #include "matador/sql/types.hpp"
#include "matador/utils/field_attributes.hpp" #include "matador/utils/field_attributes.hpp"
@ -11,28 +12,42 @@
namespace matador::sql { namespace matador::sql {
namespace detail {
}
class column { class column {
public: public:
explicit column(std::string name) explicit column(std::string name)
: name_(std::move(name)) : name_(std::move(name))
, attributes_(utils::null_attributes) {} , attributes_(utils::null_attributes) {}
column(const column&) = default;
column& operator=(const column&) = default;
column(column&&) noexcept = default;
column& operator=(column&&) noexcept = default;
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>::builtin_type(attr.size()), attr) : column(std::move(name), data_type_traits<Type>::builtin_type(attr.size()), attr)
{} {}
template<typename Type> template<typename Type>
column(std::string name, const Type &, utils::field_attributes attr = utils::null_attributes) 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::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);
template<typename Type> template<typename Type>
column(std::string name, std::string ref_table, std::string ref_column, utils::field_attributes attr = utils::null_attributes) column(std::string name, std::string ref_table, std::string ref_column, utils::field_attributes attr = utils::null_attributes)
: column(std::move(name), data_type_traits<Type>::builtin_type(attr.size()), ref_table, ref_column, attr) : column(std::move(name), data_type_traits<Type>::builtin_type(attr.size()), ref_table, ref_column, attr)
{} {}
column(std::string name, data_type_t type, std::string ref_table, std::string ref_column, utils::field_attributes attr = utils::null_attributes);
column(std::string name, data_type_t type, size_t index, std::string ref_table, std::string ref_column, utils::field_attributes attr = utils::null_attributes);
[[nodiscard]] const std::string& name() const; [[nodiscard]] const std::string& name() const;
[[nodiscard]] size_t index() const;
[[nodiscard]] const utils::field_attributes& attributes() const; [[nodiscard]] const utils::field_attributes& attributes() const;
[[nodiscard]] data_type_t type() const; [[nodiscard]] data_type_t type() const;
[[nodiscard]] const std::string& ref_table() const; [[nodiscard]] const std::string& ref_table() const;
@ -43,10 +58,40 @@ public:
return std::holds_alternative<Type>(value_); return std::holds_alternative<Type>(value_);
} }
template< typename Type > [[nodiscard]] std::string str() const;
std::optional<Type> value() const {
template<typename Type>
void set(const Type &value, const utils::field_attributes &attr = utils::null_attributes)
{
type_ = data_type_traits<Type>::builtin_type(attr.size());
attributes_ = attr;
value_ = value;
}
void set(const std::string &value, const utils::field_attributes &attr)
{
type_ = data_type_traits<std::string>::builtin_type(attr.size());
attributes_ = attr;
value_ = value;
}
void set(const char *value, const utils::field_attributes &attr)
{
type_ = data_type_traits<std::string>::builtin_type(attr.size());
attributes_ = attr;
value_ = value;
}
template<class Type>
Type as() const
{
const Type* ptr= std::get_if<Type>(&value_); const Type* ptr= std::get_if<Type>(&value_);
return ptr ? std::make_optional<Type>(*ptr) : std::nullopt; if (ptr) {
return *ptr;
}
any_type_to_visitor<Type> visitor;
std::visit(visitor, const_cast<any_type&>(value_));
return visitor.result;
} }
private: private:
@ -64,8 +109,9 @@ private:
static const data_type_index data_type_index_; static const data_type_index data_type_index_;
std::string name_; std::string name_;
size_t index_{};
utils::field_attributes attributes_; utils::field_attributes attributes_;
data_type_t type_{}; data_type_t type_{data_type_t::type_unknown};
any_type value_; any_type value_;
std::string ref_table_; std::string ref_table_;
std::string ref_column_; std::string ref_column_;
@ -107,7 +153,7 @@ column make_fk_column(const std::string &name, size_t size, const std::string &r
template < typename Type > template < typename Type >
[[maybe_unused]] column make_fk_column(const std::string &name, const std::string &ref_table, const std::string &ref_column) [[maybe_unused]] column make_fk_column(const std::string &name, const std::string &ref_table, const std::string &ref_column)
{ {
return {name, data_type_traits<Type>::builtin_type(0), ref_table, ref_column, { 0, utils::constraints::FOREIGN_KEY }}; return {name, data_type_traits<Type>::builtin_type(0), 0, ref_table, ref_column, { 0, utils::constraints::FOREIGN_KEY }};
} }
template <> template <>

View File

@ -23,7 +23,7 @@ public:
column generate(const char *id, Type &x, const std::string &ref_table, const std::string &ref_column) column generate(const char *id, Type &x, const std::string &ref_table, const std::string &ref_column)
{ {
utils::access::process(*this, x); utils::access::process(*this, x);
return column{id, type_, ref_table, ref_column, { utils::constraints::FOREIGN_KEY }}; return column{id, type_, 0, ref_table, ref_column, { utils::constraints::FOREIGN_KEY }};
} }
template<typename ValueType> template<typename ValueType>

View File

@ -30,6 +30,7 @@ public:
[[nodiscard]] const connection_info& info() const; [[nodiscard]] const connection_info& info() const;
[[nodiscard]] record describe(const std::string &table_name) const; [[nodiscard]] record describe(const std::string &table_name) const;
bool exists(const std::string &table_name) const;
template<class Type> template<class Type>
query_result<Type> fetch(const std::string &sql) query_result<Type> fetch(const std::string &sql)

View File

@ -25,6 +25,7 @@ public:
virtual void prepare(const std::string &stmt) = 0; virtual void prepare(const std::string &stmt) = 0;
virtual record describe(const std::string &table) = 0; virtual record describe(const std::string &table) = 0;
virtual bool exists(const std::string &table_name) = 0;
protected: protected:
explicit connection_impl(const connection_info &info); explicit connection_impl(const connection_info &info);

View File

@ -57,6 +57,7 @@ enum class join_type_t {
struct query struct query
{ {
std::string sql; std::string sql;
std::string table_name;
record prototype; record prototype;
std::vector<any_type> host_vars; std::vector<any_type> host_vars;
}; };
@ -128,7 +129,7 @@ public:
query_builder& offset(size_t count); query_builder& offset(size_t count);
query_builder& limit(size_t count); query_builder& limit(size_t count);
std::string compile(); query compile();
private: private:
void transition_to(state_t next); void transition_to(state_t next);

View File

@ -25,12 +25,8 @@ public:
query_intermediate(session &db, query_builder &query); query_intermediate(session &db, query_builder &query);
protected: protected:
session& db(); session &session_;
query_builder& query(); query_builder &builder_;
private:
session &db_;
query_builder &query_;
}; };
class query_execute_finish : public query_intermediate class query_execute_finish : public query_intermediate
@ -127,10 +123,20 @@ public:
query_order_by_intermediate order_by(const std::string &name); query_order_by_intermediate order_by(const std::string &name);
}; };
class query_select_intermediate : public query_intermediate class query_start_intermediate
{ {
public: public:
using query_intermediate::query_intermediate; explicit query_start_intermediate(session &s);
protected:
session &session_;
query_builder builder_;
};
class query_select_intermediate : public query_start_intermediate
{
public:
query_select_intermediate(session &s, std::vector<std::string> column_names);
query_from_intermediate from(const std::string &table, const std::string &as = ""); query_from_intermediate from(const std::string &table, const std::string &as = "");
}; };
@ -144,50 +150,50 @@ public:
template<class Type> template<class Type>
query_execute_finish values(const Type &obj) query_execute_finish values(const Type &obj)
{ {
return {db(), query().values(value_extractor::extract(obj))}; return {session_, builder_.values(value_extractor::extract(obj))};
} }
}; };
class query_create_intermediate : query_intermediate class query_create_intermediate : query_start_intermediate
{ {
public: public:
query_create_intermediate(session &db, query_builder &query, table_repository &repo); query_create_intermediate(session &s, table_repository &repo);
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> template<class Type>
query_execute_finish table(const std::string &table_name) query_execute_finish table(const std::string &table_name)
{ {
const auto &info = repository_.attach<Type>(table_name/*, record{column_generator::generate<Type>(repository_)}*/); const auto &info = repository_.attach<Type>(table_name);
return {db(), query().table(table_name, info.prototype.columns())}; return {session_, builder_.table(table_name, info.prototype.columns())};
} }
private: private:
table_repository &repository_; table_repository &repository_;
}; };
class query_drop_intermediate : query_intermediate class query_drop_intermediate : query_start_intermediate
{ {
public: public:
using query_intermediate::query_intermediate; explicit query_drop_intermediate(session &s);
query_execute_finish table(const std::string &table); query_execute_finish table(const std::string &table);
}; };
class query_insert_intermediate : public query_intermediate class query_insert_intermediate : public query_start_intermediate
{ {
public: public:
using query_intermediate::query_intermediate; explicit query_insert_intermediate(session &s);
query_into_intermediate into(const std::string &table, std::initializer_list<std::string> column_names); query_into_intermediate into(const std::string &table, std::initializer_list<std::string> column_names);
template<class Type> template<class Type>
query_into_intermediate into(const std::string &table) query_into_intermediate into(const std::string &table)
{ {
return {db(), query().into(table, column_name_generator::generate<Type>())}; return {session_, builder_.into(table, column_name_generator::generate<Type>())};
} }
template<class Type> template<class Type>
query_execute_finish into(const std::string &table, const Type &obj) query_execute_finish into(const std::string &table, const Type &obj)
{ {
return {db(), query().into(table, column_name_generator::generate<Type>()) return {session_, builder_.into(table, column_name_generator::generate<Type>())
.values(value_extractor::extract(obj))}; .values(value_extractor::extract(obj))};
} }
}; };
@ -208,16 +214,16 @@ public:
query_execute_where_intermediate where(const basic_condition &cond); query_execute_where_intermediate where(const basic_condition &cond);
}; };
class query_update_intermediate : public query_intermediate class query_update_intermediate : public query_start_intermediate
{ {
public: public:
using query_intermediate::query_intermediate; query_update_intermediate(session &s, std::string table_name);
query_set_intermediate set(std::initializer_list<key_value_pair> columns); query_set_intermediate set(std::initializer_list<key_value_pair> columns);
template<class Type> template<class Type>
query_set_intermediate set(const Type &obj) query_set_intermediate set(const Type &obj)
{ {
return {db(), query().set(key_value_generator::generate(obj))}; return {session_, builder_.set(key_value_generator::generate(obj))};
} }
}; };
@ -229,10 +235,10 @@ public:
query_execute_where_intermediate where(const basic_condition &cond); query_execute_where_intermediate where(const basic_condition &cond);
}; };
class query_delete_intermediate : public query_intermediate class query_delete_intermediate : public query_start_intermediate
{ {
public: public:
using query_intermediate::query_intermediate; explicit query_delete_intermediate(session &s);
query_delete_from_intermediate from(const std::string &table); query_delete_from_intermediate from(const std::string &table);
}; };

View File

@ -12,6 +12,8 @@
namespace matador::sql { namespace matador::sql {
class dialect;
class session class session
{ {
public: public:
@ -22,14 +24,15 @@ public:
template < class Type > template < class Type >
query_select_intermediate select() query_select_intermediate select()
{ {
return query_select_intermediate{*this, query_.select(column_name_generator::generate<Type>())}; return query_select_intermediate{*this, column_name_generator::generate<Type>()};
} }
query_select_intermediate select(std::initializer_list<std::string> column_names); query_select_intermediate select(std::initializer_list<std::string> column_names);
query_insert_intermediate insert(); query_insert_intermediate insert();
query_update_intermediate update(const std::string &table); query_update_intermediate update(const std::string &table);
query_delete_intermediate remove(); query_delete_intermediate remove();
[[nodiscard]] query_result<record> fetch(const std::string &sql) const; [[nodiscard]] query_result<record> fetch(const query &q) const;
// [[nodiscard]] query_result<record> fetch(const std::string &sql) const;
[[nodiscard]] std::pair<size_t, std::string> execute(const std::string &sql) const; [[nodiscard]] std::pair<size_t, std::string> execute(const std::string &sql) const;
template<typename Type> template<typename Type>
@ -40,6 +43,8 @@ public:
[[nodiscard]] const table_repository& tables() const; [[nodiscard]] const table_repository& tables() const;
const class dialect& dialect() const;
private: private:
friend class query_select_finish; friend class query_select_finish;
@ -47,9 +52,10 @@ private:
private: private:
connection_pool<connection> &pool_; connection_pool<connection> &pool_;
query_builder query_; const class dialect &dialect_;
table_repository table_repository_; table_repository table_repository_;
mutable std::unordered_map<std::string, record> prototypes_;
}; };
} }

View File

@ -180,7 +180,7 @@ template <> struct data_type_traits<const char*>
template <> struct data_type_traits<char*> template <> struct data_type_traits<char*>
{ {
inline static database_type_t type(std::size_t size) { return size == 0 ? database_type_t::type_text : database_type_t::type_varchar; } inline static database_type_t type(std::size_t size) { return size == 0 ? database_type_t::type_text : database_type_t::type_varchar; }
inline static data_type_t builtin_type(std::size_t size) { return size == 0 ? data_type_t::type_text : data_type_t::type_char_pointer; } inline static data_type_t builtin_type(std::size_t size) { return size == 0 ? data_type_t::type_text : data_type_t::type_varchar; }
inline static unsigned long size() { return sizeof(char*); } inline static unsigned long size() { return sizeof(char*); }
inline static const char* name() { return "char*"; } inline static const char* name() { return "char*"; }
}; };
@ -188,7 +188,7 @@ template <> struct data_type_traits<char*>
template <> struct data_type_traits<std::string> template <> struct data_type_traits<std::string>
{ {
inline static database_type_t type(std::size_t size) { return size == 0 ? database_type_t::type_text : database_type_t::type_varchar; } inline static database_type_t type(std::size_t size) { return size == 0 ? database_type_t::type_text : database_type_t::type_varchar; }
inline static data_type_t builtin_type(std::size_t size) { return size == 0 ? data_type_t::type_text : data_type_t::type_char_pointer; } inline static data_type_t builtin_type(std::size_t size) { return size == 0 ? data_type_t::type_text : data_type_t::type_varchar; }
inline static unsigned long size() { return 1023; } inline static unsigned long size() { return 1023; }
inline static const char* name() { return "std::string"; } inline static const char* name() { return "std::string"; }
}; };

View File

@ -21,6 +21,7 @@ public:
~field_attributes() = default; ~field_attributes() = default;
[[nodiscard]] size_t size() const; [[nodiscard]] size_t size() const;
void size(size_t size);
[[nodiscard]] constraints options() const; [[nodiscard]] constraints options() const;
private: private:

View File

@ -16,7 +16,8 @@ set(SQL_SOURCES
sql/column_name_generator.cpp sql/column_name_generator.cpp
sql/key_value_generator.cpp sql/key_value_generator.cpp
sql/fk_value_extractor.cpp sql/fk_value_extractor.cpp
sql/table_repository.cpp) sql/table_repository.cpp
sql/any_type_to_visitor.cpp)
set(SQL_HEADER set(SQL_HEADER
../include/matador/sql/dialect.hpp ../include/matador/sql/dialect.hpp
@ -44,7 +45,8 @@ set(SQL_HEADER
../include/matador/sql/key_value_generator.hpp ../include/matador/sql/key_value_generator.hpp
../include/matador/sql/foreign.hpp ../include/matador/sql/foreign.hpp
../include/matador/sql/fk_value_extractor.hpp ../include/matador/sql/fk_value_extractor.hpp
"../include/matador/sql/table_repository.hpp") "../include/matador/sql/table_repository.hpp"
../include/matador/sql/any_type_to_visitor.hpp)
set(UTILS_HEADER set(UTILS_HEADER
../include/matador/utils/field_attributes.hpp ../include/matador/utils/field_attributes.hpp

View File

@ -0,0 +1,60 @@
#include "matador/sql/any_type_to_visitor.hpp"
namespace matador::sql {
void convert(std::string &dest, bool source)
{
dest = source ? "true" : "false";
}
void convert(std::string &dest, const char *source)
{
dest = source;
}
long long to_long_long(const char *source)
{
if (strlen(source) == 0) {
return{};
}
char *end;
const auto result = strtoll(source, &end, 10);
if (end == nullptr) {
// Todo: check error
throw std::logic_error("couldn't convert value to number");
}
return result;
}
unsigned long long to_unsigned_long_long(const char *source)
{
if (strlen(source) == 0) {
return{};
}
char *end;
const auto result = strtoull(source, &end, 10);
if (end == nullptr) {
// Todo: check error
throw std::logic_error("couldn't convert value to number");
}
return result;
}
long double to_double(const char *source)
{
if (strlen(source) == 0) {
return{};
}
char *end;
const auto result = strtold(source, &end);
if (end == nullptr) {
// Todo: check error
throw std::logic_error("couldn't convert value to number");
}
return result;
}
}

View File

@ -29,8 +29,9 @@ column::column(std::string name, data_type_t type, utils::field_attributes attr)
, type_(type) , type_(type)
, attributes_(attr) {} , attributes_(attr) {}
column::column(std::string name, data_type_t type, std::string ref_table, std::string ref_column, utils::field_attributes attr) column::column(std::string name, data_type_t type, size_t index, std::string ref_table, std::string ref_column, utils::field_attributes attr)
: name_(std::move(name)) : name_(std::move(name))
, index_(index)
, type_(type) , type_(type)
, attributes_(attr) , attributes_(attr)
, ref_table_(std::move(ref_table)) , ref_table_(std::move(ref_table))
@ -41,6 +42,11 @@ const std::string &column::name() const
return name_; return name_;
} }
size_t column::index() const
{
return index_;
}
const utils::field_attributes &column::attributes() const const utils::field_attributes &column::attributes() const
{ {
return attributes_; return attributes_;
@ -61,6 +67,13 @@ const std::string &column::ref_column() const
return ref_column_; return ref_column_;
} }
std::string column::str() const
{
any_type_to_visitor<std::string> visitor;
std::visit(visitor, const_cast<any_type&>(value_));
return visitor.result;
}
column operator "" _col(const char *name, size_t len) column operator "" _col(const char *name, size_t len)
{ {
return column(std::string(name, len)); return column(std::string(name, len));
@ -86,6 +99,6 @@ column make_pk_column<std::string>( const std::string& name, size_t size )
template<> template<>
[[maybe_unused]] column make_fk_column<std::string>(const std::string& name, size_t size, const std::string &ref_table, const std::string &ref_column) [[maybe_unused]] column make_fk_column<std::string>(const std::string& name, size_t size, const std::string &ref_table, const std::string &ref_column)
{ {
return {name, data_type_traits<std::string>::builtin_type(size), ref_table, ref_column, { size, utils::constraints::PRIMARY_KEY | utils::constraints::NOT_NULL}}; return {name, data_type_traits<std::string>::builtin_type(size), 0, ref_table, ref_column, { size, utils::constraints::PRIMARY_KEY | utils::constraints::NOT_NULL}};
} }
} }

View File

@ -68,6 +68,11 @@ record connection::describe(const std::string &table_name) const
return std::move(connection_->describe(table_name)); return std::move(connection_->describe(table_name));
} }
bool connection::exists(const std::string &table_name) const
{
return connection_->exists(table_name);
}
query_result<record> connection::fetch(const std::string &sql) const query_result<record> connection::fetch(const std::string &sql) const
{ {
auto rec = connection_->describe("person"); auto rec = connection_->describe("person");

View File

@ -141,6 +141,7 @@ query_builder& query_builder::insert() {
query_builder& query_builder::update(const std::string &table) { query_builder& query_builder::update(const std::string &table) {
initialize(command_t::UPDATE, state_t::QUERY_UPDATE); initialize(command_t::UPDATE, state_t::QUERY_UPDATE);
query_.table_name = table;
query_parts_.emplace_back(dialect_.token_at(dialect::token_t::UPDATE) + " " + dialect_.prepare_identifier(table)); query_parts_.emplace_back(dialect_.token_at(dialect::token_t::UPDATE) + " " + dialect_.prepare_identifier(table));
return *this; return *this;
@ -178,6 +179,7 @@ query_builder &query_builder::table(const std::string &table, const std::vector<
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) + " ");
query_.table_name = table;
std::string result = "("; std::string result = "(";
@ -215,6 +217,7 @@ query_builder& query_builder::table(const std::string &table) {
transition_to(state_t::QUERY_TABLE_DROP); transition_to(state_t::QUERY_TABLE_DROP);
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));
query_.table_name = table;
return *this; return *this;
} }
@ -229,6 +232,7 @@ query_builder &query_builder::into(const std::string &table, const std::vector<s
transition_to(state_t::QUERY_INTO); transition_to(state_t::QUERY_INTO);
query_parts_.emplace_back(" " + dialect_.token_at(dialect::token_t::INTO) + " " + dialect_.prepare_identifier(table) + " "); query_parts_.emplace_back(" " + dialect_.token_at(dialect::token_t::INTO) + " " + dialect_.prepare_identifier(table) + " ");
query_.table_name = table;
std::string result{"("}; std::string result{"("};
if (column_names.size() < 2) { if (column_names.size() < 2) {
@ -290,6 +294,7 @@ query_builder& query_builder::from(const std::string &table, const std::string &
transition_to(state_t::QUERY_FROM); transition_to(state_t::QUERY_FROM);
query_parts_.emplace_back(" " + dialect_.token_at(dialect::token_t::FROM) + " " + dialect_.prepare_identifier(table) + (as.empty() ? "" : " " + as)); query_parts_.emplace_back(" " + dialect_.token_at(dialect::token_t::FROM) + " " + dialect_.prepare_identifier(table) + (as.empty() ? "" : " " + as));
query_.table_name = table;
return *this; return *this;
} }
@ -398,12 +403,11 @@ query_builder& query_builder::limit( size_t count ) {
return *this; return *this;
} }
std::string query_builder::compile() { query query_builder::compile() {
std::string result;
for (const auto &part : query_parts_) { for (const auto &part : query_parts_) {
result.append(part); query_.sql.append(part);
} }
return result; return query_;
} }
void query_builder::transition_to(query_builder::state_t next) void query_builder::transition_to(query_builder::state_t next)

View File

@ -2,145 +2,173 @@
#include "matador/sql/session.hpp" #include "matador/sql/session.hpp"
namespace matador::sql { namespace matador::sql {
session &query_intermediate::db()
{
return db_;
}
query_builder &query_intermediate::query()
{
return query_;
}
query_result<record> query_select_finish::fetch_all() query_result<record> query_select_finish::fetch_all()
{ {
return db().fetch(query().compile()); return session_.fetch(builder_.compile());
} }
record query_select_finish::fetch_one() record query_select_finish::fetch_one()
{ {
return *db().fetch(query().compile()).begin().get(); return *session_.fetch(builder_.compile()).begin().get();
} }
std::unique_ptr<query_result_impl> query_select_finish::fetch() std::unique_ptr<query_result_impl> query_select_finish::fetch()
{ {
return db().call_fetch(query().compile()); return session_.call_fetch(builder_.compile().sql);
} }
query_intermediate::query_intermediate(session &db, query_builder &query) query_intermediate::query_intermediate(session &db, query_builder &query)
: db_(db), query_(query) {} : session_(db), builder_(query) {}
query_offset_intermediate query_order_direction_intermediate::offset(size_t offset) query_offset_intermediate query_order_direction_intermediate::offset(size_t offset)
{ {
return {db(), query()}; return {session_, builder_};
} }
query_limit_intermediate query_offset_intermediate::limit(size_t limit) query_limit_intermediate query_offset_intermediate::limit(size_t limit)
{ {
return {db(), query()}; return {session_, builder_};
} }
query_limit_intermediate query_order_direction_intermediate::limit(size_t limit) query_limit_intermediate query_order_direction_intermediate::limit(size_t limit)
{ {
return {db(), query()}; return {session_, builder_};
} }
query_order_by_intermediate query_group_by_intermediate::order_by(const std::string &name) query_order_by_intermediate query_group_by_intermediate::order_by(const std::string &name)
{ {
return {db(), query().order_by(name)}; return {session_, builder_.order_by(name)};
} }
query_order_direction_intermediate query_order_by_intermediate::asc() query_order_direction_intermediate query_order_by_intermediate::asc()
{ {
return {db(), query().asc()}; return {session_, builder_.asc()};
} }
query_order_direction_intermediate query_order_by_intermediate::desc() query_order_direction_intermediate query_order_by_intermediate::desc()
{ {
return {db(), query().desc()}; return {session_, builder_.desc()};
} }
query_group_by_intermediate query_from_intermediate::group_by(const std::string &name) query_group_by_intermediate query_from_intermediate::group_by(const std::string &name)
{ {
return {db(), query().group_by(name)}; return {session_, builder_.group_by(name)};
} }
query_order_by_intermediate query_from_intermediate::order_by(const std::string &name) query_order_by_intermediate query_from_intermediate::order_by(const std::string &name)
{ {
return {db(), query().order_by(name)}; return {session_, builder_.order_by(name)};
} }
query_group_by_intermediate query_where_intermediate::group_by(const std::string &name) query_group_by_intermediate query_where_intermediate::group_by(const std::string &name)
{ {
return {db(), query().group_by(name)}; return {session_, builder_.group_by(name)};
} }
query_order_by_intermediate query_where_intermediate::order_by(const std::string &name) query_order_by_intermediate query_where_intermediate::order_by(const std::string &name)
{ {
return {db(), query().order_by(name)}; return {session_, builder_.order_by(name)};
} }
query_where_intermediate query_from_intermediate::where(const basic_condition &cond) query_where_intermediate query_from_intermediate::where(const basic_condition &cond)
{ {
return query_where_intermediate{db(), query().where(cond)}; return query_where_intermediate{session_, builder_.where(cond)};
}
query_select_intermediate::query_select_intermediate(session &s, std::vector<std::string> column_names)
: query_start_intermediate(s)
{
builder_.select(std::move(column_names));
} }
query_from_intermediate query_select_intermediate::from(const std::string &table, const std::string &as) query_from_intermediate query_select_intermediate::from(const std::string &table, const std::string &as)
{ {
return {db(), query().from(table, as)}; return {session_, builder_.from(table, as)};
}
query_insert_intermediate::query_insert_intermediate(session &s)
: query_start_intermediate(s)
{
builder_.insert();
} }
query_into_intermediate query_insert_intermediate::into(const std::string &table, std::initializer_list<std::string> column_names) query_into_intermediate query_insert_intermediate::into(const std::string &table, std::initializer_list<std::string> column_names)
{ {
return {db(), query().into(table, column_names)}; return {session_, builder_.into(table, column_names)};
} }
std::pair<size_t, std::string> query_execute_finish::execute() std::pair<size_t, std::string> query_execute_finish::execute()
{ {
return db().execute(query().compile()); return session_.execute(builder_.compile().sql);
} }
query_execute_finish query_into_intermediate::values(std::initializer_list<any_type> values) query_execute_finish query_into_intermediate::values(std::initializer_list<any_type> values)
{ {
return {db(), query().values(values)}; return {session_, builder_.values(values)};
} }
query_create_intermediate::query_create_intermediate(session &db, query_builder &query, table_repository &repo) query_create_intermediate::query_create_intermediate(session &s, table_repository &repo)
: query_intermediate(db, query) : query_start_intermediate(s)
, repository_(repo) {} , repository_(repo) {
builder_.create();
}
query_execute_finish query_create_intermediate::table(const std::string &table, std::initializer_list<column> columns) query_execute_finish query_create_intermediate::table(const std::string &table, std::initializer_list<column> columns)
{ {
return {db(), query().table(table, columns)}; return {session_, builder_.table(table, columns)};
}
query_drop_intermediate::query_drop_intermediate(session &s)
: query_start_intermediate(s)
{
builder_.drop();
} }
query_execute_finish query_drop_intermediate::table(const std::string &table) query_execute_finish query_drop_intermediate::table(const std::string &table)
{ {
return {db(), query().table(table)}; return {session_, builder_.table(table)};
} }
query_execute_finish query_execute_where_intermediate::limit(int limit) query_execute_finish query_execute_where_intermediate::limit(int limit)
{ {
return {db(), query().limit(limit)}; return {session_, builder_.limit(limit)};
} }
query_execute_where_intermediate query_set_intermediate::where(const basic_condition &cond) query_execute_where_intermediate query_set_intermediate::where(const basic_condition &cond)
{ {
return {db(), query().where(cond)}; return {session_, builder_.where(cond)};
}
query_update_intermediate::query_update_intermediate(session &s, std::string table_name)
: query_start_intermediate(s)
{
builder_.update(std::move(table_name));
} }
query_set_intermediate query_update_intermediate::set(std::initializer_list<key_value_pair> columns) query_set_intermediate query_update_intermediate::set(std::initializer_list<key_value_pair> columns)
{ {
return {db(), query().set(columns)}; return {session_, builder_.set(columns)};
} }
query_execute_where_intermediate query_delete_from_intermediate::where(const basic_condition &cond) query_execute_where_intermediate query_delete_from_intermediate::where(const basic_condition &cond)
{ {
return {db(), query().where(cond)}; return {session_, builder_.where(cond)};
}
query_delete_intermediate::query_delete_intermediate(session &s)
: query_start_intermediate(s)
{
builder_.remove();
} }
query_delete_from_intermediate query_delete_intermediate::from(const std::string &table) query_delete_from_intermediate query_delete_intermediate::from(const std::string &table)
{ {
return {db(), query().from(table)}; return {session_, builder_.from(table)};
} }
query_start_intermediate::query_start_intermediate(session &s)
: session_(s)
, builder_(s.dialect())
{}
} }

View File

@ -8,46 +8,58 @@ namespace matador::sql {
session::session(connection_pool<connection> &pool) session::session(connection_pool<connection> &pool)
: pool_(pool) : pool_(pool)
, query_(backend_provider::instance().connection_dialect(pool.info().type)) {} , dialect_(backend_provider::instance().connection_dialect(pool_.info().type)) {}
query_create_intermediate session::create() query_create_intermediate session::create()
{ {
return query_create_intermediate{*this, query_.create(), table_repository_}; return query_create_intermediate{*this, table_repository_};
} }
query_drop_intermediate session::drop() query_drop_intermediate session::drop()
{ {
return query_drop_intermediate{*this, query_.drop()}; return query_drop_intermediate{*this};
} }
query_select_intermediate session::select(std::initializer_list<std::string> column_names) query_select_intermediate session::select(std::initializer_list<std::string> column_names)
{ {
return query_select_intermediate{*this, query_.select(column_names)}; return query_select_intermediate{*this, column_names};
} }
query_insert_intermediate session::insert() query_insert_intermediate session::insert()
{ {
return query_insert_intermediate{*this, query_.insert()}; return query_insert_intermediate{*this};
} }
query_update_intermediate session::update(const std::string &table) query_update_intermediate session::update(const std::string &table)
{ {
return query_update_intermediate{*this, query_.update(table)}; return query_update_intermediate{*this, table};
} }
query_delete_intermediate session::remove() query_delete_intermediate session::remove()
{ {
return query_delete_intermediate{*this, query_.remove()}; return query_delete_intermediate{*this};
} }
query_result<record> session::fetch(const std::string &sql) const query_result<record> session::fetch(const query &q) const
{ {
auto res = call_fetch(sql); auto c = pool_.acquire();
auto proto = res->prototype(); if (!c.valid()) {
return query_result<record>{std::move(res), [proto]() { return new record(proto); }}; throw std::logic_error("no database connection available");
}
auto it = prototypes_.find(q.table_name);
if (it == prototypes_.end()) {
it = prototypes_.emplace(q.table_name, c->describe(q.table_name)).first;
}
auto res = c->call_fetch(q.sql);
return query_result<record>{std::move(res), [it]() { return new record(it->second); }};
} }
//query_result<record> session::fetch(const std::string &sql) const
//{
// return query_result<record>(std::unique_ptr());
//}
std::pair<size_t, std::string> session::execute(const std::string &sql) const { std::pair<size_t, std::string> session::execute(const std::string &sql) const {
auto c = pool_.acquire(); auto c = pool_.acquire();
if (!c.valid()) { if (!c.valid()) {
@ -61,6 +73,11 @@ const table_repository& session::tables() const
return table_repository_; return table_repository_;
} }
const class dialect &session::dialect() const
{
return dialect_;
}
std::unique_ptr<query_result_impl> session::call_fetch(const std::string &sql) const std::unique_ptr<query_result_impl> session::call_fetch(const std::string &sql) const
{ {
auto c = pool_.acquire(); auto c = pool_.acquire();

View File

@ -20,6 +20,11 @@ size_t field_attributes::size() const
return size_; return size_;
} }
void field_attributes::size(size_t size)
{
size_ = size;
}
constraints field_attributes::options() const constraints field_attributes::options() const
{ {
return options_; return options_;

View File

@ -0,0 +1,78 @@
#include <catch2/catch_test_macros.hpp>
#include "matador/sql/any_type.hpp"
#include "matador/sql/any_type_to_visitor.hpp"
using namespace matador::sql;
TEST_CASE("Convert any type to string", "[any type visitor]") {
any_type_to_visitor<std::string> to_string_visitor;
any_type value = 6;
std::visit(to_string_visitor, value);
REQUIRE(to_string_visitor.result == "6");
value = 2.5;
std::visit(to_string_visitor, value);
REQUIRE(to_string_visitor.result == "2.5");
value = true;
std::visit(to_string_visitor, value);
REQUIRE(to_string_visitor.result == "true");
value = "hello";
std::visit(to_string_visitor, value);
REQUIRE(to_string_visitor.result == "hello");
value = std::string{"world"};
std::visit(to_string_visitor, value);
REQUIRE(to_string_visitor.result == "world");
}
TEST_CASE("Convert any type to integral", "[any type visitor]") {
any_type_to_visitor<long> to_long_visitor;
any_type value = 6;
std::visit(to_long_visitor, value);
REQUIRE(to_long_visitor.result == 6);
value = 2.5;
std::visit(to_long_visitor, value);
REQUIRE(to_long_visitor.result == 2);
value = true;
std::visit(to_long_visitor, value);
REQUIRE(to_long_visitor.result == 1);
value = "hello";
std::visit(to_long_visitor, value);
REQUIRE(to_long_visitor.result == 0);
value = std::string{"world"};
std::visit(to_long_visitor, value);
REQUIRE(to_long_visitor.result == 0);
}
TEST_CASE("Convert any type to floating point", "[any type visitor]") {
any_type_to_visitor<double> to_double_visitor;
any_type value = 6;
std::visit(to_double_visitor, value);
REQUIRE(to_double_visitor.result == 6);
value = 2.5;
std::visit(to_double_visitor, value);
REQUIRE(to_double_visitor.result == 2.5);
value = true;
std::visit(to_double_visitor, value);
REQUIRE(to_double_visitor.result == 1);
value = "hello";
std::visit(to_double_visitor, value);
REQUIRE(to_double_visitor.result == 0);
value = std::string{"world"};
std::visit(to_double_visitor, value);
REQUIRE(to_double_visitor.result == 0);
}

View File

@ -23,7 +23,9 @@ add_executable(tests builder.cpp
models/supplier.hpp models/supplier.hpp
models/airplane.hpp models/airplane.hpp
models/flight.hpp models/flight.hpp
models/person.hpp) models/person.hpp
AnyTypeToVisitorTest.cpp
ColumnTest.cpp)
target_link_libraries(tests PRIVATE target_link_libraries(tests PRIVATE
Catch2::Catch2WithMain Catch2::Catch2WithMain
matador matador

63
test/ColumnTest.cpp Normal file
View File

@ -0,0 +1,63 @@
#include <catch2/catch_test_macros.hpp>
#include "matador/sql/column.hpp"
using namespace matador::sql;
TEST_CASE("Create empty column", "[column]") {
column c("name");
REQUIRE(c.name() == "name");
REQUIRE(c.index() == 0);
REQUIRE(c.type() == data_type_t::type_unknown);
REQUIRE(c.ref_table().empty());
REQUIRE(c.ref_column().empty());
c.set(std::string{"george"}, 255);
REQUIRE(c.type() == data_type_t::type_varchar);
REQUIRE(c.as<std::string>() == "george");
c.set(7);
REQUIRE(c.type() == data_type_t::type_int);
REQUIRE(c.as<std::string>() == "7");
REQUIRE(c.as<long>() == 7);
REQUIRE(c.str() == "7");
}
TEST_CASE("Copy and move column", "[column]") {
column c("name");
c.set(std::string{"george"}, 255);
REQUIRE(c.name() == "name");
REQUIRE(c.index() == 0);
REQUIRE(c.ref_table().empty());
REQUIRE(c.ref_column().empty());
REQUIRE(c.type() == data_type_t::type_varchar);
REQUIRE(c.as<std::string>() == "george");
REQUIRE(c.attributes().size() == 255);
auto c2 = c;
REQUIRE(c2.name() == "name");
REQUIRE(c2.index() == 0);
REQUIRE(c2.ref_table().empty());
REQUIRE(c2.ref_column().empty());
REQUIRE(c2.type() == data_type_t::type_varchar);
REQUIRE(c2.as<std::string>() == "george");
REQUIRE(c2.attributes().size() == 255);
auto c3 = std::move(c2);
REQUIRE(c3.name() == "name");
REQUIRE(c3.index() == 0);
REQUIRE(c3.ref_table().empty());
REQUIRE(c3.ref_column().empty());
REQUIRE(c3.type() == data_type_t::type_varchar);
REQUIRE(c3.as<std::string>() == "george");
REQUIRE(c3.attributes().size() == 255);
REQUIRE(c2.name().empty());
REQUIRE(c2.index() == 0);
REQUIRE(c2.ref_table().empty());
REQUIRE(c2.ref_column().empty());
REQUIRE(c2.type() == data_type_t::type_varchar);
REQUIRE(c2.as<std::string>().empty());
REQUIRE(c2.attributes().size() == 255);
}

View File

@ -10,118 +10,130 @@ using namespace matador::sql;
TEST_CASE("Create table sql statement string", "[query]") { TEST_CASE("Create table sql statement string", "[query]") {
dialect d; dialect d;
query_builder query(d); query_builder query(d);
auto sql = query.create().table("person", { auto q = query.create().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")
}).compile(); }).compile();
REQUIRE(sql == R"##(CREATE TABLE "person" ("id" BIGINT NOT NULL, "name" VARCHAR(255), "age" INTEGER, CONSTRAINT PK_person PRIMARY KEY (id)))##"); REQUIRE(q.sql == R"##(CREATE TABLE "person" ("id" BIGINT NOT NULL, "name" VARCHAR(255), "age" INTEGER, CONSTRAINT PK_person PRIMARY KEY (id)))##");
REQUIRE(q.table_name == "person");
sql = query.create().table("person", { q = query.create().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"),
make_fk_column<unsigned long>("address", "address", "id") make_fk_column<unsigned long>("address", "address", "id")
}).compile(); }).compile();
REQUIRE(sql == R"##(CREATE TABLE "person" ("id" BIGINT NOT NULL, "name" VARCHAR(255), "age" INTEGER, "address" BIGINT, CONSTRAINT PK_person PRIMARY KEY (id), CONSTRAINT FK_person_address FOREIGN KEY (address) REFERENCES address(id)))##"); REQUIRE(q.sql == R"##(CREATE TABLE "person" ("id" BIGINT NOT NULL, "name" VARCHAR(255), "age" INTEGER, "address" BIGINT, CONSTRAINT PK_person PRIMARY KEY (id), CONSTRAINT FK_person_address FOREIGN KEY (address) REFERENCES address(id)))##");
REQUIRE(q.table_name == "person");
} }
TEST_CASE("Drop table sql statement string", "[query]") { TEST_CASE("Drop table sql statement string", "[query]") {
dialect d; dialect d;
query_builder query(d); query_builder query(d);
const auto sql = query.drop().table("person").compile(); const auto q = query.drop().table("person").compile();
REQUIRE(sql == R"(DROP TABLE "person")"); REQUIRE(q.sql == R"(DROP TABLE "person")");
REQUIRE(q.table_name == "person");
} }
TEST_CASE("Select sql statement string", "[query]") { TEST_CASE("Select sql statement string", "[query]") {
dialect d; dialect d;
query_builder query(d); query_builder query(d);
const auto sql = query.select({"id", "name", "age"}).from("person").compile(); const auto q = query.select({"id", "name", "age"}).from("person").compile();
REQUIRE(sql == R"(SELECT "id", "name", "age" FROM "person")"); REQUIRE(q.sql == R"(SELECT "id", "name", "age" FROM "person")");
REQUIRE(q.table_name == "person");
} }
TEST_CASE("Insert sql statement string", "[query]") { TEST_CASE("Insert sql statement string", "[query]") {
dialect d; dialect d;
query_builder query(d); query_builder query(d);
const auto sql = query.insert().into("person", { const auto q = query.insert().into("person", {
"id", "name", "age" "id", "name", "age"
}).values({7UL, "george", 65U}).compile(); }).values({7UL, "george", 65U}).compile();
REQUIRE(sql == R"(INSERT INTO "person" ("id", "name", "age") VALUES (7, 'george', 65))"); REQUIRE(q.sql == R"(INSERT INTO "person" ("id", "name", "age") VALUES (7, 'george', 65))");
REQUIRE(q.table_name == "person");
} }
TEST_CASE("Update sql statement string", "[query]") { TEST_CASE("Update sql statement string", "[query]") {
dialect d; dialect d;
query_builder query(d); query_builder query(d);
const auto sql = query.update("person").set({ const auto q = query.update("person").set({
{"id", 7UL}, {"id", 7UL},
{"name", "george"}, {"name", "george"},
{"age", 65U} {"age", 65U}
}).compile(); }).compile();
REQUIRE(sql == R"(UPDATE "person" SET "id"=7, "name"='george', "age"=65)"); REQUIRE(q.sql == R"(UPDATE "person" SET "id"=7, "name"='george', "age"=65)");
REQUIRE(q.table_name == "person");
} }
TEST_CASE("Delete sql statement string", "[query]") { TEST_CASE("Delete sql statement string", "[query]") {
dialect d; dialect d;
query_builder query(d); query_builder query(d);
const auto sql = query.remove().from("person").compile(); const auto q = query.remove().from("person").compile();
REQUIRE(sql == R"(DELETE FROM "person")"); REQUIRE(q.sql == R"(DELETE FROM "person")");
REQUIRE(q.table_name == "person");
} }
TEST_CASE("Select sql statement string with where clause", "[query]") { TEST_CASE("Select sql statement string with where clause", "[query]") {
dialect d; dialect d;
query_builder query(d); query_builder query(d);
auto sql = query.select({"id", "name", "age"}) auto q = query.select({"id", "name", "age"})
.from("person") .from("person")
.where("id"_col == 8 && "age"_col > 50) .where("id"_col == 8 && "age"_col > 50)
.compile(); .compile();
REQUIRE(sql == R"(SELECT "id", "name", "age" FROM "person" WHERE ("id" = 8 AND "age" > 50))"); REQUIRE(q.sql == R"(SELECT "id", "name", "age" FROM "person" WHERE ("id" = 8 AND "age" > 50))");
REQUIRE(q.table_name == "person");
sql = query.select({"id", "name", "age"}) q = query.select({"id", "name", "age"})
.from("person") .from("person")
.where("id"_col == _ && "age"_col > 50) .where("id"_col == _ && "age"_col > 50)
.compile(); .compile();
REQUIRE(sql == R"(SELECT "id", "name", "age" FROM "person" WHERE ("id" = ? AND "age" > 50))"); REQUIRE(q.sql == R"(SELECT "id", "name", "age" FROM "person" WHERE ("id" = ? AND "age" > 50))");
REQUIRE(q.table_name == "person");
} }
TEST_CASE("Select sql statement string with order by", "[query]") { TEST_CASE("Select sql statement string with order by", "[query]") {
dialect d; dialect d;
query_builder query(d); query_builder query(d);
const auto sql = query.select({"id", "name", "age"}) const auto q = query.select({"id", "name", "age"})
.from("person") .from("person")
.order_by("name").asc() .order_by("name").asc()
.compile(); .compile();
REQUIRE(sql == R"(SELECT "id", "name", "age" FROM "person" ORDER BY "name" ASC)"); REQUIRE(q.sql == R"(SELECT "id", "name", "age" FROM "person" ORDER BY "name" ASC)");
REQUIRE(q.table_name == "person");
} }
TEST_CASE("Select sql statement string with group by", "[query]") { TEST_CASE("Select sql statement string with group by", "[query]") {
dialect d; dialect d;
query_builder query(d); query_builder query(d);
const auto sql = query.select({"id", "name", "age"}) const auto q = query.select({"id", "name", "age"})
.from("person") .from("person")
.group_by("age") .group_by("age")
.compile(); .compile();
REQUIRE(sql == R"(SELECT "id", "name", "age" FROM "person" GROUP BY "age")"); REQUIRE(q.sql == R"(SELECT "id", "name", "age" FROM "person" GROUP BY "age")");
REQUIRE(q.table_name == "person");
} }
TEST_CASE("Select sql statement string with offset and limit", "[query]") { TEST_CASE("Select sql statement string with offset and limit", "[query]") {
dialect d; dialect d;
query_builder query(d); query_builder query(d);
const auto sql = query.select({"id", "name", "age"}) const auto q = query.select({"id", "name", "age"})
.from("person") .from("person")
.offset(10) .offset(10)
.limit(20) .limit(20)
.compile(); .compile();
REQUIRE(sql == R"(SELECT "id", "name", "age" FROM "person" OFFSET 10 LIMIT 20)"); REQUIRE(q.sql == R"(SELECT "id", "name", "age" FROM "person" OFFSET 10 LIMIT 20)");
REQUIRE(q.table_name == "person");
} }

View File

@ -79,13 +79,13 @@ TEST_CASE("Execute insert record statement", "[session]") {
REQUIRE(i.size() == 3); REQUIRE(i.size() == 3);
REQUIRE(i.at(0).name() == "id"); REQUIRE(i.at(0).name() == "id");
REQUIRE(i.at(0).type() == data_type_t::type_long_long); REQUIRE(i.at(0).type() == data_type_t::type_long_long);
REQUIRE(i.at(0).value<long long>() == 7); REQUIRE(i.at(0).as<long long>() == 7);
REQUIRE(i.at(1).name() == "name"); REQUIRE(i.at(1).name() == "name");
REQUIRE(i.at(1).type() == data_type_t::type_varchar); REQUIRE(i.at(1).type() == data_type_t::type_varchar);
REQUIRE(i.at(1).value<std::string>() == "george"); REQUIRE(i.at(1).as<std::string>() == "george");
REQUIRE(i.at(2).name() == "age"); REQUIRE(i.at(2).name() == "age");
REQUIRE(i.at(2).type() == matador::sql::data_type_t::type_int); REQUIRE(i.at(2).type() == matador::sql::data_type_t::type_int);
REQUIRE(i.at(2).value<int>() == 45); REQUIRE(i.at(2).as<int>() == 45);
} }
s.drop().table("person").execute(); s.drop().table("person").execute();
@ -132,13 +132,13 @@ TEST_CASE("Execute update record statement", "[session]") {
REQUIRE(i.size() == 3); REQUIRE(i.size() == 3);
REQUIRE(i.at(0).name() == "id"); REQUIRE(i.at(0).name() == "id");
REQUIRE(i.at(0).type() == data_type_t::type_long_long); REQUIRE(i.at(0).type() == data_type_t::type_long_long);
REQUIRE(i.at(0).value<long long>() == 7); REQUIRE(i.at(0).as<long long>() == 7);
REQUIRE(i.at(1).name() == "name"); REQUIRE(i.at(1).name() == "name");
REQUIRE(i.at(1).type() == data_type_t::type_varchar); REQUIRE(i.at(1).type() == data_type_t::type_varchar);
REQUIRE(i.at(1).value<std::string>() == "jane"); REQUIRE(i.at(1).as<std::string>() == "jane");
REQUIRE(i.at(2).name() == "age"); REQUIRE(i.at(2).name() == "age");
REQUIRE(i.at(2).type() == matador::sql::data_type_t::type_int); REQUIRE(i.at(2).type() == matador::sql::data_type_t::type_int);
REQUIRE(i.at(2).value<int>() == 35); REQUIRE(i.at(2).as<int>() == 35);
} }
s.drop().table("person").execute(); s.drop().table("person").execute();
@ -161,8 +161,26 @@ TEST_CASE("Execute select statement with where clause", "[session]") {
.execute(); .execute();
REQUIRE(res.first == 1); REQUIRE(res.first == 1);
// fetch specific columns as record
auto result_record = s.select({"id", "name", "age"})
.from("person")
.fetch_all();
for (const auto& i : result_record) {
REQUIRE(i.size() == 3);
REQUIRE(i.at(0).name() == "id");
REQUIRE(i.at(0).type() == data_type_t::type_long_long);
REQUIRE(i.at(0).as<long long>() == george.id);
REQUIRE(i.at(1).name() == "name");
REQUIRE(i.at(1).type() == data_type_t::type_varchar);
REQUIRE(i.at(1).as<std::string>() == george.name);
REQUIRE(i.at(2).name() == "age");
REQUIRE(i.at(2).type() == matador::sql::data_type_t::type_long_long);
REQUIRE(i.at(2).as<long long>() == george.age);
}
// fetch person as record // fetch person as record
auto result_record = s.select<person>() result_record = s.select<person>()
.from("person") .from("person")
.where("id"_col == 7) .where("id"_col == 7)
.fetch_all(); .fetch_all();
@ -171,13 +189,13 @@ TEST_CASE("Execute select statement with where clause", "[session]") {
REQUIRE(i.size() == 3); REQUIRE(i.size() == 3);
REQUIRE(i.at(0).name() == "id"); REQUIRE(i.at(0).name() == "id");
REQUIRE(i.at(0).type() == data_type_t::type_long_long); REQUIRE(i.at(0).type() == data_type_t::type_long_long);
REQUIRE(i.at(0).value<long long>() == george.id); REQUIRE(i.at(0).as<long long>() == george.id);
REQUIRE(i.at(1).name() == "name"); REQUIRE(i.at(1).name() == "name");
REQUIRE(i.at(1).type() == data_type_t::type_varchar); REQUIRE(i.at(1).type() == data_type_t::type_varchar);
REQUIRE(i.at(1).value<std::string>() == george.name); REQUIRE(i.at(1).as<std::string>() == george.name);
REQUIRE(i.at(2).name() == "age"); REQUIRE(i.at(2).name() == "age");
REQUIRE(i.at(2).type() == matador::sql::data_type_t::type_long_long); REQUIRE(i.at(2).type() == matador::sql::data_type_t::type_long_long);
REQUIRE(i.at(2).value<long long>() == george.age); REQUIRE(i.at(2).as<long long>() == george.age);
} }
// fetch person as person // fetch person as person