Compare commits

..
12 Commits
36 changed files with 799 additions and 359 deletions
+1
View File
@@ -1,3 +1,4 @@
.idea
cmake-build-debug
Testing
debug
+4
View File
@@ -22,6 +22,10 @@ find_package(SQLite3 REQUIRED)
find_package(PostgreSQL REQUIRED)
find_package(MySQL REQUIRED)
message(STATUS "Found ODBC config ${ODBC_CONFIG}")
message(STATUS "Adding ODBC include directory: ${ODBC_INCLUDE_DIRS}")
message(STATUS "Adding ODBC libs: ${ODBC_LIBRARIES}")
message(STATUS "Found SQLite3 ${SQLite3_VERSION}")
message(STATUS "Adding SQLite3 include directory: ${SQLite3_INCLUDE_DIRS}")
message(STATUS "Adding SQLite3 libs: ${SQLite3_LIBRARIES}")
+1 -1
View File
@@ -3,7 +3,7 @@ Include(FetchContent)
FetchContent_Declare(
Catch2
GIT_REPOSITORY https://github.com/catchorg/Catch2.git
GIT_TAG v3.4.0 # or a later release
GIT_TAG v3.5.4 # or a later release
)
FetchContent_MakeAvailable(Catch2)
+1 -1
View File
@@ -3,7 +3,7 @@ Include(FetchContent)
FetchContent_Declare(
Catch2
GIT_REPOSITORY https://github.com/catchorg/Catch2.git
GIT_TAG v3.4.0 # or a later release
GIT_TAG v3.5.4 # or a later release
)
FetchContent_MakeAvailable(Catch2)
+1 -1
View File
@@ -3,7 +3,7 @@ Include(FetchContent)
FetchContent_Declare(
Catch2
GIT_REPOSITORY https://github.com/catchorg/Catch2.git
GIT_TAG v3.4.0 # or a later release
GIT_TAG v3.5.4 # or a later release
)
FetchContent_MakeAvailable(Catch2)
+11 -8
View File
@@ -5,6 +5,7 @@
#include "matador/sql/session.hpp"
#include "models/airplane.hpp"
#include "models/flight.hpp"
class SessionFixture
{
@@ -16,9 +17,8 @@ public:
~SessionFixture()
{
drop_table_if_exists("flight");
drop_table_if_exists("airplane");
drop_table_if_exists("person");
drop_table_if_exists("flights");
drop_table_if_exists("airplanes");
}
protected:
@@ -38,16 +38,19 @@ using namespace matador;
TEST_CASE_METHOD(SessionFixture, "Session relation test", "[session][relation]") {
using namespace matador;
ses.attach<test::airplane>("airplane");
ses.attach<test::airplane>("airplanes");
ses.attach<test::flight>("flights");
ses.create_schema();
ses.insert<test::airplane>(1, "Boeing", "A380");
auto plane = ses.insert<test::airplane>(1, "Boeing", "A380");
auto f = ses.insert<test::flight>(2, plane, "sully");
REQUIRE(true);
auto result = ses.find<test::flight>(2);
REQUIRE(result.is_ok());
}
TEST_CASE_METHOD(SessionFixture, "Use session to find object with id", "[session][find]") {
using namespace matador::test;
ses.attach<airplane>("airplane");
ses.attach<airplane>("airplanes");
ses.create_schema();
auto a380 = ses.insert<airplane>(1, "Boeing", "A380");
@@ -64,7 +67,7 @@ TEST_CASE_METHOD(SessionFixture, "Use session to find object with id", "[session
TEST_CASE_METHOD(SessionFixture, "Use session to find all objects", "[session][find]") {
using namespace matador::test;
ses.attach<airplane>("airplane");
ses.attach<airplane>("airplanes");
ses.create_schema();
std::vector<std::unique_ptr<airplane>> planes;
+3 -9
View File
@@ -52,10 +52,8 @@ static const matador::utils::enum_mapper<Color> color_enum({
{Color::Brown, "brown"}
});
namespace matador::sql {
template<>
struct data_type_traits<Color, void>
struct matador::sql::data_type_traits<Color, void>
{
inline static data_type_t builtin_type(std::size_t size)
{ return data_type_traits<std::string>::builtin_type(size); }
@@ -64,13 +62,12 @@ struct data_type_traits<Color, void>
{
std::string enum_string;
reader.read_value(id, index, enum_string, 64);
auto enum_opt = color_enum.to_enum(enum_string);
if (enum_opt) {
if (const auto enum_opt = color_enum.to_enum(enum_string)) {
value = enum_opt.value();
}
}
static any_type create_value(Color &value)
static any_type create_value(const Color &value)
{
return color_enum.to_string(value);
}
@@ -81,9 +78,6 @@ struct data_type_traits<Color, void>
}
};
}
TEST_CASE_METHOD(TypeTraitsTestFixture, "Special handling of attributes with type traits", "[typetraits]")
{
schema.attach<location>("location");
+196 -71
View File
@@ -5,14 +5,14 @@
#include "matador/sql/entity.hpp"
#include "matador/utils/access.hpp"
#include "matador/utils/enum_mapper.hpp"
#include "matador/sql/query_helper.hpp"
#include <iostream>
#include <string>
struct author
{
struct author {
unsigned long id{};
std::string first_name;
std::string last_name;
@@ -21,62 +21,174 @@ struct author
bool distinguished{false};
template<typename Operator>
void process(Operator &op)
{
void process( Operator& op ) {
namespace field = matador::utils::access;
field::primary_key(op, "id", id);
field::attribute(op, "first_name", first_name, 63);
field::attribute(op, "last_name", last_name, 63);
field::attribute(op, "date_of_birth", date_of_birth, 31);
field::attribute(op, "year_of_birth", year_of_birth);
field::attribute(op, "distinguished", distinguished);
field::primary_key( op, "id", id );
field::attribute( op, "first_name", first_name, 63 );
field::attribute( op, "last_name", last_name, 63 );
field::attribute( op, "date_of_birth", date_of_birth, 31 );
field::attribute( op, "year_of_birth", year_of_birth );
field::attribute( op, "distinguished", distinguished );
}
};
struct book
{
struct book {
unsigned long id{};
matador::sql::entity<author> book_author;
std::string title;
unsigned short published_in{};
template<typename Operator>
void process(Operator &op)
{
void process( Operator& op ) {
namespace field = matador::utils::access;
field::primary_key(op, "id", id);
field::attribute(op, "title", title, 511);
field::has_one(op, "author_id", book_author, matador::utils::default_foreign_attributes);
field::attribute(op, "published_in", published_in);
field::primary_key( op, "id", id );
field::attribute( op, "title", title, 511 );
field::has_one( op, "author_id", book_author, matador::utils::default_foreign_attributes );
field::attribute( op, "published_in", published_in );
}
};
QUERY_HELPER(authors, id, first_name, last_name, date_of_birth, year_of_birth, distinguished)
QUERY_HELPER(books, id, author_id, title, published_in)
struct payload {
unsigned long id{};
int main()
template<typename Operator>
void process( Operator& op ) {
namespace field = matador::utils::access;
field::primary_key( op, "id", id );
}
};
struct job {
enum class job_state {
Pending,
Running,
Succeeded,
Failed,
Canceled
};
enum class job_mode {
Foreground,
Background
};
unsigned long id{};
matador::sql::entity<payload> payload;
std::string type;
std::string description;
job_state state;
job_mode mode;
template<typename Operator>
void process( Operator& op ) {
namespace field = matador::utils::access;
field::primary_key( op, "id", id );
field::belongs_to( op, "payload", payload, matador::utils::default_foreign_attributes );
field::attribute( op, "type", type, 511 );
field::attribute( op, "description", description, 511 );
field::attribute( op, "state", state );
field::attribute( op, "mode", mode );
}
};
static const matador::utils::enum_mapper<job::job_state> job_state_enum({
{job::job_state::Pending, "Pending"},
{job::job_state::Running, "Running"},
{job::job_state::Succeeded, "Succeeded"},
{job::job_state::Failed, "Failed"},
{job::job_state::Canceled, "Canceled"}
});
static const matador::utils::enum_mapper<job::job_mode> job_mode_enum({
{job::job_mode::Foreground, "Foreground"},
{job::job_mode::Background, "Background"}
});
template<>
struct matador::sql::data_type_traits<job::job_state, void>
{
inline static data_type_t builtin_type(std::size_t size)
{ return data_type_traits<std::string>::builtin_type(size); }
static void read_value(query_result_reader &reader, const char *id, size_t index, job::job_state &value)
{
std::string enum_string;
reader.read_value(id, index, enum_string, 64);
if (const auto enum_opt = job_state_enum.to_enum(enum_string)) {
value = enum_opt.value();
}
}
static any_type create_value(const job::job_state &value)
{
return job_state_enum.to_string(value);
}
static void bind_value(parameter_binder &binder, size_t index, job::job_state &value)
{
binder.bind(index, job_state_enum.to_string(value));
}
};
template<>
struct matador::sql::data_type_traits<job::job_mode, void>
{
inline static data_type_t builtin_type(std::size_t size)
{ return data_type_traits<std::string>::builtin_type(size); }
static void read_value(query_result_reader &reader, const char *id, size_t index, job::job_mode &value)
{
std::string enum_string;
reader.read_value(id, index, enum_string, 64);
if (const auto enum_opt = job_mode_enum.to_enum(enum_string)) {
value = enum_opt.value();
}
}
static any_type create_value(const job::job_mode &value)
{
return job_mode_enum.to_string(value);
}
static void bind_value(parameter_binder &binder, size_t index, job::job_mode &value)
{
binder.bind(index, job_mode_enum.to_string(value));
}
};
QUERY_HELPER( authors, id, first_name, last_name, date_of_birth, year_of_birth, distinguished )
QUERY_HELPER( books, id, author_id, title, published_in )
QUERY_HELPER( job, id, payload, type, description, state, mode )
QUERY_HELPER( payload, id )
QUERY_HELPER( temporary_table, id );
int main() {
using namespace matador::sql;
using namespace matador;
const std::string env_var{"MATADOR_BACKENDS_PATH"};
std::string dns{"sqlite://demo.db"};
schema s("main");
s.attach<author>("authors");
s.attach<book>("books");
schema s( "main" );
s.attach<author>( "authors" );
s.attach<book>( "books" );
connection c(dns);
connection c( dns );
c.open();
s.create( c );
auto create_authors_sql = c.query(s)
auto create_authors_sql = c.query( s )
.create()
.table<author>(qh::authors)
.table<author>( qh::authors )
.execute();
c.query(s)
c.query( s )
.create()
.table<book>(qh::books)
.table<book>( qh::books )
.execute();
std::cout << "SQL: " << create_authors_sql << "\n";
@@ -88,68 +200,62 @@ int main()
mc.date_of_birth = "19.8.1954";
mc.year_of_birth = 1954;
mc.distinguished = true;
auto insert_authors_sql = c.query(s)
auto insert_authors_sql = c.query( s )
.insert()
.into(qh::authors)
.values(mc)
.into( qh::authors )
.values( mc )
.execute();
std::cout << "SQL: " << insert_authors_sql << "\n";
auto result = c.query(s)
.select(qh::authors.columns)
.from(qh::authors)
auto result = c.query( s )
.select( qh::authors.columns )
.from( qh::authors )
.fetch_all();
for (const auto &row: result) {
std::cout << "Author " << row.at(qh::authors.first_name) << "\n";
}
for (const auto& row: result) { std::cout << "Author " << row.at( qh::authors.first_name ) << "\n"; }
auto update_authors_sql = c.query(s)
.update(qh::authors)
.set({{qh::authors.first_name, "Stephen"},
{qh::authors.last_name, "King"}})
.where(qh::authors.last_name == "Crichton")
auto update_authors_sql = c.query( s )
.update( qh::authors )
.set( {{qh::authors.first_name, "Stephen"},
{qh::authors.last_name, "King"}} )
.where( qh::authors.last_name == "Crichton" )
.execute();
std::cout << "SQL: " << update_authors_sql << "\n";
auto authors = c.query(s)
.select(qh::authors.columns)
.from(qh::authors)
auto authors = c.query( s )
.select( qh::authors.columns )
.from( qh::authors )
.fetch_all<author>();
for (const auto &a: authors) {
std::cout << "Author " << a.first_name << "\n";
}
for (const auto& a: authors) { std::cout << "Author " << a.first_name << "\n"; }
c.query(s)
c.query( s )
.insert()
.into(qh::books)
.values({2, "It", mc.id, 1980})
.into( qh::books )
.values( {2, "It", mc.id, 1980} )
.execute();
c.query(s)
c.query( s )
.insert()
.into(qh::books)
.values({3, "Misery", mc.id, 1984})
.into( qh::books )
.values( {3, "Misery", mc.id, 1984} )
.execute();
auto select_books_sql = c.query(s)
.select(qh::books.columns, {qh::authors.last_name})
.from(qh::books)
.join_left(qh::authors)
.on(qh::books.author_id == qh::authors.id)
.where(qh::books.published_in < 2008 && qh::authors.last_name == "King")
.group_by(qh::books.published_in)
.order_by(qh::books.title).asc()
.limit(5)
.offset(2)
auto select_books_sql = c.query( s )
.select( qh::books.columns, {qh::authors.last_name} )
.from( qh::books )
.join_left( qh::authors )
.on( qh::books.author_id == qh::authors.id )
.where( qh::books.published_in < 2008 && qh::authors.last_name == "King" )
.group_by( qh::books.published_in )
.order_by( qh::books.title ).asc()
.limit( 5 )
.offset( 2 )
.fetch_all();
for (const auto &r: select_books_sql) {
std::cout << "R: " << r.at(qh::books.title) << ", " << r.at(qh::authors.last_name) << "\n";
}
for (const auto& r: select_books_sql) { std::cout << "R: " << r.at( qh::books.title ) << ", " << r.at( qh::authors.last_name ) << "\n"; }
// SELECT book.title, book.id, book.author_id, book.published_in, author.name
// FROM book
// INNER JOIN author ON book.author_id = author.id
@@ -157,14 +263,33 @@ int main()
// ORDER BY "book.title" ASC
// OFFSET 2 LIMIT 5
c.query(s).drop().table(qh::books).execute();
c.query( s ).drop().table( qh::books ).execute();
auto drop_authors_sql = c.query(s)
auto drop_authors_sql = c.query( s )
.drop()
.table(qh::authors)
.table( qh::authors )
.execute();
std::cout << "SQL: " << drop_authors_sql << "\n";
auto res = c.query( s )
.select( {qh::payload.id} )
.from( qh::payload )
.join_left( qh::job )
.on( qh::job.payload == qh::payload.id )
.where(
in( qh::payload.id, c.query( s )
.select( {qh::job.state} )
.from( qh::job )
.where( qh::job.state == job::job_state::Running )
) &&
in( qh::payload.id, c.query( s )
.select( {qh::temporary_table.id} )
.from( qh::temporary_table ) )
)
.build();
// .fetch_value<unsigned long>();
std::cout << "SQL: " << res.sql << "\n";
return 0;
}
+35 -7
View File
@@ -1,6 +1,8 @@
#ifndef QUERY_CONDITION_HPP
#define QUERY_CONDITION_HPP
#include "matador/sql/any_type_to_string_visitor.hpp"
#include "matador/sql/query_result.hpp"
#include "matador/sql/basic_condition.hpp"
#include "matador/sql/dialect.hpp"
#include "matador/sql/placeholder.hpp"
@@ -26,6 +28,8 @@ namespace matador::sql {
/// @cond MATADOR_DEV
class query_select;
template<class L, class R, class Enabled = void>
class condition;
@@ -43,6 +47,7 @@ public:
template<class T>
class condition<column, T, typename std::enable_if<
std::is_scalar<T>::value &&
!std::is_enum<T>::value &&
!std::is_same<std::string, T>::value &&
!std::is_same<const char*, T>::value>::type> : public basic_column_condition
{
@@ -82,10 +87,32 @@ public:
};
template<class T>
class condition<T, column, typename std::enable_if<
std::is_scalar<T>::value &&
!std::is_same<std::string, T>::value &&
!std::is_same<const char*, T>::value>::type> : public basic_column_condition
class condition<column, T, std::enable_if_t<std::is_enum_v<T>>> final : public basic_column_condition
{
public:
condition(const column &fld, basic_condition::operand_t op, T val)
: basic_column_condition(fld, op)
, value(val)
{ }
T value;
std::string evaluate(const dialect &d, query_context &query) const override
{
auto at = data_type_traits<T>::create_value(value);
any_type_to_string_visitor value_to_string(d, query);
std::visit(value_to_string, at);
return value_to_string.result + " " + operand + " " + d.prepare_identifier(field_);
}
};
template<class T>
class condition<T, column, std::enable_if_t<
std::is_scalar_v<T> &&
!std::is_same_v<std::string, T> &&
!std::is_same_v<const char*, T>>> final : public basic_column_condition
{
public:
condition(T val, basic_condition::operand_t op, const column &fld)
@@ -215,7 +242,7 @@ public:
* @param op Operand of the condition
* @param q The query to be evaluated to the IN arguments
*/
condition(column col, basic_condition::operand_t op, query_context &q);
condition(column col, basic_condition::operand_t op, const query_context &q);
/**
* @brief Evaluates the condition
@@ -229,7 +256,7 @@ public:
std::string evaluate(const dialect &d, query_context &query) const override;
private:
query_context &query_;
query_context query_;
};
/**
@@ -415,7 +442,8 @@ condition<column, std::initializer_list<V>> in(const column &col, std::initializ
* @param q The query to be executes as sub select
* @return The condition object
*/
condition<column, query_context> in(const column &col, query_context &&q);
condition<column, query_context> in(const column &col, const query_context &q);
condition<column, query_context> in(const column &col, const query_select &q);
/**
* @brief Creates a between condition.
+1
View File
@@ -7,6 +7,7 @@
#include <mutex>
#include <string>
#include <optional>
#include <condition_variable>
#include <thread>
#include <unordered_map>
+22 -20
View File
@@ -59,7 +59,7 @@ template <> struct data_type_traits<nullptr_t, void>
static void read_value(query_result_reader &reader, const char *id, size_t index, nullptr_t &/*value*/);
static void bind_value(parameter_binder &binder, size_t index, nullptr_t &/*value*/);
static void bind_result_value(result_parameter_binder &binder, size_t index, nullptr_t &/*value*/);
inline static any_type create_value(char &value) { return value; }
inline static any_type create_value(const char &value) { return value; }
};
template <> struct data_type_traits<char, void>
@@ -68,7 +68,7 @@ template <> struct data_type_traits<char, void>
static void read_value(query_result_reader &reader, const char *id, size_t index, char &value);
static void bind_value(parameter_binder &binder, size_t index, char &value);
static void bind_result_value(result_parameter_binder &binder, size_t index, char &value);
inline static any_type create_value(char &value) { return value; }
inline static any_type create_value(const char &value) { return value; }
};
template <> struct data_type_traits<short, void>
@@ -77,7 +77,7 @@ template <> struct data_type_traits<short, void>
static void read_value(query_result_reader &reader, const char *id, size_t index, short &value);
static void bind_value(parameter_binder &binder, size_t index, short &value);
static void bind_result_value(result_parameter_binder &binder, size_t index, short &value);
inline static any_type create_value(short &value) { return value; }
inline static any_type create_value(const short &value) { return value; }
};
template <> struct data_type_traits<int, void>
@@ -86,7 +86,7 @@ template <> struct data_type_traits<int, void>
static void read_value(query_result_reader &reader, const char *id, size_t index, int &value);
static void bind_value(parameter_binder &binder, size_t index, int &value);
static void bind_result_value(result_parameter_binder &binder, size_t index, int &value);
inline static any_type create_value(int &value) { return value; }
inline static any_type create_value(const int &value) { return value; }
};
template <> struct data_type_traits<long, void>
@@ -95,7 +95,7 @@ template <> struct data_type_traits<long, void>
static void read_value(query_result_reader &reader, const char *id, size_t index, long &value);
static void bind_value(parameter_binder &binder, size_t index, long &value);
static void bind_result_value(result_parameter_binder &binder, size_t index, long &value);
inline static any_type create_value(long &value) { return value; }
inline static any_type create_value(const long &value) { return value; }
};
template <> struct data_type_traits<long long, void>
@@ -104,7 +104,7 @@ template <> struct data_type_traits<long long, void>
static void read_value(query_result_reader &reader, const char *id, size_t index, long long &value);
static void bind_value(parameter_binder &binder, size_t index, long long &value);
static void bind_result_value(result_parameter_binder &binder, size_t index, long long &value);
inline static any_type create_value(long long &value) { return value; }
inline static any_type create_value(const long long &value) { return value; }
};
template <> struct data_type_traits<unsigned char, void>
@@ -113,7 +113,7 @@ template <> struct data_type_traits<unsigned char, void>
static void read_value(query_result_reader &reader, const char *id, size_t index, unsigned char &value);
static void bind_value(parameter_binder &binder, size_t index, unsigned char &value);
static void bind_result_value(result_parameter_binder &binder, size_t index, unsigned char &value);
inline static any_type create_value(unsigned char &value) { return value; }
inline static any_type create_value(const unsigned char &value) { return value; }
};
template <> struct data_type_traits<unsigned short, void>
@@ -122,7 +122,7 @@ template <> struct data_type_traits<unsigned short, void>
static void read_value(query_result_reader &reader, const char *id, size_t index, unsigned short &value);
static void bind_value(parameter_binder &binder, size_t index, unsigned short &value);
static void bind_result_value(result_parameter_binder &binder, size_t index, unsigned short &value);
inline static any_type create_value(unsigned short &value) { return value; }
inline static any_type create_value(const unsigned short &value) { return value; }
};
template <> struct data_type_traits<unsigned int, void>
@@ -131,7 +131,7 @@ template <> struct data_type_traits<unsigned int, void>
static void read_value(query_result_reader &reader, const char *id, size_t index, unsigned int &value);
static void bind_value(parameter_binder &binder, size_t index, unsigned int &value);
static void bind_result_value(result_parameter_binder &binder, size_t index, unsigned int &value);
inline static any_type create_value(unsigned int &value) { return value; }
inline static any_type create_value(const unsigned int &value) { return value; }
};
template <> struct data_type_traits<unsigned long, void>
@@ -140,7 +140,7 @@ template <> struct data_type_traits<unsigned long, void>
static void read_value(query_result_reader &reader, const char *id, size_t index, unsigned long &value);
static void bind_value(parameter_binder &binder, size_t index, unsigned long &value);
static void bind_result_value(result_parameter_binder &binder, size_t index, unsigned long &value);
inline static any_type create_value(unsigned long &value) { return value; }
inline static any_type create_value(const unsigned long &value) { return value; }
};
template <> struct data_type_traits<unsigned long long, void>
@@ -149,7 +149,7 @@ template <> struct data_type_traits<unsigned long long, void>
static void read_value(query_result_reader &reader, const char *id, size_t index, unsigned long long &value);
static void bind_value(parameter_binder &binder, size_t index, unsigned long long &value);
static void bind_result_value(result_parameter_binder &binder, size_t index, unsigned long long &value);
inline static any_type create_value(unsigned long long &value) { return value; }
inline static any_type create_value(const unsigned long long &value) { return value; }
};
template <> struct data_type_traits<bool, void>
@@ -158,7 +158,7 @@ template <> struct data_type_traits<bool, void>
static void read_value(query_result_reader &reader, const char *id, size_t index, bool &value);
static void bind_value(parameter_binder &binder, size_t index, bool &value);
static void bind_result_value(result_parameter_binder &binder, size_t index, bool &value);
inline static any_type create_value(bool &value) { return value; }
inline static any_type create_value(const bool &value) { return value; }
};
template <> struct data_type_traits<float, void>
@@ -167,7 +167,7 @@ template <> struct data_type_traits<float, void>
static void read_value(query_result_reader &reader, const char *id, size_t index, float &value);
static void bind_result_value(result_parameter_binder &binder, size_t index, float &value);
static void bind_value(parameter_binder &binder, size_t index, float &value);
inline static any_type create_value(float &value) { return value; }
inline static any_type create_value(const float &value) { return value; }
};
template <> struct data_type_traits<double, void>
@@ -176,7 +176,7 @@ template <> struct data_type_traits<double, void>
static void read_value(query_result_reader &reader, const char *id, size_t index, double &value);
static void bind_result_value(result_parameter_binder &binder, size_t index, double &value);
static void bind_value(parameter_binder &binder, size_t index, double &value);
inline static any_type create_value(double &value) { return value; }
inline static any_type create_value(const double &value) { return value; }
};
template <> struct data_type_traits<const char*, void>
@@ -203,7 +203,7 @@ template <> struct data_type_traits<std::string, void>
static void read_value(query_result_reader &reader, const char *id, size_t index, std::string &value, size_t size);
static void bind_value(parameter_binder &binder, size_t index, std::string &value, size_t size = 0);
static void bind_result_value(result_parameter_binder &binder, size_t index, std::string &value, size_t size = 0);
inline static any_type create_value(std::string &value) { return value; }
inline static any_type create_value(const std::string &value) { return value; }
};
template <> struct data_type_traits<utils::blob, void>
@@ -212,7 +212,7 @@ template <> struct data_type_traits<utils::blob, void>
static void read_value(query_result_reader &reader, const char *id, size_t index, utils::blob &value);
static void bind_value(parameter_binder &binder, size_t index, utils::blob &value);
static void bind_result_value(result_parameter_binder &binder, size_t index, utils::blob &value);
inline static any_type create_value(utils::blob &value) { return value; }
inline static any_type create_value(const utils::blob &value) { return value; }
};
//template <> struct data_type_traits<matador::date>
@@ -232,7 +232,7 @@ template <> struct data_type_traits<utils::blob, void>
//};
template < typename EnumType >
struct data_type_traits<EnumType, typename std::enable_if<std::is_enum<EnumType>::value>::type>
struct data_type_traits<EnumType, std::enable_if_t<std::is_enum_v<EnumType>>>
{
inline static data_type_t builtin_type(std::size_t /*size*/) { return data_type_t::type_int; }
static void read_value(query_result_reader &reader, const char *id, size_t index, EnumType &value)
@@ -241,13 +241,15 @@ struct data_type_traits<EnumType, typename std::enable_if<std::is_enum<EnumType>
}
static void bind_value(parameter_binder &binder, size_t index, EnumType &value)
{
data_type_traits<int>::bind_value(binder, index, (int&)value);
data_type_traits<int>::bind_value(binder, index, static_cast<int&>(value));
}
static void bind_result_value(result_parameter_binder &binder, size_t index, EnumType &value)
{
data_type_traits<int>::bind_result_value(binder, index, (int&)value);
data_type_traits<int>::bind_result_value(binder, index, static_cast<int&>(value));
}
static any_type create_value(const EnumType &value) {
return static_cast<int>(value);
}
inline static any_type create_value(EnumType &value) { return (int)value; }
};
/// @endcond
+78 -8
View File
@@ -14,14 +14,53 @@
namespace matador::sql {
struct join_data
struct join_columns
{
table join_table;
std::unique_ptr<basic_condition> condition;
std::string join_column;
std::string inverse_join_column;
};
class join_column_collector
{
public:
template<class Type>
join_columns collect()
{
join_columns_ = {};
Type obj;
matador::utils::access::process(*this, obj);
return join_columns_;
}
template < class V >
void on_primary_key(const char * /*id*/, V &, typename std::enable_if<std::is_integral<V>::value && !std::is_same<bool, V>::value>::type* = 0) {}
void on_primary_key(const char * /*id*/, std::string &, size_t) {}
void on_revision(const char * /*id*/, unsigned long long &/*rev*/) {}
template<typename Type>
void on_attribute(const char * /*id*/, Type &, const utils::field_attributes &/*attr*/ = utils::null_attributes) {}
template<class Pointer>
void on_belongs_to(const char * /*id*/, Pointer &obj, const utils::foreign_attributes &attr) {}
template<class Pointer>
void on_has_one(const char * /*id*/, Pointer &obj, const utils::foreign_attributes &attr) {}
template<class ContainerType>
void on_has_many(ContainerType &, const char *join_column, const utils::foreign_attributes &attr) {}
template<class ContainerType>
void on_has_many_to_many(const char * /*id*/, ContainerType &/*c*/, const char *join_column, const char *inverse_join_column, const utils::foreign_attributes &/*attr*/)
{
join_columns_.join_column = join_column;
join_columns_.inverse_join_column = inverse_join_column;
}
template<class ContainerType>
void on_has_many_to_many(const char * /*id*/, ContainerType &/*c*/, const utils::foreign_attributes &/*attr*/) {}
private:
join_columns join_columns_;
};
struct entity_query_data {
std::string root_table_name;
std::string pk_column_;
std::vector<column> columns;
std::vector<join_data> joins;
std::unique_ptr<basic_condition> where_clause;
@@ -97,8 +136,14 @@ public:
void on_primary_key(const char *id, V &, typename std::enable_if<std::is_integral<V>::value && !std::is_same<bool, V>::value>::type* = 0)
{
push(id);
if (is_root_entity() && pk_.is_integer()) {
if (!is_root_entity()) {
return;
}
if (pk_.is_null()) {
entity_query_data_.pk_column_ = id;
} else if (pk_.is_integer()) {
entity_query_data_.where_clause = make_condition(column{table_info_stack_.top().name, id, ""} == *pk_.as<V>());
entity_query_data_.pk_column_ = id;
}
}
@@ -148,7 +193,9 @@ public:
template<class ContainerType>
void on_has_many_to_many(const char *id, ContainerType &c, const char *join_column, const char *inverse_join_column, const utils::foreign_attributes &attr)
{
if (attr.fetch() == utils::fetch_type::EAGER) {
if (attr.fetch() != utils::fetch_type::EAGER) {
return;
}
const auto info = schema_.info<typename ContainerType::value_type::value_type>();
if (!info) {
throw query_builder_exception{query_build_error::UnknownType};
@@ -164,12 +211,34 @@ public:
}
append_join({table_info_stack_.top().name, table_info_stack_.top().prototype.primary_key()->name()}, {id, join_column});
append_join({info->name, pk->name()}, {id, inverse_join_column});
}
append_join({id, inverse_join_column}, {info->name, pk->name()});
}
template<class ContainerType>
void on_has_many_to_many(const char *id, ContainerType &c, const utils::foreign_attributes &/*attr*/) {}
void on_has_many_to_many(const char *id, ContainerType &c, const utils::foreign_attributes &attr)
{
if (attr.fetch() != utils::fetch_type::EAGER) {
return;
}
const auto info = schema_.info<typename ContainerType::value_type::value_type>();
if (!info) {
throw query_builder_exception{query_build_error::UnknownType};
}
table_info_stack_.push(info.value());
typename ContainerType::value_type::value_type obj;
matador::utils::access::process(*this , obj);
table_info_stack_.pop();
auto pk = info->prototype.primary_key();
if (!pk) {
throw query_builder_exception{query_build_error::MissingPrimaryKey};
}
const auto join_columns = join_column_collector_.collect<typename ContainerType::value_type::value_type>();
append_join({table_info_stack_.top().name, table_info_stack_.top().prototype.primary_key()->name()}, {id, join_columns.inverse_join_column});
append_join({id, join_columns.join_column}, {info->name, pk->name()});
}
private:
template<class Pointer>
@@ -184,6 +253,7 @@ private:
const schema &schema_;
entity_query_data entity_query_data_;
int column_index{0};
join_column_collector join_column_collector_;
};
template<class Pointer>
+12 -23
View File
@@ -150,12 +150,21 @@ public:
class query_join_intermediate;
class query_on_intermediate : public query_select
struct join_data
{
table join_table;
std::unique_ptr<basic_condition> condition;
};
class query_from_intermediate : public query_select
{
public:
using query_select::query_select;
query_join_intermediate join_left(const table &t);
query_from_intermediate join_left(join_data &data);
query_from_intermediate join_left(std::vector<join_data> &data_vector);
template<class Condition>
query_where_intermediate where(const Condition &cond)
{
@@ -172,6 +181,8 @@ private:
query_where_intermediate where_clause(std::unique_ptr<basic_condition> &&cond);
};
using query_on_intermediate = query_from_intermediate;
class query_join_intermediate : public query_intermediate
{
public:
@@ -191,28 +202,6 @@ private:
query_on_intermediate on_clause(std::unique_ptr<basic_condition> &&cond);
};
class query_from_intermediate : public query_select
{
public:
using query_select::query_select;
query_join_intermediate join_left(const table &t);
template<class Condition>
query_where_intermediate where(const Condition &cond)
{
return where_clause(std::make_unique<Condition>(std::move(cond)));
}
query_where_intermediate where(std::unique_ptr<basic_condition> &&cond)
{
return where_clause(std::move(cond));
}
query_group_by_intermediate group_by(const column &col);
query_order_by_intermediate order_by(const column &col);
private:
query_where_intermediate where_clause(std::unique_ptr<basic_condition> &&cond);
};
class query_start_intermediate : public basic_query_intermediate
{
public:
+6 -5
View File
@@ -15,8 +15,9 @@ struct column;
class record
{
private:
using field_by_index = std::vector<field>;
using field_index_pair = std::pair<std::reference_wrapper<field>, field_by_index::difference_type>;
using field_ref = std::reference_wrapper<field>;
using field_by_index = std::vector<field_ref>;
using field_index_pair = std::pair<field, field_by_index::difference_type>;
using field_by_name_map = std::unordered_map<std::string, field_index_pair>;
public:
@@ -36,13 +37,13 @@ public:
void process(Operator &op)
{
for(auto &f : fields_) {
f.process(op);
f.get().process(op);
}
}
void append(field col);
void append(const field &col);
[[nodiscard]] const std::vector<field>& columns() const;
[[nodiscard]] const std::vector<field_ref>& columns() const;
[[nodiscard]] const field& at(const column &col) const;
[[nodiscard]] const field& at(size_t index) const;
+2
View File
@@ -36,6 +36,8 @@ public:
[[nodiscard]] std::string name() const;
void create(connection &c);
template<typename Type>
const table_info& attach(const std::string &table_name)
{
+1 -1
View File
@@ -150,7 +150,7 @@ entity<Type> session::insert(Type *obj)
}
c->query(*schema_)
.insert()
.into(info->name, column_generator::generate<Type>(*schema_))
.into(info->name, column_generator::generate<Type>(*schema_, true))
.values(*obj)
.execute();
+10 -1
View File
@@ -14,13 +14,22 @@ struct table
table(const char *name, std::string as = "") // NOLINT(*-explicit-constructor)
: name(name), alias(std::move(as)) {}
table(std::string name, std::string as = "") // NOLINT(*-explicit-constructor)
: name(std::move(name)), alias(std::move(as)) {}
: name(std::move(name))
, alias(std::move(as)) {}
table(std::string name, std::string as, const std::vector<column> &columns)
: name(std::move(name))
, alias(std::move(as))
, columns(columns) {}
table& as(const std::string &a) {
alias = a;
return *this;
}
[[nodiscard]] table as(const std::string &a) const {
return { name, a, columns };
}
std::string name;
std::string alias;
+1
View File
@@ -2,6 +2,7 @@
#include "matador/sql/noop_connection.hpp"
#include "matador/sql/dialect_builder.hpp"
#include <algorithm>
#include <stdexcept>
namespace matador::sql {
+20 -2
View File
@@ -1,5 +1,7 @@
#include "matador/sql/condition.hpp"
#include "matador/sql/query_intermediates.hpp"
namespace matador::sql {
condition<column, placeholder, std::enable_if<true>::type>::condition(const column &fld, basic_condition::operand_t op, const placeholder &val)
@@ -12,17 +14,33 @@ std::string condition<column, placeholder, std::enable_if<true>::type>::evaluate
return d.prepare_identifier(field_) + " " + operand + " " + d.next_placeholder(query.bind_vars);
}
condition<column, query_context>::condition(column col, basic_condition::operand_t op, query_context &q)
condition<column, query_context>::condition(column col, basic_condition::operand_t op, const query_context &q)
: basic_column_condition(std::move(col), op), query_(q)
{}
std::string condition<column, query_context>::evaluate(const dialect &d, query_context &query) const
{
std::string result(d.prepare_identifier(field_) + " " + operand + " (");
result += (")");
result += query_.sql + (")");
return result;
}
condition<column, query_context> in( const column& col, const query_context &q ) {
return {col, basic_condition::operand_t::IN_LIST, q};
}
condition<column, query_context> in( const column& col, const query_select& q ) {
return in(col, q.build());
}
condition<column, std::string> like( const column& col, const std::string& val ) {
return {col, basic_condition::operand_t::LIKE, val};
}
condition<column, query_context> equals( const column& col, query_context& q ) {
return {col, basic_condition::operand_t::EQUAL, q};
}
condition<column, column> operator==(const column &a, const column &b)
{
return {a, basic_condition::operand_t::EQUAL, b};
+1 -1
View File
@@ -91,7 +91,7 @@ size_t connection::execute(const std::string &sql) const
return connection_->execute(sql);
}
sql::query connection::query(const sql::schema &schema) const
sql::query connection::query(const schema &schema) const
{
return sql::query(*const_cast<connection*>(this), schema);
}
+2 -2
View File
@@ -51,8 +51,8 @@ std::string dialect::prepare_literal(const std::string &str) const
void dialect::quote_identifier(std::string &str) const
{
str.insert(0, token_at(token_t::START_QUOTE));
str += token_at(token_t::END_QUOTE);
// str.insert(0, token_at(token_t::START_QUOTE));
// str += token_at(token_t::END_QUOTE);
}
void dialect::escape_quotes_in_identifier(std::string &str) const
+1 -1
View File
@@ -30,7 +30,7 @@ void entity_query_builder::push(const std::string &column_name)
void entity_query_builder::append_join(const column &left, const column &right)
{
entity_query_data_.joins.push_back({
{ left.table },
{ right.table },
make_condition(left == right)
});
}
+1 -1
View File
@@ -54,7 +54,7 @@ void query_compiler::visit(query_select_part &select_part)
void query_compiler::visit(query_from_part &from_part)
{
query_.table = from_part.table();
if (dialect_.default_schema_name().empty()) {
if (!dialect_.default_schema_name().empty()) {
query_.sql += " " + dialect_.token_at(dialect::token_t::FROM) +
" " + dialect_.prepare_identifier(from_part.table().name) +
(from_part.table().alias.empty() ? "" : " AS " +
+18 -24
View File
@@ -85,7 +85,9 @@ query_order_direction_intermediate query_order_by_intermediate::desc()
query_where_intermediate query_from_intermediate::where_clause(std::unique_ptr<basic_condition> &&cond)
{
if (cond) {
data_->parts.push_back(std::make_unique<query_where_part>(std::move(cond)));
}
return {connection_, schema_, data_};
}
@@ -113,30 +115,6 @@ query_order_by_intermediate query_where_intermediate::order_by(const column &col
return {connection_, schema_, data_};
}
query_join_intermediate query_on_intermediate::join_left(const table &t)
{
data_->parts.push_back(std::make_unique<query_join_part>(t));
return {connection_, schema_, data_};
}
query_where_intermediate query_on_intermediate::where_clause(std::unique_ptr<basic_condition> &&cond)
{
data_->parts.push_back(std::make_unique<query_where_part>(std::move(cond)));
return {connection_, schema_, data_};
}
query_group_by_intermediate query_on_intermediate::group_by(const column &col)
{
data_->parts.push_back(std::make_unique<query_group_by_part>(col));
return {connection_, schema_, data_};
}
query_order_by_intermediate query_on_intermediate::order_by(const column &col)
{
data_->parts.push_back(std::make_unique<query_order_by_part>(col));
return {connection_, schema_, data_};
}
query_on_intermediate query_join_intermediate::on_clause(std::unique_ptr<basic_condition> &&cond)
{
data_->parts.push_back(std::make_unique<query_on_part>(std::move(cond)));
@@ -149,6 +127,22 @@ query_join_intermediate query_from_intermediate::join_left(const table &t)
return {connection_, schema_, data_};
}
query_from_intermediate query_from_intermediate::join_left(join_data &data)
{
data_->parts.push_back(std::make_unique<query_join_part>(data.join_table));
data_->parts.push_back(std::make_unique<query_on_part>(std::move(data.condition)));
return {connection_, schema_, data_};
}
query_from_intermediate query_from_intermediate::join_left(std::vector<join_data> &data_vector)
{
for (auto &data : data_vector) {
data_->parts.push_back(std::make_unique<query_join_part>(data.join_table));
data_->parts.push_back(std::make_unique<query_on_part>(std::move(data.condition)));
}
return {connection_, schema_, data_};
}
query_select_intermediate::query_select_intermediate(connection &db, const sql::schema &schema, const std::vector<column>& columns)
: query_start_intermediate(db, schema)
{
+22 -16
View File
@@ -7,23 +7,27 @@
namespace matador::sql {
record::record(std::initializer_list<field> columns)
: fields_(columns)
{
init();
for (auto &&col :columns) {
const auto it = fields_by_name_.emplace(col.name(), field_index_pair {col, fields_.size()});
fields_.push_back(std::ref(it.first->second.first));
}
}
record::record(const std::vector<field> &columns)
: fields_(columns)
{
init();
for (auto &&col :columns) {
const auto it = fields_by_name_.emplace(col.name(), field_index_pair {col, fields_.size()});
fields_.push_back(std::ref(it.first->second.first));
}
}
record::record(const record &x)
: fields_(x.fields_)
: fields_by_name_(x.fields_by_name_)
//, pk_index_(x.pk_index_)
{
for (auto& col : fields_) {
add_to_map(col, col.index());
for (auto& col : fields_by_name_) {
fields_.push_back(std::ref(col.second.first));
}
}
@@ -33,16 +37,16 @@ record &record::operator=(const record &x)
return *this;
}
fields_ = x.fields_;
fields_by_name_.clear();
fields_by_name_ = x.fields_by_name_;
fields_.clear();
// pk_index_ = x.pk_index_;
for (auto& col : fields_) {
add_to_map(col, col.index());
for (auto& col : fields_by_name_) {
fields_.push_back(std::ref(col.second.first));
}
return *this;
}
const std::vector<field> &record::columns() const
const std::vector<record::field_ref> &record::columns() const
{
return fields_;
}
@@ -63,7 +67,9 @@ const std::vector<field> &record::columns() const
const field &record::at(const column &col) const
{
return fields_by_name_.at(col.name).first;
const auto &res = fields_by_name_.at(col.name);
const auto &f = res.first;
return f;
}
const field &record::at(size_t index) const
@@ -82,10 +88,10 @@ record::const_iterator record::find(const std::string &column_name) const {
return it != fields_by_name_.end() ? fields_.begin() + it->second.second : fields_.end();
}
void record::append(field col)
void record::append(const field &col)
{
auto &ref = fields_.emplace_back(std::move(col));
add_to_map(ref, fields_.size() - 1);
const auto it = fields_by_name_.emplace(col.name(), field_index_pair {col, fields_.size()});
fields_.push_back(std::ref(it.first->second.first));
}
record::iterator record::begin()
+7
View File
@@ -8,6 +8,13 @@ namespace matador::sql {
schema::schema(std::string name)
: name_(std::move(name)) {}
void schema::create(connection &c) {
for (const auto &ti : repository_) {
// ti.second.prototype
}
}
std::string schema::name() const
{
return name_;
+7 -18
View File
@@ -105,24 +105,13 @@ std::unique_ptr<query_result_impl> session::fetch(const std::string &sql) const
query_select session::build_select_query(connection_ptr<connection> &conn, entity_query_data &&data) const
{
auto q = conn->query(*schema_)
return conn->query(*schema_)
.select(data.columns)
.from(data.root_table_name);
.from(data.root_table_name)
.join_left(data.joins)
.where(std::move(data.where_clause))
.order_by({data.root_table_name, data.pk_column_})
.asc();
}
auto it = data.joins.begin();
if (it != data.joins.end()) {
auto qj = q.join_left(it->join_table).on(std::move(it->condition));
while (++it != data.joins.end()) {
qj.join_left(it->join_table).on(std::move(it->condition));
}
if (data.where_clause) {
return qj.where(std::move(data.where_clause));
}
return qj;
} else if (data.where_clause) {
return q.where(std::move(data.where_clause));
} else {
return q;
}
}
}
+6 -3
View File
@@ -3,7 +3,7 @@ Include(FetchContent)
FetchContent_Declare(
Catch2
GIT_REPOSITORY https://github.com/catchorg/Catch2.git
GIT_TAG v3.4.0 # or a later release
GIT_TAG v3.5.4 # or a later release
)
FetchContent_MakeAvailable(Catch2)
@@ -19,7 +19,7 @@ add_executable(tests
BackendProviderTest.cpp
models/product.hpp
models/order.hpp
models/order_details.h
models/order_details.hpp
ColumnDefinitionGeneratorTest.cpp
ColumnGeneratorTest.cpp
ValueGeneratorTest.cpp
@@ -40,7 +40,10 @@ add_executable(tests
FieldTest.cpp
models/recipe.hpp
ValueTest.cpp
ResultTest.cpp)
ResultTest.cpp
utils/auto_reset_event.hpp
utils/auto_reset_event.cpp
models/student.hpp)
target_link_libraries(tests PRIVATE
Catch2::Catch2WithMain
+14 -39
View File
@@ -1,37 +1,12 @@
#include <catch2/catch_test_macros.hpp>
#include <matador/sql/connection_pool.hpp>
#include <matador/sql/noop_connection.hpp>
#include "matador/sql/connection_pool.hpp"
#include "matador/sql/noop_connection.hpp"
class AutoResetEvent
{
public:
AutoResetEvent() : state(false) {}
AutoResetEvent(const AutoResetEvent& other) = delete;
void WaitOne() {
std::unique_lock<std::mutex> lock(sync);
underlying.wait(lock, [this](){return state.load();});
}
void Set() {
std::unique_lock<std::mutex> lock(sync);
state = true;
underlying.notify_all();
}
void Reset() {
std::unique_lock<std::mutex> lock(sync);
state = false;
}
private:
std::condition_variable underlying;
std::mutex sync;
std::atomic<bool> state;
};
#include "utils/auto_reset_event.hpp"
using namespace matador::sql;
using namespace matador::test::utils;
TEST_CASE("Create connection pool", "[connection pool]") {
using pool_t = connection_pool<noop_connection>;
@@ -145,31 +120,31 @@ TEST_CASE("Try acquire connection", "[connection pool][try acquire]") {
pool.release(ptr2);
AutoResetEvent reset_event1;
AutoResetEvent reset_event2;
AutoResetEvent reset_event3;
auto_reset_event reset_main_event;
auto_reset_event reset_thread_event;
std::thread t([&reset_event1, &reset_event2, &reset_event3, &pool]() {
std::thread t([&reset_main_event, &reset_thread_event, &pool]() {
auto c1 = pool.acquire();
REQUIRE(c1.valid());
REQUIRE(c1.id());
REQUIRE(c1.id().value() > 0);
reset_event1.Set();
reset_main_event.set();
reset_thread_event.wait_one();
reset_event2.WaitOne();
pool.release(c1);
REQUIRE(!c1.valid());
reset_event3.Set();
reset_main_event.set();
});
reset_event1.WaitOne();
reset_main_event.wait_one();
ptr2 = pool.try_acquire();
REQUIRE(!ptr2.valid());
reset_event2.Set();
reset_thread_event.set();
reset_event3.WaitOne();
reset_main_event.wait_one();
ptr2 = pool.try_acquire();
REQUIRE(ptr2.valid());
REQUIRE(ptr2.id());
+94 -4
View File
@@ -3,13 +3,59 @@
#include <matador/sql/connection.hpp>
#include <matador/sql/entity_query_builder.hpp>
#include "models/airplane.hpp"
#include "models/author.hpp"
#include "models/book.hpp"
#include "models/flight.hpp"
#include "models/recipe.hpp"
#include "models/order.hpp"
#include "models/student.hpp"
using namespace matador::sql;
TEST_CASE("Create sql query data for entity with eager has one", "[query][entity][builder]") {
using namespace matador::test;
connection db("noop://noop.db");
schema scm("noop");
scm.attach<airplane>("airplanes");
scm.attach<flight>("flights");
entity_query_builder eqb(scm);
auto data = eqb.build<flight>(17);
REQUIRE(data.is_ok());
REQUIRE(data->root_table_name == "flights");
REQUIRE(data->joins.size() == 1);
const std::vector<column> expected_columns {
{ "flights", "id", "c01" },
{ "airplanes", "id", "c02" },
{ "airplanes", "brand", "c03" },
{ "airplanes", "model", "c04" },
{ "flights", "pilot_name", "c05" },
};
REQUIRE(data->columns.size() == expected_columns.size());
for (size_t i = 0; i != expected_columns.size(); ++i) {
REQUIRE(expected_columns[i].equals(data->columns[i]));
}
std::vector<std::pair<std::string, std::string>> expected_join_data {
{ "airplanes", R"("flights"."airplane_id" = "airplanes"."id")"}
};
query_context qc;
size_t index{0};
for (const auto &jd : data->joins) {
REQUIRE(jd.join_table.name == expected_join_data[index].first);
REQUIRE(jd.condition->evaluate(db.dialect(), qc) == expected_join_data[index].second);
++index;
}
REQUIRE(data->where_clause);
auto cond = data->where_clause->evaluate(db.dialect(), qc);
REQUIRE(cond == R"("flights"."id" = 17)");
}
TEST_CASE("Create sql query data for entity with eager belongs to", "[query][entity][builder]") {
using namespace matador::test;
connection db("noop://noop.db");
@@ -41,7 +87,7 @@ TEST_CASE("Create sql query data for entity with eager belongs to", "[query][ent
}
std::vector<std::pair<std::string, std::string>> expected_join_data {
{ "books", R"("books"."author_id" = "authors"."id")"}
{ "authors", R"("books"."author_id" = "authors"."id")"}
};
query_context qc;
@@ -107,7 +153,7 @@ TEST_CASE("Create sql query data for entity with eager has many belongs to", "[q
}
std::vector<std::pair<std::string, std::string>> expected_join_data {
{ "orders", R"("orders"."order_id" = "order_details"."order_id")"}
{ "order_details", R"("orders"."order_id" = "order_details"."order_id")"}
};
query_context qc;
@@ -150,8 +196,8 @@ TEST_CASE("Create sql query data for entity with eager many to many", "[query][e
}
std::vector<std::pair<std::string, std::string>> expected_join_data {
{ "ingredients", R"("ingredients"."id" = "recipe_ingredients"."ingredient_id")"},
{ "recipes", R"("recipes"."id" = "recipe_ingredients"."recipe_id")"}
{ "recipe_ingredients", R"("ingredients"."id" = "recipe_ingredients"."ingredient_id")"},
{ "recipes", R"("recipe_ingredients"."recipe_id" = "recipes"."id")"}
};
query_context qc;
@@ -166,3 +212,47 @@ TEST_CASE("Create sql query data for entity with eager many to many", "[query][e
auto cond = data->where_clause->evaluate(db.dialect(), qc);
REQUIRE(cond == R"("ingredients"."id" = 17)");
}
TEST_CASE("Create sql query data for entity with eager many to many (inverse part)", "[query][entity][builder]") {
using namespace matador::test;
connection db("noop://noop.db");
schema scm("noop");
scm.attach<student>("students");
scm.attach<course>("courses");
scm.attach<student_course>("student_courses");
entity_query_builder eqb(scm);
auto data = eqb.build<course>(17);
REQUIRE(data.is_ok());
REQUIRE(data->root_table_name == "courses");
REQUIRE(data->joins.size() == 2);
const std::vector<column> expected_columns {
{ "courses", "id", "c01" },
{ "courses", "title", "c02" },
{ "students", "id", "c03" },
{ "students", "name", "c04" }
};
REQUIRE(data->columns.size() == expected_columns.size());
for (size_t i = 0; i != expected_columns.size(); ++i) {
REQUIRE(expected_columns[i].equals(data->columns[i]));
}
std::vector<std::pair<std::string, std::string>> expected_join_data {
{ "student_courses", R"("courses"."id" = "student_courses"."course_id")"},
{ "students", R"("student_courses"."student_id" = "students"."id")"}
};
query_context qc;
size_t index{0};
for (const auto &jd : data->joins) {
REQUIRE(jd.join_table.name == expected_join_data[index].first);
REQUIRE(jd.condition->evaluate(db.dialect(), qc) == expected_join_data[index].second);
++index;
}
REQUIRE(data->where_clause);
auto cond = data->where_clause->evaluate(db.dialect(), qc);
REQUIRE(cond == R"("courses"."id" = 17)");
}
+7 -2
View File
@@ -25,7 +25,12 @@ utils::result<float, math_error>plus(int x, int y) {
}
utils::result<float, std::string>error_to_string(math_error err) {
return utils::error(std::string("error"));
switch (err) {
case math_error::DIVISION_BY_ZERO:
return utils::error(std::string("division by zero error"));
default:
return utils::error(std::string("unknown error"));
}
}
}
@@ -76,7 +81,7 @@ TEST_CASE("Result tests", "[result]") {
REQUIRE(!res2);
REQUIRE(!res2.is_ok());
REQUIRE(res2.is_error());
REQUIRE((res2.err() == "error"));
REQUIRE((res2.err() == "division by zero error"));
res = test::divide(4, 2)
.and_then([](const auto &val) { return test::multiply(val, 5); })
+1 -1
View File
@@ -1,7 +1,7 @@
#ifndef QUERY_ORDER_HPP
#define QUERY_ORDER_HPP
#include "order_details.h"
#include "order_details.hpp"
#include "matador/utils/access.hpp"
@@ -1,5 +1,5 @@
#ifndef QUERY_ORDER_DETAILS_H
#define QUERY_ORDER_DETAILS_H
#ifndef QUERY_ORDER_DETAILS_HPP
#define QUERY_ORDER_DETAILS_HPP
#include "product.hpp"
@@ -25,4 +25,4 @@ struct order_details
};
}
#endif //QUERY_ORDER_DETAILS_H
#endif //QUERY_ORDER_DETAILS_HPP
+68
View File
@@ -0,0 +1,68 @@
#ifndef QUERY_STUDENT_HPP
#define QUERY_STUDENT_HPP
#include "matador/utils/access.hpp"
#include "matador/utils/foreign_attributes.hpp"
#include "matador/sql/entity.hpp"
#include "matador/sql/has_many_to_many_relation.hpp"
#include <utility>
#include <vector>
namespace matador::test {
class course;
struct student
{
unsigned long id{};
std::string name;
std::vector<matador::sql::entity<course>> courses;
student() = default;
explicit student(unsigned long id, std::string name)
: id(id)
, name(std::move(name)) {}
template < class Operator >
void process(Operator &op)
{
namespace field = matador::utils::access;
field::primary_key(op, "id", id);
field::attribute(op, "name", name, 255);
field::has_many_to_many(op, "student_courses", courses, "student_id", "course_id", utils::fetch_type::LAZY);
}
};
struct course
{
unsigned long id{};
std::string title;
std::vector<matador::sql::entity<student>> students;
course() = default;
explicit course(unsigned long id, std::string title)
: id(id)
, title(std::move(title)) {}
template < class Operator >
void process(Operator &op)
{
namespace field = matador::utils::access;
field::primary_key(op, "id", id);
field::attribute(op, "title", title, 255);
field::has_many_to_many(op, "student_courses", students, utils::fetch_type::EAGER);
}
};
class student_course : public sql::has_many_to_many_relation<student, course>
{
public:
student_course()
: has_many_to_many_relation("student_id", "course_id") {}
};
}
#endif //QUERY_STUDENT_HPP
+26
View File
@@ -0,0 +1,26 @@
#include "auto_reset_event.hpp"
namespace matador::test::utils {
auto_reset_event::auto_reset_event() : state(false) {}
void auto_reset_event::wait_one()
{
std::unique_lock<std::mutex> lock(sync);
underlying.wait(lock, [this](){return state.load();});
state = false;
}
void auto_reset_event::set()
{
std::unique_lock<std::mutex> lock(sync);
state = true;
underlying.notify_one();
}
void auto_reset_event::reset()
{
std::unique_lock<std::mutex> lock(sync);
state = false;
}
}
+29
View File
@@ -0,0 +1,29 @@
#ifndef QUERY_AUTO_RESET_EVENT_HPP
#define QUERY_AUTO_RESET_EVENT_HPP
#include <atomic>
#include <condition_variable>
#include <mutex>
namespace matador::test::utils {
class auto_reset_event
{
public:
auto_reset_event();
auto_reset_event(const auto_reset_event& other) = delete;
void wait_one();
void set();
void reset();
private:
std::condition_variable underlying;
std::mutex sync;
std::atomic<bool> state;
};
}
#endif //QUERY_AUTO_RESET_EVENT_HPP