Compare commits

..
28 Commits
Author SHA1 Message Date
Sascha Kühl 19de5714f4 added data_type_traits for enums 2024-08-22 16:08:05 +02:00
Sascha Kühl 484c2d8b05 removed quotes for enum value in condition 2024-08-22 16:07:45 +02:00
Sascha Kühl 43d2f81b95 added all value constructor to class table 2024-08-22 16:07:10 +02:00
Sascha Kühl 27f6c81da2 Merge remote-tracking branch 'origin/main'
# Conflicts:
#	demo/main.cpp
2024-08-21 15:36:34 +02:00
Sascha Kühl 12e5b27a8c progress on in conditions with queries and enums 2024-08-21 15:35:29 +02:00
sascha ff3a2e4217 fixed record class 2024-07-15 08:14:11 +02:00
Sascha Kühl ac8b2ed4e4 added more join_left methods to query_from_intermediate to write shorter fluent sql queries 2024-04-16 17:11:08 +02:00
Sascha Kühl 38da76bfb3 improved result test with specific error test 2024-04-16 17:09:52 +02:00
Sascha Kühl 4199c5029c added session test with has one relation 2024-04-15 15:54:37 +02:00
sascha fb57545cce added join_column_collector and implemented inverse has_many_to_many 2024-04-14 22:12:48 +02:00
sascha 7bb7afa227 integrated auto_reset_event into test utils 2024-04-14 11:41:36 +02:00
sascha 2022f6f75b updated catch2 to 3.5.4 2024-04-14 11:41:09 +02:00
Sascha Kühl b4765cac88 added multi threaded pool test 2024-04-12 16:34:30 +02:00
Sascha Kühl 25bcc362f2 use result in entity_query_builder 2024-04-10 16:13:37 +02:00
sascha b7c12d8217 added result class 2024-04-10 07:58:41 +02:00
sascha 1fe5c9bac4 added missing include 2024-04-09 20:19:30 +02:00
Sascha Kühl 8f754f3542 added value class 2024-04-09 16:03:42 +02:00
Sascha Kühl f977b2afc9 added session tests 2024-04-08 16:38:49 +02:00
sascha 5a3cdc8645 removed query directory 2024-04-07 15:18:41 +02:00
sascha fa7ff832c9 handle many-to-many relations separately when process entities 2024-04-07 14:45:57 +02:00
sascha 30e2e0c221 made dialect parameter const in condition::evaluate 2024-04-07 14:44:42 +02:00
sascha 85f87367c0 added default values for column alias name in constructor 2024-04-07 14:44:13 +02:00
sascha 31e9c7e9ac added missing checks to QueryTest 2024-03-29 11:41:37 +01:00
sascha 6f3e589e10 added to many fields to entities author, book, recipe and ingredient 2024-03-29 11:41:14 +01:00
sascha 05c0c0393d added type check methods to column_definition 2024-03-29 11:40:02 +01:00
sascha e14d9a77eb fixed name ambiguity 2024-03-29 11:39:41 +01:00
Sascha Kühl 4cda0f2664 many to many query progress (multiple joins) 2024-03-28 15:51:45 +01:00
Sascha Kühl 50a0eb580a renamed column_generator to column_definition_generator and column_name_generator to column_generator 2024-03-27 15:15:45 +01:00
96 changed files with 2925 additions and 1566 deletions
+1
View File
@@ -1,3 +1,4 @@
.idea .idea
cmake-build-debug cmake-build-debug
Testing Testing
debug
+4
View File
@@ -22,6 +22,10 @@ find_package(SQLite3 REQUIRED)
find_package(PostgreSQL REQUIRED) find_package(PostgreSQL REQUIRED)
find_package(MySQL 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 "Found SQLite3 ${SQLite3_VERSION}")
message(STATUS "Adding SQLite3 include directory: ${SQLite3_INCLUDE_DIRS}") message(STATUS "Adding SQLite3 include directory: ${SQLite3_INCLUDE_DIRS}")
message(STATUS "Adding SQLite3 libs: ${SQLite3_LIBRARIES}") message(STATUS "Adding SQLite3 libs: ${SQLite3_LIBRARIES}")
+41
View File
@@ -4,3 +4,44 @@
- Read in entity fields - Read in entity fields
- Add special handling for update in backends - Add special handling for update in backends
- Add ODBC/SQL Server backend - Add ODBC/SQL Server backend
Fetch eager strategies
======================
ONE TO ONE/MANY
*person* *address*
- has one address - belongs to person
=> join "address" on "person.id" == "address.person_id"
*address* *person*
- belongs to person - has one address
=> join "person" on "address.person_id" == "person.id"
*book* *author*
- belongs to author - has many books
- => join "author" on "book.author_id" == "author.id"
HAS MANY TO ONE (WITHOUT RELATION TABLE)
*author* *book*
- has many books - belongs to author
- => join "book" on "author.id" == "book.author_id"
if "has many" type has primary key & field "author_id"
if table name belongs to entity template type?
HAS MANY TO MANY (WITHOUT RELATION TABLE)
*student* *student_course* *course*
- has many courses - belongs to student
- belongs to course - has many students
=> join "student_course" on "student.id" == "student_course.student_id"
join "student_course" on "course.id" == "student_course.course_id"
if has many type hasn't primary key (is relation table)
+1 -1
View File
@@ -3,7 +3,7 @@ Include(FetchContent)
FetchContent_Declare( FetchContent_Declare(
Catch2 Catch2
GIT_REPOSITORY https://github.com/catchorg/Catch2.git 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) FetchContent_MakeAvailable(Catch2)
+1 -1
View File
@@ -3,7 +3,7 @@ Include(FetchContent)
FetchContent_Declare( FetchContent_Declare(
Catch2 Catch2
GIT_REPOSITORY https://github.com/catchorg/Catch2.git 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) FetchContent_MakeAvailable(Catch2)
@@ -32,7 +32,7 @@ public:
void read_value(const char *id, size_t index, char *value, size_t s) override; void read_value(const char *id, size_t index, char *value, size_t s) override;
void read_value(const char *id, size_t index, std::string &value) override; void read_value(const char *id, size_t index, std::string &value) override;
void read_value(const char *id, size_t index, std::string &value, size_t s) override; void read_value(const char *id, size_t index, std::string &value, size_t s) override;
void read_value(const char *id, size_t index, sql::any_type &value, sql::data_type_t type, size_t size) override; void read_value(const char *id, size_t index, sql::value &val, size_t size) override;
private: private:
sqlite3 *db_{nullptr}; sqlite3 *db_{nullptr};
@@ -107,8 +107,8 @@ void sqlite_prepared_result_reader::read_value(const char *id, size_t index, std
query_result_reader::read_value(id, index, value, s); query_result_reader::read_value(id, index, value, s);
} }
void sqlite_prepared_result_reader::read_value(const char *id, size_t index, sql::any_type &value, sql::data_type_t type, size_t size) void sqlite_prepared_result_reader::read_value(const char *id, size_t index, sql::value &val, size_t size)
{ {
query_result_reader::read_value(id, index, value, type, size); query_result_reader::read_value(id, index, val, size);
} }
} }
+1 -1
View File
@@ -3,7 +3,7 @@ Include(FetchContent)
FetchContent_Declare( FetchContent_Declare(
Catch2 Catch2
GIT_REPOSITORY https://github.com/catchorg/Catch2.git 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) FetchContent_MakeAvailable(Catch2)
+208 -21
View File
@@ -10,6 +10,9 @@
#include "models/airplane.hpp" #include "models/airplane.hpp"
#include "models/flight.hpp" #include "models/flight.hpp"
#include "models/person.hpp" #include "models/person.hpp"
#include "models/recipe.hpp"
#include <iostream>
using namespace matador::sql; using namespace matador::sql;
using namespace matador::test; using namespace matador::test;
@@ -29,6 +32,9 @@ public:
drop_table_if_exists("flight"); drop_table_if_exists("flight");
drop_table_if_exists("airplane"); drop_table_if_exists("airplane");
drop_table_if_exists("person"); drop_table_if_exists("person");
drop_table_if_exists("recipe_ingredients");
drop_table_if_exists("recipes");
drop_table_if_exists("ingredients");
} }
protected: protected:
@@ -79,13 +85,14 @@ TEST_CASE_METHOD(QueryFixture, "Execute select statement with where clause", "[s
auto res = db.query(schema) auto res = db.query(schema)
.insert() .insert()
.into<person>("person") .into("person", column_generator::generate<person>(schema, true))
.values(george) .values(george)
.execute(); .execute();
REQUIRE(res == 1); REQUIRE(res == 1);
// fetch person as record // fetch person as record
auto result_record = db.query(schema).select<person>() auto result_record = db.query(schema)
.select(column_generator::generate<person>(schema, true))
.from("person") .from("person")
.where("id"_col == 7) .where("id"_col == 7)
.fetch_all(); .fetch_all();
@@ -104,7 +111,8 @@ TEST_CASE_METHOD(QueryFixture, "Execute select statement with where clause", "[s
} }
// fetch person as person // fetch person as person
auto result_person = db.query(schema).select<person>() auto result_person = db.query(schema)
.select(column_generator::generate<person>(schema, true))
.from("person") .from("person")
.where("id"_col == 7) .where("id"_col == 7)
.fetch_all<person>(); .fetch_all<person>();
@@ -176,20 +184,31 @@ TEST_CASE_METHOD(QueryFixture, "Select statement with foreign key", "[session]")
}; };
for (const auto &plane: planes) { for (const auto &plane: planes) {
auto res = db.query(schema).insert().into<airplane>("airplane").values(*plane).execute(); auto res = db.query(schema)
.insert()
.into("airplane", column_generator::generate<airplane>(schema, true))
.values(*plane)
.execute();
REQUIRE(res == 1); REQUIRE(res == 1);
} }
auto count = db.query(schema).select({count_all()}).from("airplane").fetch_value<int>(); auto count = db.query(schema)
.select({count_all()})
.from("airplane")
.fetch_value<int>().value();
REQUIRE(count == 3); REQUIRE(count == 3);
flight f4711{4, planes.at(1), "hans"}; flight f4711{4, planes.at(1), "hans"};
auto res = db.query(schema).insert().into<flight>("flight").values(f4711).execute(); auto res = db.query(schema)
.insert()
.into("flight", column_generator::generate<flight>(schema, true))
.values(f4711)
.execute();
REQUIRE(res == 1); REQUIRE(res == 1);
auto f = *db.query(schema) auto f = *db.query(schema)
.select<flight>() .select(column_generator::generate<flight>(schema, true))
.from("flight") .from("flight")
.fetch_all().begin(); .fetch_all().begin();
REQUIRE(f.at(0).as<unsigned long>() == 4); REQUIRE(f.at(0).as<unsigned long>() == 4);
@@ -219,11 +238,18 @@ TEST_CASE_METHOD(QueryFixture, "Select statement with foreign key and join_left"
}; };
for (const auto &plane: planes) { for (const auto &plane: planes) {
auto res = db.query(schema).insert().into<airplane>("airplane").values(*plane).execute(); auto res = db.query(schema)
.insert()
.into("airplane", column_generator::generate<airplane>(schema, true))
.values(*plane)
.execute();
REQUIRE(res == 1); REQUIRE(res == 1);
} }
auto count = db.query(schema).select({count_all()}).from("airplane").fetch_value<int>(); auto count = db.query(schema)
.select({count_all()})
.from("airplane")
.fetch_value<int>().value();
REQUIRE(count == 3); REQUIRE(count == 3);
std::vector<entity<flight>> flights{ std::vector<entity<flight>> flights{
@@ -234,12 +260,16 @@ TEST_CASE_METHOD(QueryFixture, "Select statement with foreign key and join_left"
}; };
for (const auto &f: flights) { for (const auto &f: flights) {
auto res = db.query(schema).insert().into<flight>("flight").values(*f).execute(); auto res = db.query(schema)
.insert()
.into("flight", {"id", "airplane_id", "pilot_name"})
.values(*f)
.execute();
REQUIRE(res == 1); REQUIRE(res == 1);
} }
auto f = *db.query(schema) auto f = *db.query(schema)
.select<flight>() .select(column_generator::generate<flight>(schema, true))
.from("flight") .from("flight")
.fetch_all().begin(); .fetch_all().begin();
REQUIRE(f.at(0).as<unsigned long>() == 4); REQUIRE(f.at(0).as<unsigned long>() == 4);
@@ -291,7 +321,7 @@ TEST_CASE_METHOD(QueryFixture, "Select statement with foreign key and for single
auto res = db auto res = db
.query(schema) .query(schema)
.insert() .insert()
.into<airplane>("airplane") .into("airplane", column_generator::generate<airplane>(schema, true))
.values(*plane) .values(*plane)
.execute(); .execute();
REQUIRE(res == 1); REQUIRE(res == 1);
@@ -301,7 +331,7 @@ TEST_CASE_METHOD(QueryFixture, "Select statement with foreign key and for single
.query(schema) .query(schema)
.select({count_all()}) .select({count_all()})
.from("airplane") .from("airplane")
.fetch_value<int>(); .fetch_value<int>().value();
REQUIRE(count == 3); REQUIRE(count == 3);
std::vector<entity<flight>> flights{ std::vector<entity<flight>> flights{
@@ -312,17 +342,22 @@ TEST_CASE_METHOD(QueryFixture, "Select statement with foreign key and for single
}; };
for (const auto &f: flights) { for (const auto &f: flights) {
auto res = db.query(schema).insert().into<flight>("flight").values(*f).execute(); auto res = db.query(schema)
.insert()
.into("flight", column_generator::generate<flight>(schema, true))
.values(*f)
.execute();
REQUIRE(res == 1); REQUIRE(res == 1);
} }
auto f = *db.query(schema) auto f = db.query(schema)
.select<flight>() .select(column_generator::generate<flight>(schema, true))
.from("flight") .from("flight")
.fetch_all().begin(); .fetch_one();
REQUIRE(f.at(0).as<unsigned long>() == 4); REQUIRE(f.has_value());
REQUIRE(f.at(1).as<unsigned long>() == 1); REQUIRE(f->at(0).as<unsigned long>() == 4);
REQUIRE(f.at(2).as<std::string>() == "hans"); REQUIRE(f->at(1).as<unsigned long>() == 1);
REQUIRE(f->at(2).as<std::string>() == "hans");
auto result = db auto result = db
.query(schema) .query(schema)
@@ -335,7 +370,7 @@ TEST_CASE_METHOD(QueryFixture, "Select statement with foreign key and for single
auto expected_flight = flights[0]; auto expected_flight = flights[0];
REQUIRE(result.has_value()); REQUIRE(result);
REQUIRE(result->id == expected_flight->id); REQUIRE(result->id == expected_flight->id);
REQUIRE(result->pilot_name == expected_flight->pilot_name); REQUIRE(result->pilot_name == expected_flight->pilot_name);
REQUIRE(result->airplane.get()); REQUIRE(result->airplane.get());
@@ -346,3 +381,155 @@ TEST_CASE_METHOD(QueryFixture, "Select statement with foreign key and for single
db.query(schema).drop().table("flight").execute(); db.query(schema).drop().table("flight").execute();
db.query(schema).drop().table("airplane").execute(); db.query(schema).drop().table("airplane").execute();
} }
TEST_CASE_METHOD(QueryFixture, "Select statement with many to many relationship", "[session][join][many_to_many]") {
schema.attach<recipe>("recipes");
schema.attach<ingredient>("ingredients");
schema.attach<recipe_ingredient>("recipe_ingredients");
db.query(schema).create()
.table<recipe>("recipes")
.execute();
db.query(schema).create()
.table<ingredient>("ingredients")
.execute();
db.query(schema).create()
.table<recipe_ingredient>("recipe_ingredients")
.execute();
std::vector<ingredient> ingredients {
{1, "Apple"},
{2, "Strawberry"},
{3, "Pineapple"},
{4, "Sugar"},
{5, "Flour"},
{6, "Butter"},
{7, "Beans"}
};
for (const auto &i: ingredients) {
auto res = db
.query(schema)
.insert()
.into("ingredients", column_generator::generate<ingredient>(schema, true))
.values(i)
.execute();
REQUIRE(res == 1);
}
std::vector<recipe> recipes{
{7, "Apple Crumble"},
{8, "Beans Chili"},
{9, "Fruit Salad"}
};
for (const auto &r: recipes) {
auto res = db
.query(schema)
.insert()
.into("recipes", column_generator::generate<recipe>(schema, true))
.values(r)
.execute();
REQUIRE(res == 1);
}
std::vector<std::pair<int, int>> recipe_ingredients {
{ 7, 1 },
{ 7, 4 },
{ 7, 5 },
{ 8, 6 },
{ 8, 7 },
{ 9, 1 },
{ 9, 2 },
{ 9, 3 }
};
for (const auto &ri: recipe_ingredients) {
auto res = db
.query(schema)
.insert()
.into("recipe_ingredients", column_generator::generate<recipe_ingredient>(schema, true))
.values({ri.first, ri.second})
.execute();
REQUIRE(res == 1);
}
auto result = db
.query(schema)
.select({"r.id", "r.name", "ri.ingredient_id"})
.from({"recipes", "r"})
.join_left({"recipe_ingredients", "ri"})
.on("r.id"_col == "ri.recipe_id"_col)
.fetch_all();
std::vector<std::tuple<unsigned long, std::string, unsigned long>> expected_result_one_join {
{7, "Apple Crumble", 1},
{7, "Apple Crumble", 4},
{7, "Apple Crumble", 5},
{8, "Beans Chili", 6},
{8, "Beans Chili", 7},
{9, "Fruit Salad", 1},
{9, "Fruit Salad", 2},
{9, "Fruit Salad", 3}
};
size_t index{0};
for (const auto &r: result) {
REQUIRE(r.size() == 3);
REQUIRE(r.at(0).as<unsigned long>().value() == std::get<0>(expected_result_one_join[index]));
REQUIRE(r.at(1).as<std::string>().value() == std::get<1>(expected_result_one_join[index]));
REQUIRE(r.at(2).as<unsigned long>().value() == std::get<2>(expected_result_one_join[index]));
++index;
}
result = db
.query(schema)
.select({"r.id", "r.name", "ri.ingredient_id", "i.name"})
.from({"recipes", "r"})
.join_left({"recipe_ingredients", "ri"}).on("r.id"_col == "ri.recipe_id"_col)
.join_left({"ingredients", "i"}).on("ri.ingredient_id"_col == "i.id"_col)
.fetch_all();
std::vector<std::tuple<unsigned long, std::string, unsigned long, std::string>> expected_result_two_joins {
{7, "Apple Crumble", 1, "Apple"},
{7, "Apple Crumble", 4, "Sugar"},
{7, "Apple Crumble", 5, "Flour"},
{8, "Beans Chili", 6, "Butter"},
{8, "Beans Chili", 7, "Beans"},
{9, "Fruit Salad", 1, "Apple"},
{9, "Fruit Salad", 2, "Strawberry"},
{9, "Fruit Salad", 3, "Pineapple"}
};
index = 0;
for (const auto &r: result) {
REQUIRE(r.size() == 4);
REQUIRE(r.at(0).as<unsigned long>().value() == std::get<0>(expected_result_two_joins[index]));
REQUIRE(r.at(1).as<std::string>().value() == std::get<1>(expected_result_two_joins[index]));
REQUIRE(r.at(2).as<unsigned long>().value() == std::get<2>(expected_result_two_joins[index]));
REQUIRE(r.at(3).as<std::string>().value() == std::get<3>(expected_result_two_joins[index]));
++index;
}
result = db
.query(schema)
.select({"r.id", "r.name", "ri.ingredient_id", "i.name"})
.from({"recipes", "r"})
.join_left({"recipe_ingredients", "ri"}).on("r.id"_col == "ri.recipe_id"_col)
.join_left({"ingredients", "i"}).on("ri.ingredient_id"_col == "i.id"_col)
.where("r.id"_col == 8)
.fetch_all();
index = 3;
for (const auto &r: result) {
REQUIRE(r.size() == 4);
REQUIRE(r.at(0).as<unsigned long>().value() == std::get<0>(expected_result_two_joins[index]));
REQUIRE(r.at(1).as<std::string>().value() == std::get<1>(expected_result_two_joins[index]));
REQUIRE(r.at(2).as<unsigned long>().value() == std::get<2>(expected_result_two_joins[index]));
REQUIRE(r.at(3).as<std::string>().value() == std::get<3>(expected_result_two_joins[index]));
++index;
}
db.query(schema).drop().table("recipe_ingredients").execute();
db.query(schema).drop().table("recipes").execute();
db.query(schema).drop().table("ingredients").execute();
}
+52 -9
View File
@@ -5,6 +5,7 @@
#include "matador/sql/session.hpp" #include "matador/sql/session.hpp"
#include "models/airplane.hpp" #include "models/airplane.hpp"
#include "models/flight.hpp"
class SessionFixture class SessionFixture
{ {
@@ -16,9 +17,8 @@ public:
~SessionFixture() ~SessionFixture()
{ {
drop_table_if_exists("flight"); drop_table_if_exists("flights");
drop_table_if_exists("airplane"); drop_table_if_exists("airplanes");
drop_table_if_exists("person");
} }
protected: protected:
@@ -38,19 +38,62 @@ using namespace matador;
TEST_CASE_METHOD(SessionFixture, "Session relation test", "[session][relation]") { TEST_CASE_METHOD(SessionFixture, "Session relation test", "[session][relation]") {
using namespace matador; using namespace matador;
ses.attach<test::airplane>("airplane"); ses.attach<test::airplane>("airplanes");
ses.attach<test::flight>("flights");
ses.create_schema(); 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, "Find object with id", "[session][find]") { TEST_CASE_METHOD(SessionFixture, "Use session to find object with id", "[session][find]") {
using namespace matador::test; using namespace matador::test;
ses.attach<airplane>("airplane"); ses.attach<airplane>("airplanes");
ses.create_schema(); ses.create_schema();
auto a380 = ses.insert<airplane>(1, "Boeing", "A380"); auto a380 = ses.insert<airplane>(1, "Boeing", "A380");
ses.find<airplane>(1); auto result = ses.find<airplane>(2);
REQUIRE(!result.is_ok());
REQUIRE((result.err() == sql::session_error::FailedToFindObject));
result = ses.find<airplane>(1);
REQUIRE(result);
auto read_a380 = result.value();
REQUIRE(a380->id == read_a380->id);
}
TEST_CASE_METHOD(SessionFixture, "Use session to find all objects", "[session][find]") {
using namespace matador::test;
ses.attach<airplane>("airplanes");
ses.create_schema();
std::vector<std::unique_ptr<airplane>> planes;
planes.emplace_back(new airplane(1, "Airbus", "A380"));
planes.emplace_back(new airplane(2, "Boeing", "707"));
planes.emplace_back(new airplane(3, "Boeing", "747"));
for (auto &&plane: planes) {
ses.insert(plane.release());
}
auto result = ses.find<airplane>();
std::vector<std::tuple<int, std::string, std::string>> expected_result {
{1, "Airbus", "A380"},
{2, "Boeing", "707"},
{3, "Boeing", "747"}
};
REQUIRE(result);
auto all_planes = result.release();
size_t index {0};
for (const auto &i: all_planes) {
REQUIRE(i.id == std::get<0>(expected_result[index]));
REQUIRE(i.brand == std::get<1>(expected_result[index]));
REQUIRE(i.model == std::get<2>(expected_result[index]));
++index;
}
} }
+15 -4
View File
@@ -52,7 +52,7 @@ TEST_CASE_METHOD(StatementTestFixture, "Create prepared statement", "[statement]
table ap{"airplane"}; table ap{"airplane"};
SECTION("Insert with prepared statement and placeholder") { SECTION("Insert with prepared statement and placeholder") {
auto stmt = db.query(schema).insert() auto stmt = db.query(schema).insert()
.into<airplane>("airplane") .into("airplane", column_generator::generate<airplane>(schema, true))
.values<airplane>() .values<airplane>()
.prepare(); .prepare();
@@ -62,7 +62,10 @@ TEST_CASE_METHOD(StatementTestFixture, "Create prepared statement", "[statement]
stmt.reset(); stmt.reset();
} }
auto result = db.query(schema).select<airplane>().from(ap).fetch_all<airplane>(); auto result = db.query(schema)
.select(column_generator::generate<airplane>(schema, true))
.from(ap)
.fetch_all<airplane>();
size_t index{0}; size_t index{0};
for (const auto &i: result) { for (const auto &i: result) {
@@ -74,11 +77,19 @@ TEST_CASE_METHOD(StatementTestFixture, "Create prepared statement", "[statement]
SECTION("Select with prepared statement") { SECTION("Select with prepared statement") {
for (const auto &plane: planes) { for (const auto &plane: planes) {
auto res = db.query(schema).insert().into<airplane>("airplane").values(*plane).execute(); auto res = db.query(schema)
.insert()
.into("airplane", column_generator::generate<airplane>(schema, true))
.values(*plane)
.execute();
REQUIRE(res == 1); REQUIRE(res == 1);
} }
auto stmt = db.query(schema).select<airplane>().from(ap).where("brand"_col == _).prepare(); auto stmt = db.query(schema)
.select(column_generator::generate<airplane>(schema, true))
.from(ap)
.where("brand"_col == _)
.prepare();
stmt.bind(0, "Airbus"); stmt.bind(0, "Airbus");
+9 -13
View File
@@ -1,6 +1,7 @@
#include <catch2/catch_test_macros.hpp> #include <catch2/catch_test_macros.hpp>
#include "matador/sql/connection.hpp" #include "matador/sql/connection.hpp"
#include "matador/sql/column_generator.hpp"
#include "matador/utils/enum_mapper.hpp" #include "matador/utils/enum_mapper.hpp"
@@ -8,6 +9,7 @@
#include "models/location.hpp" #include "models/location.hpp"
using namespace matador::sql;
using namespace matador::test; using namespace matador::test;
class TypeTraitsTestFixture class TypeTraitsTestFixture
@@ -50,10 +52,8 @@ static const matador::utils::enum_mapper<Color> color_enum({
{Color::Brown, "brown"} {Color::Brown, "brown"}
}); });
namespace matador::sql {
template<> 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) inline static data_type_t builtin_type(std::size_t size)
{ return data_type_traits<std::string>::builtin_type(size); } { return data_type_traits<std::string>::builtin_type(size); }
@@ -62,13 +62,12 @@ struct data_type_traits<Color, void>
{ {
std::string enum_string; std::string enum_string;
reader.read_value(id, index, enum_string, 64); reader.read_value(id, index, enum_string, 64);
auto enum_opt = color_enum.to_enum(enum_string); if (const auto enum_opt = color_enum.to_enum(enum_string)) {
if (enum_opt) {
value = enum_opt.value(); 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); return color_enum.to_string(value);
} }
@@ -79,9 +78,6 @@ struct data_type_traits<Color, void>
} }
}; };
}
TEST_CASE_METHOD(TypeTraitsTestFixture, "Special handling of attributes with type traits", "[typetraits]") TEST_CASE_METHOD(TypeTraitsTestFixture, "Special handling of attributes with type traits", "[typetraits]")
{ {
schema.attach<location>("location"); schema.attach<location>("location");
@@ -91,14 +87,14 @@ TEST_CASE_METHOD(TypeTraitsTestFixture, "Special handling of attributes with typ
auto res = db auto res = db
.query(schema) .query(schema)
.insert() .insert()
.into<location>("location") .into("location", column_generator::generate<location>(schema, true))
.values(loc) .values(loc)
.execute(); .execute();
REQUIRE(res == 1); REQUIRE(res == 1);
auto result = db auto result = db
.query(schema) .query(schema)
.select<location>() .select(column_generator::generate<location>(schema, true))
.from("location") .from("location")
.fetch_all<location>(); .fetch_all<location>();
@@ -113,7 +109,7 @@ TEST_CASE_METHOD(TypeTraitsTestFixture, "Special handling of attributes with typ
auto stmt = db auto stmt = db
.query(schema) .query(schema)
.insert() .insert()
.into<location>("location") .into("location", column_generator::generate<location>(schema, true))
.values<location>() .values<location>()
.prepare(); .prepare();
@@ -124,7 +120,7 @@ TEST_CASE_METHOD(TypeTraitsTestFixture, "Special handling of attributes with typ
auto result = db auto result = db
.query(schema) .query(schema)
.select<location>() .select(column_generator::generate<location>(schema, true))
.from("location") .from("location")
.fetch_all<location>(); .fetch_all<location>();
+196 -71
View File
@@ -5,14 +5,14 @@
#include "matador/sql/entity.hpp" #include "matador/sql/entity.hpp"
#include "matador/utils/access.hpp" #include "matador/utils/access.hpp"
#include "matador/utils/enum_mapper.hpp"
#include "matador/sql/query_helper.hpp" #include "matador/sql/query_helper.hpp"
#include <iostream> #include <iostream>
#include <string> #include <string>
struct author struct author {
{
unsigned long id{}; unsigned long id{};
std::string first_name; std::string first_name;
std::string last_name; std::string last_name;
@@ -21,62 +21,174 @@ struct author
bool distinguished{false}; bool distinguished{false};
template<typename Operator> template<typename Operator>
void process(Operator &op) void process( Operator& op ) {
{
namespace field = matador::utils::access; namespace field = matador::utils::access;
field::primary_key(op, "id", id); field::primary_key( op, "id", id );
field::attribute(op, "first_name", first_name, 63); field::attribute( op, "first_name", first_name, 63 );
field::attribute(op, "last_name", last_name, 63); field::attribute( op, "last_name", last_name, 63 );
field::attribute(op, "date_of_birth", date_of_birth, 31); field::attribute( op, "date_of_birth", date_of_birth, 31 );
field::attribute(op, "year_of_birth", year_of_birth); field::attribute( op, "year_of_birth", year_of_birth );
field::attribute(op, "distinguished", distinguished); field::attribute( op, "distinguished", distinguished );
} }
}; };
struct book struct book {
{
unsigned long id{}; unsigned long id{};
matador::sql::entity<author> book_author; matador::sql::entity<author> book_author;
std::string title; std::string title;
unsigned short published_in{}; unsigned short published_in{};
template<typename Operator> template<typename Operator>
void process(Operator &op) void process( Operator& op ) {
{
namespace field = matador::utils::access; namespace field = matador::utils::access;
field::primary_key(op, "id", id); field::primary_key( op, "id", id );
field::attribute(op, "title", title, 511); field::attribute( op, "title", title, 511 );
field::has_one(op, "author_id", book_author, matador::utils::default_foreign_attributes); field::has_one( op, "author_id", book_author, matador::utils::default_foreign_attributes );
field::attribute(op, "published_in", published_in); field::attribute( op, "published_in", published_in );
} }
}; };
QUERY_HELPER(authors, id, first_name, last_name, date_of_birth, year_of_birth, distinguished) struct payload {
QUERY_HELPER(books, id, author_id, title, published_in) 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::sql;
using namespace matador; using namespace matador;
const std::string env_var{"MATADOR_BACKENDS_PATH"}; const std::string env_var{"MATADOR_BACKENDS_PATH"};
std::string dns{"sqlite://demo.db"}; std::string dns{"sqlite://demo.db"};
schema s("main"); schema s( "main" );
s.attach<author>("authors"); s.attach<author>( "authors" );
s.attach<book>("books"); s.attach<book>( "books" );
connection c(dns); connection c( dns );
c.open(); c.open();
s.create( c );
auto create_authors_sql = c.query(s) auto create_authors_sql = c.query( s )
.create() .create()
.table<author>(qh::authors) .table<author>( qh::authors )
.execute(); .execute();
c.query(s) c.query( s )
.create() .create()
.table<book>(qh::books) .table<book>( qh::books )
.execute(); .execute();
std::cout << "SQL: " << create_authors_sql << "\n"; std::cout << "SQL: " << create_authors_sql << "\n";
@@ -88,68 +200,62 @@ int main()
mc.date_of_birth = "19.8.1954"; mc.date_of_birth = "19.8.1954";
mc.year_of_birth = 1954; mc.year_of_birth = 1954;
mc.distinguished = true; mc.distinguished = true;
auto insert_authors_sql = c.query(s) auto insert_authors_sql = c.query( s )
.insert() .insert()
.into<author>(qh::authors) .into( qh::authors )
.values(mc) .values( mc )
.execute(); .execute();
std::cout << "SQL: " << insert_authors_sql << "\n"; std::cout << "SQL: " << insert_authors_sql << "\n";
auto result = c.query(s) auto result = c.query( s )
.select<author>() .select( qh::authors.columns )
.from(qh::authors) .from( qh::authors )
.fetch_all(); .fetch_all();
for (const auto &row: result) { for (const auto& row: result) { std::cout << "Author " << row.at( qh::authors.first_name ) << "\n"; }
std::cout << "Author " << row.at(qh::authors.first_name) << "\n";
}
auto update_authors_sql = c.query(s) auto update_authors_sql = c.query( s )
.update(qh::authors) .update( qh::authors )
.set({{qh::authors.first_name, "Stephen"}, .set( {{qh::authors.first_name, "Stephen"},
{qh::authors.last_name, "King"}}) {qh::authors.last_name, "King"}} )
.where(qh::authors.last_name == "Crichton") .where( qh::authors.last_name == "Crichton" )
.execute(); .execute();
std::cout << "SQL: " << update_authors_sql << "\n"; std::cout << "SQL: " << update_authors_sql << "\n";
auto authors = c.query(s) auto authors = c.query( s )
.select<author>() .select( qh::authors.columns )
.from(qh::authors) .from( qh::authors )
.fetch_all<author>(); .fetch_all<author>();
for (const auto &a: authors) { for (const auto& a: authors) { std::cout << "Author " << a.first_name << "\n"; }
std::cout << "Author " << a.first_name << "\n";
}
c.query(s) c.query( s )
.insert() .insert()
.into<book>(qh::books) .into( qh::books )
.values({2, "It", mc.id, 1980}) .values( {2, "It", mc.id, 1980} )
.execute(); .execute();
c.query(s) c.query( s )
.insert() .insert()
.into<book>(qh::books) .into( qh::books )
.values({3, "Misery", mc.id, 1984}) .values( {3, "Misery", mc.id, 1984} )
.execute(); .execute();
auto select_books_sql = c.query(s) auto select_books_sql = c.query( s )
.select<book>({qh::authors.last_name}) .select( qh::books.columns, {qh::authors.last_name} )
.from(qh::books) .from( qh::books )
.join_left(qh::authors) .join_left( qh::authors )
.on(qh::books.author_id == qh::authors.id) .on( qh::books.author_id == qh::authors.id )
.where(qh::books.published_in < 2008 && qh::authors.last_name == "King") .where( qh::books.published_in < 2008 && qh::authors.last_name == "King" )
.group_by(qh::books.published_in) .group_by( qh::books.published_in )
.order_by(qh::books.title).asc() .order_by( qh::books.title ).asc()
.limit(5) .limit( 5 )
.offset(2) .offset( 2 )
.fetch_all(); .fetch_all();
for (const auto &r: select_books_sql) { for (const auto& r: select_books_sql) { std::cout << "R: " << r.at( qh::books.title ) << ", " << r.at( qh::authors.last_name ) << "\n"; }
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 // SELECT book.title, book.id, book.author_id, book.published_in, author.name
// FROM book // FROM book
// INNER JOIN author ON book.author_id = author.id // INNER JOIN author ON book.author_id = author.id
@@ -157,14 +263,33 @@ int main()
// ORDER BY "book.title" ASC // ORDER BY "book.title" ASC
// OFFSET 2 LIMIT 5 // 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() .drop()
.table(qh::authors) .table( qh::authors )
.execute(); .execute();
std::cout << "SQL: " << drop_authors_sql << "\n"; 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; return 0;
} }
-25
View File
@@ -1,25 +0,0 @@
#ifndef QUERY_COLUMN_HPP
#define QUERY_COLUMN_HPP
#include <string>
namespace matador::query {
class column {
public:
column(const char *name); // NOLINT(*-explicit-constructor)
column(std::string name); // NOLINT(*-explicit-constructor)
column(std::string table_name, std::string name, std::string as);
column& as(std::string a);
std::string table;
std::string name;
std::string alias;
};
column operator "" _col(const char *name, size_t len);
}
#endif //QUERY_COLUMN_HPP
-22
View File
@@ -1,22 +0,0 @@
#ifndef QUERY_QUERY_HPP
#define QUERY_QUERY_HPP
#include "matador/query/query_parts.hpp"
namespace matador::query {
class connection;
class query
{
public:
explicit query(connection &c);
query& select(const std::vector<column> &columns);
query& from(const std::string &table, const std::string &as = "");
private:
connection &connection_;
};
}
#endif //QUERY_QUERY_HPP
-24
View File
@@ -1,24 +0,0 @@
#ifndef QUERY_QUERY_BUILDER_HPP
#define QUERY_QUERY_BUILDER_HPP
#include "matador/query/column.hpp"
#include "matador/query/query_intermediates.hpp"
#include "matador/sql/schema.hpp"
#include <memory>
namespace matador::query {
class query_builder
{
public:
explicit query_builder(const std::shared_ptr<sql::schema> &scm);
query_select_intermediate select(std::initializer_list<column> columns);
private:
std::shared_ptr<sql::schema> schema_;
};
}
#endif //QUERY_QUERY_BUILDER_HPP
-37
View File
@@ -1,37 +0,0 @@
#ifndef QUERY_QUERY_COMPILER_HPP
#define QUERY_QUERY_COMPILER_HPP
#include "matador/query/query_part_visitor.hpp"
#include "matador/query/query_parts.hpp"
#include <typeindex>
#include <string>
namespace matador::sql {
class dialect;
}
namespace matador::query {
struct query_data;
class query_compiler : public query_part_visitor
{
public:
explicit query_compiler(sql::dialect& d);
std::string compile(const query_data *data);
private:
void visit(query_select_part &select_part) override;
void visit(query_from_part &from_part) override;
private:
sql::dialect &dialect_;
std::string sql_;
};
}
#endif //QUERY_QUERY_COMPILER_HPP
-24
View File
@@ -1,24 +0,0 @@
#ifndef QUERY_QUERY_DATA_HPP
#define QUERY_QUERY_DATA_HPP
#include "matador/query/query_parts.hpp"
#include "matador/query/sql_commands.hpp"
#include "matador/query/table.hpp"
#include <memory>
#include <vector>
namespace matador::query {
struct query_data
{
SqlCommands command;
std::vector<std::unique_ptr<query_part>> parts;
std::vector<column> columns;
std::unordered_map<std::type_index, table_data> table_map_by_index;
using table_data_ref = std::reference_wrapper<table_data>;
std::unordered_map<std::string, table_data_ref> table_map_by_name;
};
}
#endif //QUERY_QUERY_DATA_HPP
@@ -1,132 +0,0 @@
#ifndef QUERY_QUERY_INTERMEDIATES_HPP
#define QUERY_QUERY_INTERMEDIATES_HPP
#include "matador/sql/schema.hpp"
#include <string>
#include <memory>
namespace matador::sql {
class basic_condition;
}
namespace matador::query {
class query_from_intermediate;
class query_join_intermediate;
class query_on_intermediate;
class query_where_intermediate;
class query_group_by_intermediate;
class query_order_by_intermediate;
class query_order_direction_intermediate;
class query_offset_intermediate;
class query_limit_intermediate;
class query_intermediate
{
public:
explicit query_intermediate(const std::shared_ptr<sql::schema> &scm);
protected:
sql::schema& schema() const;
private:
std::shared_ptr<sql::schema> schema_;
};
class query_select_finish_intermediate : public query_intermediate
{
public:
using query_intermediate::query_intermediate;
void compile();
};
class query_select_intermediate : public query_intermediate
{
public:
using query_intermediate::query_intermediate;
query_from_intermediate from(const std::string &table, const std::string &as = "");
};
class query_from_intermediate : public query_select_finish_intermediate
{
public:
using query_select_finish_intermediate::query_intermediate;
query_join_intermediate join(const std::string &join_table_name, const std::string &as);
query_where_intermediate where(const sql::basic_condition &cond);
query_group_by_intermediate group_by(const std::string &name);
query_order_by_intermediate order_by(const std::string &name);
};
class query_join_intermediate : public query_intermediate
{
public:
using query_intermediate::query_intermediate;
query_on_intermediate on(const std::string &column, const std::string &join_column);
};
class query_on_intermediate : public query_select_finish_intermediate
{
public:
using query_select_finish_intermediate::query_intermediate;
query_join_intermediate join(const std::string &join_table_name, const std::string &as);
query_where_intermediate where(const sql::basic_condition &cond);
query_group_by_intermediate group_by(const std::string &name);
query_order_by_intermediate order_by(const std::string &name);
};
class query_where_intermediate : public query_select_finish_intermediate
{
public:
using query_select_finish_intermediate::query_intermediate;
query_group_by_intermediate group_by(const std::string &name);
query_order_by_intermediate order_by(const std::string &name);
};
class query_group_by_intermediate : public query_select_finish_intermediate
{
public:
using query_select_finish_intermediate::query_intermediate;
query_order_by_intermediate order_by(const std::string &name);
};
class query_order_by_intermediate : public query_intermediate
{
public:
using query_intermediate::query_intermediate;
query_order_direction_intermediate asc();
query_order_direction_intermediate desc();
};
class query_order_direction_intermediate : public query_select_finish_intermediate
{
public:
using query_select_finish_intermediate::query_intermediate;
query_offset_intermediate offset(size_t offset);
query_limit_intermediate limit(size_t limit);
};
class query_offset_intermediate : public query_intermediate
{
public:
using query_intermediate::query_intermediate;
query_limit_intermediate limit(size_t limit);
};
class query_limit_intermediate : public query_select_finish_intermediate
{
public:
using query_select_finish_intermediate::query_intermediate;
};
}
#endif //QUERY_QUERY_INTERMEDIATES_HPP
@@ -1,19 +0,0 @@
#ifndef QUERY_QUERY_PART_VISITOR_HPP
#define QUERY_QUERY_PART_VISITOR_HPP
namespace matador::query {
class query_select_part;
class query_from_part;
class query_part_visitor
{
public:
virtual ~query_part_visitor() = default;
virtual void visit(query_select_part &select_part) = 0;
virtual void visit(query_from_part &from_part) = 0;
};
}
#endif //QUERY_QUERY_PART_VISITOR_HPP
-55
View File
@@ -1,55 +0,0 @@
#ifndef QUERY_QUERY_PARTS_HPP
#define QUERY_QUERY_PARTS_HPP
#include "matador/query/query_part_visitor.hpp"
#include "matador/query/column.hpp"
#include "matador/sql/dialect.hpp"
namespace matador::query {
class query_part
{
protected:
explicit query_part(sql::dialect::token_t token);
public:
virtual ~query_part() = default;
virtual void accept(query_part_visitor &visitor) = 0;
protected:
sql::dialect::token_t token_;
};
/**
* Represents the SQL SELECT part
*/
class query_select_part : public query_part
{
public:
explicit query_select_part(std::vector<column> columns);
void accept(query_part_visitor &visitor) override;
private:
std::vector<column> columns_;
};
/**
* Represents the SQL FROM part
*/
class query_from_part : public query_part
{
public:
explicit query_from_part(std::string table_name);
query_from_part(std::string table_name, std::string as);
private:
void accept(query_part_visitor &visitor) override;
private:
std::string table_;
std::string alias_;
};
}
#endif //QUERY_QUERY_PARTS_HPP
-31
View File
@@ -1,31 +0,0 @@
#ifndef QUERY_SQL_COMMANDS_HPP
#define QUERY_SQL_COMMANDS_HPP
#include "matador/utils/enum_mapper.hpp"
namespace matador::query {
enum class SqlCommands
{
UNKNOWN, /**< Unknown query command */
CREATE, /**< Create query command */
DROP, /**< Drop query command */
SELECT, /**< Select query command */
INSERT, /**< Insert query command */
UPDATE, /**< Update query command */
REMOVE /**< Remove query command */
};
static const utils::enum_mapper<SqlCommands> sql_command_enum({
{SqlCommands::UNKNOWN, "unknown"},
{SqlCommands::CREATE, "create"},
{SqlCommands::DROP, "drop"},
{SqlCommands::SELECT, "select"},
{SqlCommands::INSERT, "insert"},
{SqlCommands::UPDATE, "update"},
{SqlCommands::REMOVE, "delete"}
});
}
#endif //QUERY_SQL_COMMANDS_HPP
-16
View File
@@ -1,16 +0,0 @@
#ifndef QUERY_TABLE_HPP
#define QUERY_TABLE_HPP
#include <typeindex>
#include <string>
namespace matador::query {
struct table_data
{
std::type_index index;
std::string name;
};
}
#endif //QUERY_TABLE_HPP
+1 -1
View File
@@ -33,7 +33,7 @@ public:
LIKE LIKE
}; };
virtual std::string evaluate(dialect &dialect, query_context &query) const = 0; virtual std::string evaluate(const dialect &dialect, query_context &query) const = 0;
static std::unordered_map<operand_t, std::string> operands; static std::unordered_map<operand_t, std::string> operands;
}; };
+12 -18
View File
@@ -5,6 +5,8 @@
namespace matador::sql { namespace matador::sql {
struct table;
enum class sql_function_t { enum class sql_function_t {
NONE, NONE,
COUNT, COUNT,
@@ -16,26 +18,18 @@ enum class sql_function_t {
struct column struct column
{ {
column(const char *name) : name(name) {} // NOLINT(*-explicit-constructor) column(const char *name); // NOLINT(*-explicit-constructor)
column(std::string name) : name(std::move(name)) {} // NOLINT(*-explicit-constructor) column(std::string name); // NOLINT(*-explicit-constructor)
column(sql_function_t func, std::string name) : name(std::move(name)), function_(func) {} // NOLINT(*-explicit-constructor) column(sql_function_t func, std::string name); // NOLINT(*-explicit-constructor)
column(std::string table_name, std::string name, std::string as) column(std::string table_name, std::string name, std::string as = "");
: table(std::move(table_name)) column(std::string table_name, const char* name, std::string as = "");
, name(std::move(name)) column(struct table &t, const char* name, std::string as = "");
, alias(std::move(as)) {}
column(std::string table_name, const char* name, std::string as)
: table(std::move(table_name))
, name(name)
, alias(std::move(as)) {}
column& as(std::string a) { [[nodiscard]] bool equals(const column &x) const;
alias = std::move(a);
return *this;
}
[[nodiscard]] bool is_function() const { column& as(std::string a);
return function_ != sql_function_t::NONE;
} [[nodiscard]] bool is_function() const;
std::string table; std::string table;
std::string name; std::string name;
@@ -53,6 +53,15 @@ public:
[[nodiscard]] const std::string& ref_table() const; [[nodiscard]] const std::string& ref_table() const;
[[nodiscard]] const std::string& ref_column() const; [[nodiscard]] const std::string& ref_column() const;
[[nodiscard]] bool is_integer() const;
[[nodiscard]] bool is_floating_point() const;
[[nodiscard]] bool is_bool() const;
[[nodiscard]] bool is_string() const;
[[nodiscard]] bool is_varchar() const;
[[nodiscard]] bool is_blob() const;
[[nodiscard]] bool is_null() const;
[[nodiscard]] bool is_unknown() const;
void type(data_type_t type); void type(data_type_t type);
template< typename Type > template< typename Type >
@@ -0,0 +1,133 @@
#ifndef QUERY_COLUMN_DEFINITION_GENERATOR_HPP
#define QUERY_COLUMN_DEFINITION_GENERATOR_HPP
#include "matador/sql/column_definition.hpp"
#include "matador/sql/data_type_traits.hpp"
#include "matador/utils/access.hpp"
#include "matador/utils/field_attributes.hpp"
#include "matador/utils/foreign_attributes.hpp"
#include <typeindex>
#include <vector>
namespace matador::sql {
class schema;
class fk_column_generator
{
public:
fk_column_generator() = default;
template<class Type>
column_definition generate(const char *id, Type &x, const std::string &ref_table, const std::string &ref_column)
{
utils::access::process(*this, x);
return column_definition{id, type_, 0, ref_table, ref_column, {utils::constraints::FOREIGN_KEY }, null_option::NOT_NULL};
}
template<typename ValueType>
void on_primary_key(const char *, ValueType &/*pk*/, typename std::enable_if<std::is_integral<ValueType>::value && !std::is_same<bool, ValueType>::value>::type* = 0)
{
type_ = data_type_traits<ValueType>::builtin_type(0);
}
void on_primary_key(const char * /*id*/, std::string &/*pk*/, size_t size);
void on_revision(const char * /*id*/, unsigned long long &/*rev*/) {}
template < class Type >
void on_attribute(const char * /*id*/, Type &/*x*/, const utils::field_attributes &/*attr*/ = utils::null_attributes) {}
void on_attribute(const char * /*id*/, char * /*x*/, const utils::field_attributes &/*attr*/ = utils::null_attributes) {}
template<class Pointer>
void on_belongs_to(const char * /*id*/, Pointer &/*x*/, const utils::foreign_attributes &/*attr*/) {}
template<class Pointer>
void on_has_one(const char * /*id*/, Pointer &/*x*/, const utils::foreign_attributes &/*attr*/) {}
template<class ContainerType>
void on_has_many(ContainerType &, const char *, 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*/) {}
template<class ContainerType>
void on_has_many_to_many(const char *id, ContainerType &c, const utils::foreign_attributes &/*attr*/) {}
private:
data_type_t type_{};
};
class column_definition_generator
{
private:
column_definition_generator(std::vector<column_definition> &columns, const schema &repo);
public:
~column_definition_generator() = default;
template < class Type >
static std::vector<column_definition> generate(const schema &repo)
{
std::vector<column_definition> columns;
column_definition_generator gen(columns, repo);
Type obj;
matador::utils::access::process(gen, obj);
return std::move(columns);
}
template < class V >
void on_primary_key(const char *, V &x, typename std::enable_if<std::is_integral<V>::value && !std::is_same<bool, V>::value>::type* = 0);
void on_primary_key(const char *id, std::string &pk, size_t size);
void on_revision(const char *id, unsigned long long &rev);
template<typename Type>
void on_attribute(const char *id, Type &x, const utils::field_attributes &attr = utils::null_attributes);
template<typename Type>
void on_attribute(const char *id, std::optional<Type> &x, const utils::field_attributes &attr = utils::null_attributes);
template<class Pointer>
void on_belongs_to(const char *id, Pointer &x, const utils::foreign_attributes &/*attr*/)
{
const auto [ref_table, ref_column] = determine_foreign_ref(std::type_index(typeid(typename Pointer::value_type)));
columns_.push_back(fk_column_generator_.generate(id, *x, ref_table, ref_column));
}
template<class Pointer>
void on_has_one(const char *id, Pointer &x, const utils::foreign_attributes &/*attr*/)
{
const auto [ref_table, ref_column] = determine_foreign_ref(std::type_index(typeid(typename Pointer::value_type)));
columns_.push_back(fk_column_generator_.generate(id, *x, ref_table, ref_column));
}
template<class ContainerType>
void on_has_many(ContainerType &, const char *, 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*/) {}
template<class ContainerType>
void on_has_many_to_many(const char *id, ContainerType &c, const utils::foreign_attributes &/*attr*/) {}
private:
std::pair<std::string, std::string> determine_foreign_ref(const std::type_index &ti);
private:
size_t index_ = 0;
std::vector<column_definition> &columns_;
const schema &repo_;
fk_column_generator fk_column_generator_;
};
template<typename V>
void column_definition_generator::on_primary_key(const char *id, V &x, typename std::enable_if<std::is_integral<V>::value && !std::is_same<bool, V>::value>::type*)
{
on_attribute(id, x, { utils::constraints::PRIMARY_KEY });
}
template<typename Type>
void column_definition_generator::on_attribute(const char *id, Type &x, const utils::field_attributes &attr)
{
columns_.emplace_back(id, x, attr, null_option::NOT_NULL);
}
template<typename Type>
void column_definition_generator::on_attribute(const char *id, std::optional<Type> &x, const utils::field_attributes &attr)
{
columns_.emplace_back(id, data_type_traits<Type>::builtin_type(attr.size()), attr, null_option::NULLABLE);
}
}
#endif //QUERY_COLUMN_DEFINITION_GENERATOR_HPP
+82 -83
View File
@@ -1,129 +1,128 @@
#ifndef QUERY_COLUMN_GENERATOR_HPP #ifndef QUERY_COLUMN_GENERATOR_HPP
#define QUERY_COLUMN_GENERATOR_HPP #define QUERY_COLUMN_GENERATOR_HPP
#include "matador/sql/column_definition.hpp"
#include "matador/sql/data_type_traits.hpp"
#include "matador/utils/access.hpp" #include "matador/utils/access.hpp"
#include "matador/utils/field_attributes.hpp" #include "matador/utils/field_attributes.hpp"
#include "matador/utils/foreign_attributes.hpp" #include "matador/utils/foreign_attributes.hpp"
#include <typeindex> #include "matador/sql/column.hpp"
#include "matador/sql/schema.hpp"
#include <string>
#include <vector> #include <vector>
#include <stack>
namespace matador::sql { namespace matador::sql {
class schema;
class fk_column_generator
{
public:
fk_column_generator() = default;
template<class Type>
column_definition generate(const char *id, Type &x, const std::string &ref_table, const std::string &ref_column)
{
utils::access::process(*this, x);
return column_definition{id, type_, 0, ref_table, ref_column, {utils::constraints::FOREIGN_KEY }, null_option::NOT_NULL};
}
template<typename ValueType>
void on_primary_key(const char *, ValueType &/*pk*/, typename std::enable_if<std::is_integral<ValueType>::value && !std::is_same<bool, ValueType>::value>::type* = 0)
{
type_ = data_type_traits<ValueType>::builtin_type(0);
}
void on_primary_key(const char * /*id*/, std::string &/*pk*/, size_t size);
void on_revision(const char * /*id*/, unsigned long long &/*rev*/) {}
template < class Type >
void on_attribute(const char * /*id*/, Type &/*x*/, const utils::field_attributes &/*attr*/ = utils::null_attributes) {}
void on_attribute(const char * /*id*/, char * /*x*/, const utils::field_attributes &/*attr*/ = utils::null_attributes) {}
template<class Pointer>
void on_belongs_to(const char * /*id*/, Pointer &/*x*/, const utils::foreign_attributes &/*attr*/) {}
template<class Pointer>
void on_has_one(const char * /*id*/, Pointer &/*x*/, const utils::foreign_attributes &/*attr*/) {}
template<class ContainerType>
void on_has_many(const char *, ContainerType &, const char *, const char *, const utils::foreign_attributes &/*attr*/) {}
template<class ContainerType>
void on_has_many(const char *, ContainerType &, const utils::foreign_attributes &/*attr*/) {}
private:
data_type_t type_{};
};
class column_generator class column_generator
{ {
private: private:
column_generator(std::vector<column_definition> &columns, const schema &repo); column_generator(std::vector<column> &column_infos,
const sql::schema &ts,
const std::string &table_name,
bool force_lazy);
public: public:
~column_generator() = default; ~column_generator() = default;
template < class Type > template < class Type >
static std::vector<column_definition> generate(const schema &repo) static std::vector<column> generate(const sql::schema &ts, bool force_lazy = false)
{ {
std::vector<column_definition> columns; const auto info = ts.info<Type>();
column_generator gen(columns, repo); if (!info) {
return {};
}
std::vector<column> columns;
column_generator gen(columns, ts, info.value().name, force_lazy);
Type obj; Type obj;
matador::utils::access::process(gen, obj); matador::utils::access::process(gen, obj);
return std::move(columns); return std::move(columns);
} }
template < class V > template < class V >
void on_primary_key(const char *, V &x, typename std::enable_if<std::is_integral<V>::value && !std::is_same<bool, V>::value>::type* = 0); void on_primary_key(const char *id, 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 &pk, size_t size); {
void on_revision(const char *id, unsigned long long &rev); push(id);
}
void on_primary_key(const char *id, std::string &, size_t);
void on_revision(const char *id, unsigned long long &/*rev*/);
template<typename Type> template<typename Type>
void on_attribute(const char *id, Type &x, const utils::field_attributes &attr = utils::null_attributes); void on_attribute(const char *id, Type &, const utils::field_attributes &/*attr*/ = utils::null_attributes)
{
template<typename Type> push(id);
void on_attribute(const char *id, std::optional<Type> &x, const utils::field_attributes &attr = utils::null_attributes); }
template<class Pointer> template<class Pointer>
void on_belongs_to(const char *id, Pointer &x, const utils::foreign_attributes &/*attr*/) void on_belongs_to(const char *id, Pointer &, const utils::foreign_attributes &attr)
{ {
const auto [ref_table, ref_column] = determine_foreign_ref(std::type_index(typeid(typename Pointer::value_type))); if (attr.fetch() == utils::fetch_type::LAZY || force_lazy_) {
columns_.push_back(fk_column_generator_.generate(id, *x, ref_table, ref_column)); push(id);
} else {
const auto info = table_schema_.info<typename Pointer::value_type>();
if (!info) {
return;
}
table_name_stack_.push(info.value().name);
typename Pointer::value_type obj;
matador::utils::access::process(*this, obj);
table_name_stack_.pop();
}
} }
template<class Pointer> template<class Pointer>
void on_has_one(const char *id, Pointer &x, const utils::foreign_attributes &/*attr*/) void on_has_one(const char *id, Pointer &, const utils::foreign_attributes &attr)
{ {
const auto [ref_table, ref_column] = determine_foreign_ref(std::type_index(typeid(typename Pointer::value_type))); if (attr.fetch() == utils::fetch_type::LAZY || force_lazy_) {
columns_.push_back(fk_column_generator_.generate(id, *x, ref_table, ref_column)); push(id);
} else {
const auto info = table_schema_.info<typename Pointer::value_type>();
if (!info) {
return;
}
table_name_stack_.push(info.value().name);
typename Pointer::value_type obj;
matador::utils::access::process(*this, obj);
table_name_stack_.pop();
}
} }
template<class ContainerType> template<class ContainerType>
void on_has_many(const char *, ContainerType &, const char *, const char *, const utils::foreign_attributes &/*attr*/) {} void on_has_many(ContainerType &, const char *, const utils::foreign_attributes &attr)
{
if (attr.fetch() == utils::fetch_type::LAZY || force_lazy_) {
return;
}
const auto info = table_schema_.info<typename ContainerType::value_type::value_type>();
if (!info) {
return;
}
table_name_stack_.push(info.value().name);
typename ContainerType::value_type::value_type obj;
matador::utils::access::process(*this, obj);
table_name_stack_.pop();
}
template<class ContainerType> template<class ContainerType>
void on_has_many(const char *, ContainerType &, const utils::foreign_attributes &/*attr*/) {} 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)
{
}
template<class ContainerType>
void on_has_many_to_many(const char *id, ContainerType &c, const utils::foreign_attributes &attr)
{
}
private: private:
std::pair<std::string, std::string> determine_foreign_ref(const std::type_index &ti); void push(const std::string &column_name);
private: private:
size_t index_ = 0; std::stack<std::string> table_name_stack_;
std::vector<column_definition> &columns_; std::vector<column> &column_infos_;
const schema &repo_; const sql::schema &table_schema_;
int column_index{0};
fk_column_generator fk_column_generator_; bool force_lazy_{false};
}; };
template<typename V>
void column_generator::on_primary_key(const char *id, V &x, typename std::enable_if<std::is_integral<V>::value && !std::is_same<bool, V>::value>::type*)
{
on_attribute(id, x, { utils::constraints::PRIMARY_KEY });
} }
template<typename Type>
void column_generator::on_attribute(const char *id, Type &x, const utils::field_attributes &attr)
{
columns_.emplace_back(id, x, attr, null_option::NOT_NULL);
}
template<typename Type>
void column_generator::on_attribute(const char *id, std::optional<Type> &x, const utils::field_attributes &attr)
{
columns_.emplace_back(id, data_type_traits<Type>::builtin_type(attr.size()), attr, null_option::NULLABLE);
}
}
#endif //QUERY_COLUMN_GENERATOR_HPP #endif //QUERY_COLUMN_GENERATOR_HPP
@@ -1,97 +0,0 @@
#ifndef QUERY_COLUMN_NAME_GENERATOR_HPP
#define QUERY_COLUMN_NAME_GENERATOR_HPP
#include "matador/utils/access.hpp"
#include "matador/utils/field_attributes.hpp"
#include "matador/utils/foreign_attributes.hpp"
#include "matador/sql/column.hpp"
#include "matador/sql/schema.hpp"
#include <string>
#include <vector>
#include <stack>
namespace matador::sql {
class column_name_generator
{
private:
column_name_generator(std::vector<column> &column_infos, const sql::schema &ts, const std::string &table_name);
public:
~column_name_generator() = default;
template < class Type >
static std::vector<column> generate(const sql::schema &ts)
{
const auto info = ts.info<Type>();
if (!info) {
return {};
}
std::vector<column> columns;
column_name_generator gen(columns, ts, info.value().name);
Type obj;
matador::utils::access::process(gen, obj);
return std::move(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)
{
push(id);
}
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)
{
push(id);
}
template<class Pointer>
void on_belongs_to(const char *id, Pointer &, const utils::foreign_attributes &/*attr*/)
{
push(id);
}
template<class Pointer>
void on_has_one(const char *id, Pointer &, const utils::foreign_attributes &/*attr*/)
{
push(id);
}
template<class ContainerType>
void on_has_many(const char *, ContainerType &, const char *, const char *, const utils::foreign_attributes &attr)
{
if (attr.fetch() == utils::fetch_type::LAZY) {
return;
}
const auto info = table_schema_.info<typename ContainerType::value_type::value_type>();
if (!info) {
return;
}
table_name_stack_.push(info.value().name);
typename ContainerType::value_type::value_type obj;
matador::utils::access::process(*this, obj);
table_name_stack_.pop();
}
template<class ContainerType>
void on_has_many(const char *id, ContainerType &c, const utils::foreign_attributes &attr)
{
on_has_many(id, c, "", "", attr);
}
private:
void push(const std::string &column_name);
private:
std::stack<std::string> table_name_stack_;
std::vector<column> &column_infos_;
const sql::schema &table_schema_;
int column_index{0};
};
}
#endif //QUERY_COLUMN_NAME_GENERATOR_HPP
+46 -18
View File
@@ -1,6 +1,8 @@
#ifndef QUERY_CONDITION_HPP #ifndef QUERY_CONDITION_HPP
#define 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/basic_condition.hpp"
#include "matador/sql/dialect.hpp" #include "matador/sql/dialect.hpp"
#include "matador/sql/placeholder.hpp" #include "matador/sql/placeholder.hpp"
@@ -26,6 +28,8 @@ namespace matador::sql {
/// @cond MATADOR_DEV /// @cond MATADOR_DEV
class query_select;
template<class L, class R, class Enabled = void> template<class L, class R, class Enabled = void>
class condition; class condition;
@@ -37,12 +41,13 @@ public:
placeholder value; placeholder value;
std::string evaluate(dialect &d, query_context &query) const override; std::string evaluate(const dialect &d, query_context &query) const override;
}; };
template<class T> template<class T>
class condition<column, T, typename std::enable_if< class condition<column, T, typename std::enable_if<
std::is_scalar<T>::value && std::is_scalar<T>::value &&
!std::is_enum<T>::value &&
!std::is_same<std::string, T>::value && !std::is_same<std::string, T>::value &&
!std::is_same<const char*, T>::value>::type> : public basic_column_condition !std::is_same<const char*, T>::value>::type> : public basic_column_condition
{ {
@@ -54,7 +59,7 @@ public:
T value; T value;
std::string evaluate(dialect &d, query_context &query) const override std::string evaluate(const dialect &d, query_context &query) const override
{ {
query.bind_vars.emplace_back(field_.name); query.bind_vars.emplace_back(field_.name);
return d.prepare_identifier(field_) + " " + operand + " " + std::to_string(value); return d.prepare_identifier(field_) + " " + operand + " " + std::to_string(value);
@@ -74,7 +79,7 @@ public:
T value; T value;
std::string evaluate(dialect &d, query_context &query) const override std::string evaluate(const dialect &d, query_context &query) const override
{ {
query.bind_vars.emplace_back(field_.name); query.bind_vars.emplace_back(field_.name);
return d.prepare_identifier(field_) + " " + operand + " '" + value + "'"; return d.prepare_identifier(field_) + " " + operand + " '" + value + "'";
@@ -82,10 +87,32 @@ public:
}; };
template<class T> template<class T>
class condition<T, column, typename std::enable_if< class condition<column, T, std::enable_if_t<std::is_enum_v<T>>> final : public basic_column_condition
std::is_scalar<T>::value && {
!std::is_same<std::string, T>::value && public:
!std::is_same<const char*, T>::value>::type> : public basic_column_condition 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: public:
condition(T val, basic_condition::operand_t op, const column &fld) condition(T val, basic_condition::operand_t op, const column &fld)
@@ -95,7 +122,7 @@ public:
T value; T value;
std::string evaluate(dialect &d, query_context &query) const override std::string evaluate(const dialect &d, query_context &query) const override
{ {
return std::to_string(value) + " " + operand + " " + d.prepare_identifier(field_); return std::to_string(value) + " " + operand + " " + d.prepare_identifier(field_);
} }
@@ -114,7 +141,7 @@ public:
T value; T value;
std::string evaluate(dialect &d, query_context &query) const override std::string evaluate(const dialect &d, query_context &query) const override
{ {
return "'" + std::to_string(value) + "' " + operand + " " + d.prepare_identifier(field_); return "'" + std::to_string(value) + "' " + operand + " " + d.prepare_identifier(field_);
} }
@@ -156,7 +183,7 @@ public:
* @param d The d used to evaluate * @param d The d used to evaluate
* @return A condition IN part of the query * @return A condition IN part of the query
*/ */
std::string evaluate(dialect &d, query_context &query) const override { std::string evaluate(const dialect &d, query_context &query) const override {
auto count = size(); auto count = size();
for (size_t i = 0; i < count; ++i) { for (size_t i = 0; i < count; ++i) {
query.bind_vars.emplace_back(field_.name); query.bind_vars.emplace_back(field_.name);
@@ -215,7 +242,7 @@ public:
* @param op Operand of the condition * @param op Operand of the condition
* @param q The query to be evaluated to the IN arguments * @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 * @brief Evaluates the condition
@@ -226,10 +253,10 @@ public:
* @param d The d used to evaluate * @param d The d used to evaluate
* @return A condition IN part of the query * @return A condition IN part of the query
*/ */
std::string evaluate(dialect &d, query_context &query) const override; std::string evaluate(const dialect &d, query_context &query) const override;
private: private:
query_context &query_; query_context query_;
}; };
/** /**
@@ -261,7 +288,7 @@ public:
* @param d The d used to evaluate * @param d The d used to evaluate
* @return A condition BETWEEN part of the query * @return A condition BETWEEN part of the query
*/ */
std::string evaluate(dialect &d, query_context &query) const override { std::string evaluate(const dialect &d, query_context &query) const override {
query.bind_vars.emplace_back(field_.name); query.bind_vars.emplace_back(field_.name);
query.bind_vars.emplace_back(field_.name); query.bind_vars.emplace_back(field_.name);
return d.prepare_identifier(field_) + " BETWEEN " + std::to_string(range_.first) + " AND " + std::to_string(range_.second); return d.prepare_identifier(field_) + " BETWEEN " + std::to_string(range_.first) + " AND " + std::to_string(range_.second);
@@ -303,7 +330,7 @@ public:
* @param d The d used to evaluate * @param d The d used to evaluate
* @return The evaluated string based on the compile type * @return The evaluated string based on the compile type
*/ */
std::string evaluate(dialect &d, query_context &query) const override std::string evaluate(const dialect &d, query_context &query) const override
{ {
// ensure the numbering order for host vars // ensure the numbering order for host vars
auto cl = left.evaluate(d, query); auto cl = left.evaluate(d, query);
@@ -346,7 +373,7 @@ public:
* @param d The d used to evaluate * @param d The d used to evaluate
* @return The evaluated string based on the compile type * @return The evaluated string based on the compile type
*/ */
std::string evaluate(dialect &d, query_context &query) const override std::string evaluate(const dialect &d, query_context &query) const override
{ {
return operand + " (" + cond.evaluate(d) + ")"; return operand + " (" + cond.evaluate(d) + ")";
} }
@@ -369,7 +396,7 @@ public:
* @param d The d used to evaluate * @param d The d used to evaluate
* @return The evaluated string based on the compile type * @return The evaluated string based on the compile type
*/ */
std::string evaluate(dialect &d, query_context &query) const override std::string evaluate(const dialect &d, query_context &query) const override
{ {
return d.prepare_identifier(field_) + " " + operand + " " + d.prepare_identifier(other_column_); return d.prepare_identifier(field_) + " " + operand + " " + d.prepare_identifier(other_column_);
} }
@@ -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 * @param q The query to be executes as sub select
* @return The condition object * @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. * @brief Creates a between condition.
+24 -8
View File
@@ -7,6 +7,7 @@
#include <mutex> #include <mutex>
#include <string> #include <string>
#include <optional> #include <optional>
#include <condition_variable>
#include <thread> #include <thread>
#include <unordered_map> #include <unordered_map>
@@ -86,19 +87,21 @@ public:
} }
connection_pointer acquire() { connection_pointer acquire() {
std::unique_lock<std::mutex> lock(mutex_);
while (idle_connections_.empty()) {
cv.wait(lock);
}
return get_next_connection();
}
connection_pointer try_acquire() {
std::unique_lock<std::mutex> lock(mutex_); std::unique_lock<std::mutex> lock(mutex_);
if (idle_connections_.empty()) { if (idle_connections_.empty()) {
return {nullptr, this}; return {nullptr, this};
} }
pointer next_connection{nullptr}; return get_next_connection();
for (auto &item : idle_connections_) {
next_connection = item.second;
auto node = idle_connections_.extract(item.first);
inuse_connections_.insert(std::move(node));
break;
}
return {next_connection, this};
} }
connection_pointer acquire(size_t id) { connection_pointer acquire(size_t id) {
@@ -151,8 +154,21 @@ public:
const connection_info &info() const { const connection_info &info() const {
return info_; return info_;
} }
private:
connection_pointer get_next_connection() {
pointer next_connection{nullptr};
for (auto &item : idle_connections_) {
next_connection = item.second;
auto node = idle_connections_.extract(item.first);
inuse_connections_.insert(std::move(node));
break;
}
return {next_connection, this};
}
private: private:
mutable std::mutex mutex_; mutable std::mutex mutex_;
std::condition_variable cv;
std::vector<IdConnection<Connection>> connection_repo_; std::vector<IdConnection<Connection>> connection_repo_;
using pointer = IdConnection<Connection>*; using pointer = IdConnection<Connection>*;
using connection_map = std::unordered_map<size_t, pointer>; using connection_map = std::unordered_map<size_t, pointer>;
+31 -20
View File
@@ -53,13 +53,22 @@ template < class Type, class Enable = void >
struct data_type_traits; struct data_type_traits;
/// @cond MATADOR_DEV /// @cond MATADOR_DEV
template <> struct data_type_traits<nullptr_t, void>
{
inline static data_type_t builtin_type(std::size_t /*size*/) { return data_type_t::type_null; }
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(const char &value) { return value; }
};
template <> struct data_type_traits<char, void> template <> struct data_type_traits<char, void>
{ {
inline static data_type_t builtin_type(std::size_t /*size*/) { return data_type_t::type_char; } inline static data_type_t builtin_type(std::size_t /*size*/) { return data_type_t::type_char; }
static void read_value(query_result_reader &reader, const char *id, size_t index, char &value); 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_value(parameter_binder &binder, size_t index, char &value);
static void bind_result_value(result_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> template <> struct data_type_traits<short, void>
@@ -68,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 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_value(parameter_binder &binder, size_t index, short &value);
static void bind_result_value(result_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> template <> struct data_type_traits<int, void>
@@ -77,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 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_value(parameter_binder &binder, size_t index, int &value);
static void bind_result_value(result_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> template <> struct data_type_traits<long, void>
@@ -86,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 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_value(parameter_binder &binder, size_t index, long &value);
static void bind_result_value(result_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> template <> struct data_type_traits<long long, void>
@@ -95,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 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_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); 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> template <> struct data_type_traits<unsigned char, void>
@@ -104,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 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_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); 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> template <> struct data_type_traits<unsigned short, void>
@@ -113,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 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_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); 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> template <> struct data_type_traits<unsigned int, void>
@@ -122,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 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_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); 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> template <> struct data_type_traits<unsigned long, void>
@@ -131,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 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_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); 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> template <> struct data_type_traits<unsigned long long, void>
@@ -140,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 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_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); 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> template <> struct data_type_traits<bool, void>
@@ -149,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 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_value(parameter_binder &binder, size_t index, bool &value);
static void bind_result_value(result_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> template <> struct data_type_traits<float, void>
@@ -158,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 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_result_value(result_parameter_binder &binder, size_t index, float &value);
static void bind_value(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> template <> struct data_type_traits<double, void>
@@ -167,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 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_result_value(result_parameter_binder &binder, size_t index, double &value);
static void bind_value(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> template <> struct data_type_traits<const char*, void>
@@ -194,16 +203,16 @@ 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 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_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); 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> template <> struct data_type_traits<utils::blob, void>
{ {
inline static data_type_t builtin_type(std::size_t size) { return data_type_t::type_blob; } inline static data_type_t builtin_type(std::size_t /*size*/) { return data_type_t::type_blob; }
static void read_value(query_result_reader &reader, const char *id, size_t index, utils::blob &value); 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_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); 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> //template <> struct data_type_traits<matador::date>
@@ -223,7 +232,7 @@ template <> struct data_type_traits<utils::blob, void>
//}; //};
template < typename EnumType > 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; } 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) static void read_value(query_result_reader &reader, const char *id, size_t index, EnumType &value)
@@ -232,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) 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) 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 /// @endcond
+227 -57
View File
@@ -2,112 +2,282 @@
#define QUERY_ENTITY_QUERY_BUILDER_HPP #define QUERY_ENTITY_QUERY_BUILDER_HPP
#include "matador/sql/connection.hpp" #include "matador/sql/connection.hpp"
#include "matador/sql/condition.hpp"
#include "matador/sql/query_context.hpp" #include "matador/sql/query_context.hpp"
#include "matador/sql/query.hpp" #include "matador/sql/query.hpp"
#include "matador/sql/query_intermediates.hpp" #include "matador/sql/query_intermediates.hpp"
#include "matador/sql/value.hpp"
#include "matador/utils/result.hpp"
#include <iostream> #include <iostream>
namespace matador::sql { namespace matador::sql {
template < typename PrimaryKeyType > struct join_columns
{
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;
};
enum class query_build_error : std::uint8_t {
Ok = 0,
UnknownType,
MissingPrimaryKey,
UnexpectedError
};
class query_builder_exception : public std::exception
{
public:
explicit query_builder_exception(query_build_error error) : error_(error) {}
[[nodiscard]] query_build_error error() const { return error_; }
private:
const query_build_error error_;
};
class entity_query_builder class entity_query_builder
{ {
public: public:
explicit entity_query_builder(const PrimaryKeyType &pk, const schema &scm) explicit entity_query_builder(const schema &scm)
: pk_(pk) : schema_(scm) {}
, schema_(scm) {}
// determine pk
// collect eager relations for joins
template<class EntityType>
std::optional<query_context> build(connection &db) {
EntityType obj;
matador::utils::access::process(*this, obj);
template<class EntityType, typename PrimaryKeyType>
utils::result<entity_query_data, query_build_error> build(const PrimaryKeyType &pk) {
const auto info = schema_.info<EntityType>(); const auto info = schema_.info<EntityType>();
if (!info) { if (!info) {
return std::nullopt; return utils::error(query_build_error::UnknownType);
}
pk_ = pk;
table_info_stack_.push(info.value());
entity_query_data_ = { info->name };
EntityType obj;
try {
matador::utils::access::process(*this, obj);
return {utils::ok(std::move(entity_query_data_))};
} catch (const query_builder_exception &ex) {
return {utils::error(ex.error())};
} catch (...) {
return {utils::error(query_build_error::UnexpectedError)};
}
}
template<class EntityType>
utils::result<entity_query_data, query_build_error> build() {
const auto info = schema_.info<EntityType>();
if (!info) {
return utils::error(query_build_error::UnknownType);
}
pk_ = nullptr;
table_info_stack_.push(info.value());
entity_query_data_ = { info->name };
EntityType obj;
try {
matador::utils::access::process(*this, obj);
return {utils::ok(std::move(entity_query_data_))};
} catch (const query_builder_exception &ex) {
return {utils::error(ex.error())};
} catch (...) {
return {utils::error(query_build_error::UnexpectedError)};
} }
columns_.clear();
table_name_ = info.value().name;
query q(db, schema_);
return q.select<EntityType>().from({table_name_}).build();
// auto from_intermediate = q.select<EntityType>().from({"t"});
// return {};
} }
template < class V > 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, V &, typename std::enable_if<std::is_integral<V>::value && !std::is_same<bool, V>::value>::type* = 0)
{ {
auto b = std::is_integral_v<PrimaryKeyType>; push(id);
std::cout << "is matching primary key: " << std::boolalpha << b << "\n"; if (!is_root_entity()) {
columns_.emplace_back(table_name_, id, ""); 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;
}
} }
void on_primary_key(const char *id, std::string &, size_t) void on_primary_key(const char *id, std::string &, size_t);
{ void on_revision(const char *id, unsigned long long &/*rev*/);
auto b = std::is_same_v<PrimaryKeyType, std::string>;
std::cout << "is matching primary key: " << std::boolalpha << b << "\n";
columns_.emplace_back(table_name_, id, "");
}
void on_revision(const char *id, unsigned long long &/*rev*/)
{
columns_.emplace_back(table_name_, id, "");
}
template<typename Type> template<typename Type>
void on_attribute(const char *id, Type &, const utils::field_attributes &/*attr*/ = utils::null_attributes) void on_attribute(const char *id, Type &, const utils::field_attributes &/*attr*/ = utils::null_attributes)
{ {
columns_.emplace_back(table_name_, id, ""); push(id);
} }
template<class Pointer> template<class Pointer>
void on_belongs_to(const char *id, Pointer &, const utils::foreign_attributes &attr) void on_belongs_to(const char *id, Pointer &obj, const utils::foreign_attributes &attr)
{ {
if (attr.fetch() == utils::fetch_type::EAGER) { on_foreign_object(id, obj, attr);
column col{id};
// collect join information (determine primary key of foreign entity)
}
columns_.emplace_back(table_name_, id, "");
} }
template<class Pointer> template<class Pointer>
void on_has_one(const char *id, Pointer &, const utils::foreign_attributes &attr) void on_has_one(const char *id, Pointer &obj, const utils::foreign_attributes &attr)
{ {
if (attr.fetch() == utils::fetch_type::EAGER) { on_foreign_object(id, obj, attr);
auto info = schema_.info<typename Pointer::value_type>();
if (info) {
column lcol(table_name_, id, "");
// column rcol(info.value())
}
// collect join information
}
columns_.emplace_back(table_name_, id, "");
} }
template<class ContainerType> template<class ContainerType>
void on_has_many(const char *, ContainerType &, const char *, const char *, const utils::foreign_attributes &attr) void on_has_many(ContainerType &, const char *join_column, const utils::foreign_attributes &attr)
{ {
if (attr.fetch() == utils::fetch_type::EAGER) { if (attr.fetch() == utils::fetch_type::EAGER) {
// collect join information 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};
}
append_join({table_info_stack_.top().name, table_info_stack_.top().prototype.primary_key()->name()}, {info->name, join_column});
} }
} }
template<class ContainerType> template<class ContainerType>
void on_has_many(const char *id, ContainerType &c, const utils::foreign_attributes &attr) 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)
{ {
on_has_many(id, c, "", "", 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};
}
append_join({table_info_stack_.top().name, table_info_stack_.top().prototype.primary_key()->name()}, {id, 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)
{
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: private:
PrimaryKeyType pk_; template<class Pointer>
std::vector<column> columns_; void on_foreign_object(const char *id, Pointer &, const utils::foreign_attributes &attr);
void push(const std::string &column_name);
[[nodiscard]] bool is_root_entity() const;
void append_join(const column &left, const column &right);
private:
value pk_;
std::stack<table_info> table_info_stack_;
const schema &schema_; const schema &schema_;
std::string table_name_; entity_query_data entity_query_data_;
int column_index{0};
join_column_collector join_column_collector_;
}; };
template<class Pointer>
void entity_query_builder::on_foreign_object(const char *id, Pointer &, const utils::foreign_attributes &attr)
{
if (attr.fetch() == utils::fetch_type::EAGER) {
const auto info = schema_.info<typename Pointer::value_type>();
if (!info) {
throw query_builder_exception{query_build_error::UnknownType};
}
table_info_stack_.push(info.value());
typename Pointer::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};
}
append_join({table_info_stack_.top().name, id}, {info->name, pk->name()});
} else {
push(id);
}
}
} }
#endif //QUERY_ENTITY_QUERY_BUILDER_HPP #endif //QUERY_ENTITY_QUERY_BUILDER_HPP
+7 -16
View File
@@ -1,11 +1,7 @@
#ifndef QUERY_FIELD_HPP #ifndef QUERY_FIELD_HPP
#define QUERY_FIELD_HPP #define QUERY_FIELD_HPP
#include "matador/sql/any_type.hpp" #include "matador/sql/value.hpp"
#include "matador/sql/any_type_to_visitor.hpp"
#include "matador/sql/data_type_traits.hpp"
#include "matador/utils/types.hpp"
#include <optional> #include <optional>
#include <string> #include <string>
@@ -19,10 +15,8 @@ public:
template<typename Type> template<typename Type>
field(std::string name, Type value, size_t size = 0, int index = -1) field(std::string name, Type value, size_t size = 0, int index = -1)
: name_(std::move(name)) : name_(std::move(name))
, size_(size)
, index_(index) , index_(index)
, value_(value) , value_(value, size) {}
, type_(data_type_traits<Type>::builtin_type(size)) {}
field(std::string name, data_type_t data_type, size_t size = 0, int index = -1); field(std::string name, data_type_t data_type, size_t size = 0, int index = -1);
field(const field &x) = default; field(const field &x) = default;
field& operator=(const field &x) = default; field& operator=(const field &x) = default;
@@ -32,7 +26,7 @@ public:
template<typename Type> template<typename Type>
field& operator=(Type value) { field& operator=(Type value) {
value_ = std::move(value); value_ = std::move(value);
type_ = data_type_traits<Type>::builtin_type(-1);
return *this; return *this;
} }
@@ -43,9 +37,7 @@ public:
template<class Type> template<class Type>
std::optional<Type> as() const std::optional<Type> as() const
{ {
any_type_to_visitor<Type> visitor; return value_.as<Type>();
std::visit(visitor, const_cast<any_type&>(value_));
return visitor.result;
} }
[[nodiscard]] std::string str() const; [[nodiscard]] std::string str() const;
@@ -65,7 +57,7 @@ private:
template<class Operator> template<class Operator>
void process(Operator &op) void process(Operator &op)
{ {
op.on_attribute(name_.c_str(), value_, type_, size_); op.on_attribute(name_.c_str(), value_, value_.size());
} }
private: private:
@@ -73,9 +65,8 @@ private:
std::string name_; std::string name_;
int index_{-1}; int index_{-1};
any_type value_;
size_t size_{}; value value_;
data_type_t type_;
}; };
} }
@@ -0,0 +1,38 @@
#ifndef QUERY_HAS_MANY_TO_MANY_RELATION_HPP
#define QUERY_HAS_MANY_TO_MANY_RELATION_HPP
#include "matador/sql/entity.hpp"
#include "matador/utils/access.hpp"
#include "matador/utils/foreign_attributes.hpp"
namespace matador::sql {
template < class LocalType, class ForeignType >
class has_many_to_many_relation
{
public:
has_many_to_many_relation() = default;
has_many_to_many_relation(std::string local_name, std::string remote_name)
: local_name_(std::move(local_name))
, remote_name_(std::move(remote_name)) {}
template<class Operator>
void process(Operator &op) {
namespace field = matador::utils::access;
field::belongs_to(op, local_name_.c_str(), local_, utils::default_foreign_attributes);
field::belongs_to(op, remote_name_.c_str(), remote_, utils::default_foreign_attributes);
}
entity<LocalType> local() const { return local_; }
entity<LocalType> remote() const { return remote_; }
private:
std::string local_name_;
std::string remote_name_;
sql::entity<LocalType> local_;
sql::entity<ForeignType> remote_;
};
}
#endif //QUERY_HAS_MANY_TO_MANY_RELATION_HPP
+1 -18
View File
@@ -16,12 +16,9 @@ public:
query_create_intermediate create(); query_create_intermediate create();
query_drop_intermediate drop(); query_drop_intermediate drop();
template < class Type >
query_select_intermediate select();
template < class Type >
query_select_intermediate select(std::initializer_list<column> columns);
query_select_intermediate select(std::initializer_list<column> columns); query_select_intermediate select(std::initializer_list<column> columns);
query_select_intermediate select(const std::vector<column>& columns); query_select_intermediate select(const std::vector<column>& columns);
query_select_intermediate select(std::vector<column> columns, std::initializer_list<column> additional_columns);
query_insert_intermediate insert(); query_insert_intermediate insert();
query_update_intermediate update(const sql::table &table); query_update_intermediate update(const sql::table &table);
query_delete_intermediate remove(); query_delete_intermediate remove();
@@ -31,19 +28,5 @@ private:
const sql::schema &schema_; const sql::schema &schema_;
}; };
template<class Type>
query_select_intermediate query::select()
{
return select(column_name_generator::generate<Type>(schema_));
}
template<class Type>
query_select_intermediate query::select(std::initializer_list<column> columns)
{
auto cols = column_name_generator::generate<Type>(schema_);
cols.insert(cols.end(), columns);
return select(cols);
}
} }
#endif //QUERY_QUERY_HPP #endif //QUERY_QUERY_HPP
+1 -1
View File
@@ -9,7 +9,7 @@
#include <string> #include <string>
#include <ostream> #include <ostream>
#define FIELD(x) const sql::column x{this->name, #x, ""}; #define FIELD(x) const sql::column x{*this, #x, ""};
#define QUERY_HELPER(C, ...) \ #define QUERY_HELPER(C, ...) \
namespace matador::qh { \ namespace matador::qh { \
+56 -62
View File
@@ -2,8 +2,8 @@
#define QUERY_QUERY_INTERMEDIATES_HPP #define QUERY_QUERY_INTERMEDIATES_HPP
#include "matador/sql/column_definition.hpp" #include "matador/sql/column_definition.hpp"
#include "matador/sql/column_definition_generator.hpp"
#include "matador/sql/column_generator.hpp" #include "matador/sql/column_generator.hpp"
#include "matador/sql/column_name_generator.hpp"
#include "matador/sql/key_value_generator.hpp" #include "matador/sql/key_value_generator.hpp"
#include "matador/sql/key_value_pair.hpp" #include "matador/sql/key_value_pair.hpp"
#include "matador/sql/placeholder_generator.hpp" #include "matador/sql/placeholder_generator.hpp"
@@ -34,13 +34,13 @@ protected:
class query_intermediate : public basic_query_intermediate class query_intermediate : public basic_query_intermediate
{ {
public: public:
query_intermediate(connection &db, const sql::schema &schema, query_data &data); query_intermediate(connection &db, const sql::schema &schema, const std::shared_ptr<query_data> &data);
protected: protected:
query_data &data_; std::shared_ptr<query_data> data_;
}; };
class query_execute_finish : public query_intermediate class query_execute : public query_intermediate
{ {
public: public:
using query_intermediate::query_intermediate; using query_intermediate::query_intermediate;
@@ -50,7 +50,7 @@ public:
[[nodiscard]] query_context build() const; [[nodiscard]] query_context build() const;
}; };
class query_select_finish : public query_intermediate class query_select : public query_intermediate
{ {
protected: protected:
using query_intermediate::query_intermediate; using query_intermediate::query_intermediate;
@@ -64,15 +64,15 @@ public:
query_result<record> fetch_all(); query_result<record> fetch_all();
template < class Type > template < class Type >
std::optional<Type> fetch_one() std::unique_ptr<Type> fetch_one()
{ {
auto result = query_result<Type>(fetch()); auto result = query_result<Type>(fetch());
auto first = result.begin(); auto first = result.begin();
if (first == result.end()) { if (first == result.end()) {
return std::nullopt; return nullptr;
} }
return *first.get(); return std::unique_ptr<Type>{first.release()};
} }
std::optional<record> fetch_one(); std::optional<record> fetch_one();
@@ -96,36 +96,36 @@ private:
class query_offset_intermediate; class query_offset_intermediate;
class query_limit_intermediate : public query_select_finish class query_limit_intermediate : public query_select
{ {
public: public:
using query_select_finish::query_select_finish; using query_select::query_select;
query_offset_intermediate offset(size_t offset); query_offset_intermediate offset(size_t offset);
}; };
class query_offset_intermediate : public query_select_finish class query_offset_intermediate : public query_select
{ {
public: public:
using query_select_finish::query_select_finish; using query_select::query_select;
query_limit_intermediate limit(size_t limit); query_limit_intermediate limit(size_t limit);
}; };
class query_order_direction_intermediate : public query_select_finish class query_order_direction_intermediate : public query_select
{ {
public: public:
using query_select_finish::query_select_finish; using query_select::query_select;
query_limit_intermediate limit(size_t limit); query_limit_intermediate limit(size_t limit);
}; };
class query_order_by_intermediate; class query_order_by_intermediate;
class query_group_by_intermediate : public query_select_finish class query_group_by_intermediate : public query_select
{ {
public: public:
using query_select_finish::query_select_finish; using query_select::query_select;
query_order_by_intermediate order_by(const column &col); query_order_by_intermediate order_by(const column &col);
}; };
@@ -139,10 +139,10 @@ public:
query_order_direction_intermediate desc(); query_order_direction_intermediate desc();
}; };
class query_where_intermediate : public query_select_finish class query_where_intermediate : public query_select
{ {
public: public:
using query_select_finish::query_select_finish; using query_select::query_select;
query_group_by_intermediate group_by(const column &col); query_group_by_intermediate group_by(const column &col);
query_order_by_intermediate order_by(const column &col); query_order_by_intermediate order_by(const column &col);
@@ -150,17 +150,30 @@ public:
class query_join_intermediate; class query_join_intermediate;
class query_on_intermediate : public query_select_finish struct join_data
{
table join_table;
std::unique_ptr<basic_condition> condition;
};
class query_from_intermediate : public query_select
{ {
public: public:
using query_select_finish::query_select_finish; using query_select::query_select;
query_join_intermediate join_left(const table &t); 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> template<class Condition>
query_where_intermediate where(const Condition &cond) query_where_intermediate where(const Condition &cond)
{ {
return where_clause(std::make_unique<Condition>(std::move(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_group_by_intermediate group_by(const column &col);
query_order_by_intermediate order_by(const column &col); query_order_by_intermediate order_by(const column &col);
@@ -168,6 +181,8 @@ private:
query_where_intermediate where_clause(std::unique_ptr<basic_condition> &&cond); 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 class query_join_intermediate : public query_intermediate
{ {
public: public:
@@ -178,36 +193,22 @@ public:
{ {
return on_clause(std::make_unique<Condition>(std::move(cond))); return on_clause(std::make_unique<Condition>(std::move(cond)));
} }
query_on_intermediate on(std::unique_ptr<basic_condition> &&cond)
{
return on_clause(std::move(cond));
}
private: private:
query_on_intermediate on_clause(std::unique_ptr<basic_condition> &&cond); query_on_intermediate on_clause(std::unique_ptr<basic_condition> &&cond);
}; };
class query_from_intermediate : public query_select_finish
{
public:
using query_select_finish::query_select_finish;
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_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 class query_start_intermediate : public basic_query_intermediate
{ {
public: public:
explicit query_start_intermediate(connection &db, const sql::schema &schema); explicit query_start_intermediate(connection &db, const sql::schema &schema);
protected: protected:
query_data data_; std::shared_ptr<query_data> data_ { std::make_shared<query_data>() };
}; };
class query_select_intermediate : public query_start_intermediate class query_select_intermediate : public query_start_intermediate
@@ -232,16 +233,16 @@ class query_into_intermediate : public query_intermediate
public: public:
using query_intermediate::query_intermediate; using query_intermediate::query_intermediate;
query_execute_finish values(std::initializer_list<any_type> values); query_execute values(std::initializer_list<any_type> values);
query_execute_finish values(std::vector<any_type> &&values); query_execute values(std::vector<any_type> &&values);
template<class Type> template<class Type>
query_execute_finish values() query_execute values()
{ {
Type obj; Type obj;
return values(std::move(as_placeholder(obj))); return values(std::move(as_placeholder(obj)));
} }
template<class Type> template<class Type>
query_execute_finish values(const Type &obj) query_execute values(const Type &obj)
{ {
return values(std::move(value_extractor::extract(obj))); return values(std::move(value_extractor::extract(obj)));
} }
@@ -252,15 +253,12 @@ class query_create_intermediate : public query_start_intermediate
public: public:
explicit query_create_intermediate(connection &db, const sql::schema &schema); explicit query_create_intermediate(connection &db, const sql::schema &schema);
query_execute_finish table(const sql::table &table, std::initializer_list<column_definition> columns); query_execute table(const sql::table &table, std::initializer_list<column_definition> columns);
query_execute_finish table(const sql::table &table, const std::vector<column_definition> &columns); query_execute table(const sql::table &table, const std::vector<column_definition> &columns);
template<class Type> template<class Type>
query_execute_finish table(const sql::table &table) query_execute table(const sql::table &table)
{ {
// if (!schema_.exists<Type>()) { return this->table(table, column_definition_generator::generate<Type>(schema_));
// schema_.attach<Type>(table.name);
// }
return this->table(table, column_generator::generate<Type>(schema_));
} }
}; };
@@ -269,7 +267,7 @@ class query_drop_intermediate : query_start_intermediate
public: public:
explicit query_drop_intermediate(connection &db, const sql::schema &schema); explicit query_drop_intermediate(connection &db, const sql::schema &schema);
query_execute_finish table(const sql::table &table); query_execute table(const sql::table &table);
}; };
class query_insert_intermediate : public query_start_intermediate class query_insert_intermediate : public query_start_intermediate
@@ -279,25 +277,21 @@ public:
query_into_intermediate into(const sql::table &table, std::initializer_list<column> column_names); query_into_intermediate into(const sql::table &table, std::initializer_list<column> column_names);
query_into_intermediate into(const sql::table &table, std::vector<column> &&column_names); query_into_intermediate into(const sql::table &table, std::vector<column> &&column_names);
template<class Type> query_into_intermediate into(const sql::table &table);
query_into_intermediate into(const sql::table &table)
{
return into(table, column_name_generator::generate<Type>(schema_));
}
}; };
class query_execute_where_intermediate : public query_execute_finish class query_execute_where_intermediate : public query_execute
{ {
public: public:
using query_execute_finish::query_execute_finish; using query_execute::query_execute;
query_order_by_intermediate order_by(const column &col); query_order_by_intermediate order_by(const column &col);
}; };
class query_set_intermediate : public query_execute_finish class query_set_intermediate : public query_execute
{ {
public: public:
using query_execute_finish::query_execute_finish; using query_execute::query_execute;
template<class Condition> template<class Condition>
query_execute_where_intermediate where(const Condition &cond) query_execute_where_intermediate where(const Condition &cond)
@@ -323,10 +317,10 @@ public:
} }
}; };
class query_delete_from_intermediate : public query_execute_finish class query_delete_from_intermediate : public query_execute
{ {
public: public:
using query_execute_finish::query_execute_finish; using query_execute::query_execute;
template<class Condition> template<class Condition>
query_execute_where_intermediate where(const Condition &cond) query_execute_where_intermediate where(const Condition &cond)
+7 -3
View File
@@ -15,6 +15,8 @@
namespace matador::sql { namespace matador::sql {
class value;
namespace detail { namespace detail {
class pk_reader class pk_reader
{ {
@@ -41,9 +43,11 @@ public:
void on_has_one(const char * /*id*/, Pointer &/*x*/, const utils::foreign_attributes &/*attr*/) {} void on_has_one(const char * /*id*/, Pointer &/*x*/, const utils::foreign_attributes &/*attr*/) {}
template<class ContainerType> template<class ContainerType>
void on_has_many(const char *, ContainerType &, const char *, const char *, const utils::foreign_attributes &/*attr*/) {} void on_has_many(ContainerType &, const char *, const utils::foreign_attributes &/*attr*/) {}
template<class ContainerType> template<class ContainerType>
void on_has_many(const char *, ContainerType &, const utils::foreign_attributes &/*attr*/) {} 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*/) {}
template<class ContainerType>
void on_has_many_to_many(const char *id, ContainerType &c, const utils::foreign_attributes &/*attr*/) {}
private: private:
size_t column_index_{}; size_t column_index_{};
@@ -72,7 +76,7 @@ public:
} }
void on_attribute(const char *id, char *value, const utils::field_attributes &attr = utils::null_attributes); void on_attribute(const char *id, char *value, const utils::field_attributes &attr = utils::null_attributes);
void on_attribute(const char *id, std::string &value, const utils::field_attributes &attr = utils::null_attributes); void on_attribute(const char *id, std::string &value, const utils::field_attributes &attr = utils::null_attributes);
void on_attribute(const char *id, any_type &value, data_type_t type, const utils::field_attributes &attr = utils::null_attributes); void on_attribute(const char *id, value &val, const utils::field_attributes &attr = utils::null_attributes);
template < class Pointer > template < class Pointer >
void on_belongs_to(const char * /*id*/, Pointer &x, const utils::foreign_attributes &attr) void on_belongs_to(const char * /*id*/, Pointer &x, const utils::foreign_attributes &attr)
+3 -1
View File
@@ -6,6 +6,8 @@
namespace matador::sql { namespace matador::sql {
class value;
class query_result_reader class query_result_reader
{ {
public: public:
@@ -34,7 +36,7 @@ public:
virtual void read_value(const char *id, size_t index, std::string &value); virtual void read_value(const char *id, size_t index, std::string &value);
virtual void read_value(const char *id, size_t index, std::string &value, size_t s); virtual void read_value(const char *id, size_t index, std::string &value, size_t s);
virtual void read_value(const char *id, size_t index, utils::blob &value); virtual void read_value(const char *id, size_t index, utils::blob &value);
virtual void read_value(const char *id, size_t index, any_type &value, data_type_t type, size_t size); virtual void read_value(const char *id, size_t index, value &val, size_t size);
}; };
} }
+6 -5
View File
@@ -15,8 +15,9 @@ struct column;
class record class record
{ {
private: private:
using field_by_index = std::vector<field>; using field_ref = std::reference_wrapper<field>;
using field_index_pair = std::pair<std::reference_wrapper<field>, field_by_index::difference_type>; 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>; using field_by_name_map = std::unordered_map<std::string, field_index_pair>;
public: public:
@@ -36,13 +37,13 @@ public:
void process(Operator &op) void process(Operator &op)
{ {
for(auto &f : fields_) { 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(const column &col) const;
[[nodiscard]] const field& at(size_t index) const; [[nodiscard]] const field& at(size_t index) const;
+7 -2
View File
@@ -1,7 +1,7 @@
#ifndef QUERY_SCHEMA_HPP #ifndef QUERY_SCHEMA_HPP
#define QUERY_SCHEMA_HPP #define QUERY_SCHEMA_HPP
#include "matador/sql/column_generator.hpp" #include "matador/sql/column_definition_generator.hpp"
#include "matador/sql/table_definition.hpp" #include "matador/sql/table_definition.hpp"
#include <optional> #include <optional>
@@ -23,6 +23,7 @@ class schema
{ {
public: public:
using repository = std::unordered_map<std::type_index, table_info>; using repository = std::unordered_map<std::type_index, table_info>;
using repository_by_name = std::unordered_map<std::string, std::reference_wrapper<table_info>>;
using iterator = repository::iterator; using iterator = repository::iterator;
using const_iterator = repository::const_iterator; using const_iterator = repository::const_iterator;
@@ -35,10 +36,12 @@ public:
[[nodiscard]] std::string name() const; [[nodiscard]] std::string name() const;
void create(connection &c);
template<typename Type> template<typename Type>
const table_info& attach(const std::string &table_name) const table_info& attach(const std::string &table_name)
{ {
return attach(std::type_index(typeid(Type)), table_info{table_name, table_definition{column_generator::generate<Type>(*this)}}); return attach(std::type_index(typeid(Type)), table_info{table_name, table_definition{column_definition_generator::generate<Type>(*this)}});
} }
const table_info& attach(std::type_index ti, const table_info& table); const table_info& attach(std::type_index ti, const table_info& table);
@@ -50,6 +53,7 @@ public:
} }
[[nodiscard]] std::optional<table_info> info(std::type_index ti) const; [[nodiscard]] std::optional<table_info> info(std::type_index ti) const;
[[nodiscard]] std::optional<table_info> info(const std::string &name) const;
template<typename Type> template<typename Type>
[[nodiscard]] std::pair<std::string, std::string> reference() const [[nodiscard]] std::pair<std::string, std::string> reference() const
@@ -77,6 +81,7 @@ public:
private: private:
std::string name_; std::string name_;
repository repository_; repository repository_;
repository_by_name repository_by_name_;
}; };
} }
+70 -19
View File
@@ -4,6 +4,7 @@
#include "matador/sql/connection.hpp" #include "matador/sql/connection.hpp"
#include "matador/sql/connection_pool.hpp" #include "matador/sql/connection_pool.hpp"
#include "matador/sql/entity.hpp" #include "matador/sql/entity.hpp"
#include "matador/sql/entity_query_builder.hpp"
#include "matador/sql/statement.hpp" #include "matador/sql/statement.hpp"
#include "matador/sql/schema.hpp" #include "matador/sql/schema.hpp"
@@ -13,6 +14,14 @@ namespace matador::sql {
class dialect; class dialect;
enum class session_error {
Ok = 0,
NoConnectionAvailable,
UnknownType,
FailedToBuildQuery,
FailedToFindObject
};
class session class session
{ {
public: public:
@@ -32,30 +41,68 @@ public:
} }
template<typename Type, typename PrimaryKeyType> template<typename Type, typename PrimaryKeyType>
entity<Type> find(const PrimaryKeyType &pk) { utils::result<entity<Type>, session_error> find(const PrimaryKeyType &pk) {
auto c = pool_.acquire(); auto c = pool_.acquire();
if (!c.valid()) { if (!c.valid()) {
throw std::logic_error("no database connection available"); return utils::error(session_error::NoConnectionAvailable);
} }
// collect all columns
// - evaluate fetch::Eager flag for relations
auto info = schema_->info<Type>(); auto info = schema_->info<Type>();
if (!info) { if (!info) {
return {}; return utils::error(session_error::UnknownType);
} }
c->query(*schema_).select({}).from(info->name); entity_query_builder eqb(*schema_);
// build pk where condition auto data = eqb.build<Type>(pk);
// - check if type has pk if (!data.is_ok()) {
// - check type return utils::error(session_error::FailedToBuildQuery);
// pk_condition_builder<Type> builder; }
// auto cond = builder.build(pk);
// create query with relations as requested auto obj = build_select_query(c, data.release()).template fetch_one<Type>();
//
// c->query(*schema_).select<Type>().from("xyz").where().fetch_all();
return {}; if (!obj) {
return utils::error(session_error::FailedToFindObject);
}
return utils::ok(entity<Type>{ obj.release() });
}
template<typename Type>
utils::result<query_result<Type>, session_error> find() {
auto c = pool_.acquire();
if (!c.valid()) {
return utils::error(session_error::NoConnectionAvailable);
}
auto info = schema_->info<Type>();
if (!info) {
return utils::error(session_error::UnknownType);
}
entity_query_builder eqb(*schema_);
auto data = eqb.build<Type>();
if (!data.is_ok()) {
return utils::error(session_error::FailedToBuildQuery);
}
return utils::ok(build_select_query(c, data.release()).template fetch_all<Type>());
}
template<typename Type>
utils::result<query_from_intermediate, session_error> select() {
auto c = pool_.acquire();
if (!c.valid()) {
return utils::error(session_error::NoConnectionAvailable);
}
auto info = schema_->info<Type>();
if (!info) {
return utils::error(session_error::UnknownType);
}
entity_query_builder eqb(*schema_);
auto data = eqb.build<Type>();
if (!data.is_ok()) {
return utils::error(session_error::FailedToBuildQuery);
}
return utils::ok(build_select_query(c, data.release()).template fetch_all<Type>());
} }
template<typename Type> template<typename Type>
@@ -70,15 +117,15 @@ public:
std::vector<sql::column_definition> describe_table(const std::string &table_name) const; std::vector<sql::column_definition> describe_table(const std::string &table_name) const;
bool table_exists(const std::string &table_name) const; bool table_exists(const std::string &table_name) const;
[[nodiscard]] const schema& tables() const;
const class dialect& dialect() const; const class dialect& dialect() const;
private: private:
friend class query_select_finish; friend class query_select;
[[nodiscard]] std::unique_ptr<query_result_impl> fetch(const std::string &sql) const; [[nodiscard]] std::unique_ptr<query_result_impl> fetch(const std::string &sql) const;
query_select build_select_query(connection_ptr<connection> &conn, entity_query_data &&data) const;
private: private:
connection_pool<connection> &pool_; connection_pool<connection> &pool_;
const class dialect &dialect_; const class dialect &dialect_;
@@ -101,7 +148,11 @@ entity<Type> session::insert(Type *obj)
if (!info) { if (!info) {
return {}; return {};
} }
c->query(*schema_).insert().into<Type>(info->name).values(*obj).execute(); c->query(*schema_)
.insert()
.into(info->name, column_generator::generate<Type>(*schema_, true))
.values(*obj)
.execute();
return entity{obj}; return entity{obj};
} }
+17 -4
View File
@@ -1,26 +1,39 @@
#ifndef QUERY_TABLE_HPP #ifndef QUERY_TABLE_HPP
#define QUERY_TABLE_HPP #define QUERY_TABLE_HPP
#include "matador/sql/column.hpp"
#include <typeindex> #include <typeindex>
#include <string> #include <string>
#include <vector>
namespace matador::sql { namespace matador::sql {
struct table struct table
{ {
std::string name;
std::string alias;
table(const char *name, std::string as = "") // NOLINT(*-explicit-constructor) table(const char *name, std::string as = "") // NOLINT(*-explicit-constructor)
: name(name), alias(std::move(as)) {} : name(name), alias(std::move(as)) {}
table(std::string name, std::string as = "") // NOLINT(*-explicit-constructor) 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) { table& as(const std::string &a) {
alias = a; alias = a;
return *this; return *this;
} }
[[nodiscard]] table as(const std::string &a) const {
return { name, a, columns };
}
std::string name;
std::string alias;
std::vector<column> columns;
}; };
} }
+81
View File
@@ -0,0 +1,81 @@
#ifndef QUERY_VALUE_HPP
#define QUERY_VALUE_HPP
#include "matador/sql/any_type.hpp"
#include "matador/sql/any_type_to_visitor.hpp"
#include "matador/sql/data_type_traits.hpp"
#include "matador/utils/types.hpp"
#include <optional>
namespace matador::sql {
namespace detail {
template<typename Type>
size_t determine_size(const Type &/*val*/)
{
return 0;
}
size_t determine_size(const std::string &val);
size_t determine_size(const char *val);
size_t determine_size(const utils::blob &val);
}
class value
{
public:
value() = default;
template<typename Type>
explicit value(Type value, size_t size = 0)
: size_(size)
, value_(value)
, type_(data_type_traits<Type>::builtin_type(size)) {}
explicit value(data_type_t data_type, size_t size = 0);
value(const value &x) = default;
value& operator=(const value &x) = default;
template<typename Type>
value& operator=(Type val)
{
value_ = val;
size_ = detail::determine_size(val);
type_ = data_type_traits<Type>::builtin_type(size_);
return *this;
}
value(value &&x) noexcept;
value& operator=(value &&x) noexcept;
template<class Type>
std::optional<Type> as() const
{
if (std::holds_alternative<Type>(value_)) {
return std::get<Type>(value_);
} else {
any_type_to_visitor<Type> visitor;
std::visit(visitor, const_cast<any_type &>(value_));
return visitor.result;
}
}
[[nodiscard]] std::string str() const;
[[nodiscard]] size_t size() const;
[[nodiscard]] data_type_t type() const;
[[nodiscard]] bool is_integer() const;
[[nodiscard]] bool is_floating_point() const;
[[nodiscard]] bool is_bool() const;
[[nodiscard]] bool is_string() const;
[[nodiscard]] bool is_varchar() const;
[[nodiscard]] bool is_blob() const;
[[nodiscard]] bool is_null() const;
[[nodiscard]] bool is_unknown() const;
private:
any_type value_;
size_t size_{};
data_type_t type_{data_type_t::type_unknown};
};
}
#endif //QUERY_VALUE_HPP
+4 -2
View File
@@ -53,9 +53,11 @@ public:
values_.emplace_back(fk_value_extractor_.extract(*x)); values_.emplace_back(fk_value_extractor_.extract(*x));
} }
template<class ContainerType> template<class ContainerType>
void on_has_many(const char *, ContainerType &, const char *, const char *, const utils::foreign_attributes &/*attr*/) {} void on_has_many(ContainerType &, const char *, const utils::foreign_attributes &/*attr*/) {}
template<class ContainerType> template<class ContainerType>
void on_has_many(const char *, ContainerType &, const utils::foreign_attributes &/*attr*/) {} 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*/) {}
template<class ContainerType>
void on_has_many_to_many(const char *id, ContainerType &c, const utils::foreign_attributes &/*attr*/) {}
private: private:
template<typename Type> template<typename Type>
+8 -13
View File
@@ -64,24 +64,19 @@ void belongs_to(Operator &op, const char *id, Type &value, const foreign_attribu
op.on_belongs_to(id, value, attr); op.on_belongs_to(id, value, attr);
} }
//template<class Operator, class Type, template<class ...> class ContainerType>
//void has_many(Operator &op, const char *id, container<Type, ContainerType> &container, const foreign_attributes &attr) {
// op.on_has_many(id, container, attr);
//}
template<class Operator, class Type, template<class ...> class ContainerType> template<class Operator, class Type, template<class ...> class ContainerType>
void has_many(Operator &op, const char *id, ContainerType<Type> &container, const foreign_attributes &attr) { void has_many(Operator &op, ContainerType<Type> &container, const char *join_column, const foreign_attributes &attr) {
op.on_has_many(id, container, attr); op.on_has_many(container, join_column, attr);
} }
//template<class Operator, class Type, template<class ...> class ContainerType> template<class Operator, class Type, template<class ...> class ContainerType>
//void has_many(Operator &op, const char *id, container<Type, ContainerType> &container, const char *left_column, const char *right_column, const foreign_attributes &attr) { void has_many_to_many(Operator &op, const char *id, ContainerType<Type> &container, const char *join_column, const char *inverse_join_column, const foreign_attributes &attr) {
// op.on_has_many(id, container, left_column, right_column, attr); op.on_has_many_to_many(id, container, join_column, inverse_join_column, attr);
//} }
template<class Operator, class Type, template<class ...> class ContainerType> template<class Operator, class Type, template<class ...> class ContainerType>
void has_many(Operator &op, const char *id, ContainerType<Type> &container, const char *left_column, const char *right_column, const foreign_attributes &attr) { void has_many_to_many(Operator &op, const char *id, ContainerType<Type> &container, const foreign_attributes &attr) {
op.on_has_many(id, container, left_column, right_column, attr); op.on_has_many_to_many(id, container, attr);
} }
} }
+113
View File
@@ -0,0 +1,113 @@
#ifndef QUERY_RESULT_HPP
#define QUERY_RESULT_HPP
#include <variant>
namespace matador::utils {
template < typename ValueType, typename ErrorType >
class [[nodiscard]] result;
template <typename ValueType>
struct is_result : std::false_type {};
template < typename ValueType, typename ErrorType >
struct is_result<result<ValueType, ErrorType>> : std::true_type {};
template < typename ValueType >
class ok
{
public:
using value_type = ValueType;
explicit constexpr ok(const ValueType &value) : value_(value) {}
explicit constexpr ok(ValueType &&value) : value_(std::move(value)) {}
constexpr ValueType&& release() { return std::move(value_); }
const ValueType& value() const { return value_; }
ValueType& value() { return value_; }
private:
ValueType value_;
};
template < typename ErrorType >
class error
{
public:
using value_type = ErrorType;
explicit constexpr error(const ErrorType &error) : error_(error) {}
explicit constexpr error(ErrorType &&error) : error_(std::move(error)) {}
constexpr ErrorType&& release() { return std::move(error_); }
const ErrorType& value() const { return error_; }
ErrorType value() { return error_; }
private:
ErrorType error_;
};
template < typename ValueType, typename ErrorType >
class result
{
public:
using value_type = ok<ValueType>;
using error_type = error<ErrorType>;
result() : result_(ValueType{}) {}
result(value_type value) : result_(std::move(value)) {} // NOLINT(*-explicit-constructor)
result(error_type error) : result_(std::move(error)) {} // NOLINT(*-explicit-constructor)
result(const result<ValueType, ErrorType> &x) = default;
result& operator=(const result<ValueType, ErrorType> &x) = default;
result(result<ValueType, ErrorType> &&x) = default;
result& operator=(result<ValueType, ErrorType> &&x) = default;
operator bool() const { return is_ok(); } // NOLINT(*-explicit-constructor)
[[nodiscard]] bool is_ok() const { return std::holds_alternative<value_type>(result_); }
[[nodiscard]] bool is_error() const { return std::holds_alternative<error_type>(result_); }
ValueType&& release() { return std::get<value_type>(result_).release(); }
ErrorType&& release_error() { return std::get<error_type>(result_).release(); }
const ValueType& value() const { return std::get<value_type>(result_).value(); }
const ErrorType& err() const { return std::get<error_type>(result_).value(); }
ErrorType err() { return std::get<error_type>(result_).value(); }
constexpr const ValueType* operator->() const { return &value(); }
constexpr ValueType* operator->() { return &std::get<value_type>(result_).value(); }
template<typename Func, typename SecondValueType = typename std::invoke_result_t<Func, ValueType >>
result<SecondValueType, ErrorType> transform(Func &&f) {
if (is_ok()) {
return result<SecondValueType, ErrorType>(ok(f(release())));
}
return result<SecondValueType, ErrorType>(error(release_error()));
}
template<typename Func, typename SecondValueType = typename std::invoke_result_t<Func, ValueType >::value_type::value_type>
result<SecondValueType, ErrorType> and_then(Func &&f) {
if (is_ok()) {
return f(release());
}
return result<SecondValueType, ErrorType>(error(release_error()));
}
template<typename Func, typename SecondErrorType = typename std::invoke_result_t<Func, ErrorType >::error_type::value_type>
result<ValueType, SecondErrorType> or_else(Func &&f) {
if (is_error()) {
return f(err());
}
return result<ValueType, SecondErrorType>(ok(release()));
}
private:
std::variant<value_type, error_type> result_;
};
}
#endif //QUERY_RESULT_HPP
+7 -24
View File
@@ -12,8 +12,8 @@ set(SQL_SOURCES
sql/session.cpp sql/session.cpp
sql/backend_provider.cpp sql/backend_provider.cpp
sql/query_result_impl.cpp sql/query_result_impl.cpp
sql/column_definition_generator.cpp
sql/column_generator.cpp sql/column_generator.cpp
sql/column_name_generator.cpp
sql/key_value_generator.cpp sql/key_value_generator.cpp
sql/fk_value_extractor.cpp sql/fk_value_extractor.cpp
sql/schema.cpp sql/schema.cpp
@@ -37,6 +37,8 @@ set(SQL_SOURCES
sql/any_type_to_string_visitor.cpp sql/any_type_to_string_visitor.cpp
sql/field.cpp sql/field.cpp
sql/table_definition.cpp sql/table_definition.cpp
sql/value.cpp
sql/entity_query_builder.cpp
) )
set(SQL_HEADER set(SQL_HEADER
@@ -57,8 +59,8 @@ set(SQL_HEADER
../include/matador/sql/session.hpp ../include/matador/sql/session.hpp
../include/matador/sql/backend_provider.hpp ../include/matador/sql/backend_provider.hpp
../include/matador/sql/query_result_impl.hpp ../include/matador/sql/query_result_impl.hpp
../include/matador/sql/column_definition_generator.hpp
../include/matador/sql/column_generator.hpp ../include/matador/sql/column_generator.hpp
../include/matador/sql/column_name_generator.hpp
../include/matador/sql/value_extractor.hpp ../include/matador/sql/value_extractor.hpp
../include/matador/sql/any_type.hpp ../include/matador/sql/any_type.hpp
../include/matador/sql/key_value_generator.hpp ../include/matador/sql/key_value_generator.hpp
@@ -91,28 +93,8 @@ set(SQL_HEADER
../include/matador/sql/field.hpp ../include/matador/sql/field.hpp
../include/matador/sql/entity_query_builder.hpp ../include/matador/sql/entity_query_builder.hpp
../include/matador/sql/table_definition.hpp ../include/matador/sql/table_definition.hpp
) ../include/matador/sql/has_many_to_many_relation.hpp
../include/matador/sql/value.hpp
set(QUERY_SOURCES
query/query.cpp
query/query_compiler.cpp
query/query_parts.cpp
query/column.cpp
query/query_builder.cpp
query/query_intermediates.cpp
)
set(QUERY_HEADER
../include/matador/query/query.hpp
../include/matador/query/query_part_visitor.hpp
../include/matador/query/query_compiler.hpp
../include/matador/query/query_parts.hpp
../include/matador/query/column.hpp
../include/matador/query/query_builder.hpp
../include/matador/query/query_intermediates.hpp
../include/matador/query/sql_commands.hpp
../include/matador/query/query_data.hpp
../include/matador/query/table.hpp
) )
set(UTILS_HEADER set(UTILS_HEADER
@@ -129,6 +111,7 @@ set(UTILS_HEADER
../include/matador/utils/types.hpp ../include/matador/utils/types.hpp
../include/matador/utils/foreign_attributes.hpp ../include/matador/utils/foreign_attributes.hpp
../include/matador/utils/fetch_type.hpp ../include/matador/utils/fetch_type.hpp
../include/matador/utils/result.hpp
) )
set(UTILS_SOURCES set(UTILS_SOURCES
-21
View File
@@ -1,21 +0,0 @@
#include "matador/query/column.hpp"
namespace matador::query {
column::column(const char *name)
: name(name) {}
column::column(std::string name)
: name(std::move(name)) {}
column::column(std::string table_name, std::string name, std::string as)
: table(std::move(table_name))
, name(std::move(name))
, alias(std::move(as)) {}
column& column::as(std::string a) {
alias = std::move(a);
return *this;
}
}
-6
View File
@@ -1,6 +0,0 @@
#include "matador/query/query.hpp"
namespace matador::sql {
}
-13
View File
@@ -1,13 +0,0 @@
#include "matador/query/query_builder.hpp"
namespace matador::query {
query_builder::query_builder(const std::shared_ptr<sql::schema> &scm)
: schema_(scm) {}
query_select_intermediate query_builder::select(std::initializer_list<column> columns)
{
return query_select_intermediate{schema_};
}
}
-33
View File
@@ -1,33 +0,0 @@
#include "matador/query/query_compiler.hpp"
#include "matador/query/query_data.hpp"
#include "matador/query/column.hpp"
#include "matador/sql/dialect.hpp"
namespace matador::query {
query_compiler::query_compiler(sql::dialect &d)
: dialect_(d)
{}
std::string query_compiler::compile(const query_data *data)
{
sql_.clear();
for (const auto &part : data->parts) {
part->accept(*this);
}
return sql_;
}
void query_compiler::visit(query_select_part &select_part)
{
auto ci = "name"_col.as("c01");
}
void query_compiler::visit(query_from_part &from_part)
{
}
}
-8
View File
@@ -1,8 +0,0 @@
#include "matador/query/query_intermediates.hpp"
namespace matador::query {
query_intermediate::query_intermediate(const std::shared_ptr<sql::schema> &scm)
: schema_(scm) {}
}
-30
View File
@@ -1,30 +0,0 @@
#include "matador/query/query_parts.hpp"
namespace matador::query {
query_part::query_part(sql::dialect::token_t token)
: token_(token) {}
query_select_part::query_select_part(std::vector<column> columns)
: query_part(sql::dialect::token_t::SELECT)
, columns_(std::move(columns)) {}
void query_select_part::accept(query_part_visitor &visitor)
{
visitor.visit(*this);
}
query_from_part::query_from_part(std::string table_name)
: query_part(sql::dialect::token_t::FROM)
, table_(std::move(table_name)) {}
query_from_part::query_from_part(std::string table_name, std::string as)
: query_part(sql::dialect::token_t::FROM)
, table_(std::move(table_name))
, alias_(std::move(as)) {}
void query_from_part::accept(query_part_visitor &visitor)
{
visitor.visit(*this);
}
}
+1
View File
@@ -2,6 +2,7 @@
#include "matador/sql/noop_connection.hpp" #include "matador/sql/noop_connection.hpp"
#include "matador/sql/dialect_builder.hpp" #include "matador/sql/dialect_builder.hpp"
#include <algorithm>
#include <stdexcept> #include <stdexcept>
namespace matador::sql { namespace matador::sql {
+43
View File
@@ -1,4 +1,5 @@
#include "matador/sql/column.hpp" #include "matador/sql/column.hpp"
#include "matador/sql/table.hpp"
namespace matador::sql { namespace matador::sql {
@@ -7,4 +8,46 @@ column operator ""_col(const char *name, size_t len)
return {{name, len}}; return {{name, len}};
} }
column::column(const char *name) : name(name) {}
column::column(std::string name) : name(std::move(name)) {}
column::column(sql_function_t func, std::string name) : name(std::move(name)), function_(func) {}
column::column(std::string table_name, std::string name, std::string as)
: table(std::move(table_name))
, name(std::move(name))
, alias(std::move(as)) {}
column::column(std::string table_name, const char *name, std::string as)
: table(std::move(table_name))
, name(name)
, alias(std::move(as)) {}
column::column(struct table &t, const char *name, std::string as)
: table(t.name)
, name(name)
, alias(std::move(as))
{
t.columns.push_back(*this);
}
bool column::equals(const column &x) const
{
return table == x.table &&
name == x.name &&
alias == x.alias &&
function_ == x.function_;
}
column &column::as(std::string a)
{
alias = std::move(a);
return *this;
}
bool column::is_function() const
{
return function_ != sql_function_t::NONE;
}
} }
+44
View File
@@ -63,6 +63,46 @@ const std::string &column_definition::ref_column() const
return ref_column_; return ref_column_;
} }
bool column_definition::is_integer() const
{
return type_ >= data_type_t::type_char && type_ <= data_type_t::type_unsigned_long_long;
}
bool column_definition::is_floating_point() const
{
return type_ == data_type_t::type_float || type_ == data_type_t::type_double;
}
bool column_definition::is_bool() const
{
return type_ == data_type_t::type_bool;
}
bool column_definition::is_string() const
{
return type_ == data_type_t::type_text;
}
bool column_definition::is_varchar() const
{
return type_ == data_type_t::type_varchar;
}
bool column_definition::is_blob() const
{
return type_ == data_type_t::type_blob;
}
bool column_definition::is_null() const
{
return type_ == data_type_t::type_null;
}
bool column_definition::is_unknown() const
{
return type_ == data_type_t::type_unknown;
}
void column_definition::type(data_type_t type) void column_definition::type(data_type_t type)
{ {
type_ = type; type_ = type;
@@ -70,6 +110,10 @@ void column_definition::type(data_type_t type)
std::string column_definition::str() const std::string column_definition::str() const
{ {
if (std::holds_alternative<std::string>(value_)) {
return std::get<std::string>(value_);
}
any_type_to_visitor<std::string> visitor; any_type_to_visitor<std::string> visitor;
std::visit(visitor, const_cast<any_type &>(value_)); std::visit(visitor, const_cast<any_type &>(value_));
return visitor.result; return visitor.result;
+31
View File
@@ -0,0 +1,31 @@
#include "matador/sql/column_definition_generator.hpp"
#include "matador/sql/schema.hpp"
namespace matador::sql {
column_definition_generator::column_definition_generator(std::vector<column_definition> &columns, const schema &repo)
: columns_(columns)
, repo_(repo)
{}
void column_definition_generator::on_primary_key(const char *id, std::string &pk, size_t size)
{
on_attribute(id, pk, { size, utils::constraints::PRIMARY_KEY });
}
void column_definition_generator::on_revision(const char *id, unsigned long long int &x)
{
on_attribute(id, x);
}
std::pair<std::string, std::string> column_definition_generator::determine_foreign_ref(const std::type_index &ti)
{
return repo_.reference(ti);
}
void fk_column_generator::on_primary_key(const char *, std::string &, size_t size)
{
type_ = data_type_traits<std::string>::builtin_type(size);
}
}
+16 -14
View File
@@ -1,31 +1,33 @@
#include "matador/sql/column_generator.hpp" #include "matador/sql/column_generator.hpp"
#include "matador/sql/schema.hpp"
namespace matador::sql { namespace matador::sql {
column_generator::column_generator(std::vector<column_definition> &columns, const schema &repo) column_generator::column_generator(std::vector<column> &column_infos,
: columns_(columns) const sql::schema &ts,
, repo_(repo) const std::string &table_name,
{} bool force_lazy)
: column_infos_(column_infos)
void column_generator::on_primary_key(const char *id, std::string &pk, size_t size) , table_schema_(ts)
, force_lazy_(force_lazy)
{ {
on_attribute(id, pk, { size, utils::constraints::PRIMARY_KEY }); table_name_stack_.push(table_name);
} }
void column_generator::on_revision(const char *id, unsigned long long int &x) void column_generator::on_primary_key(const char *id, std::string &, size_t)
{ {
on_attribute(id, x); push(id);
} }
std::pair<std::string, std::string> column_generator::determine_foreign_ref(const std::type_index &ti) void column_generator::on_revision(const char *id, unsigned long long int &)
{ {
return repo_.reference(ti); push(id);
} }
void fk_column_generator::on_primary_key(const char *, std::string &, size_t size) void column_generator::push(const std::string &column_name)
{ {
type_ = data_type_traits<std::string>::builtin_type(size); char str[4];
snprintf(str, 4, "c%02d", ++column_index);
column_infos_.emplace_back(table_name_stack_.top(), column_name, str);
} }
} }
-31
View File
@@ -1,31 +0,0 @@
#include "matador/sql/column_name_generator.hpp"
namespace matador::sql {
column_name_generator::column_name_generator(std::vector<column> &column_infos,
const sql::schema &ts,
const std::string &table_name)
: column_infos_(column_infos)
, table_schema_(ts)
{
table_name_stack_.push(table_name);
}
void column_name_generator::on_primary_key(const char *id, std::string &, size_t)
{
push(id);
}
void column_name_generator::on_revision(const char *id, unsigned long long int &)
{
push(id);
}
void column_name_generator::push(const std::string &column_name)
{
char str[4];
snprintf(str, 4, "c%02d", ++column_index);
column_infos_.emplace_back(table_name_stack_.top(), column_name, str);
}
}
+22 -4
View File
@@ -1,28 +1,46 @@
#include "matador/sql/condition.hpp" #include "matador/sql/condition.hpp"
#include "matador/sql/query_intermediates.hpp"
namespace matador::sql { namespace matador::sql {
condition<column, placeholder, std::enable_if<true>::type>::condition(const column &fld, basic_condition::operand_t op, const placeholder &val) condition<column, placeholder, std::enable_if<true>::type>::condition(const column &fld, basic_condition::operand_t op, const placeholder &val)
: basic_column_condition(fld, op), value(val) : basic_column_condition(fld, op), value(val)
{} {}
std::string condition<column, placeholder, std::enable_if<true>::type>::evaluate(dialect &d, query_context &query) const std::string condition<column, placeholder, std::enable_if<true>::type>::evaluate(const dialect &d, query_context &query) const
{ {
query.bind_vars.emplace_back(field_.name); query.bind_vars.emplace_back(field_.name);
return d.prepare_identifier(field_) + " " + operand + " " + d.next_placeholder(query.bind_vars); 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) : basic_column_condition(std::move(col), op), query_(q)
{} {}
std::string condition<column, query_context>::evaluate(dialect &d, query_context &query) const std::string condition<column, query_context>::evaluate(const dialect &d, query_context &query) const
{ {
std::string result(d.prepare_identifier(field_) + " " + operand + " ("); std::string result(d.prepare_identifier(field_) + " " + operand + " (");
result += (")"); result += query_.sql + (")");
return result; 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) condition<column, column> operator==(const column &a, const column &b)
{ {
return {a, basic_condition::operand_t::EQUAL, 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); 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); return sql::query(*const_cast<connection*>(this), schema);
} }
+15
View File
@@ -6,6 +6,21 @@
namespace matador::sql { namespace matador::sql {
void data_type_traits<nullptr_t>::read_value(query_result_reader &reader, const char *id, size_t index, nullptr_t &/*value*/)
{
// reader.read_value(id, index, value);
}
void data_type_traits<nullptr_t>::bind_value(parameter_binder &binder, size_t index, nullptr_t &/*value*/)
{
// binder.bind(index, value);
}
void data_type_traits<nullptr_t>::bind_result_value(result_parameter_binder &binder, size_t index, nullptr_t &/*value*/)
{
// binder.bind_result_value(index, value);
}
void data_type_traits<char>::read_value(query_result_reader &reader, const char *id, size_t index, char &value) void data_type_traits<char>::read_value(query_result_reader &reader, const char *id, size_t index, char &value)
{ {
reader.read_value(id, index, value); reader.read_value(id, index, value);
+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 void dialect::quote_identifier(std::string &str) const
{ {
str.insert(0, token_at(token_t::START_QUOTE)); // str.insert(0, token_at(token_t::START_QUOTE));
str += token_at(token_t::END_QUOTE); // str += token_at(token_t::END_QUOTE);
} }
void dialect::escape_quotes_in_identifier(std::string &str) const void dialect::escape_quotes_in_identifier(std::string &str) const
+38
View File
@@ -0,0 +1,38 @@
#include "matador/sql/entity_query_builder.hpp"
namespace matador::sql {
void entity_query_builder::on_primary_key(const char *id, std::string &, size_t)
{
push(id);
if (!is_root_entity()) {
auto b = pk_.is_varchar();
std::cout << "is matching primary key: " << std::boolalpha << b << "\n";
}
}
void entity_query_builder::on_revision(const char *id, unsigned long long &/*rev*/)
{
push(id);
}
void entity_query_builder::push(const std::string &column_name)
{
char str[4];
snprintf(str, 4, "c%02d", ++column_index);
entity_query_data_.columns.emplace_back(table_info_stack_.top().name, column_name, str);
}
[[nodiscard]] bool entity_query_builder::is_root_entity() const {
return table_info_stack_.size() == 1;
}
void entity_query_builder::append_join(const column &left, const column &right)
{
entity_query_data_.joins.push_back({
{ right.table },
make_condition(left == right)
});
}
}
+10 -16
View File
@@ -7,24 +7,20 @@ namespace matador::sql {
field::field(std::string name) field::field(std::string name)
: name_(std::move(name)) : name_(std::move(name))
, value_(nullptr) , value_(nullptr)
, type_(data_type_t::type_unknown)
{} {}
field::field(std::string name, data_type_t data_type, size_t size, int index) field::field(std::string name, data_type_t data_type, size_t size, int index)
: name_(std::move(name)) : name_(std::move(name))
, size_(size)
, index_(index) , index_(index)
, type_(data_type) {} , value_(data_type, size) {}
field::field(field &&x) noexcept field::field(field &&x) noexcept
: name_(std::move(x.name_)) : name_(std::move(x.name_))
, index_(x.index_) , index_(x.index_)
, value_(std::move(x.value_)) , value_(std::move(x.value_))
, type_(x.type_)
{ {
x.value_ = nullptr; x.value_ = nullptr;
x.index_ = -1; x.index_ = -1;
x.type_ = data_type_t::type_unknown;
} }
field &field::operator=(field &&x) noexcept field &field::operator=(field &&x) noexcept
@@ -32,10 +28,8 @@ field &field::operator=(field &&x) noexcept
name_ = std::move(x.name_); name_ = std::move(x.name_);
index_ = x.index_; index_ = x.index_;
value_ = std::move(x.value_); value_ = std::move(x.value_);
type_ = x.type_;
x.index_ = -1; x.index_ = -1;
x.value_ = nullptr; x.value_ = nullptr;
x.type_ = data_type_t::type_unknown;
return *this; return *this;
} }
@@ -47,7 +41,7 @@ const std::string &field::name() const
size_t field::size() const size_t field::size() const
{ {
return size_; return value_.size();
} }
int field::index() const int field::index() const
@@ -68,42 +62,42 @@ std::string field::str() const
bool field::is_integer() const bool field::is_integer() const
{ {
return type_ >= data_type_t::type_char && type_ <= data_type_t::type_unsigned_long_long; return value_.is_integer();
} }
bool field::is_floating_point() const bool field::is_floating_point() const
{ {
return type_ == data_type_t::type_float || type_ == data_type_t::type_double; return value_.is_floating_point();
} }
bool field::is_bool() const bool field::is_bool() const
{ {
return type_ == data_type_t::type_bool; return value_.is_bool();
} }
bool field::is_string() const bool field::is_string() const
{ {
return type_ == data_type_t::type_text; return value_.is_string();
} }
bool field::is_varchar() const bool field::is_varchar() const
{ {
return type_ == data_type_t::type_varchar; return value_.is_varchar();
} }
bool field::is_blob() const bool field::is_blob() const
{ {
return type_ == data_type_t::type_blob; return value_.is_blob();
} }
bool field::is_null() const bool field::is_null() const
{ {
return type_ == data_type_t::type_null; return value_.is_null();
} }
bool field::is_unknown() const bool field::is_unknown() const
{ {
return type_ == data_type_t::type_unknown; return value_.is_unknown();
} }
} }
+8
View File
@@ -28,6 +28,14 @@ query_select_intermediate query::select(const std::vector<column>& columns)
return {connection_, schema_, columns}; return {connection_, schema_, columns};
} }
query_select_intermediate query::select(std::vector<column> columns, std::initializer_list<column> additional_columns)
{
for (const auto &col : additional_columns) {
columns.push_back(col);
}
return {connection_, schema_, columns};
}
query_insert_intermediate query::insert() query_insert_intermediate query::insert()
{ {
return query_insert_intermediate{connection_, schema_}; return query_insert_intermediate{connection_, schema_};
+1 -1
View File
@@ -1,5 +1,5 @@
#include "matador/sql/query_builder.hpp" #include "matador/sql/query_builder.hpp"
#include "matador/sql/column_name_generator.hpp" #include "matador/sql/column_generator.hpp"
#include "matador/sql/column.hpp" #include "matador/sql/column.hpp"
#include "matador/sql/dialect.hpp" #include "matador/sql/dialect.hpp"
+9 -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) void query_compiler::visit(query_from_part &from_part)
{ {
query_.table = from_part.table(); 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) + query_.sql += " " + dialect_.token_at(dialect::token_t::FROM) +
" " + dialect_.prepare_identifier(from_part.table().name) + " " + dialect_.prepare_identifier(from_part.table().name) +
(from_part.table().alias.empty() ? "" : " AS " + (from_part.table().alias.empty() ? "" : " AS " +
@@ -70,9 +70,17 @@ void query_compiler::visit(query_from_part &from_part)
void query_compiler::visit(query_join_part &join_part) void query_compiler::visit(query_join_part &join_part)
{ {
if (dialect_.default_schema_name().empty()) {
query_.sql += " " + dialect_.token_at(dialect::token_t::JOIN) + query_.sql += " " + dialect_.token_at(dialect::token_t::JOIN) +
" " + dialect_.prepare_identifier(join_part.table().name) + " " + dialect_.prepare_identifier(join_part.table().name) +
(join_part.table().alias.empty() ? "" : " AS " + dialect_.prepare_identifier(join_part.table().alias)); (join_part.table().alias.empty() ? "" : " AS " + dialect_.prepare_identifier(join_part.table().alias));
} else {
query_.sql += " " + dialect_.token_at(dialect::token_t::JOIN) +
" " + dialect_.prepare_identifier(dialect_.default_schema_name()) +
"." + dialect_.prepare_identifier(join_part.table().name) +
(join_part.table().alias.empty() ? "" : " AS " + dialect_.prepare_identifier(join_part.table().alias));
}
} }
void query_compiler::visit(query_on_part &on_part) void query_compiler::visit(query_on_part &on_part)
+76 -76
View File
@@ -8,16 +8,16 @@ basic_query_intermediate::basic_query_intermediate(connection &db, const sql::sc
: connection_(db) : connection_(db)
, schema_(schema) {} , schema_(schema) {}
query_result<record> query_select_finish::fetch_all() query_result<record> query_select::fetch_all()
{ {
query_compiler compiler(connection_.dialect()); query_compiler compiler(connection_.dialect());
return connection_.fetch(compiler.compile(&data_)); return connection_.fetch(compiler.compile(data_.get()));
} }
std::optional<record> query_select_finish::fetch_one() std::optional<record> query_select::fetch_one()
{ {
query_compiler compiler(connection_.dialect()); query_compiler compiler(connection_.dialect());
auto result = connection_.fetch(compiler.compile(&data_)); auto result = connection_.fetch(compiler.compile(data_.get()));
auto first = result.begin(); auto first = result.begin();
if (first == result.end()) { if (first == result.end()) {
return std::nullopt; return std::nullopt;
@@ -26,145 +26,139 @@ std::optional<record> query_select_finish::fetch_one()
return *first.get(); return *first.get();
} }
query_context query_select_finish::build() const query_context query_select::build() const
{ {
query_compiler compiler(connection_.dialect()); query_compiler compiler(connection_.dialect());
return compiler.compile(&data_); return compiler.compile(data_.get());
} }
std::unique_ptr<query_result_impl> query_select_finish::fetch() std::unique_ptr<query_result_impl> query_select::fetch()
{ {
query_compiler compiler(connection_.dialect()); query_compiler compiler(connection_.dialect());
return connection_.fetch(compiler.compile(&data_).sql); return connection_.fetch(compiler.compile(data_.get()).sql);
} }
statement query_select_finish::prepare() statement query_select::prepare()
{ {
query_compiler compiler(connection_.dialect()); query_compiler compiler(connection_.dialect());
return connection_.prepare(compiler.compile(&data_)); return connection_.prepare(compiler.compile(data_.get()));
} }
query_intermediate::query_intermediate(connection &db, const sql::schema &schema, query_data &data) query_intermediate::query_intermediate(connection &db, const sql::schema &schema, const std::shared_ptr<query_data> &data)
: basic_query_intermediate(db, schema), data_(data) {} : basic_query_intermediate(db, schema), data_(data) {}
query_offset_intermediate query_limit_intermediate::offset(size_t offset) query_offset_intermediate query_limit_intermediate::offset(size_t offset)
{ {
data_.parts.push_back(std::make_unique<query_offset_part>(offset)); data_->parts.push_back(std::make_unique<query_offset_part>(offset));
return {connection_, schema_, data_}; return {connection_, schema_, data_};
} }
query_limit_intermediate query_offset_intermediate::limit(size_t limit) query_limit_intermediate query_offset_intermediate::limit(size_t limit)
{ {
data_.parts.push_back(std::make_unique<query_limit_part>(limit)); data_->parts.push_back(std::make_unique<query_limit_part>(limit));
return {connection_, schema_, data_}; return {connection_, schema_, data_};
} }
query_limit_intermediate query_order_direction_intermediate::limit(size_t limit) query_limit_intermediate query_order_direction_intermediate::limit(size_t limit)
{ {
data_.parts.push_back(std::make_unique<query_limit_part>(limit)); data_->parts.push_back(std::make_unique<query_limit_part>(limit));
return {connection_, schema_, data_}; return {connection_, schema_, data_};
} }
query_order_by_intermediate query_group_by_intermediate::order_by(const column &col) query_order_by_intermediate query_group_by_intermediate::order_by(const column &col)
{ {
data_.parts.push_back(std::make_unique<query_order_by_part>(col)); data_->parts.push_back(std::make_unique<query_order_by_part>(col));
return {connection_, schema_, data_}; return {connection_, schema_, data_};
} }
query_order_direction_intermediate query_order_by_intermediate::asc() query_order_direction_intermediate query_order_by_intermediate::asc()
{ {
data_.parts.push_back(std::make_unique<query_order_by_asc_part>()); data_->parts.push_back(std::make_unique<query_order_by_asc_part>());
return {connection_, schema_, data_}; return {connection_, schema_, data_};
} }
query_order_direction_intermediate query_order_by_intermediate::desc() query_order_direction_intermediate query_order_by_intermediate::desc()
{ {
data_.parts.push_back(std::make_unique<query_order_by_desc_part>()); data_->parts.push_back(std::make_unique<query_order_by_desc_part>());
return {connection_, schema_, data_}; return {connection_, schema_, data_};
} }
query_where_intermediate query_from_intermediate::where_clause(std::unique_ptr<basic_condition> &&cond) query_where_intermediate query_from_intermediate::where_clause(std::unique_ptr<basic_condition> &&cond)
{ {
data_.parts.push_back(std::make_unique<query_where_part>(std::move(cond))); if (cond) {
data_->parts.push_back(std::make_unique<query_where_part>(std::move(cond)));
}
return {connection_, schema_, data_}; return {connection_, schema_, data_};
} }
query_group_by_intermediate query_from_intermediate::group_by(const column &col) query_group_by_intermediate query_from_intermediate::group_by(const column &col)
{ {
data_.parts.push_back(std::make_unique<query_group_by_part>(col)); data_->parts.push_back(std::make_unique<query_group_by_part>(col));
return {connection_, schema_, data_}; return {connection_, schema_, data_};
} }
query_order_by_intermediate query_from_intermediate::order_by(const column &col) query_order_by_intermediate query_from_intermediate::order_by(const column &col)
{ {
data_.parts.push_back(std::make_unique<query_order_by_part>(col)); data_->parts.push_back(std::make_unique<query_order_by_part>(col));
return {connection_, schema_, data_}; return {connection_, schema_, data_};
} }
query_group_by_intermediate query_where_intermediate::group_by(const column &col) query_group_by_intermediate query_where_intermediate::group_by(const column &col)
{ {
data_.parts.push_back(std::make_unique<query_group_by_part>(col)); data_->parts.push_back(std::make_unique<query_group_by_part>(col));
return {connection_, schema_, data_}; return {connection_, schema_, data_};
} }
query_order_by_intermediate query_where_intermediate::order_by(const column &col) query_order_by_intermediate query_where_intermediate::order_by(const column &col)
{ {
data_.parts.push_back(std::make_unique<query_order_by_part>(col)); data_->parts.push_back(std::make_unique<query_order_by_part>(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_}; return {connection_, schema_, data_};
} }
query_on_intermediate query_join_intermediate::on_clause(std::unique_ptr<basic_condition> &&cond) 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))); data_->parts.push_back(std::make_unique<query_on_part>(std::move(cond)));
return {connection_, schema_, data_}; return {connection_, schema_, data_};
} }
query_join_intermediate query_from_intermediate::join_left(const table &t) query_join_intermediate query_from_intermediate::join_left(const table &t)
{ {
data_.parts.push_back(std::make_unique<query_join_part>(t)); data_->parts.push_back(std::make_unique<query_join_part>(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_}; return {connection_, schema_, data_};
} }
query_select_intermediate::query_select_intermediate(connection &db, const sql::schema &schema, const std::vector<column>& columns) query_select_intermediate::query_select_intermediate(connection &db, const sql::schema &schema, const std::vector<column>& columns)
: query_start_intermediate(db, schema) : query_start_intermediate(db, schema)
{ {
data_.parts.push_back(std::make_unique<query_select_part>(columns)); data_->parts.push_back(std::make_unique<query_select_part>(columns));
} }
query_from_intermediate query_select_intermediate::from(const table& t) query_from_intermediate query_select_intermediate::from(const table& t)
{ {
data_.parts.push_back(std::make_unique<query_from_part>(t)); data_->parts.push_back(std::make_unique<query_from_part>(t));
return {connection_, schema_, data_}; return {connection_, schema_, data_};
} }
query_insert_intermediate::query_insert_intermediate(connection &db, const sql::schema &schema) query_insert_intermediate::query_insert_intermediate(connection &db, const sql::schema &schema)
: query_start_intermediate(db, schema) : query_start_intermediate(db, schema)
{ {
data_.parts.push_back(std::make_unique<query_insert_part>()); data_->parts.push_back(std::make_unique<query_insert_part>());
} }
query_into_intermediate query_insert_intermediate::into(const sql::table &table, std::initializer_list<column> column_names) query_into_intermediate query_insert_intermediate::into(const sql::table &table, std::initializer_list<column> column_names)
@@ -174,83 +168,89 @@ query_into_intermediate query_insert_intermediate::into(const sql::table &table,
query_into_intermediate query_insert_intermediate::into(const table &table, std::vector<column> &&column_names) query_into_intermediate query_insert_intermediate::into(const table &table, std::vector<column> &&column_names)
{ {
data_.parts.push_back(std::make_unique<query_into_part>(table, column_names)); data_->parts.push_back(std::make_unique<query_into_part>(table, column_names));
return {connection_, schema_, data_}; return {connection_, schema_, data_};
} }
size_t query_execute_finish::execute() query_into_intermediate query_insert_intermediate::into(const table &table)
{ {
query_compiler compiler(connection_.dialect()); data_->parts.push_back(std::make_unique<query_into_part>(table, table.columns));
return connection_.execute(compiler.compile(&data_).sql); return {connection_, schema_, data_};
} }
statement query_execute_finish::prepare() size_t query_execute::execute()
{ {
query_compiler compiler(connection_.dialect()); query_compiler compiler(connection_.dialect());
return connection_.prepare(compiler.compile(&data_)); return connection_.execute(compiler.compile(data_.get()).sql);
} }
query_context query_execute_finish::build() const statement query_execute::prepare()
{ {
query_compiler compiler(connection_.dialect()); query_compiler compiler(connection_.dialect());
return compiler.compile(&data_); return connection_.prepare(compiler.compile(data_.get()));
} }
query_execute_finish query_into_intermediate::values(std::initializer_list<any_type> values) query_context query_execute::build() const
{
query_compiler compiler(connection_.dialect());
return compiler.compile(data_.get());
}
query_execute query_into_intermediate::values(std::initializer_list<any_type> values)
{ {
return this->values(std::vector<any_type>(values)); return this->values(std::vector<any_type>(values));
} }
query_execute_finish query_into_intermediate::values(std::vector<any_type> &&values) query_execute query_into_intermediate::values(std::vector<any_type> &&values)
{ {
data_.parts.push_back(std::make_unique<query_values_part>(std::move(values))); data_->parts.push_back(std::make_unique<query_values_part>(std::move(values)));
return {connection_, schema_, data_}; return {connection_, schema_, data_};
} }
query_create_intermediate::query_create_intermediate(connection &db, const sql::schema &schema) query_create_intermediate::query_create_intermediate(connection &db, const sql::schema &schema)
: query_start_intermediate(db, schema) { : query_start_intermediate(db, schema) {
data_.parts.push_back(std::make_unique<query_create_part>()); data_->parts.push_back(std::make_unique<query_create_part>());
} }
query_execute_finish query_create_intermediate::table(const sql::table &table, std::initializer_list<column_definition> columns) query_execute query_create_intermediate::table(const sql::table &table, std::initializer_list<column_definition> columns)
{ {
return this->table(table, std::vector<column_definition>{columns}); return this->table(table, std::vector<column_definition>{columns});
} }
query_execute_finish query_create_intermediate::table(const sql::table &table, const std::vector<column_definition> &columns) query_execute query_create_intermediate::table(const sql::table &table, const std::vector<column_definition> &columns)
{ {
data_.parts.push_back(std::make_unique<query_create_table_part>(table, columns)); data_->parts.push_back(std::make_unique<query_create_table_part>(table, columns));
return {connection_, schema_, data_}; return {connection_, schema_, data_};
} }
query_drop_intermediate::query_drop_intermediate(connection &db, const sql::schema &schema) query_drop_intermediate::query_drop_intermediate(connection &db, const sql::schema &schema)
: query_start_intermediate(db, schema) : query_start_intermediate(db, schema)
{ {
data_.parts.push_back(std::make_unique<query_drop_part>()); data_->parts.push_back(std::make_unique<query_drop_part>());
} }
query_execute_finish query_drop_intermediate::table(const sql::table &table) query_execute query_drop_intermediate::table(const sql::table &table)
{ {
data_.parts.push_back(std::make_unique<query_drop_table_part>(table)); data_->parts.push_back(std::make_unique<query_drop_table_part>(table));
return {connection_, schema_, data_}; return {connection_, schema_, data_};
} }
query_order_by_intermediate query_execute_where_intermediate::order_by(const column &col) query_order_by_intermediate query_execute_where_intermediate::order_by(const column &col)
{ {
data_.parts.push_back(std::make_unique<query_order_by_part>(col)); data_->parts.push_back(std::make_unique<query_order_by_part>(col));
return {connection_, schema_, data_}; return {connection_, schema_, data_};
} }
query_execute_where_intermediate query_set_intermediate::where_clause(std::unique_ptr<basic_condition> &&cond) query_execute_where_intermediate query_set_intermediate::where_clause(std::unique_ptr<basic_condition> &&cond)
{ {
data_.parts.push_back(std::make_unique<query_where_part>(std::move(cond))); data_->parts.push_back(std::make_unique<query_where_part>(std::move(cond)));
return {connection_, schema_, data_}; return {connection_, schema_, data_};
} }
query_update_intermediate::query_update_intermediate(connection &db, const sql::schema &schema, const sql::table& table) query_update_intermediate::query_update_intermediate(connection &db, const sql::schema &schema, const sql::table& table)
: query_start_intermediate(db, schema) : query_start_intermediate(db, schema)
{ {
data_.parts.push_back(std::make_unique<query_update_part>(table)); data_->parts.push_back(std::make_unique<query_update_part>(table));
} }
query_set_intermediate query_update_intermediate::set(std::initializer_list<key_value_pair> columns) query_set_intermediate query_update_intermediate::set(std::initializer_list<key_value_pair> columns)
@@ -260,26 +260,26 @@ query_set_intermediate query_update_intermediate::set(std::initializer_list<key_
query_set_intermediate query_update_intermediate::set(std::vector<key_value_pair> &&columns) query_set_intermediate query_update_intermediate::set(std::vector<key_value_pair> &&columns)
{ {
data_.parts.push_back(std::make_unique<query_set_part>(std::move(columns))); data_->parts.push_back(std::make_unique<query_set_part>(std::move(columns)));
return {connection_, schema_, data_}; return {connection_, schema_, data_};
} }
query_execute_where_intermediate query_delete_from_intermediate::where_clause(std::unique_ptr<basic_condition> &&cond) query_execute_where_intermediate query_delete_from_intermediate::where_clause(std::unique_ptr<basic_condition> &&cond)
{ {
data_.parts.push_back(std::make_unique<query_where_part>(std::move(cond))); data_->parts.push_back(std::make_unique<query_where_part>(std::move(cond)));
return {connection_, schema_, data_}; return {connection_, schema_, data_};
} }
query_delete_intermediate::query_delete_intermediate(connection &db, const sql::schema &schema) query_delete_intermediate::query_delete_intermediate(connection &db, const sql::schema &schema)
: query_start_intermediate(db, schema) : query_start_intermediate(db, schema)
{ {
data_.parts.push_back(std::make_unique<query_delete_part>()); data_->parts.push_back(std::make_unique<query_delete_part>());
} }
query_delete_from_intermediate query_delete_intermediate::from(const sql::table &table) query_delete_from_intermediate query_delete_intermediate::from(const sql::table &table)
{ {
data_.parts.push_back(std::make_unique<query_delete_from_part>(table)); data_->parts.push_back(std::make_unique<query_delete_from_part>(table));
return {connection_, schema_, data_}; return {connection_, schema_, data_};
} }
+3 -2
View File
@@ -1,5 +1,6 @@
#include "matador/sql/query_result_impl.hpp" #include "matador/sql/query_result_impl.hpp"
#include "matador/sql/query_result_reader.hpp" #include "matador/sql/query_result_reader.hpp"
#include "matador/sql/value.hpp"
namespace matador::sql { namespace matador::sql {
@@ -39,9 +40,9 @@ void query_result_impl::on_attribute(const char *id, std::string &value, const u
} }
void void
query_result_impl::on_attribute(const char *id, any_type &value, data_type_t type, const utils::field_attributes &attr) query_result_impl::on_attribute(const char *id, value &val, const utils::field_attributes &attr)
{ {
reader_->read_value(id, column_index_++, value, type, attr.size()); reader_->read_value(id, column_index_++, val, attr.size());
} }
const std::vector<column_definition>& query_result_impl::prototype() const const std::vector<column_definition>& query_result_impl::prototype() const
+28 -27
View File
@@ -1,5 +1,6 @@
#include "matador/sql/query_result_reader.hpp" #include "matador/sql/query_result_reader.hpp"
#include "matador/sql/to_value.hpp" #include "matador/sql/to_value.hpp"
#include "matador/sql/value.hpp"
namespace matador::sql { namespace matador::sql {
@@ -104,82 +105,82 @@ void query_result_reader::read_value(const char *id, size_t index, utils::blob &
} }
template < typename Type > template < typename Type >
void convert(const char *valstr, sql::any_type &value) void convert(const char *valstr, value &val)
{ {
Type val{}; Type local_val{};
sql::to_value(val, valstr); sql::to_value(local_val, valstr);
value = val; val = local_val;
} }
void query_result_reader::read_value(const char *id, size_t index, any_type &value, data_type_t type, size_t size) void query_result_reader::read_value(const char *id, size_t index, value &val, size_t size)
{ {
switch (type) { switch (val.type()) {
case sql::data_type_t::type_char: case sql::data_type_t::type_char:
convert<char>(column(index), value); convert<char>(column(index), val);
break; break;
case sql::data_type_t::type_short: case sql::data_type_t::type_short:
convert<short>(column(index), value); convert<short>(column(index), val);
break; break;
case sql::data_type_t::type_int: case sql::data_type_t::type_int:
convert<int>(column(index), value); convert<int>(column(index), val);
break; break;
case sql::data_type_t::type_long: case sql::data_type_t::type_long:
convert<long>(column(index), value); convert<long>(column(index), val);
break; break;
case sql::data_type_t::type_long_long: case sql::data_type_t::type_long_long:
convert<long long>(column(index), value); convert<long long>(column(index), val);
break; break;
case sql::data_type_t::type_unsigned_char: case sql::data_type_t::type_unsigned_char:
convert<unsigned char>(column(index), value); convert<unsigned char>(column(index), val);
break; break;
case sql::data_type_t::type_unsigned_short: case sql::data_type_t::type_unsigned_short:
convert<unsigned short>(column(index), value); convert<unsigned short>(column(index), val);
break; break;
case sql::data_type_t::type_unsigned_int: case sql::data_type_t::type_unsigned_int:
convert<unsigned int>(column(index), value); convert<unsigned int>(column(index), val);
break; break;
case sql::data_type_t::type_unsigned_long: case sql::data_type_t::type_unsigned_long:
convert<unsigned long>(column(index), value); convert<unsigned long>(column(index), val);
break; break;
case sql::data_type_t::type_unsigned_long_long: case sql::data_type_t::type_unsigned_long_long:
convert<unsigned long long>(column(index), value); convert<unsigned long long>(column(index), val);
break; break;
case sql::data_type_t::type_float: case sql::data_type_t::type_float:
convert<float>(column(index), value); convert<float>(column(index), val);
break; break;
case sql::data_type_t::type_double: case sql::data_type_t::type_double:
convert<double>(column(index), value); convert<double>(column(index), val);
break; break;
case sql::data_type_t::type_bool: { case sql::data_type_t::type_bool: {
int val{}; int local_val{};
sql::to_value(val, column(index)); sql::to_value(local_val, column(index));
value = val > 0; val = local_val > 0;
break; break;
} }
case sql::data_type_t::type_text: case sql::data_type_t::type_text:
case sql::data_type_t::type_varchar: { case sql::data_type_t::type_varchar: {
value = std::string{column(index)}; val = std::string{column(index)};
break; break;
} }
case sql::data_type_t::type_char_pointer: { case sql::data_type_t::type_char_pointer: {
value = column(index); val = column(index);
break; break;
} }
case sql::data_type_t::type_time: case sql::data_type_t::type_time:
case sql::data_type_t::type_date: { case sql::data_type_t::type_date: {
value = std::string{column(index)}; val = std::string{column(index)};
break; break;
} }
case sql::data_type_t::type_null: { case sql::data_type_t::type_null: {
value = nullptr_t{}; val = nullptr_t{};
break; break;
} }
case sql::data_type_t::type_blob: { case sql::data_type_t::type_blob: {
value = utils::blob{}; val = utils::blob{};
break; break;
} }
case sql::data_type_t::type_unknown: { case sql::data_type_t::type_unknown: {
value = std::string(column(index)); val = std::string(column(index));
break; break;
} }
} }
+22 -16
View File
@@ -7,23 +7,27 @@
namespace matador::sql { namespace matador::sql {
record::record(std::initializer_list<field> columns) 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) 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) record::record(const record &x)
: fields_(x.fields_) : fields_by_name_(x.fields_by_name_)
//, pk_index_(x.pk_index_) //, pk_index_(x.pk_index_)
{ {
for (auto& col : fields_) { for (auto& col : fields_by_name_) {
add_to_map(col, col.index()); fields_.push_back(std::ref(col.second.first));
} }
} }
@@ -33,16 +37,16 @@ record &record::operator=(const record &x)
return *this; return *this;
} }
fields_ = x.fields_; fields_by_name_ = x.fields_by_name_;
fields_by_name_.clear(); fields_.clear();
// pk_index_ = x.pk_index_; // pk_index_ = x.pk_index_;
for (auto& col : fields_) { for (auto& col : fields_by_name_) {
add_to_map(col, col.index()); fields_.push_back(std::ref(col.second.first));
} }
return *this; return *this;
} }
const std::vector<field> &record::columns() const const std::vector<record::field_ref> &record::columns() const
{ {
return fields_; return fields_;
} }
@@ -63,7 +67,9 @@ const std::vector<field> &record::columns() const
const field &record::at(const column &col) 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 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(); 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)); const auto it = fields_by_name_.emplace(col.name(), field_index_pair {col, fields_.size()});
add_to_map(ref, fields_.size() - 1); fields_.push_back(std::ref(it.first->second.first));
} }
record::iterator record::begin() record::iterator record::begin()
+20 -1
View File
@@ -8,6 +8,13 @@ namespace matador::sql {
schema::schema(std::string name) schema::schema(std::string name)
: name_(std::move(name)) {} : name_(std::move(name)) {}
void schema::create(connection &c) {
for (const auto &ti : repository_) {
// ti.second.prototype
}
}
std::string schema::name() const std::string schema::name() const
{ {
return name_; return name_;
@@ -15,7 +22,10 @@ std::string schema::name() const
const table_info& schema::attach(const std::type_index ti, const table_info& table) const table_info& schema::attach(const std::type_index ti, const table_info& table)
{ {
return repository_.try_emplace(ti, table).first->second;
auto &ref = repository_.try_emplace(ti, table).first->second;
repository_by_name_.try_emplace(ref.name, std::ref(ref));
return ref;
} }
std::optional<table_info> schema::info(std::type_index ti) const std::optional<table_info> schema::info(std::type_index ti) const
@@ -27,6 +37,15 @@ std::optional<table_info> schema::info(std::type_index ti) const
return it->second; return it->second;
} }
std::optional<table_info> schema::info(const std::string &name) const
{
const auto it = repository_by_name_.find(name);
if (it == repository_by_name_.end()) {
return std::nullopt;
}
return it->second;
}
std::pair<std::string, std::string> schema::reference(const std::type_index &ti) const std::pair<std::string, std::string> schema::reference(const std::type_index &ti) const
{ {
const auto it = repository_.find(ti); const auto it = repository_.find(ti);
+11 -5
View File
@@ -89,11 +89,6 @@ bool session::table_exists(const std::string &table_name) const
return c->exists(dialect_.default_schema_name(), table_name); return c->exists(dialect_.default_schema_name(), table_name);
} }
const schema& session::tables() const
{
return *schema_;
}
const class dialect &session::dialect() const const class dialect &session::dialect() const
{ {
return dialect_; return dialect_;
@@ -108,4 +103,15 @@ std::unique_ptr<query_result_impl> session::fetch(const std::string &sql) const
return c->fetch(sql); return c->fetch(sql);
} }
query_select session::build_select_query(connection_ptr<connection> &conn, entity_query_data &&data) const
{
return conn->query(*schema_)
.select(data.columns)
.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();
}
} }
+101
View File
@@ -0,0 +1,101 @@
#include "matador/sql/value.hpp"
#include <cstring>
namespace matador::sql {
namespace detail {
size_t determine_size(const std::string &val)
{
return val.size();
}
size_t determine_size(const char *val)
{
return strlen(val);
}
size_t determine_size(const utils::blob &val)
{
return val.size();
}
}
value::value(data_type_t data_type, size_t size)
: size_(size)
, type_(data_type) {}
value::value(value &&x) noexcept
: value_(std::move(x.value_))
, type_(x.type_)
{
x.value_ = nullptr;
x.type_ = data_type_t::type_unknown;
}
value &value::operator=(value &&x) noexcept
{
value_ = std::move(x.value_);
type_ = x.type_;
x.value_ = nullptr;
x.type_ = data_type_t::type_unknown;
return *this;
}
std::string value::str() const
{
return as<std::string>().value();
}
size_t value::size() const
{
return size_;
}
data_type_t value::type() const
{
return type_;
}
bool value::is_integer() const
{
return type_ >= data_type_t::type_char && type_ <= data_type_t::type_unsigned_long_long;
}
bool value::is_floating_point() const
{
return type_ == data_type_t::type_float || type_ == data_type_t::type_double;
}
bool value::is_bool() const
{
return type_ == data_type_t::type_bool;
}
bool value::is_string() const
{
return type_ == data_type_t::type_text;
}
bool value::is_varchar() const
{
return type_ == data_type_t::type_varchar || type_ == data_type_t::type_char_pointer;
}
bool value::is_blob() const
{
return type_ == data_type_t::type_blob;
}
bool value::is_null() const
{
return type_ == data_type_t::type_null;
}
bool value::is_unknown() const
{
return type_ == data_type_t::type_unknown;
}
}
+10 -6
View File
@@ -3,7 +3,7 @@ Include(FetchContent)
FetchContent_Declare( FetchContent_Declare(
Catch2 Catch2
GIT_REPOSITORY https://github.com/catchorg/Catch2.git 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) FetchContent_MakeAvailable(Catch2)
@@ -19,9 +19,9 @@ add_executable(tests
BackendProviderTest.cpp BackendProviderTest.cpp
models/product.hpp models/product.hpp
models/order.hpp models/order.hpp
models/order_details.h models/order_details.hpp
ColumnDefinitionGeneratorTest.cpp
ColumnGeneratorTest.cpp ColumnGeneratorTest.cpp
ColumnNameGeneratorTest.cpp
ValueGeneratorTest.cpp ValueGeneratorTest.cpp
models/category.hpp models/category.hpp
models/supplier.hpp models/supplier.hpp
@@ -34,12 +34,16 @@ add_executable(tests
models/location.hpp models/location.hpp
models/optional.hpp models/optional.hpp
ConvertTest.cpp ConvertTest.cpp
DummyConnection.hpp
DummyConnection.cpp
EntityQueryBuilderTest.cpp EntityQueryBuilderTest.cpp
models/author.hpp models/author.hpp
models/book.hpp models/book.hpp
FieldTest.cpp) FieldTest.cpp
models/recipe.hpp
ValueTest.cpp
ResultTest.cpp
utils/auto_reset_event.hpp
utils/auto_reset_event.cpp
models/student.hpp)
target_link_libraries(tests PRIVATE target_link_libraries(tests PRIVATE
Catch2::Catch2WithMain Catch2::Catch2WithMain
+56
View File
@@ -0,0 +1,56 @@
#include <catch2/catch_test_macros.hpp>
#include "matador/sql/column_definition_generator.hpp"
#include "matador/sql/schema.hpp"
#include "models/product.hpp"
#include "models/optional.hpp"
using namespace matador::sql;
using namespace matador::utils;
TEST_CASE("Generate column definitions from object", "[column][definition][generator]") {
schema repo("main");
auto columns = column_definition_generator::generate<matador::test::product>(repo);
const std::vector<column_definition> expected_columns = {
column_definition{"product_name", data_type_t::type_varchar, constraints::PRIMARY_KEY, null_option::NOT_NULL },
column_definition{"supplier_id", data_type_t::type_unsigned_long, constraints::FOREIGN_KEY, null_option::NOT_NULL },
column_definition{"category_id", data_type_t::type_unsigned_long, constraints::FOREIGN_KEY, null_option::NOT_NULL },
column_definition{"quantity_per_unit", data_type_t::type_varchar, null_attributes, null_option::NOT_NULL },
column_definition{"unit_price", data_type_t::type_unsigned_int, null_attributes, null_option::NOT_NULL },
column_definition{"units_in_stock", data_type_t::type_unsigned_int, null_attributes, null_option::NOT_NULL },
column_definition{"units_in_order", data_type_t::type_unsigned_int, null_attributes, null_option::NOT_NULL },
column_definition{"reorder_level", data_type_t::type_unsigned_int, null_attributes, null_option::NOT_NULL },
column_definition{"discontinued", data_type_t::type_bool, null_attributes, null_option::NOT_NULL }
};
REQUIRE(!columns.empty());
REQUIRE(columns.size() == expected_columns.size());
for (size_t i = 0; i != expected_columns.size(); ++i) {
REQUIRE(expected_columns[i].name() == columns[i].name());
REQUIRE(expected_columns[i].attributes().options() == columns[i].attributes().options() );
REQUIRE(expected_columns[i].type() == columns[i].type() );
}
}
TEST_CASE("Generate columns from object with nullable columns", "[column generator]") {
schema repo("main");
auto columns = column_definition_generator::generate<matador::test::optional>(repo);
const std::vector<column_definition> expected_columns = {
column_definition{"id", data_type_t::type_unsigned_long, constraints::PRIMARY_KEY, null_option::NOT_NULL },
column_definition{"name", data_type_t::type_varchar, null_attributes, null_option::NOT_NULL },
column_definition{"age", data_type_t::type_unsigned_int, null_attributes, null_option::NOT_NULL }
};
REQUIRE(!columns.empty());
REQUIRE(columns.size() == expected_columns.size());
for (size_t i = 0; i != expected_columns.size(); ++i) {
REQUIRE(expected_columns[i].name() == columns[i].name());
REQUIRE(expected_columns[i].attributes().options() == columns[i].attributes().options() );
REQUIRE(expected_columns[i].type() == columns[i].type() );
}
}
+70 -28
View File
@@ -3,54 +3,96 @@
#include "matador/sql/column_generator.hpp" #include "matador/sql/column_generator.hpp"
#include "matador/sql/schema.hpp" #include "matador/sql/schema.hpp"
#include "models/order.hpp"
#include "models/product.hpp" #include "models/product.hpp"
#include "models/optional.hpp" #include "models/book.hpp"
#include "models/author.hpp"
using namespace matador::sql; using namespace matador::sql;
using namespace matador::utils;
TEST_CASE("Generate columns from object", "[column generator]") { TEST_CASE("Generate columns from object", "[column][generator]") {
schema repo("main"); using namespace matador::test;
schema s("main");
s.attach<product>("product");
auto columns = column_generator::generate<matador::test::product>(repo); auto columns = column_generator::generate<product>(s);
const std::vector<column_definition> expected_columns = { const std::vector<std::string> expected_columns = {
column_definition{"product_name", data_type_t::type_varchar, constraints::PRIMARY_KEY, null_option::NOT_NULL }, "product_name",
column_definition{"supplier_id", data_type_t::type_unsigned_long, constraints::FOREIGN_KEY, null_option::NOT_NULL }, "supplier_id",
column_definition{"category_id", data_type_t::type_unsigned_long, constraints::FOREIGN_KEY, null_option::NOT_NULL }, "category_id",
column_definition{"quantity_per_unit", data_type_t::type_varchar, null_attributes, null_option::NOT_NULL }, "quantity_per_unit",
column_definition{"unit_price", data_type_t::type_unsigned_int, null_attributes, null_option::NOT_NULL }, "unit_price",
column_definition{"units_in_stock", data_type_t::type_unsigned_int, null_attributes, null_option::NOT_NULL }, "units_in_stock",
column_definition{"units_in_order", data_type_t::type_unsigned_int, null_attributes, null_option::NOT_NULL }, "units_in_order",
column_definition{"reorder_level", data_type_t::type_unsigned_int, null_attributes, null_option::NOT_NULL }, "reorder_level",
column_definition{"discontinued", data_type_t::type_bool, null_attributes, null_option::NOT_NULL } "discontinued"
}; };
REQUIRE(!columns.empty()); REQUIRE(!columns.empty());
REQUIRE(columns.size() == expected_columns.size()); REQUIRE(columns.size() == expected_columns.size());
for (size_t i = 0; i != expected_columns.size(); ++i) { for (size_t i = 0; i != expected_columns.size(); ++i) {
REQUIRE(expected_columns[i].name() == columns[i].name()); REQUIRE(expected_columns[i] == columns[i].name);
REQUIRE(expected_columns[i].attributes().options() == columns[i].attributes().options() );
REQUIRE(expected_columns[i].type() == columns[i].type() );
} }
} }
TEST_CASE("Generate columns from object with nullable columns", "[column generator]") { TEST_CASE("Generate columns for object with has many relation", "[column][generator][relation]") {
schema repo("main"); using namespace matador::test;
schema s("main");
s.attach<product>("product");
s.attach<order_details>("order_details");
s.attach<order>("order");
auto columns = column_generator::generate<matador::test::optional>(repo); auto columns = column_generator::generate<order>(s);
const std::vector<column_definition> expected_columns = { const std::vector<column> expected_columns = {
column_definition{"id", data_type_t::type_unsigned_long, constraints::PRIMARY_KEY, null_option::NOT_NULL }, { "order", "order_id", "c01" },
column_definition{"name", data_type_t::type_varchar, null_attributes, null_option::NOT_NULL }, { "order", "order_date", "c02" },
column_definition{"age", data_type_t::type_unsigned_int, null_attributes, null_option::NOT_NULL } { "order", "required_date", "c03" },
{ "order", "shipped_date", "c04" },
{ "order", "ship_via", "c05" },
{ "order", "freight", "c06" },
{ "order", "ship_name", "c07" },
{ "order", "ship_address", "c08" },
{ "order", "ship_city", "c09" },
{ "order", "ship_region", "c10" },
{ "order", "ship_postal_code", "c11" },
{ "order", "ship_country", "c12" },
{ "order_details", "order_details_id", "c13" },
{ "order_details", "order_id", "c14" },
{ "order_details", "product_id", "c15" }
}; };
REQUIRE(!columns.empty()); REQUIRE(!columns.empty());
REQUIRE(columns.size() == expected_columns.size()); REQUIRE(columns.size() == expected_columns.size());
for (size_t i = 0; i != expected_columns.size(); ++i) { for (size_t i = 0; i != expected_columns.size(); ++i) {
REQUIRE(expected_columns[i].name() == columns[i].name()); REQUIRE(expected_columns[i].equals(columns[i]));
REQUIRE(expected_columns[i].attributes().options() == columns[i].attributes().options() ); }
REQUIRE(expected_columns[i].type() == columns[i].type() );
}
TEST_CASE("Generate columns for object with eager foreign key relation", "[column][generator][eager]") {
using namespace matador::test;
schema s("main");
s.attach<book>("books");
s.attach<author>("authors");
const std::vector<column> expected_columns {
{ "books", "id", "c01" },
{ "books", "title", "c02" },
{ "authors", "id", "c03" },
{ "authors", "first_name", "c04" },
{ "authors", "last_name", "c05" },
{ "authors", "date_of_birth", "c06" },
{ "authors", "year_of_birth", "c07" },
{ "authors", "distinguished", "c08" },
{ "books", "published_in", "c09" }
};
auto columns = column_generator::generate<book>(s);
REQUIRE(!columns.empty());
REQUIRE(columns.size() == expected_columns.size());
for (size_t i = 0; i != expected_columns.size(); ++i) {
REQUIRE(expected_columns[i].equals(columns[i]));
} }
} }
-68
View File
@@ -1,68 +0,0 @@
#include <catch2/catch_test_macros.hpp>
#include "matador/sql/column_name_generator.hpp"
#include "matador/sql/schema.hpp"
#include "models/order.hpp"
#include "models/product.hpp"
using namespace matador::sql;
TEST_CASE("Generate column names from object", "[column name generator]") {
schema s("main");
s.attach<matador::test::product>("product");
auto columns = column_name_generator::generate<matador::test::product>(s);
const std::vector<std::string> expected_columns = {
"product_name",
"supplier_id",
"category_id",
"quantity_per_unit",
"unit_price",
"units_in_stock",
"units_in_order",
"reorder_level",
"discontinued"
};
REQUIRE(!columns.empty());
REQUIRE(columns.size() == expected_columns.size());
for (size_t i = 0; i != expected_columns.size(); ++i) {
REQUIRE(expected_columns[i] == columns[i].name);
}
}
TEST_CASE("Generate column names for object with has many relation", "[column][relation]") {
schema s("main");
s.attach<matador::test::product>("product");
s.attach<matador::test::order_details>("order_details");
s.attach<matador::test::order>("order");
auto columns = column_name_generator::generate<matador::test::order>(s);
const std::vector<std::string> expected_columns = {
"order_id",
"order_date",
"required_date",
"shipped_date",
"ship_via",
"freight",
"ship_name",
"ship_address",
"ship_city",
"ship_region",
"ship_postal_code",
"ship_country",
"order_details_id",
"order_id",
"product_id",
};
REQUIRE(!columns.empty());
REQUIRE(columns.size() == expected_columns.size());
for (size_t i = 0; i != expected_columns.size(); ++i) {
REQUIRE(expected_columns[i] == columns[i].name);
}
}
+79 -6
View File
@@ -1,15 +1,17 @@
#include <catch2/catch_test_macros.hpp> #include <catch2/catch_test_macros.hpp>
#include <matador/sql/connection_pool.hpp> #include "matador/sql/connection_pool.hpp"
#include "matador/sql/noop_connection.hpp"
#include "DummyConnection.hpp" #include "utils/auto_reset_event.hpp"
using namespace matador::sql; using namespace matador::sql;
using namespace matador::test::utils;
TEST_CASE("Create connection pool", "[connection pool]") { TEST_CASE("Create connection pool", "[connection pool]") {
using pool_t = connection_pool<matador::test::DummyConnection>; using pool_t = connection_pool<noop_connection>;
pool_t pool("sqlite://sqlite.db", 4); pool_t pool("noop://noop.db", 4);
REQUIRE(pool.size() == 4); REQUIRE(pool.size() == 4);
REQUIRE(pool.idle() == 4); REQUIRE(pool.idle() == 4);
@@ -51,9 +53,9 @@ TEST_CASE("Create connection pool", "[connection pool]") {
} }
TEST_CASE("Acquire connection by id", "[connection pool]") { TEST_CASE("Acquire connection by id", "[connection pool]") {
using pool_t = connection_pool<matador::test::DummyConnection>; using pool_t = connection_pool<noop_connection>;
pool_t pool("sqlite://sqlite.db", 4); pool_t pool("noop://noop.db", 4);
REQUIRE(pool.size() == 4); REQUIRE(pool.size() == 4);
REQUIRE(pool.idle() == 4); REQUIRE(pool.idle() == 4);
@@ -79,3 +81,74 @@ TEST_CASE("Acquire connection by id", "[connection pool]") {
REQUIRE(same_ptr.valid()); REQUIRE(same_ptr.valid());
REQUIRE(same_ptr.id() == connection_id); REQUIRE(same_ptr.id() == connection_id);
} }
TEST_CASE("Try acquire connection", "[connection pool][try acquire]") {
using pool_t = connection_pool<noop_connection>;
pool_t pool("noop://noop.db", 1);
REQUIRE(pool.size() == 1);
REQUIRE(pool.idle() == 1);
REQUIRE(pool.inuse() == 0);
auto ptr = pool.try_acquire();
REQUIRE(ptr.valid());
REQUIRE(ptr.id());
REQUIRE(ptr.id().value() > 0);
REQUIRE(ptr->is_open());
REQUIRE(pool.size() == 1);
REQUIRE(pool.idle() == 0);
REQUIRE(pool.inuse() == 1);
auto ptr2 = pool.try_acquire();
REQUIRE(!ptr2.valid());
pool.release(ptr);
REQUIRE(!ptr.valid());
REQUIRE(pool.size() == 1);
REQUIRE(pool.idle() == 1);
REQUIRE(pool.inuse() == 0);
ptr2 = pool.try_acquire();
REQUIRE(ptr2.valid());
REQUIRE(ptr2.id());
REQUIRE(ptr2.id().value() > 0);
REQUIRE(ptr2->is_open());
REQUIRE(pool.size() == 1);
REQUIRE(pool.idle() == 0);
REQUIRE(pool.inuse() == 1);
pool.release(ptr2);
auto_reset_event reset_main_event;
auto_reset_event reset_thread_event;
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_main_event.set();
reset_thread_event.wait_one();
pool.release(c1);
REQUIRE(!c1.valid());
reset_main_event.set();
});
reset_main_event.wait_one();
ptr2 = pool.try_acquire();
REQUIRE(!ptr2.valid());
reset_thread_event.set();
reset_main_event.wait_one();
ptr2 = pool.try_acquire();
REQUIRE(ptr2.valid());
REQUIRE(ptr2.id());
REQUIRE(ptr2.id().value() > 0);
t.join();
}
-28
View File
@@ -1,28 +0,0 @@
#include "DummyConnection.hpp"
namespace matador::test {
DummyConnection::DummyConnection(sql::connection_info info)
: connection_info_(std::move(info))
{}
DummyConnection::DummyConnection(const std::string &dns)
: DummyConnection(sql::connection_info::parse(dns))
{}
void DummyConnection::open()
{
is_open_ = true;
}
void DummyConnection::close()
{
is_open_ = false;
}
bool DummyConnection::is_open() const
{
return is_open_;
}
}
-30
View File
@@ -1,30 +0,0 @@
#ifndef QUERY_DUMMYCONNECTION_HPP
#define QUERY_DUMMYCONNECTION_HPP
#include "matador/sql/connection_info.hpp"
namespace matador::test {
class DummyConnection
{
public:
explicit DummyConnection(sql::connection_info info);
explicit DummyConnection(const std::string& dns);
DummyConnection(const DummyConnection &x) = default;
DummyConnection& operator=(const DummyConnection &x) = default;
DummyConnection(DummyConnection &&x) noexcept = default;
DummyConnection& operator=(DummyConnection &&x) noexcept = default;
~DummyConnection() = default;
void open();
void close();
[[nodiscard]] bool is_open() const;
private:
sql::connection_info connection_info_;
bool is_open_{};
};
}
#endif //QUERY_DUMMYCONNECTION_HPP
+244 -19
View File
@@ -3,31 +3,256 @@
#include <matador/sql/connection.hpp> #include <matador/sql/connection.hpp>
#include <matador/sql/entity_query_builder.hpp> #include <matador/sql/entity_query_builder.hpp>
#include "models/airplane.hpp"
#include "models/author.hpp" #include "models/author.hpp"
#include "models/book.hpp" #include "models/book.hpp"
#include "models/flight.hpp"
struct schiff { #include "models/recipe.hpp"
std::string id; #include "models/order.hpp"
#include "models/student.hpp"
template<typename Operator>
void process(Operator &op)
{
namespace field = matador::utils::access;
field::primary_key(op, "id", id, 255);
}
};
using namespace matador::sql; using namespace matador::sql;
TEST_CASE("Create sql query for entity", "[query][entity][builder]") { TEST_CASE("Create sql query data for entity with eager has one", "[query][entity][builder]") {
connection noop("noop://noop.db"); using namespace matador::test;
connection db("noop://noop.db");
schema scm("noop"); schema scm("noop");
scm.attach<matador::test::author>("authors"); scm.attach<airplane>("airplanes");
scm.attach<matador::test::book>("books"); scm.attach<flight>("flights");
entity_query_builder eqb(17, scm); entity_query_builder eqb(scm);
auto context = eqb.build<matador::test::book>(noop); auto data = eqb.build<flight>(17);
std::cout << "SQL: " << context.value().sql << "\n";
context = eqb.build<schiff>(noop); 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");
schema scm("noop");
scm.attach<author>("authors");
scm.attach<book>("books");
entity_query_builder eqb(scm);
auto data = eqb.build<book>(17);
REQUIRE(data.is_ok());
REQUIRE(data->root_table_name == "books");
REQUIRE(data->joins.size() == 1);
const std::vector<column> expected_columns {
{ "books", "id", "c01" },
{ "books", "title", "c02" },
{ "authors", "id", "c03" },
{ "authors", "first_name", "c04" },
{ "authors", "last_name", "c05" },
{ "authors", "date_of_birth", "c06" },
{ "authors", "year_of_birth", "c07" },
{ "authors", "distinguished", "c08" },
{ "books", "published_in", "c09" }
};
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 {
{ "authors", R"("books"."author_id" = "authors"."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"("books"."id" = 17)");
auto q = db.query(scm)
.select(data->columns)
.from(data->root_table_name);
for (auto &jd : data->joins) {
q.join_left(jd.join_table)
.on(std::move(jd.condition));
}
auto context = q
.where(std::move(data->where_clause))
.build();
}
TEST_CASE("Create sql query data for entity with eager has many belongs to", "[query][entity][builder]") {
using namespace matador::test;
connection db("noop://noop.db");
schema scm("noop");
scm.attach<product>("products");
scm.attach<order_details>("order_details");
scm.attach<order>("orders");
entity_query_builder eqb(scm);
auto data = eqb.build<order>(17);
REQUIRE(data.is_ok());
REQUIRE(data->root_table_name == "orders");
REQUIRE(data->joins.size() == 1);
const std::vector<column> expected_columns = {
{ "orders", "order_id", "c01" },
{ "orders", "order_date", "c02" },
{ "orders", "required_date", "c03" },
{ "orders", "shipped_date", "c04" },
{ "orders", "ship_via", "c05" },
{ "orders", "freight", "c06" },
{ "orders", "ship_name", "c07" },
{ "orders", "ship_address", "c08" },
{ "orders", "ship_city", "c09" },
{ "orders", "ship_region", "c10" },
{ "orders", "ship_postal_code", "c11" },
{ "orders", "ship_country", "c12" },
{ "order_details", "order_details_id", "c13" },
{ "order_details", "order_id", "c14" },
{ "order_details", "product_id", "c15" }
};
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 {
{ "order_details", R"("orders"."order_id" = "order_details"."order_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"("orders"."order_id" = 17)");
}
TEST_CASE("Create sql query data for entity with eager many to many", "[query][entity][builder]") {
using namespace matador::test;
connection db("noop://noop.db");
schema scm("noop");
scm.attach<recipe>("recipes");
scm.attach<ingredient>("ingredients");
scm.attach<recipe_ingredient>("recipe_ingredients");
entity_query_builder eqb(scm);
auto data = eqb.build<ingredient>(17);
REQUIRE(data.is_ok());
REQUIRE(data->root_table_name == "ingredients");
REQUIRE(data->joins.size() == 2);
const std::vector<column> expected_columns {
{ "ingredients", "id", "c01" },
{ "ingredients", "name", "c02" },
{ "recipes", "id", "c03" },
{ "recipes", "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 {
{ "recipe_ingredients", R"("ingredients"."id" = "recipe_ingredients"."ingredient_id")"},
{ "recipes", R"("recipe_ingredients"."recipe_id" = "recipes"."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"("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)");
} }
+2 -2
View File
@@ -9,8 +9,8 @@ TEST_CASE("Field test", "[field]") {
REQUIRE(f.name() == "name"); REQUIRE(f.name() == "name");
REQUIRE(f.index() == -1); REQUIRE(f.index() == -1);
REQUIRE(f.is_unknown()); REQUIRE(!f.is_unknown());
REQUIRE(!f.is_null()); REQUIRE(f.is_null());
REQUIRE(!f.is_integer()); REQUIRE(!f.is_integer());
REQUIRE(!f.is_floating_point()); REQUIRE(!f.is_floating_point());
REQUIRE(!f.is_blob()); REQUIRE(!f.is_blob());
+95
View File
@@ -0,0 +1,95 @@
#include <catch2/catch_test_macros.hpp>
#include "matador/utils/result.hpp"
namespace matador::test {
enum class math_error : int32_t {
DIVISION_BY_ZERO = 1
};
utils::result<float, math_error>divide(int x, int y) {
if (y == 0) {
return utils::error(math_error::DIVISION_BY_ZERO);
}
return utils::ok(float(x) / y);
}
utils::result<float, math_error>multiply(int x, int y) {
return utils::ok(float(x) * y);
}
utils::result<float, math_error>plus(int x, int y) {
return utils::ok(float(x) + y);
}
utils::result<float, std::string>error_to_string(math_error err) {
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"));
}
}
}
using namespace matador;
TEST_CASE("Result tests", "[result]") {
auto res = test::divide(4, 2);
REQUIRE(res);
REQUIRE(res.is_ok());
REQUIRE(!res.is_error());
REQUIRE((res.value() == 2.0));
REQUIRE_THROWS(res.err());
res = test::divide(4, 0);
REQUIRE(!res);
REQUIRE(!res.is_ok());
REQUIRE(res.is_error());
REQUIRE((res.err() == test::math_error::DIVISION_BY_ZERO));
res = test::divide(4, 2)
.and_then([](const auto &val) { return test::multiply(val, 5); })
.and_then([](const auto &val) { return test::plus(val, 10); });
REQUIRE(res);
REQUIRE(res.is_ok());
REQUIRE(!res.is_error());
REQUIRE((res.value() == 20.0));
res = test::divide(4, 0)
.and_then([](const auto &val) {
return test::multiply(val, 5);
});
REQUIRE(!res);
REQUIRE(!res.is_ok());
REQUIRE(res.is_error());
REQUIRE((res.err() == test::math_error::DIVISION_BY_ZERO));
auto res2 = test::divide(4, 0)
.or_else([](const auto &err) {
return test::error_to_string(err);
});
REQUIRE(!res2);
REQUIRE(!res2.is_ok());
REQUIRE(res2.is_error());
REQUIRE((res2.err() == "division by zero error"));
res = test::divide(4, 2)
.and_then([](const auto &val) { return test::multiply(val, 5); })
.transform([](const auto &val) { return val + 10; });
REQUIRE(res);
REQUIRE(res.is_ok());
REQUIRE(!res.is_error());
REQUIRE((res.value() == 20.0));
}
+70
View File
@@ -0,0 +1,70 @@
#include <catch2/catch_test_macros.hpp>
#include "matador/sql/value.hpp"
#include "matador/utils/types.hpp"
using namespace matador::sql;
TEST_CASE("Test value class", "[value]") {
value v;
REQUIRE(v.is_unknown());
REQUIRE(v.type() == data_type_t::type_unknown);
REQUIRE(v.size() == 0);
v = 7;
REQUIRE(v.is_integer());
REQUIRE(v.type() == data_type_t::type_int);
REQUIRE(v.size() == 0);
REQUIRE(v.as<int>() == 7);
REQUIRE(v.as<long>() == 7);
v = "test";
REQUIRE(v.is_varchar());
REQUIRE(v.type() == data_type_t::type_char_pointer);
REQUIRE(v.size() == 4);
v = std::string{"hello"};
REQUIRE(v.is_varchar());
REQUIRE(v.type() == data_type_t::type_varchar);
REQUIRE(v.size() == 5);
v = 4.5;
REQUIRE(v.is_floating_point());
REQUIRE(v.type() == data_type_t::type_double);
REQUIRE(v.size() == 0);
v = 6.7f;
REQUIRE(v.is_floating_point());
REQUIRE(v.type() == data_type_t::type_float);
REQUIRE(v.size() == 0);
v = std::string();
REQUIRE(v.is_string());
REQUIRE(v.type() == data_type_t::type_text);
REQUIRE(v.size() == 0);
v = true;
REQUIRE(v.is_bool());
REQUIRE(v.type() == data_type_t::type_bool);
REQUIRE(v.size() == 0);
v = nullptr;
REQUIRE(v.is_null());
REQUIRE(v.type() == data_type_t::type_null);
REQUIRE(v.size() == 0);
v = matador::utils::blob{ 1, 2, 3, 4 };
REQUIRE(v.is_blob());
REQUIRE(v.type() == data_type_t::type_blob);
REQUIRE(v.size() == 4);
}
+6
View File
@@ -3,10 +3,14 @@
#include "matador/utils/access.hpp" #include "matador/utils/access.hpp"
#include "matador/sql/entity.hpp"
#include <string> #include <string>
namespace matador::test { namespace matador::test {
struct book;
struct author struct author
{ {
unsigned long id{}; unsigned long id{};
@@ -15,6 +19,7 @@ struct author
std::string date_of_birth; std::string date_of_birth;
unsigned short year_of_birth{}; unsigned short year_of_birth{};
bool distinguished{false}; bool distinguished{false};
std::vector<matador::sql::entity<book>> books;
template<typename Operator> template<typename Operator>
void process(Operator &op) void process(Operator &op)
@@ -26,6 +31,7 @@ struct author
field::attribute(op, "date_of_birth", date_of_birth, 31); field::attribute(op, "date_of_birth", date_of_birth, 31);
field::attribute(op, "year_of_birth", year_of_birth); field::attribute(op, "year_of_birth", year_of_birth);
field::attribute(op, "distinguished", distinguished); field::attribute(op, "distinguished", distinguished);
field::has_many(op, books, "author_id", utils::fetch_type::LAZY);
} }
}; };
+1 -1
View File
@@ -25,7 +25,7 @@ struct book
namespace field = matador::utils::access; namespace field = matador::utils::access;
field::primary_key(op, "id", id); field::primary_key(op, "id", id);
field::attribute(op, "title", title, 511); field::attribute(op, "title", title, 511);
field::has_one(op, "author_id", book_author, matador::utils::default_foreign_attributes); field::belongs_to(op, "author_id", book_author, utils::fetch_type::EAGER);
field::attribute(op, "published_in", published_in); field::attribute(op, "published_in", published_in);
} }
}; };
+2 -2
View File
@@ -1,7 +1,7 @@
#ifndef QUERY_ORDER_HPP #ifndef QUERY_ORDER_HPP
#define QUERY_ORDER_HPP #define QUERY_ORDER_HPP
#include "order_details.h" #include "order_details.hpp"
#include "matador/utils/access.hpp" #include "matador/utils/access.hpp"
@@ -42,7 +42,7 @@ struct order
field::attribute(op, "ship_region", ship_region, 255); field::attribute(op, "ship_region", ship_region, 255);
field::attribute(op, "ship_postal_code", ship_postal_code, 255); field::attribute(op, "ship_postal_code", ship_postal_code, 255);
field::attribute(op, "ship_country", ship_country, 255); field::attribute(op, "ship_country", ship_country, 255);
field::has_many(op, "order_details", order_details_, utils::fetch_type::EAGER); field::has_many(op, order_details_, "order_id", utils::fetch_type::EAGER);
} }
}; };
@@ -1,5 +1,5 @@
#ifndef QUERY_ORDER_DETAILS_H #ifndef QUERY_ORDER_DETAILS_HPP
#define QUERY_ORDER_DETAILS_H #define QUERY_ORDER_DETAILS_HPP
#include "product.hpp" #include "product.hpp"
@@ -25,4 +25,4 @@ struct order_details
}; };
} }
#endif //QUERY_ORDER_DETAILS_H #endif //QUERY_ORDER_DETAILS_HPP
+55
View File
@@ -0,0 +1,55 @@
#ifndef QUERY_RECIPE_HPP
#define QUERY_RECIPE_HPP
#include "matador/utils/access.hpp"
#include "matador/utils/field_attributes.hpp"
#include "matador/utils/foreign_attributes.hpp"
#include "matador/sql/entity.hpp"
#include "matador/sql/has_many_to_many_relation.hpp"
#include <string>
namespace matador::test {
struct recipe;
struct ingredient
{
unsigned long id{};
std::string name;
std::vector<matador::sql::entity<recipe>> recipes;
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, "recipe_ingredients", recipes, "ingredient_id", "recipe_id", utils::fetch_type::EAGER);
}
};
struct recipe
{
unsigned long id{};
std::string name;
std::vector<matador::sql::entity<ingredient>> ingredients;
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, "recipe_ingredients", ingredients, utils::fetch_type::LAZY);
}
};
class recipe_ingredient : public sql::has_many_to_many_relation<recipe, ingredient>
{
public:
recipe_ingredient()
: has_many_to_many_relation("recipe_id", "ingredient_id") {}
};
}
#endif //QUERY_RECIPE_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