added any type visitor and converter
This commit is contained in:
parent
171e8d36ce
commit
145c4dc0a3
|
|
@ -4,6 +4,8 @@ project(query)
|
|||
set(CMAKE_CXX_STANDARD 17)
|
||||
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)
|
||||
|
||||
find_package(ODBC REQUIRED)
|
||||
|
|
|
|||
44
README.md
44
README.md
|
|
@ -1,3 +1,45 @@
|
|||
# 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";
|
||||
}
|
||||
|
||||
```
|
||||
|
|
@ -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
|
||||
|
|
@ -33,6 +33,8 @@ public:
|
|||
|
||||
sql::record describe(const std::string& table) override;
|
||||
|
||||
bool exists(const std::string &table_name) override;
|
||||
|
||||
private:
|
||||
static int parse_result(void* param, int column_count, char** values, char** columns);
|
||||
|
||||
|
|
|
|||
|
|
@ -153,14 +153,27 @@ sql::record sqlite_connection::describe(const std::string& table)
|
|||
options = utils::constraints::NOT_NULL;
|
||||
}
|
||||
// f.default_value(res->column(4));
|
||||
// end = nullptr;
|
||||
// f.is_primary_key(strtoul(res->column(3), &end, 10) == 0);
|
||||
prototype.append({name, type, {options}});
|
||||
}
|
||||
|
||||
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"
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -2,6 +2,7 @@
|
|||
#define QUERY_COLUMN_HPP
|
||||
|
||||
#include "matador/sql/any_type.hpp"
|
||||
#include "matador/sql/any_type_to_visitor.hpp"
|
||||
#include "matador/sql/types.hpp"
|
||||
|
||||
#include "matador/utils/field_attributes.hpp"
|
||||
|
|
@ -11,28 +12,42 @@
|
|||
|
||||
namespace matador::sql {
|
||||
|
||||
namespace detail {
|
||||
|
||||
|
||||
}
|
||||
class column {
|
||||
public:
|
||||
explicit column(std::string name)
|
||||
: name_(std::move(name))
|
||||
, 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>
|
||||
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)
|
||||
{}
|
||||
|
||||
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);
|
||||
template<typename Type>
|
||||
|
||||
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::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]] size_t index() const;
|
||||
[[nodiscard]] const utils::field_attributes& attributes() const;
|
||||
[[nodiscard]] data_type_t type() const;
|
||||
[[nodiscard]] const std::string& ref_table() const;
|
||||
|
|
@ -43,10 +58,40 @@ public:
|
|||
return std::holds_alternative<Type>(value_);
|
||||
}
|
||||
|
||||
template< typename Type >
|
||||
std::optional<Type> value() const {
|
||||
[[nodiscard]] std::string str() 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_);
|
||||
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:
|
||||
|
|
@ -64,8 +109,9 @@ private:
|
|||
static const data_type_index data_type_index_;
|
||||
|
||||
std::string name_;
|
||||
size_t index_{};
|
||||
utils::field_attributes attributes_;
|
||||
data_type_t type_{};
|
||||
data_type_t type_{data_type_t::type_unknown};
|
||||
any_type value_;
|
||||
std::string ref_table_;
|
||||
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 >
|
||||
[[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 <>
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@ public:
|
|||
column generate(const char *id, Type &x, const std::string &ref_table, const std::string &ref_column)
|
||||
{
|
||||
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>
|
||||
|
|
|
|||
|
|
@ -30,6 +30,7 @@ public:
|
|||
[[nodiscard]] const connection_info& info() const;
|
||||
|
||||
[[nodiscard]] record describe(const std::string &table_name) const;
|
||||
bool exists(const std::string &table_name) const;
|
||||
|
||||
template<class Type>
|
||||
query_result<Type> fetch(const std::string &sql)
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@ public:
|
|||
virtual void prepare(const std::string &stmt) = 0;
|
||||
|
||||
virtual record describe(const std::string &table) = 0;
|
||||
virtual bool exists(const std::string &table_name) = 0;
|
||||
|
||||
protected:
|
||||
explicit connection_impl(const connection_info &info);
|
||||
|
|
|
|||
|
|
@ -57,6 +57,7 @@ enum class join_type_t {
|
|||
struct query
|
||||
{
|
||||
std::string sql;
|
||||
std::string table_name;
|
||||
record prototype;
|
||||
std::vector<any_type> host_vars;
|
||||
};
|
||||
|
|
@ -128,7 +129,7 @@ public:
|
|||
query_builder& offset(size_t count);
|
||||
query_builder& limit(size_t count);
|
||||
|
||||
std::string compile();
|
||||
query compile();
|
||||
|
||||
private:
|
||||
void transition_to(state_t next);
|
||||
|
|
|
|||
|
|
@ -25,12 +25,8 @@ public:
|
|||
query_intermediate(session &db, query_builder &query);
|
||||
|
||||
protected:
|
||||
session& db();
|
||||
query_builder& query();
|
||||
|
||||
private:
|
||||
session &db_;
|
||||
query_builder &query_;
|
||||
session &session_;
|
||||
query_builder &builder_;
|
||||
};
|
||||
|
||||
class query_execute_finish : public query_intermediate
|
||||
|
|
@ -127,10 +123,20 @@ public:
|
|||
query_order_by_intermediate order_by(const std::string &name);
|
||||
};
|
||||
|
||||
class query_select_intermediate : public query_intermediate
|
||||
class query_start_intermediate
|
||||
{
|
||||
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 = "");
|
||||
};
|
||||
|
|
@ -144,50 +150,50 @@ public:
|
|||
template<class Type>
|
||||
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:
|
||||
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);
|
||||
template<class Type>
|
||||
query_execute_finish table(const std::string &table_name)
|
||||
{
|
||||
const auto &info = repository_.attach<Type>(table_name/*, record{column_generator::generate<Type>(repository_)}*/);
|
||||
return {db(), query().table(table_name, info.prototype.columns())};
|
||||
const auto &info = repository_.attach<Type>(table_name);
|
||||
return {session_, builder_.table(table_name, info.prototype.columns())};
|
||||
}
|
||||
|
||||
private:
|
||||
table_repository &repository_;
|
||||
};
|
||||
|
||||
class query_drop_intermediate : query_intermediate
|
||||
class query_drop_intermediate : query_start_intermediate
|
||||
{
|
||||
public:
|
||||
using query_intermediate::query_intermediate;
|
||||
explicit query_drop_intermediate(session &s);
|
||||
|
||||
query_execute_finish table(const std::string &table);
|
||||
};
|
||||
|
||||
class query_insert_intermediate : public query_intermediate
|
||||
class query_insert_intermediate : public query_start_intermediate
|
||||
{
|
||||
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);
|
||||
template<class Type>
|
||||
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>
|
||||
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))};
|
||||
}
|
||||
};
|
||||
|
|
@ -208,16 +214,16 @@ public:
|
|||
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:
|
||||
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);
|
||||
template<class Type>
|
||||
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);
|
||||
};
|
||||
|
||||
class query_delete_intermediate : public query_intermediate
|
||||
class query_delete_intermediate : public query_start_intermediate
|
||||
{
|
||||
public:
|
||||
using query_intermediate::query_intermediate;
|
||||
explicit query_delete_intermediate(session &s);
|
||||
|
||||
query_delete_from_intermediate from(const std::string &table);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -12,6 +12,8 @@
|
|||
|
||||
namespace matador::sql {
|
||||
|
||||
class dialect;
|
||||
|
||||
class session
|
||||
{
|
||||
public:
|
||||
|
|
@ -22,14 +24,15 @@ public:
|
|||
template < class Type >
|
||||
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_insert_intermediate insert();
|
||||
query_update_intermediate update(const std::string &table);
|
||||
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;
|
||||
|
||||
template<typename Type>
|
||||
|
|
@ -40,6 +43,8 @@ public:
|
|||
|
||||
[[nodiscard]] const table_repository& tables() const;
|
||||
|
||||
const class dialect& dialect() const;
|
||||
|
||||
private:
|
||||
friend class query_select_finish;
|
||||
|
||||
|
|
@ -47,9 +52,10 @@ private:
|
|||
|
||||
private:
|
||||
connection_pool<connection> &pool_;
|
||||
query_builder query_;
|
||||
const class dialect &dialect_;
|
||||
|
||||
table_repository table_repository_;
|
||||
mutable std::unordered_map<std::string, record> prototypes_;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -180,7 +180,7 @@ template <> struct data_type_traits<const 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 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 const char* name() { return "char*"; }
|
||||
};
|
||||
|
|
@ -188,7 +188,7 @@ template <> struct data_type_traits<char*>
|
|||
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 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 const char* name() { return "std::string"; }
|
||||
};
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@ public:
|
|||
~field_attributes() = default;
|
||||
|
||||
[[nodiscard]] size_t size() const;
|
||||
void size(size_t size);
|
||||
[[nodiscard]] constraints options() const;
|
||||
|
||||
private:
|
||||
|
|
|
|||
|
|
@ -16,7 +16,8 @@ set(SQL_SOURCES
|
|||
sql/column_name_generator.cpp
|
||||
sql/key_value_generator.cpp
|
||||
sql/fk_value_extractor.cpp
|
||||
sql/table_repository.cpp)
|
||||
sql/table_repository.cpp
|
||||
sql/any_type_to_visitor.cpp)
|
||||
|
||||
set(SQL_HEADER
|
||||
../include/matador/sql/dialect.hpp
|
||||
|
|
@ -44,7 +45,8 @@ set(SQL_HEADER
|
|||
../include/matador/sql/key_value_generator.hpp
|
||||
../include/matador/sql/foreign.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
|
||||
../include/matador/utils/field_attributes.hpp
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -29,8 +29,9 @@ column::column(std::string name, data_type_t type, utils::field_attributes attr)
|
|||
, type_(type)
|
||||
, 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))
|
||||
, index_(index)
|
||||
, type_(type)
|
||||
, attributes_(attr)
|
||||
, ref_table_(std::move(ref_table))
|
||||
|
|
@ -41,6 +42,11 @@ const std::string &column::name() const
|
|||
return name_;
|
||||
}
|
||||
|
||||
size_t column::index() const
|
||||
{
|
||||
return index_;
|
||||
}
|
||||
|
||||
const utils::field_attributes &column::attributes() const
|
||||
{
|
||||
return attributes_;
|
||||
|
|
@ -61,6 +67,13 @@ const std::string &column::ref_column() const
|
|||
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)
|
||||
{
|
||||
return column(std::string(name, len));
|
||||
|
|
@ -86,6 +99,6 @@ column make_pk_column<std::string>( const std::string& name, size_t size )
|
|||
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)
|
||||
{
|
||||
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}};
|
||||
}
|
||||
}
|
||||
|
|
@ -68,6 +68,11 @@ record connection::describe(const std::string &table_name) const
|
|||
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
|
||||
{
|
||||
auto rec = connection_->describe("person");
|
||||
|
|
|
|||
|
|
@ -141,6 +141,7 @@ query_builder& query_builder::insert() {
|
|||
query_builder& query_builder::update(const std::string &table) {
|
||||
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));
|
||||
|
||||
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);
|
||||
|
||||
query_parts_.emplace_back(" " + dialect_.token_at(dialect::token_t::TABLE) + " " + dialect_.prepare_identifier(table) + " ");
|
||||
query_.table_name = table;
|
||||
|
||||
std::string result = "(";
|
||||
|
||||
|
|
@ -215,6 +217,7 @@ query_builder& query_builder::table(const std::string &table) {
|
|||
transition_to(state_t::QUERY_TABLE_DROP);
|
||||
|
||||
query_parts_.emplace_back(" " + dialect_.token_at(dialect::token_t::TABLE) + " " + dialect_.prepare_identifier(table));
|
||||
query_.table_name = table;
|
||||
|
||||
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);
|
||||
|
||||
query_parts_.emplace_back(" " + dialect_.token_at(dialect::token_t::INTO) + " " + dialect_.prepare_identifier(table) + " ");
|
||||
query_.table_name = table;
|
||||
|
||||
std::string result{"("};
|
||||
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);
|
||||
|
||||
query_parts_.emplace_back(" " + dialect_.token_at(dialect::token_t::FROM) + " " + dialect_.prepare_identifier(table) + (as.empty() ? "" : " " + as));
|
||||
query_.table_name = table;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
|
@ -398,12 +403,11 @@ query_builder& query_builder::limit( size_t count ) {
|
|||
return *this;
|
||||
}
|
||||
|
||||
std::string query_builder::compile() {
|
||||
std::string result;
|
||||
query query_builder::compile() {
|
||||
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)
|
||||
|
|
|
|||
|
|
@ -2,145 +2,173 @@
|
|||
#include "matador/sql/session.hpp"
|
||||
|
||||
namespace matador::sql {
|
||||
session &query_intermediate::db()
|
||||
{
|
||||
return db_;
|
||||
}
|
||||
|
||||
query_builder &query_intermediate::query()
|
||||
{
|
||||
return query_;
|
||||
}
|
||||
|
||||
query_result<record> query_select_finish::fetch_all()
|
||||
{
|
||||
return db().fetch(query().compile());
|
||||
return session_.fetch(builder_.compile());
|
||||
}
|
||||
|
||||
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()
|
||||
{
|
||||
return db().call_fetch(query().compile());
|
||||
return session_.call_fetch(builder_.compile().sql);
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
return {db(), query()};
|
||||
return {session_, builder_};
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
return {db(), query()};
|
||||
return {session_, builder_};
|
||||
}
|
||||
|
||||
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()
|
||||
{
|
||||
return {db(), query().asc()};
|
||||
return {session_, builder_.asc()};
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
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)
|
||||
{
|
||||
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)
|
||||
{
|
||||
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)
|
||||
{
|
||||
return {db(), query().order_by(name)};
|
||||
return {session_, builder_.order_by(name)};
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
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)
|
||||
{
|
||||
return {db(), query().into(table, column_names)};
|
||||
return {session_, builder_.into(table, column_names)};
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
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_intermediate(db, query)
|
||||
, repository_(repo) {}
|
||||
query_create_intermediate::query_create_intermediate(session &s, table_repository &repo)
|
||||
: query_start_intermediate(s)
|
||||
, repository_(repo) {
|
||||
builder_.create();
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
return {db(), query().table(table)};
|
||||
return {session_, builder_.table(table)};
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
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)
|
||||
{
|
||||
return {db(), query().set(columns)};
|
||||
return {session_, builder_.set(columns)};
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
return {db(), query().from(table)};
|
||||
return {session_, builder_.from(table)};
|
||||
}
|
||||
|
||||
query_start_intermediate::query_start_intermediate(session &s)
|
||||
: session_(s)
|
||||
, builder_(s.dialect())
|
||||
{}
|
||||
}
|
||||
|
|
@ -8,46 +8,58 @@ namespace matador::sql {
|
|||
|
||||
session::session(connection_pool<connection> &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()
|
||||
{
|
||||
return query_create_intermediate{*this, query_.create(), table_repository_};
|
||||
return query_create_intermediate{*this, table_repository_};
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
|
||||
return query_select_intermediate{*this, query_.select(column_names)};
|
||||
return query_select_intermediate{*this, column_names};
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
return query_update_intermediate{*this, query_.update(table)};
|
||||
return query_update_intermediate{*this, table};
|
||||
}
|
||||
|
||||
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 proto = res->prototype();
|
||||
return query_result<record>{std::move(res), [proto]() { return new record(proto); }};
|
||||
auto c = pool_.acquire();
|
||||
if (!c.valid()) {
|
||||
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 {
|
||||
auto c = pool_.acquire();
|
||||
if (!c.valid()) {
|
||||
|
|
@ -61,6 +73,11 @@ const table_repository& session::tables() const
|
|||
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
|
||||
{
|
||||
auto c = pool_.acquire();
|
||||
|
|
|
|||
|
|
@ -20,6 +20,11 @@ size_t field_attributes::size() const
|
|||
return size_;
|
||||
}
|
||||
|
||||
void field_attributes::size(size_t size)
|
||||
{
|
||||
size_ = size;
|
||||
}
|
||||
|
||||
constraints field_attributes::options() const
|
||||
{
|
||||
return options_;
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
@ -23,7 +23,9 @@ add_executable(tests builder.cpp
|
|||
models/supplier.hpp
|
||||
models/airplane.hpp
|
||||
models/flight.hpp
|
||||
models/person.hpp)
|
||||
models/person.hpp
|
||||
AnyTypeToVisitorTest.cpp
|
||||
ColumnTest.cpp)
|
||||
target_link_libraries(tests PRIVATE
|
||||
Catch2::Catch2WithMain
|
||||
matador
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
118
test/builder.cpp
118
test/builder.cpp
|
|
@ -8,120 +8,132 @@
|
|||
using namespace matador::sql;
|
||||
|
||||
TEST_CASE("Create table sql statement string", "[query]") {
|
||||
dialect d;
|
||||
query_builder query(d);
|
||||
auto sql = query.create().table("person", {
|
||||
make_pk_column<unsigned long>("id"),
|
||||
make_column<std::string>("name", 255),
|
||||
make_column<unsigned short>("age")
|
||||
}).compile();
|
||||
dialect d;
|
||||
query_builder query(d);
|
||||
auto q = query.create().table("person", {
|
||||
make_pk_column<unsigned long>("id"),
|
||||
make_column<std::string>("name", 255),
|
||||
make_column<unsigned short>("age")
|
||||
}).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", {
|
||||
make_pk_column<unsigned long>("id"),
|
||||
make_column<std::string>("name", 255),
|
||||
make_column<unsigned short>("age"),
|
||||
make_fk_column<unsigned long>("address", "address", "id")
|
||||
}).compile();
|
||||
q = query.create().table("person", {
|
||||
make_pk_column<unsigned long>("id"),
|
||||
make_column<std::string>("name", 255),
|
||||
make_column<unsigned short>("age"),
|
||||
make_fk_column<unsigned long>("address", "address", "id")
|
||||
}).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]") {
|
||||
dialect 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]") {
|
||||
dialect d;
|
||||
query_builder query(d);
|
||||
const auto sql = query.select({"id", "name", "age"}).from("person").compile();
|
||||
dialect d;
|
||||
query_builder query(d);
|
||||
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]") {
|
||||
dialect d;
|
||||
query_builder query(d);
|
||||
const auto sql = query.insert().into("person", {
|
||||
const auto q = query.insert().into("person", {
|
||||
"id", "name", "age"
|
||||
}).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]") {
|
||||
dialect d;
|
||||
query_builder query(d);
|
||||
const auto sql = query.update("person").set({
|
||||
const auto q = query.update("person").set({
|
||||
{"id", 7UL},
|
||||
{"name", "george"},
|
||||
{"age", 65U}
|
||||
}).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]") {
|
||||
dialect 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]") {
|
||||
dialect d;
|
||||
query_builder query(d);
|
||||
auto sql = query.select({"id", "name", "age"})
|
||||
.from("person")
|
||||
.where("id"_col == 8 && "age"_col > 50)
|
||||
.compile();
|
||||
auto q = query.select({"id", "name", "age"})
|
||||
.from("person")
|
||||
.where("id"_col == 8 && "age"_col > 50)
|
||||
.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"})
|
||||
.from("person")
|
||||
.where("id"_col == _ && "age"_col > 50)
|
||||
.compile();
|
||||
q = query.select({"id", "name", "age"})
|
||||
.from("person")
|
||||
.where("id"_col == _ && "age"_col > 50)
|
||||
.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]") {
|
||||
dialect d;
|
||||
query_builder query(d);
|
||||
const auto sql = query.select({"id", "name", "age"})
|
||||
.from("person")
|
||||
.order_by("name").asc()
|
||||
.compile();
|
||||
dialect d;
|
||||
query_builder query(d);
|
||||
const auto q = query.select({"id", "name", "age"})
|
||||
.from("person")
|
||||
.order_by("name").asc()
|
||||
.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]") {
|
||||
dialect d;
|
||||
query_builder query(d);
|
||||
const auto sql = query.select({"id", "name", "age"})
|
||||
.from("person")
|
||||
.group_by("age")
|
||||
.compile();
|
||||
dialect d;
|
||||
query_builder query(d);
|
||||
const auto q = query.select({"id", "name", "age"})
|
||||
.from("person")
|
||||
.group_by("age")
|
||||
.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]") {
|
||||
dialect d;
|
||||
query_builder query(d);
|
||||
const auto sql = query.select({"id", "name", "age"})
|
||||
const auto q = query.select({"id", "name", "age"})
|
||||
.from("person")
|
||||
.offset(10)
|
||||
.limit(20)
|
||||
.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");
|
||||
}
|
||||
|
|
@ -79,13 +79,13 @@ TEST_CASE("Execute insert record statement", "[session]") {
|
|||
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).value<long long>() == 7);
|
||||
REQUIRE(i.at(0).as<long long>() == 7);
|
||||
REQUIRE(i.at(1).name() == "name");
|
||||
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).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();
|
||||
|
|
@ -132,13 +132,13 @@ TEST_CASE("Execute update record statement", "[session]") {
|
|||
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).value<long long>() == 7);
|
||||
REQUIRE(i.at(0).as<long long>() == 7);
|
||||
REQUIRE(i.at(1).name() == "name");
|
||||
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).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();
|
||||
|
|
@ -161,23 +161,41 @@ TEST_CASE("Execute select statement with where clause", "[session]") {
|
|||
.execute();
|
||||
REQUIRE(res.first == 1);
|
||||
|
||||
// fetch person as record
|
||||
auto result_record = s.select<person>()
|
||||
// fetch specific columns as record
|
||||
auto result_record = s.select({"id", "name", "age"})
|
||||
.from("person")
|
||||
.where("id"_col == 7)
|
||||
.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).value<long long>() == george.id);
|
||||
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).value<std::string>() == george.name);
|
||||
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).value<long long>() == george.age);
|
||||
REQUIRE(i.at(2).as<long long>() == george.age);
|
||||
}
|
||||
|
||||
// fetch person as record
|
||||
result_record = s.select<person>()
|
||||
.from("person")
|
||||
.where("id"_col == 7)
|
||||
.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 person
|
||||
|
|
|
|||
Loading…
Reference in New Issue