Compare commits
118
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
24677d61df | ||
|
|
5da70ba3a5 | ||
|
|
c880728093 | ||
|
|
78b30f9742 | ||
|
|
b19d0fd3ca | ||
|
|
730ab05213 | ||
|
|
6ecda781f5 | ||
|
|
d4865e4d10 | ||
|
|
06f6166f05 | ||
|
|
89b795c488 | ||
|
|
1b98eb4019 | ||
|
|
47fd6d68e4 | ||
|
|
34a5cfcc88 | ||
|
|
705d361aaa | ||
|
|
87bd2f8b89 | ||
|
|
8914c06833 | ||
|
|
34a9a8aa4a | ||
|
|
12f8590634 | ||
|
|
cea7b97f2b | ||
|
|
bd06034ebf | ||
|
|
68d67b17b7 | ||
|
|
73bd6f641c | ||
|
|
98d58d8e60 | ||
|
|
b22a830d18 | ||
|
|
85e6ee4b93 | ||
|
|
2e1b583741 | ||
|
|
6701031007 | ||
|
|
57f57956de | ||
|
|
141d798a41 | ||
|
|
cc0bcbeb61 | ||
|
|
5ec7c13420 | ||
|
|
ddf19bbf07 | ||
|
|
194e139e8b | ||
|
|
6629a71f6e | ||
|
|
72019bc1e7 | ||
|
|
7216f07c9f | ||
|
|
aa8da1f76f | ||
|
|
627af1d1a8 | ||
|
|
0f3028c5c7 | ||
|
|
25405de2e0 | ||
|
|
e58b7b9b0d | ||
|
|
052ff657c1 | ||
|
|
08a28b1627 | ||
|
|
60f03246dc | ||
|
|
caacdfa34a | ||
|
|
8c8423bf64 | ||
|
|
820b5432b2 | ||
|
|
bf99642af4 | ||
|
|
9f54716df2 | ||
|
|
57703dfb67 | ||
|
|
22f6f71412 | ||
|
|
1cdd83cb31 | ||
|
|
e3618c60e3 | ||
|
|
ceb7795d9c | ||
|
|
68eb2b6a6e | ||
|
|
a714cd2a53 | ||
|
|
1e08996087 | ||
|
|
5e6ba3666e | ||
|
|
3d4a61749b | ||
|
|
3658664eb9 | ||
|
|
8753042b3c | ||
|
|
85d297995d | ||
|
|
e6adffc264 | ||
|
|
95cd363097 | ||
|
|
9c37e989ba | ||
|
|
e7523e2bba | ||
|
|
37855dc6fb | ||
|
|
433b0e40e1 | ||
|
|
3fb23e167e | ||
|
|
8e1dbe2ccd | ||
|
|
96c6c78cb8 | ||
|
|
1a22c70320 | ||
|
|
75d5cba6c3 | ||
|
|
656d045ccf | ||
|
|
3a82f4891a | ||
|
|
a34a3dc266 | ||
|
|
b6456356b4 | ||
|
|
9b25f7f556 | ||
|
|
78eb5d04cd | ||
|
|
59e1533cca | ||
|
|
f144b26dbb | ||
|
|
d734d647ed | ||
|
|
0a0e3752ea | ||
|
|
a0326632ab | ||
|
|
f03664d60a | ||
|
|
53ac6810d9 | ||
|
|
f62f3e626b | ||
|
|
d59b41d06c | ||
|
|
57246d8742 | ||
|
|
1806e2670c | ||
|
|
9e4660c4ee | ||
|
|
f4e984a288 | ||
|
|
855c9295fb | ||
|
|
40dfc30dae | ||
|
|
920fdc68ed | ||
|
|
f751541bdd | ||
|
|
833cb64352 | ||
|
|
597965b62f | ||
|
|
fedce38270 | ||
|
|
3fc08fb7ff | ||
|
|
ee8ce62687 | ||
|
|
905ee019d7 | ||
|
|
907bcf90a6 | ||
|
|
6b2db17f58 | ||
|
|
6423bd505b | ||
|
|
7996608f62 | ||
|
|
53b10fd1f9 | ||
|
|
86c9e6b1cd | ||
|
|
6bdf281eab | ||
|
|
801fbdd187 | ||
|
|
2aff0cabbb | ||
|
|
9b4738a5ff | ||
|
|
bc8158a6ca | ||
|
|
d9eaa2009f | ||
|
|
85c56584be | ||
|
|
1e78430855 | ||
|
|
626eae1ac8 | ||
|
|
0157233b02 |
@@ -33,22 +33,26 @@ public:
|
|||||||
|
|
||||||
utils::result<sql::execute_result, utils::error> execute(const sql::query_context &context) override;
|
utils::result<sql::execute_result, utils::error> execute(const sql::query_context &context) override;
|
||||||
utils::result<std::unique_ptr<sql::statement_impl>, utils::error> prepare(const sql::query_context &context) override;
|
utils::result<std::unique_ptr<sql::statement_impl>, utils::error> prepare(const sql::query_context &context) override;
|
||||||
utils::result<std::unique_ptr<sql::query_result_impl>, utils::error> fetch(const sql::query_context &context) override;
|
utils::result<std::unique_ptr<sql::query_result_impl>, utils::error> fetch(const sql::query_context &ctx) override;
|
||||||
|
|
||||||
utils::result<std::vector<object::attribute>, utils::error> describe(const std::string& table) override;
|
utils::result<std::vector<object::attribute>, utils::error> describe(const std::string& table) override;
|
||||||
utils::result<bool, utils::error> exists(const std::string &schema_name, const std::string &table_name) override;
|
utils::result<bool, utils::error> exists(const std::string &schema_name, const std::string &table_name) override;
|
||||||
|
|
||||||
|
utils::result<bool, utils::error> sequence_exists(const std::string &schema_name, const std::string &sequence_name) override;
|
||||||
|
|
||||||
[[nodiscard]] std::string to_escaped_string( const utils::blob_type_t& value ) const override;
|
[[nodiscard]] std::string to_escaped_string( const utils::blob_type_t& value ) const override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
[[nodiscard]] static std::string generate_statement_name(const sql::query_context &query) ;
|
[[nodiscard]] static std::string generate_statement_name(const sql::query_context &ctx) ;
|
||||||
|
|
||||||
|
[[nodiscard]] utils::result<sql::execute_result, utils::error> execute(const std::string &stmt) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
PGconn *conn_{nullptr};
|
PGconn *conn_{nullptr};
|
||||||
|
|
||||||
using string_to_int_map = std::unordered_map<std::string, unsigned long>;
|
using hash_to_string_map = std::unordered_map<size_t, std::string>;
|
||||||
|
|
||||||
static string_to_int_map statement_name_map_;
|
static hash_to_string_map statement_name_map_;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,14 +7,15 @@
|
|||||||
|
|
||||||
#include "matador/sql/error_code.hpp"
|
#include "matador/sql/error_code.hpp"
|
||||||
#include "matador/sql/record.hpp"
|
#include "matador/sql/record.hpp"
|
||||||
|
|
||||||
#include "matador/sql/internal/query_result_impl.hpp"
|
#include "matador/sql/internal/query_result_impl.hpp"
|
||||||
|
|
||||||
|
#include "matador/utils/string.hpp"
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
|
|
||||||
namespace matador::backends::postgres {
|
namespace matador::backends::postgres {
|
||||||
postgres_connection::string_to_int_map postgres_connection::statement_name_map_{};
|
postgres_connection::hash_to_string_map postgres_connection::statement_name_map_{};
|
||||||
|
|
||||||
postgres_connection::postgres_connection(const sql::connection_info &info)
|
postgres_connection::postgres_connection(const sql::connection_info &info)
|
||||||
: connection_impl(info) {
|
: connection_impl(info) {
|
||||||
@@ -34,7 +35,7 @@ utils::result<void, utils::error> postgres_connection::open() {
|
|||||||
const std::string msg = PQerrorMessage(conn_);
|
const std::string msg = PQerrorMessage(conn_);
|
||||||
PQfinish(conn_);
|
PQfinish(conn_);
|
||||||
conn_ = nullptr;
|
conn_ = nullptr;
|
||||||
return utils::failure(make_error(sql::error_code::OPEN_ERROR, nullptr, conn_, "Failed to connect"));
|
return utils::failure(make_error(sql::error_code::OpenError, nullptr, conn_, "Failed to connect"));
|
||||||
}
|
}
|
||||||
|
|
||||||
return utils::ok<void>();
|
return utils::ok<void>();
|
||||||
@@ -70,7 +71,7 @@ utils::result<utils::version, utils::error> postgres_connection::server_version(
|
|||||||
const auto server_version = PQserverVersion(conn_);
|
const auto server_version = PQserverVersion(conn_);
|
||||||
|
|
||||||
if (server_version == 0) {
|
if (server_version == 0) {
|
||||||
return utils::failure(make_error(sql::error_code::FAILURE, nullptr, conn_, "Failed to get server version"));
|
return utils::failure(make_error(sql::error_code::Failure, nullptr, conn_, "Failed to get server version"));
|
||||||
}
|
}
|
||||||
|
|
||||||
return utils::ok(utils::version{
|
return utils::ok(utils::version{
|
||||||
@@ -82,20 +83,20 @@ utils::result<utils::version, utils::error> postgres_connection::server_version(
|
|||||||
|
|
||||||
utils::basic_type oid2type(Oid oid);
|
utils::basic_type oid2type(Oid oid);
|
||||||
|
|
||||||
utils::result<std::unique_ptr<sql::query_result_impl>, utils::error> postgres_connection::fetch(const sql::query_context &context) {
|
utils::result<std::unique_ptr<sql::query_result_impl>, utils::error> postgres_connection::fetch(const sql::query_context &ctx) {
|
||||||
PGresult *res = PQexec(conn_, context.sql.c_str());
|
PGresult *res = PQexec(conn_, ctx.sql.c_str());
|
||||||
|
|
||||||
if (is_result_error(res)) {
|
if (is_result_error(res)) {
|
||||||
const auto err = make_error(sql::error_code::FETCH_FAILED, res, conn_, "Failed to fetch", context.sql);
|
const auto err = make_error(sql::error_code::FetchFailed, res, conn_, "Failed to fetch", ctx.sql);
|
||||||
PQclear(res);
|
PQclear(res);
|
||||||
return utils::failure(err);
|
return utils::failure(err);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<object::attribute> prototype = context.prototype;
|
std::vector<object::attribute> prototype = ctx.prototype;
|
||||||
|
|
||||||
const int num_col = PQnfields(res);
|
const int num_col = PQnfields(res);
|
||||||
if (prototype.size() != static_cast<size_t>(num_col)) {
|
if (prototype.size() != static_cast<size_t>(num_col)) {
|
||||||
const auto err = make_error(sql::error_code::FETCH_FAILED, res, conn_, "Number of received columns doesn't match expected columns.", context.sql);
|
const auto err = make_error(sql::error_code::FetchFailed, res, conn_, "Number of received columns doesn't match expected columns.", ctx.sql);
|
||||||
PQclear(res);
|
PQclear(res);
|
||||||
return utils::failure(err);
|
return utils::failure(err);
|
||||||
}
|
}
|
||||||
@@ -111,22 +112,32 @@ utils::result<std::unique_ptr<sql::query_result_impl>, utils::error> postgres_co
|
|||||||
|
|
||||||
return utils::ok(std::make_unique<sql::query_result_impl>(std::make_unique<postgres_result_reader>(res),
|
return utils::ok(std::make_unique<sql::query_result_impl>(std::make_unique<postgres_result_reader>(res),
|
||||||
std::move(prototype),
|
std::move(prototype),
|
||||||
context.resolver,
|
ctx.resolver,
|
||||||
context.result_type));
|
ctx.result_type));
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string postgres_connection::generate_statement_name(const sql::query_context &query) {
|
std::string postgres_connection::generate_statement_name(const sql::query_context &ctx) {
|
||||||
std::stringstream name;
|
auto it = statement_name_map_.find(ctx.sql_hash);
|
||||||
name << query.table_name << "_" << query.command_name;
|
|
||||||
auto result = statement_name_map_.find(name.str());
|
|
||||||
|
|
||||||
if (result == statement_name_map_.end()) {
|
if (it == statement_name_map_.end()) {
|
||||||
result = statement_name_map_.insert(std::make_pair(name.str(), 0)).first;
|
it = statement_name_map_.insert(std::make_pair(ctx.sql_hash, utils::to_hex_string(ctx.sql_hash))).first;
|
||||||
}
|
}
|
||||||
|
|
||||||
name << "_" << ++result->second;
|
return it->second;
|
||||||
|
}
|
||||||
|
|
||||||
return name.str();
|
utils::result<sql::execute_result, utils::error> postgres_connection::execute(const std::string& stmt) const {
|
||||||
|
PGresult *res = PQexec(conn_, stmt.c_str());
|
||||||
|
|
||||||
|
if (const auto status = PQresultStatus(res); status != PGRES_COMMAND_OK && status != PGRES_TUPLES_OK) {
|
||||||
|
return utils::failure(make_error(sql::error_code::Failure, res, conn_, "Failed to execute", stmt));
|
||||||
|
}
|
||||||
|
|
||||||
|
const size_t affected_rows = utils::to<size_t>(PQcmdTuples(res));
|
||||||
|
|
||||||
|
PQclear(res);
|
||||||
|
|
||||||
|
return utils::ok(sql::execute_result{affected_rows});
|
||||||
}
|
}
|
||||||
|
|
||||||
utils::result<std::unique_ptr<sql::statement_impl>, utils::error> postgres_connection::prepare(const sql::query_context &context) {
|
utils::result<std::unique_ptr<sql::statement_impl>, utils::error> postgres_connection::prepare(const sql::query_context &context) {
|
||||||
@@ -135,7 +146,7 @@ utils::result<std::unique_ptr<sql::statement_impl>, utils::error> postgres_conne
|
|||||||
PGresult *result = PQprepare(conn_, statement_name.c_str(), context.sql.c_str(), static_cast<int>(context.bind_vars.size()), nullptr);
|
PGresult *result = PQprepare(conn_, statement_name.c_str(), context.sql.c_str(), static_cast<int>(context.bind_vars.size()), nullptr);
|
||||||
|
|
||||||
if (is_result_error(result)) {
|
if (is_result_error(result)) {
|
||||||
return utils::failure(make_error(sql::error_code::PREPARE_FAILED, result, conn_, "Failed to prepare", context.sql));
|
return utils::failure(make_error(sql::error_code::PrepareFailed, result, conn_, "Failed to prepare", context.sql));
|
||||||
}
|
}
|
||||||
|
|
||||||
std::unique_ptr<sql::statement_impl> s(std::make_unique<postgres_statement>(conn_, result, statement_name, context));
|
std::unique_ptr<sql::statement_impl> s(std::make_unique<postgres_statement>(conn_, result, statement_name, context));
|
||||||
@@ -143,13 +154,11 @@ utils::result<std::unique_ptr<sql::statement_impl>, utils::error> postgres_conne
|
|||||||
}
|
}
|
||||||
|
|
||||||
utils::result<sql::execute_result, utils::error> postgres_connection::execute(const sql::query_context &context) {
|
utils::result<sql::execute_result, utils::error> postgres_connection::execute(const sql::query_context &context) {
|
||||||
if (context.command == sql::sql_command::Insert) {
|
return execute(context.sql);
|
||||||
// handle insert command with the primary key generator strategy
|
|
||||||
}
|
|
||||||
PGresult *res = PQexec(conn_, context.sql.c_str());
|
PGresult *res = PQexec(conn_, context.sql.c_str());
|
||||||
|
|
||||||
if (const auto status = PQresultStatus(res); status != PGRES_COMMAND_OK && status != PGRES_TUPLES_OK) {
|
if (const auto status = PQresultStatus(res); status != PGRES_COMMAND_OK && status != PGRES_TUPLES_OK) {
|
||||||
return utils::failure(make_error(sql::error_code::FAILURE, res, conn_, "Failed to execute", context.sql));
|
return utils::failure(make_error(sql::error_code::Failure, res, conn_, "Failed to execute", context.sql));
|
||||||
}
|
}
|
||||||
|
|
||||||
const size_t affected_rows = utils::to<size_t>(PQcmdTuples(res));
|
const size_t affected_rows = utils::to<size_t>(PQcmdTuples(res));
|
||||||
@@ -244,7 +253,7 @@ utils::result<std::vector<object::attribute>, utils::error> postgres_connection:
|
|||||||
PGresult *res = PQexec(conn_, stmt.c_str());
|
PGresult *res = PQexec(conn_, stmt.c_str());
|
||||||
|
|
||||||
if (is_result_error(res)) {
|
if (is_result_error(res)) {
|
||||||
return utils::failure(make_error(sql::error_code::DESCRIBE_FAILED, res, conn_, "Failed to describe", stmt));
|
return utils::failure(make_error(sql::error_code::DescribeFailed, res, conn_, "Failed to describe", stmt));
|
||||||
}
|
}
|
||||||
|
|
||||||
postgres_result_reader reader(res);
|
postgres_result_reader reader(res);
|
||||||
@@ -269,7 +278,7 @@ utils::result<std::vector<object::attribute>, utils::error> postgres_connection:
|
|||||||
null_opt = object::null_option_type::NotNull;
|
null_opt = object::null_option_type::NotNull;
|
||||||
}
|
}
|
||||||
// f.default_value(res->column(4));
|
// f.default_value(res->column(4));
|
||||||
prototype.emplace_back(name, type, utils::null_attributes, null_opt);
|
prototype.emplace_back(name, type, NullAttributes, null_opt);
|
||||||
}
|
}
|
||||||
|
|
||||||
return utils::ok(prototype);
|
return utils::ok(prototype);
|
||||||
@@ -285,19 +294,33 @@ utils::result<bool, utils::error> postgres_connection::exists(const std::string
|
|||||||
PGresult *res = PQexec(conn_, stmt.c_str());
|
PGresult *res = PQexec(conn_, stmt.c_str());
|
||||||
|
|
||||||
if (is_result_error(res)) {
|
if (is_result_error(res)) {
|
||||||
return utils::failure(make_error(sql::error_code::TABLE_EXISTS_FAILED, res, conn_, "Failed check if table exists", stmt));
|
return utils::failure(make_error(sql::error_code::TableExistsFailed, res, conn_, "Failed check if table exists", stmt));
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto result = utils::to<size_t>(PQcmdTuples(res));
|
const auto result = utils::to<size_t>(PQcmdTuples(res));
|
||||||
if (!result) {
|
if (!result) {
|
||||||
PQclear(res);
|
PQclear(res);
|
||||||
return utils::failure(make_error(sql::error_code::FAILURE, res, conn_, "Failed to convert result value", stmt));
|
return utils::failure(make_error(sql::error_code::Failure, res, conn_, "Failed to convert result value", stmt));
|
||||||
}
|
}
|
||||||
|
|
||||||
PQclear(res);
|
PQclear(res);
|
||||||
return utils::ok(*result == 1);
|
return utils::ok(*result == 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
utils::result<bool, utils::error> postgres_connection::sequence_exists(const std::string& schema_name, const std::string& sequence_name) {
|
||||||
|
const std::string sql{"SELECT to_regclass('" + schema_name + "." + sequence_name + "')"};
|
||||||
|
PGresult* res = PQexec(conn_, sql.c_str());
|
||||||
|
|
||||||
|
if (PQresultStatus(res) != PGRES_TUPLES_OK) {
|
||||||
|
return utils::failure(make_error(sql::error_code::SequenceExistsFailed, res, conn_, "Failed check if sequence exists", sql));
|
||||||
|
}
|
||||||
|
|
||||||
|
const bool exists = PQgetisnull(res, 0, 0) ? false : true;
|
||||||
|
PQclear(res);
|
||||||
|
|
||||||
|
return utils::ok(exists);
|
||||||
|
}
|
||||||
|
|
||||||
std::string postgres_connection::to_escaped_string(const utils::blob_type_t &value) const {
|
std::string postgres_connection::to_escaped_string(const utils::blob_type_t &value) const {
|
||||||
size_t escapedDataLength;
|
size_t escapedDataLength;
|
||||||
unsigned char *escapedData = PQescapeByteaConn(conn_, value.data(), value.size(), &escapedDataLength);
|
unsigned char *escapedData = PQescapeByteaConn(conn_, value.data(), value.size(), &escapedDataLength);
|
||||||
|
|||||||
@@ -6,7 +6,10 @@
|
|||||||
|
|
||||||
namespace matador::backends::postgres {
|
namespace matador::backends::postgres {
|
||||||
|
|
||||||
utils::error make_error(const sql::error_code ec, const PGresult *res, const PGconn *db, const std::string &msg,
|
utils::error make_error(const sql::error_code ec,
|
||||||
|
const PGresult *res,
|
||||||
|
const PGconn *db,
|
||||||
|
const std::string &msg,
|
||||||
const std::string &sql) {
|
const std::string &sql) {
|
||||||
utils::error err(ec, msg);
|
utils::error err(ec, msg);
|
||||||
err.add_error_info("dbms", "postgres");
|
err.add_error_info("dbms", "postgres");
|
||||||
@@ -30,22 +33,19 @@ bool is_result_error(const PGresult *res) {
|
|||||||
return status != PGRES_TUPLES_OK && status != PGRES_COMMAND_OK;
|
return status != PGRES_TUPLES_OK && status != PGRES_COMMAND_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
void throw_postgres_error(const char *what, const std::string &source)
|
void throw_postgres_error(const char *what, const std::string &source) {
|
||||||
{
|
|
||||||
std::stringstream msg;
|
std::stringstream msg;
|
||||||
msg << "postgres error (" << source << "): " << what;
|
msg << "postgres error (" << source << "): " << what;
|
||||||
throw std::logic_error(msg.str());
|
throw std::logic_error(msg.str());
|
||||||
}
|
}
|
||||||
|
|
||||||
void throw_postgres_error(PGconn *db, const std::string &source)
|
void throw_postgres_error(const PGconn *db, const std::string &source) {
|
||||||
{
|
|
||||||
if (PQstatus(db) == CONNECTION_BAD) {
|
if (PQstatus(db) == CONNECTION_BAD) {
|
||||||
throw_postgres_error(PQerrorMessage(db), source);
|
throw_postgres_error(PQerrorMessage(db), source);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void throw_postgres_error(PGresult *res, PGconn *db, const std::string &source, const std::string &sql)
|
void throw_postgres_error(const PGresult *res, const PGconn *db, const std::string &source, const std::string &sql) {
|
||||||
{
|
|
||||||
if (res == nullptr) {
|
if (res == nullptr) {
|
||||||
std::stringstream msg;
|
std::stringstream msg;
|
||||||
msg << "postgres error (" << source << ", " << PQerrorMessage(db) << ": " << sql;
|
msg << "postgres error (" << source << ", " << PQerrorMessage(db) << ": " << sql;
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ postgres_statement::~postgres_statement() {
|
|||||||
utils::result<sql::execute_result, utils::error> postgres_statement::execute(const sql::parameter_binder& bindings) {
|
utils::result<sql::execute_result, utils::error> postgres_statement::execute(const sql::parameter_binder& bindings) {
|
||||||
const auto* postgres_bindings = dynamic_cast<const postgres_parameter_binder*>(&bindings);
|
const auto* postgres_bindings = dynamic_cast<const postgres_parameter_binder*>(&bindings);
|
||||||
if (!postgres_bindings) {
|
if (!postgres_bindings) {
|
||||||
return utils::failure(utils::error(sql::error_code::EXECUTE_FAILED, "Failed to cast bindings to postgres bindings"));
|
return utils::failure(utils::error(sql::error_code::ExecuteFailed, "Failed to cast bindings to postgres bindings"));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (query_.command == sql::sql_command::Insert) {
|
if (query_.command == sql::sql_command::Insert) {
|
||||||
@@ -37,7 +37,7 @@ utils::result<sql::execute_result, utils::error> postgres_statement::execute(con
|
|||||||
0);
|
0);
|
||||||
|
|
||||||
if (is_result_error(res)) {
|
if (is_result_error(res)) {
|
||||||
return utils::failure(make_error(sql::error_code::EXECUTE_FAILED, res, db_, "Failed to execute statement", query_.sql));
|
return utils::failure(make_error(sql::error_code::ExecuteFailed, res, db_, "Failed to execute statement", query_.sql));
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t value{0};
|
size_t value{0};
|
||||||
@@ -53,7 +53,7 @@ utils::result<sql::execute_result, utils::error> postgres_statement::execute(con
|
|||||||
utils::result<std::unique_ptr<sql::query_result_impl>, utils::error> postgres_statement::fetch(const sql::parameter_binder& bindings) {
|
utils::result<std::unique_ptr<sql::query_result_impl>, utils::error> postgres_statement::fetch(const sql::parameter_binder& bindings) {
|
||||||
const auto* postgres_bindings = dynamic_cast<const postgres_parameter_binder*>(&bindings);
|
const auto* postgres_bindings = dynamic_cast<const postgres_parameter_binder*>(&bindings);
|
||||||
if (!postgres_bindings) {
|
if (!postgres_bindings) {
|
||||||
return utils::failure(utils::error(sql::error_code::EXECUTE_FAILED, "Failed to cast bindings to postgres bindings"));
|
return utils::failure(utils::error(sql::error_code::ExecuteFailed, "Failed to cast bindings to postgres bindings"));
|
||||||
}
|
}
|
||||||
PGresult *res = PQexecPrepared(db_,
|
PGresult *res = PQexecPrepared(db_,
|
||||||
name_.c_str(),
|
name_.c_str(),
|
||||||
@@ -64,7 +64,7 @@ utils::result<std::unique_ptr<sql::query_result_impl>, utils::error> postgres_st
|
|||||||
0);
|
0);
|
||||||
|
|
||||||
if (is_result_error(res)) {
|
if (is_result_error(res)) {
|
||||||
return utils::failure(make_error(sql::error_code::FETCH_FAILED, res, db_, "Failed to fetch statement", query_.sql));
|
return utils::failure(make_error(sql::error_code::FetchFailed, res, db_, "Failed to fetch statement", query_.sql));
|
||||||
}
|
}
|
||||||
|
|
||||||
return utils::ok(std::make_unique<sql::query_result_impl>(std::make_unique<postgres_result_reader>(res),
|
return utils::ok(std::make_unique<sql::query_result_impl>(std::make_unique<postgres_result_reader>(res),
|
||||||
|
|||||||
@@ -1,18 +1,15 @@
|
|||||||
CPMAddPackage("gh:catchorg/Catch2@3.7.1")
|
CPMAddPackage("gh:catchorg/Catch2@3.14.0")
|
||||||
|
|
||||||
list(APPEND CMAKE_MODULE_PATH ${catch2_SOURCE_DIR}/extras)
|
list(APPEND CMAKE_MODULE_PATH ${catch2_SOURCE_DIR}/extras)
|
||||||
|
|
||||||
set(POSTGRES_CONNECTION_STRING "postgres://test:test123!@127.0.0.1:15442/matador")
|
#set(POSTGRES_CONNECTION_STRING "postgres://test:test123!@127.0.0.1:15442/matador")
|
||||||
#set(POSTGRES_CONNECTION_STRING "postgres://test:test123!@127.0.0.1:5432/matador")
|
set(POSTGRES_CONNECTION_STRING "postgres://test:test123!@127.0.0.1:5432/matador")
|
||||||
|
|
||||||
configure_file(Connection.hpp.in ${PROJECT_BINARY_DIR}/backends/postgres/test/connection.hpp @ONLY IMMEDIATE)
|
configure_file(Connection.hpp.in ${PROJECT_BINARY_DIR}/backends/postgres/test/connection.hpp @ONLY IMMEDIATE)
|
||||||
|
|
||||||
message(STATUS "postgresql connection string: ${POSTGRES_CONNECTION_STRING}")
|
message(STATUS "postgresql connection string: ${POSTGRES_CONNECTION_STRING}")
|
||||||
|
|
||||||
set(TEST_SOURCES
|
set(TEST_SOURCES
|
||||||
../../../test/models/coordinate.hpp
|
|
||||||
../../../test/models/location.hpp
|
|
||||||
../../../test/models/types.hpp
|
|
||||||
../../../test/backends/ColorEnumTraits.cpp
|
../../../test/backends/ColorEnumTraits.cpp
|
||||||
../../../test/backends/ColorEnumTraits.hpp
|
../../../test/backends/ColorEnumTraits.hpp
|
||||||
../../../test/backends/ConnectionTest.cpp
|
../../../test/backends/ConnectionTest.cpp
|
||||||
@@ -25,15 +22,32 @@ set(TEST_SOURCES
|
|||||||
../../../test/backends/SchemaFixture.cpp
|
../../../test/backends/SchemaFixture.cpp
|
||||||
../../../test/backends/SchemaFixture.hpp
|
../../../test/backends/SchemaFixture.hpp
|
||||||
../../../test/backends/SchemaTest.cpp
|
../../../test/backends/SchemaTest.cpp
|
||||||
|
../../../test/backends/SequenceFixture.cpp
|
||||||
|
../../../test/backends/SequenceFixture.hpp
|
||||||
|
../../../test/backends/SequenceTest.cpp
|
||||||
|
../../../test/backends/SessionDeleteHasMany.cpp
|
||||||
|
../../../test/backends/SessionDeleteHasManyToMany.cpp
|
||||||
../../../test/backends/SessionFixture.cpp
|
../../../test/backends/SessionFixture.cpp
|
||||||
../../../test/backends/SessionFixture.hpp
|
../../../test/backends/SessionFixture.hpp
|
||||||
|
../../../test/backends/SessionInsertBelongsTo.cpp
|
||||||
|
../../../test/backends/SessionInsertHasMany.cpp
|
||||||
|
../../../test/backends/SessionInsertHasManyToManyTest.cpp
|
||||||
|
../../../test/backends/SessionInsertHasOne.cpp
|
||||||
../../../test/backends/SessionTest.cpp
|
../../../test/backends/SessionTest.cpp
|
||||||
../../../test/backends/StatementCacheTest.cpp
|
../../../test/backends/StatementCacheTest.cpp
|
||||||
../../../test/backends/StatementTest.cpp
|
../../../test/backends/StatementTest.cpp
|
||||||
|
../../../test/backends/TableSequenceFixture.cpp
|
||||||
|
../../../test/backends/TableSequenceFixture.hpp
|
||||||
|
../../../test/backends/TableSequenceTest.cpp
|
||||||
../../../test/backends/TypeTraitsTest.cpp
|
../../../test/backends/TypeTraitsTest.cpp
|
||||||
../../../test/utils/record_printer.hpp
|
../../../test/models/author.hpp
|
||||||
../../../test/utils/record_printer.cpp
|
../../../test/models/coordinate.hpp
|
||||||
|
../../../test/models/location.hpp
|
||||||
../../../test/models/model_metas.hpp
|
../../../test/models/model_metas.hpp
|
||||||
|
../../../test/models/types.hpp
|
||||||
|
../../../test/models/user.hpp
|
||||||
|
../../../test/utils/RecordPrinter.cpp
|
||||||
|
../../../test/utils/RecordPrinter.hpp
|
||||||
)
|
)
|
||||||
|
|
||||||
set(LIBRARY_TEST_TARGET PostgresTests)
|
set(LIBRARY_TEST_TARGET PostgresTests)
|
||||||
|
|||||||
+2
-1
@@ -1,4 +1,5 @@
|
|||||||
add_executable(demo main.cpp)
|
add_executable(demo main.cpp
|
||||||
|
../include/matador/query/query_column.hpp)
|
||||||
|
|
||||||
target_link_libraries(demo PRIVATE
|
target_link_libraries(demo PRIVATE
|
||||||
matador-core
|
matador-core
|
||||||
|
|||||||
+6
-5
@@ -20,14 +20,15 @@ struct author {
|
|||||||
|
|
||||||
template<typename Operator>
|
template<typename Operator>
|
||||||
void process( Operator& op ) {
|
void process( Operator& op ) {
|
||||||
namespace field = matador::access;
|
using namespace matador;
|
||||||
|
namespace field = 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, VarChar63 );
|
||||||
field::attribute( op, "last_name", last_name, 63 );
|
field::attribute( op, "last_name", last_name, VarChar63 );
|
||||||
field::attribute( op, "date_of_birth", date_of_birth, 31 );
|
field::attribute( op, "date_of_birth", date_of_birth, VarChar63 );
|
||||||
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", books, "author_id", matador::utils::CascadeNoneFetchLazy );
|
field::has_many( op, "books", books, "author_id", utils::CascadeNoneFetchLazy );
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
+4
-3
@@ -15,10 +15,11 @@ struct book {
|
|||||||
|
|
||||||
template<typename Operator>
|
template<typename Operator>
|
||||||
void process( Operator& op ) {
|
void process( Operator& op ) {
|
||||||
namespace field = matador::access;
|
using namespace matador;
|
||||||
|
namespace field = access;
|
||||||
field::primary_key( op, "id", id );
|
field::primary_key( op, "id", id );
|
||||||
field::attribute( op, "title", title, 511 );
|
field::attribute( op, "title", title, VarChar511 );
|
||||||
field::belongs_to( op, "author_id", book_author, matador::utils::CascadeNoneFetchLazy );
|
field::belongs_to( op, "author_id", book_author, utils::CascadeNoneFetchLazy );
|
||||||
field::attribute( op, "published_in", published_in );
|
field::attribute( op, "published_in", published_in );
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
+9
-9
@@ -190,7 +190,7 @@ 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 = query::insert()
|
auto insert_authors_sql = insert()
|
||||||
.into(AUTHOR)
|
.into(AUTHOR)
|
||||||
.values(mc)
|
.values(mc)
|
||||||
.execute(*c);
|
.execute(*c);
|
||||||
@@ -201,7 +201,7 @@ int main() {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto authors_result = query::select(AUTHOR)
|
auto authors_result = select(AUTHOR)
|
||||||
.from(AUTHOR)
|
.from(AUTHOR)
|
||||||
.fetch_all(*c);
|
.fetch_all(*c);
|
||||||
|
|
||||||
@@ -243,8 +243,8 @@ int main() {
|
|||||||
// .values( {3, "Misery", mc.id, 1984} )
|
// .values( {3, "Misery", mc.id, 1984} )
|
||||||
// .execute();
|
// .execute();
|
||||||
//
|
//
|
||||||
// auto select_books_sql = query::select( BOOK, {AUTHOR.last_name} )
|
// auto select_books_sql = select( BOOK, {AUTHOR.last_name} )
|
||||||
auto select_books_sql = query::select({BOOK.id, BOOK.title, BOOK.author_id, BOOK.published_in, AUTHOR.last_name})
|
auto select_books_sql = select({BOOK.id, BOOK.title, BOOK.author_id, BOOK.published_in, AUTHOR.last_name})
|
||||||
.from(BOOK)
|
.from(BOOK)
|
||||||
.join_left(AUTHOR)
|
.join_left(AUTHOR)
|
||||||
.on(BOOK.author_id == AUTHOR.id)
|
.on(BOOK.author_id == AUTHOR.id)
|
||||||
@@ -288,17 +288,17 @@ int main() {
|
|||||||
//
|
//
|
||||||
// std::cout << "SQL: " << drop_authors_sql << "\n";
|
// std::cout << "SQL: " << drop_authors_sql << "\n";
|
||||||
//
|
//
|
||||||
auto res = query::select({PAYLOAD.id})
|
auto res = select({PAYLOAD.id})
|
||||||
.from(PAYLOAD)
|
.from(PAYLOAD)
|
||||||
.join_left(JOB)
|
.join_left(JOB)
|
||||||
.on(JOB.payload == PAYLOAD.id)
|
.on(JOB.payload == PAYLOAD.id)
|
||||||
.where(
|
.where(
|
||||||
in(PAYLOAD.id, query::select({JOB.state})
|
in(PAYLOAD.id, select({JOB.state})
|
||||||
.from(JOB)
|
.from(JOB)
|
||||||
.where(JOB.state == job::job_state::Running).compile(c->dialect())
|
.where(JOB.state == job::job_state::Running)
|
||||||
) &&
|
) &&
|
||||||
in(PAYLOAD.id, query::select({TEMPORARY_TABLE.id})
|
in(PAYLOAD.id, select({TEMPORARY_TABLE.id})
|
||||||
.from(TEMPORARY_TABLE).compile(c->dialect())
|
.from(TEMPORARY_TABLE)
|
||||||
)
|
)
|
||||||
).compile(c->dialect());
|
).compile(c->dialect());
|
||||||
// // .fetch_value<unsigned long>();
|
// // .fetch_value<unsigned long>();
|
||||||
|
|||||||
+1
-1
@@ -29,7 +29,7 @@ public:
|
|||||||
attribute() = default;
|
attribute() = default;
|
||||||
attribute(std::string name,
|
attribute(std::string name,
|
||||||
utils::basic_type type,
|
utils::basic_type type,
|
||||||
const utils::field_attributes& opts = utils::null_attributes,
|
const utils::field_attributes& opts = matador::NullAttributes,
|
||||||
null_option_type null_opt = null_option_type::NotNull)
|
null_option_type null_opt = null_option_type::NotNull)
|
||||||
: name_(std::move(name)), type_(type), options_(opts), null_option_type_(null_opt) {}
|
: name_(std::move(name)), type_(type), options_(opts), null_option_type_(null_opt) {}
|
||||||
|
|
||||||
|
|||||||
+10
-10
@@ -13,8 +13,7 @@
|
|||||||
namespace demo {
|
namespace demo {
|
||||||
|
|
||||||
struct recipe;
|
struct recipe;
|
||||||
struct ingredient
|
struct ingredient {
|
||||||
{
|
|
||||||
unsigned int id{};
|
unsigned int id{};
|
||||||
std::string name;
|
std::string name;
|
||||||
matador::object::collection<matador::object::object_ptr<demo::recipe>> recipes{};
|
matador::object::collection<matador::object::object_ptr<demo::recipe>> recipes{};
|
||||||
@@ -25,15 +24,15 @@ struct ingredient
|
|||||||
|
|
||||||
template<class Operator>
|
template<class Operator>
|
||||||
void process(Operator &op) {
|
void process(Operator &op) {
|
||||||
namespace field = matador::access;
|
using namespace matador;
|
||||||
|
namespace field = access;
|
||||||
field::primary_key(op, "id", id);
|
field::primary_key(op, "id", id);
|
||||||
field::attribute(op, "name", name, 255);
|
field::attribute(op, "name", name, VarChar255);
|
||||||
field::has_many_to_many(op, "recipe_ingredients", recipes, "ingredient_id", "recipe_id", matador::utils::fetch_type::Eager);
|
field::has_many_to_many(op, "recipe_ingredients", recipes, "ingredient_id", "recipe_id", utils::CascadeAllFetchEager);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
struct recipe
|
struct recipe {
|
||||||
{
|
|
||||||
unsigned int id{};
|
unsigned int id{};
|
||||||
std::string name;
|
std::string name;
|
||||||
matador::object::collection<matador::object::object_ptr<demo::ingredient>> ingredients{};
|
matador::object::collection<matador::object::object_ptr<demo::ingredient>> ingredients{};
|
||||||
@@ -44,10 +43,11 @@ struct recipe
|
|||||||
|
|
||||||
template<class Operator>
|
template<class Operator>
|
||||||
void process(Operator &op) {
|
void process(Operator &op) {
|
||||||
namespace field = matador::access;
|
using namespace matador;
|
||||||
|
namespace field = access;
|
||||||
field::primary_key(op, "id", id);
|
field::primary_key(op, "id", id);
|
||||||
field::attribute(op, "name", name, 255);
|
field::attribute(op, "name", name, VarChar255);
|
||||||
field::has_many_to_many(op, "recipe_ingredients", ingredients, matador::utils::fetch_type::Lazy);
|
field::has_many_to_many(op, "recipe_ingredients", ingredients, utils::CascadeAllFetchLazy);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
+12
-9
@@ -81,11 +81,12 @@ struct profile {
|
|||||||
|
|
||||||
template<typename Operator>
|
template<typename Operator>
|
||||||
void process(Operator &op) {
|
void process(Operator &op) {
|
||||||
namespace field = matador::access;
|
using namespace matador;
|
||||||
|
namespace field = access;
|
||||||
field::primary_key( op, "id", id );
|
field::primary_key( op, "id", id );
|
||||||
field::attribute( op, "first_name", first_name, 255 );
|
field::attribute( op, "first_name", first_name, VarChar255 );
|
||||||
field::attribute( op, "last_name", last_name, 255 );
|
field::attribute( op, "last_name", last_name, VarChar255 );
|
||||||
field::belongs_to( op, "user_id", user, matador::utils::CascadeNoneFetchLazy );
|
field::belongs_to( op, "user_id", user, utils::CascadeNoneFetchLazy );
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
struct user {
|
struct user {
|
||||||
@@ -95,10 +96,11 @@ struct user {
|
|||||||
|
|
||||||
template<typename Operator>
|
template<typename Operator>
|
||||||
void process(Operator &op) {
|
void process(Operator &op) {
|
||||||
namespace field = matador::access;
|
using namespace matador;
|
||||||
|
namespace field = access;
|
||||||
field::primary_key( op, "id", id );
|
field::primary_key( op, "id", id );
|
||||||
field::attribute( op, "username", username, 255 );
|
field::attribute( op, "username", username, VarChar255 );
|
||||||
field::has_one(op, "profile_id", profile, matador::utils::CascadeNoneFetchLazy );
|
field::has_one(op, "profiles", profile, "profile_id", utils::CascadeNoneFetchLazy );
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -108,9 +110,10 @@ struct person {
|
|||||||
|
|
||||||
template<typename Operator>
|
template<typename Operator>
|
||||||
void process(Operator &op) {
|
void process(Operator &op) {
|
||||||
|
using namespace matador;
|
||||||
namespace field = matador::access;
|
namespace field = matador::access;
|
||||||
field::primary_key( op, "id", id );
|
field::primary_key( op, "id", id );
|
||||||
field::attribute( op, "name", name, 255 );
|
field::attribute( op, "name", name, VarChar255 );
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -136,7 +139,7 @@ int main() {
|
|||||||
|
|
||||||
sql::resolver_service rs;
|
sql::resolver_service rs;
|
||||||
|
|
||||||
std::weak_ptr cr = rs.collection_resolver<object::object_ptr<names>>( typeid(person), "names" );
|
std::weak_ptr cr = rs.joined_collection_resolver<object::object_ptr<names>>( typeid(person), "names" );
|
||||||
utils::identifier id{1};
|
utils::identifier id{1};
|
||||||
|
|
||||||
object::collection_proxy cp(cr, id);
|
object::collection_proxy cp(cr, id);
|
||||||
|
|||||||
+14
-13
@@ -23,7 +23,7 @@
|
|||||||
|
|
||||||
#include "matador/query/schema.hpp"
|
#include "matador/query/schema.hpp"
|
||||||
|
|
||||||
#include "matador/orm/session.hpp"
|
#include "matador/query/session.hpp"
|
||||||
|
|
||||||
template <> struct matador::utils::data_type_traits<work::core::UserInfo, void> {
|
template <> struct matador::utils::data_type_traits<work::core::UserInfo, void> {
|
||||||
static basic_type type(std::size_t /*size*/) { return basic_type::UInt64; }
|
static basic_type type(std::size_t /*size*/) { return basic_type::UInt64; }
|
||||||
@@ -88,6 +88,7 @@ int main() {
|
|||||||
.and_then([&admin_schema] { return admin_schema.attach<jobs::IdPayload, jobs::Payload>("id_list_payloads"); })
|
.and_then([&admin_schema] { return admin_schema.attach<jobs::IdPayload, jobs::Payload>("id_list_payloads"); })
|
||||||
.and_then([&admin_schema] { return admin_schema.attach<jobs::IdListPayload, jobs::Payload>("id_payloads"); })
|
.and_then([&admin_schema] { return admin_schema.attach<jobs::IdListPayload, jobs::Payload>("id_payloads"); })
|
||||||
.and_then([&admin_schema] { return admin_schema.attach<jobs::Task>("tasks"); })
|
.and_then([&admin_schema] { return admin_schema.attach<jobs::Task>("tasks"); })
|
||||||
|
.and_then([&admin_schema, &pool] { return admin_schema.create( *pool.acquire() ); })
|
||||||
;
|
;
|
||||||
admin_schema.dump(std::cout);
|
admin_schema.dump(std::cout);
|
||||||
|
|
||||||
@@ -109,7 +110,7 @@ int main() {
|
|||||||
// }
|
// }
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* const auto statement = QString( "SELECT %5.%6, %1.%2 FROM %3 %1, %4 %5 WHERE %1.%2=%5.%6 AND %7" )
|
const auto statement = QString( "SELECT %5.%6, %1.%2 FROM %3 %1, %4 %5 WHERE %1.%2=%5.%6 AND %7" )
|
||||||
.arg( payloadTableAliasName,
|
.arg( payloadTableAliasName,
|
||||||
payloadSqlMetaInfo.columnName( Payload::Attributes::idAttributeId() ),
|
payloadSqlMetaInfo.columnName( Payload::Attributes::idAttributeId() ),
|
||||||
Payload::staticSqlMetaInfo().tableName(),
|
Payload::staticSqlMetaInfo().tableName(),
|
||||||
@@ -125,7 +126,7 @@ int main() {
|
|||||||
const auto payloads = PAYLOAD.as( "payloads" );
|
const auto payloads = PAYLOAD.as( "payloads" );
|
||||||
const auto tasks = TASK.as( "tasks" );
|
const auto tasks = TASK.as( "tasks" );
|
||||||
|
|
||||||
const auto stmt1 = query:: query::select( { tasks.payload, payloads.id } )
|
const auto stmt1 = query::select( { tasks.payload, payloads.id } )
|
||||||
.from( tasks, payloads )
|
.from( tasks, payloads )
|
||||||
.where ( payloads.id == tasks.payload )
|
.where ( payloads.id == tasks.payload )
|
||||||
.str( conn->dialect() );
|
.str( conn->dialect() );
|
||||||
@@ -158,12 +159,12 @@ int main() {
|
|||||||
const auto iPT = ID_PAYLOAD.as("iPT");
|
const auto iPT = ID_PAYLOAD.as("iPT");
|
||||||
const auto pT = PAYLOAD.as("pT");
|
const auto pT = PAYLOAD.as("pT");
|
||||||
|
|
||||||
const auto stmt2 = query::query::select({ iPT.payload_id })
|
const auto stmt2 = query::select({ iPT.payload_id })
|
||||||
.from( iPT )
|
.from( iPT )
|
||||||
.join_left( pT ).on( iPT.id == pT.id )
|
.join_left( pT ).on( iPT.id == pT.id )
|
||||||
.where(
|
.where(
|
||||||
in( pT.id, query::query::select({ JOB.payload }).from( JOB ).where( JOB.state == _) ) &&
|
in( pT.id, query::select({ JOB.payload }).from( JOB ).where( JOB.state == _) ) &&
|
||||||
in( iPT.payload_id, query::query::select({ temp_id_col }).from( temporary ) )
|
in( iPT.payload_id, query::select({ temp_id_col }).from( temporary ) )
|
||||||
)
|
)
|
||||||
.str( conn->dialect() );
|
.str( conn->dialect() );
|
||||||
|
|
||||||
@@ -177,13 +178,13 @@ int main() {
|
|||||||
// .arg( LoginHistory::staticSqlMetaInfo().columnName( LoginHistory::Attributes::failReasonSqlAttributeId() ) )
|
// .arg( LoginHistory::staticSqlMetaInfo().columnName( LoginHistory::Attributes::failReasonSqlAttributeId() ) )
|
||||||
// .arg( LoginHistory::staticSqlMetaInfo().columnName( LoginHistory::Attributes::idAttributeId() ) );
|
// .arg( LoginHistory::staticSqlMetaInfo().columnName( LoginHistory::Attributes::idAttributeId() ) );
|
||||||
|
|
||||||
const auto q = query::query::select({LOGIN_HISTORY.client, query::maximum(LOGIN_HISTORY.login_time).as( "lastSuccessfulLogin" ) } )
|
const auto q = query::select({LOGIN_HISTORY.client, query::maximum(LOGIN_HISTORY.login_time).as( "lastSuccessfulLogin" ) } )
|
||||||
.from( LOGIN_HISTORY )
|
.from( LOGIN_HISTORY )
|
||||||
.where( LOGIN_HISTORY.fail_reason == _ )
|
.where( LOGIN_HISTORY.fail_reason == _ )
|
||||||
.group_by( LOGIN_HISTORY.client );
|
.group_by( LOGIN_HISTORY.client );
|
||||||
// .as_table("ll");
|
// .as_table("ll");
|
||||||
|
|
||||||
const auto stmt3 = query::query::select({ LOGIN_HISTORY.client })
|
const auto stmt3 = query::select({ LOGIN_HISTORY.client })
|
||||||
.from( LOGIN_HISTORY )
|
.from( LOGIN_HISTORY )
|
||||||
.join_left(q/*, "ll"*/ )
|
.join_left(q/*, "ll"*/ )
|
||||||
.on( LOGIN_HISTORY.client == "ll.client" )
|
.on( LOGIN_HISTORY.client == "ll.client" )
|
||||||
@@ -287,11 +288,11 @@ int main() {
|
|||||||
// )
|
// )
|
||||||
// )
|
// )
|
||||||
|
|
||||||
const auto stmt4 = query::query::select({JOB.id})
|
const auto stmt4 = query::select({JOB.id})
|
||||||
.from(JOB)
|
.from(JOB)
|
||||||
.where( JOB.id > _ && JOB.name == _ && in( JOB.payload, query::query::select({PAYLOAD.id}).from(PAYLOAD).where(
|
.where( JOB.id > _ && JOB.name == _ && in( JOB.payload, query::select({PAYLOAD.id}).from(PAYLOAD).where(
|
||||||
in( PAYLOAD.id, query::query::select({ID_PAYLOAD.id}).from(ID_PAYLOAD).where(
|
in( PAYLOAD.id, query::select({ID_PAYLOAD.id}).from(ID_PAYLOAD).where(
|
||||||
in( ID_PAYLOAD.id, query::query::select({temp_id_col}).from(temporary))
|
in( ID_PAYLOAD.id, query::select({temp_id_col}).from(temporary))
|
||||||
) ) ) ) )
|
) ) ) ) )
|
||||||
.str( conn->dialect() );
|
.str( conn->dialect() );
|
||||||
|
|
||||||
@@ -306,7 +307,7 @@ int main() {
|
|||||||
// WHERE nj.ID > ?
|
// WHERE nj.ID > ?
|
||||||
// AND nj.TYPE = ?;
|
// AND nj.TYPE = ?;
|
||||||
|
|
||||||
const auto stmt5 = query::query::select({JOB.id})
|
const auto stmt5 = query::select({JOB.id})
|
||||||
.from(JOB)
|
.from(JOB)
|
||||||
.join_left(PAYLOAD).on(JOB.payload == PAYLOAD.id)
|
.join_left(PAYLOAD).on(JOB.payload == PAYLOAD.id)
|
||||||
.join_left(ID_PAYLOAD).on(PAYLOAD.id == ID_PAYLOAD.id)
|
.join_left(ID_PAYLOAD).on(PAYLOAD.id == ID_PAYLOAD.id)
|
||||||
|
|||||||
@@ -36,12 +36,13 @@ struct CollectionCenter : core::Model {
|
|||||||
|
|
||||||
template<typename Operator>
|
template<typename Operator>
|
||||||
void process( Operator& op ) {
|
void process( Operator& op ) {
|
||||||
|
using namespace matador;
|
||||||
namespace field = matador::access;
|
namespace field = matador::access;
|
||||||
field::process( op, *matador::base_class<Model>( this ) );
|
field::process( op, *matador::base_class<Model>( this ) );
|
||||||
field::attribute( op, "name", name, 511 );
|
field::attribute( op, "name", name, UniqueVarChar511 );
|
||||||
field::attribute( op, "symbol", symbol );
|
field::attribute( op, "symbol", symbol );
|
||||||
field::attribute( op, "type", type );
|
field::attribute( op, "type", type );
|
||||||
field::has_many( op, "collection_center_users", users, "users_id", matador::utils::fetch_type::Lazy );
|
field::has_many( op, "collection_center_users", users, "users_id", utils::CascadeAllFetchLazy );
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -15,4 +15,4 @@ struct InternalUserDirectory : UserDirectory {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif //INTERNALUSERDIRECTORY_HPP
|
#endif //INTERNAL_USER_DIRECTORY_HPP
|
||||||
|
|||||||
@@ -16,11 +16,12 @@ struct LdapGroupSchemaSettings : core::Model {
|
|||||||
|
|
||||||
template<typename Operator>
|
template<typename Operator>
|
||||||
void process( Operator& op ) {
|
void process( Operator& op ) {
|
||||||
namespace field = matador::access;
|
using namespace matador;
|
||||||
|
namespace field = access;
|
||||||
field::process( op, *matador::base_class<Model>( this ) );
|
field::process( op, *matador::base_class<Model>( this ) );
|
||||||
field::has_one(op, "ldap_user_directory", ldap_user_directory, matador::utils::CascadeAllFetchLazy);
|
field::has_one(op, "ldap_user_directory", ldap_user_directory, utils::CascadeAllFetchLazy);
|
||||||
field::attribute( op, "group_object_filter", group_object_filter, 511 );
|
field::attribute( op, "group_object_filter", group_object_filter, VarChar511 );
|
||||||
field::attribute( op, "user_member_attribute", user_member_attribute, 511 );
|
field::attribute( op, "user_member_attribute", user_member_attribute, VarChar511 );
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -17,10 +17,11 @@ struct LdapImportSettings : core::Model {
|
|||||||
|
|
||||||
template<typename Operator>
|
template<typename Operator>
|
||||||
void process( Operator& op ) {
|
void process( Operator& op ) {
|
||||||
namespace field = matador::access;
|
using namespace matador;
|
||||||
|
namespace field = access;
|
||||||
field::process( op, *matador::base_class<Model>( this ) );
|
field::process( op, *matador::base_class<Model>( this ) );
|
||||||
field::has_one(op, "ldap_user_directory", ldap_user_directory, matador::utils::CascadeAllFetchLazy);
|
field::has_one(op, "ldap_user_directory", ldap_user_directory, utils::CascadeAllFetchLazy);
|
||||||
field::attribute( op, "default_role", default_role, 511 );
|
field::attribute( op, "default_role", default_role, VarChar511 );
|
||||||
field::attribute( op, "sync_interval", sync_interval );
|
field::attribute( op, "sync_interval", sync_interval );
|
||||||
field::attribute( op, "network_timeout", network_timeout );
|
field::attribute( op, "network_timeout", network_timeout );
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,16 +25,17 @@ struct LdapUserDirectory : UserDirectory {
|
|||||||
|
|
||||||
template<typename Operator>
|
template<typename Operator>
|
||||||
void process( Operator& op ) {
|
void process( Operator& op ) {
|
||||||
namespace field = matador::access;
|
using namespace matador;
|
||||||
|
namespace field = access;
|
||||||
field::process( op, *matador::base_class<UserDirectory>( this ) );
|
field::process( op, *matador::base_class<UserDirectory>( this ) );
|
||||||
field::attribute( op, "schema_base_dn", schema_base_dn, 511 );
|
field::attribute( op, "schema_base_dn", schema_base_dn, VarChar511 );
|
||||||
field::attribute( op, "additional_user_base_dn", additional_user_base_dn, 511 );
|
field::attribute( op, "additional_user_base_dn", additional_user_base_dn, VarChar511 );
|
||||||
field::attribute( op, "additional_group_base_dn", additional_group_base_dn, 511 );
|
field::attribute( op, "additional_group_base_dn", additional_group_base_dn, VarChar511 );
|
||||||
field::belongs_to( op, "user_schema_settings", user_schema_settings, matador::utils::CascadeAllFetchLazy );
|
field::belongs_to( op, "user_schema_settings", user_schema_settings, utils::CascadeAllFetchLazy );
|
||||||
field::belongs_to( op, "group_schema_settings", group_schema_settings, matador::utils::CascadeAllFetchLazy );
|
field::belongs_to( op, "group_schema_settings", group_schema_settings, utils::CascadeAllFetchLazy );
|
||||||
field::belongs_to( op, "import_settings", import_settings, matador::utils::CascadeAllFetchLazy );
|
field::belongs_to( op, "import_settings", import_settings, utils::CascadeAllFetchLazy );
|
||||||
field::has_many( op, "ldap_users", users, "users_id", matador::utils::CascadeAllFetchEager );
|
field::has_many( op, "ldap_users", users, "users_id", utils::CascadeAllFetchEager );
|
||||||
field::has_many( op, "ldap_groups", groups, "groups_id", matador::utils::CascadeAllFetchEager );
|
field::has_many( op, "ldap_groups", groups, "groups_id", utils::CascadeAllFetchEager );
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,13 +18,14 @@ struct LdapUserSchemaSettings : core::Model {
|
|||||||
|
|
||||||
template<typename Operator>
|
template<typename Operator>
|
||||||
void process( Operator& op ) {
|
void process( Operator& op ) {
|
||||||
namespace field = matador::access;
|
using namespace matador;
|
||||||
|
namespace field = access;
|
||||||
field::process(op, *matador::base_class<Model>( this ));
|
field::process(op, *matador::base_class<Model>( this ));
|
||||||
field::has_one(op, "ldap_user_directory", ldap_user_directory, matador::utils::CascadeAllFetchLazy);
|
field::has_one(op, "ldap_user_directory", ldap_user_directory, utils::CascadeAllFetchLazy);
|
||||||
field::attribute(op, "user_object_filter", user_object_filter, 511);
|
field::attribute(op, "user_object_filter", user_object_filter, VarChar511);
|
||||||
field::attribute(op, "user_unique_id_attribute", user_unique_id_attribute, 511);
|
field::attribute(op, "user_unique_id_attribute", user_unique_id_attribute, VarChar511);
|
||||||
field::attribute(op, "user_member_of_attribute", user_member_of_attribute, 511);
|
field::attribute(op, "user_member_of_attribute", user_member_of_attribute, VarChar511);
|
||||||
field::attribute(op, "user_name_attribute", user_name_attribute, 511);
|
field::attribute(op, "user_name_attribute", user_name_attribute, VarChar511);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -51,12 +51,14 @@ struct LoginHistory : core::Model {
|
|||||||
|
|
||||||
template<typename Operator>
|
template<typename Operator>
|
||||||
void process( Operator& op ) {
|
void process( Operator& op ) {
|
||||||
namespace field = matador::access;
|
using namespace matador;
|
||||||
|
namespace field = access;
|
||||||
|
field::process_base<Model>( op, *this );
|
||||||
field::process( op, *matador::base_class<Model>( this ) );
|
field::process( op, *matador::base_class<Model>( this ) );
|
||||||
field::belongs_to( op, "client", client, matador::utils::CascadeAllFetchLazy );
|
field::belongs_to( op, "client", client, utils::CascadeAllFetchLazy );
|
||||||
field::belongs_to( op, "scenario", scenario, matador::utils::CascadeAllFetchLazy );
|
field::belongs_to( op, "scenario", scenario, utils::CascadeAllFetchLazy );
|
||||||
field::belongs_to( op, "collection_center", collection_center, matador::utils::CascadeAllFetchLazy );
|
field::belongs_to( op, "collection_center", collection_center, utils::CascadeAllFetchLazy );
|
||||||
field::attribute( op, "login_name", login_name, 511 );
|
field::attribute( op, "login_name", login_name, VarChar511 );
|
||||||
field::attribute( op, "fail_reason", fail_reason );
|
field::attribute( op, "fail_reason", fail_reason );
|
||||||
field::attribute( op, "login_time", login_time );
|
field::attribute( op, "login_time", login_time );
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -46,14 +46,15 @@ struct Scenario : core::Model {
|
|||||||
|
|
||||||
template<typename Operator>
|
template<typename Operator>
|
||||||
void process( Operator& op ) {
|
void process( Operator& op ) {
|
||||||
namespace field = matador::access;
|
using namespace matador;
|
||||||
|
namespace field = access;
|
||||||
field::process( op, *matador::base_class<Model>( this ) );
|
field::process( op, *matador::base_class<Model>( this ) );
|
||||||
field::attribute( op, "name", name, 511 );
|
field::attribute( op, "name", name, UniqueVarChar511 );
|
||||||
field::attribute( op, "mode", mode );
|
field::attribute( op, "mode", mode );
|
||||||
field::attribute( op, "description", description, 511 );
|
field::attribute( op, "description", description, VarChar511 );
|
||||||
field::attribute( op, "symbol", symbol );
|
field::attribute( op, "symbol", symbol );
|
||||||
field::attribute( op, "state", state );
|
field::attribute( op, "state", state );
|
||||||
field::has_many( op, "collection_center", collection_center, "scenario_id", matador::utils::fetch_type::Lazy );
|
field::has_many( op, "collection_center", collection_center, "scenario_id", utils::CascadeAllFetchLazy );
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -29,17 +29,18 @@ struct User : core::Model {
|
|||||||
|
|
||||||
template<typename Operator>
|
template<typename Operator>
|
||||||
void process( Operator& op ) {
|
void process( Operator& op ) {
|
||||||
namespace field = matador::access;
|
using namespace matador;
|
||||||
|
namespace field = access;
|
||||||
field::process( op, *matador::base_class<Model>( this ) );
|
field::process( op, *matador::base_class<Model>( this ) );
|
||||||
field::attribute( op, "name", name, 511 );
|
field::attribute( op, "name", name, UniqueVarChar511 );
|
||||||
field::attribute( op, "symbol", symbol );
|
field::attribute( op, "symbol", symbol );
|
||||||
field::attribute( op, "salt", salt, 511 );
|
field::attribute( op, "salt", salt, VarChar511 );
|
||||||
field::attribute( op, "password", password, 511 );
|
field::attribute( op, "password", password, VarChar511 );
|
||||||
field::attribute( op, "lock_type", lock_type );
|
field::attribute( op, "lock_type", lock_type );
|
||||||
field::attribute( op, "locked_at", locked_at );
|
field::attribute( op, "locked_at", locked_at );
|
||||||
field::attribute( op, "lock_reason", lock_reason, 511 );
|
field::attribute( op, "lock_reason", lock_reason, VarChar511 );
|
||||||
field::attribute( op, "role", role, 63 );
|
field::attribute( op, "role", role, VarChar63 );
|
||||||
field::belongs_to( op, "user_directory", user_directory, matador::utils::CascadeAllFetchLazy );
|
field::belongs_to( op, "user_directory", user_directory, utils::CascadeAllFetchLazy );
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ struct UserDirectory : core::Model {
|
|||||||
void process( Operator& op ) {
|
void process( Operator& op ) {
|
||||||
namespace field = matador::access;
|
namespace field = matador::access;
|
||||||
field::process( op, *matador::base_class<Model>( this ) );
|
field::process( op, *matador::base_class<Model>( this ) );
|
||||||
field::attribute( op, "name", name, 511 );
|
field::attribute( op, "name", name, matador::VarChar511 );
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,11 +21,12 @@ struct UserSession : core::Model {
|
|||||||
|
|
||||||
template<typename Operator>
|
template<typename Operator>
|
||||||
void process( Operator& op ) {
|
void process( Operator& op ) {
|
||||||
namespace field = matador::access;
|
using namespace matador;
|
||||||
|
namespace field = access;
|
||||||
field::process( op, *matador::base_class<Model>( this ) );
|
field::process( op, *matador::base_class<Model>( this ) );
|
||||||
field::belongs_to( op, "client", client, matador::utils::CascadeAllFetchLazy );
|
field::belongs_to( op, "client", client, utils::CascadeAllFetchLazy );
|
||||||
field::belongs_to( op, "scenario", scenario, matador::utils::CascadeAllFetchLazy );
|
field::belongs_to( op, "scenario", scenario, utils::CascadeAllFetchLazy );
|
||||||
field::belongs_to( op, "collection_center", collection_center, matador::utils::CascadeAllFetchLazy );
|
field::belongs_to( op, "collection_center", collection_center, utils::CascadeAllFetchLazy );
|
||||||
field::attribute( op, "offline_since", offline_since );
|
field::attribute( op, "offline_since", offline_since );
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
#define USERINFO_HPP
|
#define USERINFO_HPP
|
||||||
|
|
||||||
#include "matador/utils/access.hpp"
|
#include "matador/utils/access.hpp"
|
||||||
|
#include "matador/utils/field_attributes.hpp"
|
||||||
|
|
||||||
namespace work::core {
|
namespace work::core {
|
||||||
struct UserInfo {
|
struct UserInfo {
|
||||||
@@ -12,9 +13,10 @@ struct UserInfo {
|
|||||||
template<typename Operator>
|
template<typename Operator>
|
||||||
void process( Operator& op ) {
|
void process( Operator& op ) {
|
||||||
namespace field = matador::access;
|
namespace field = matador::access;
|
||||||
|
using namespace matador;
|
||||||
field::attribute( op, "user_session_id", user_session_id );
|
field::attribute( op, "user_session_id", user_session_id );
|
||||||
field::attribute( op, "user_role", user_role, 255 );
|
field::attribute( op, "user_role", user_role, VarChar255 );
|
||||||
field::attribute( op, "database_name", database_name, 255 );
|
field::attribute( op, "database_name", database_name, VarChar255 );
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,15 +30,16 @@ struct Job : core::Model {
|
|||||||
|
|
||||||
template<typename Operator>
|
template<typename Operator>
|
||||||
void process( Operator& op ) {
|
void process( Operator& op ) {
|
||||||
|
using namespace matador;
|
||||||
namespace field = matador::access;
|
namespace field = matador::access;
|
||||||
field::process( op, *matador::base_class<Model>( this ) );
|
field::process( op, *matador::base_class<Model>( this ) );
|
||||||
field::attribute( op, "name", name, 511 );
|
field::attribute( op, "name", name, UniqueVarChar511 );
|
||||||
field::attribute( op, "description", description, 511 );
|
field::attribute( op, "description", description, VarChar1023 );
|
||||||
field::attribute( op, "state", state );
|
field::attribute( op, "state", state );
|
||||||
field::attribute( op, "mode", mode );
|
field::attribute( op, "mode", mode );
|
||||||
field::attribute( op, "created_at", created_at );
|
field::attribute( op, "created_at", created_at );
|
||||||
field::has_one(op, "payload", payload, matador::utils::CascadeAllFetchLazy );
|
field::has_one(op, "payload", payload, utils::CascadeAllFetchLazy );
|
||||||
field::belongs_to( op, "task", task, matador::utils::CascadeAllFetchLazy );
|
field::belongs_to( op, "task", task, utils::CascadeAllFetchLazy );
|
||||||
field::attribute( op, "user_info", user_info );
|
field::attribute( op, "user_info", user_info );
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -16,10 +16,11 @@ struct Payload : core::Model {
|
|||||||
|
|
||||||
template<typename Operator>
|
template<typename Operator>
|
||||||
void process( Operator& op ) {
|
void process( Operator& op ) {
|
||||||
|
using namespace matador;
|
||||||
namespace field = matador::access;
|
namespace field = matador::access;
|
||||||
field::process( op, *matador::base_class<Model>( this ) );
|
field::process( op, *matador::base_class<Model>( this ) );
|
||||||
field::attribute( op, "type", type, 255 );
|
field::attribute( op, "type", type, VarChar255 );
|
||||||
field::belongs_to( op, "job", job, matador::utils::CascadeAllFetchLazy );
|
field::belongs_to( op, "job", job, utils::CascadeAllFetchLazy );
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,13 +26,14 @@ struct Task : core::Model {
|
|||||||
|
|
||||||
template<typename Operator>
|
template<typename Operator>
|
||||||
void process( Operator& op ) {
|
void process( Operator& op ) {
|
||||||
|
using namespace matador;
|
||||||
namespace field = matador::access;
|
namespace field = matador::access;
|
||||||
field::process( op, *matador::base_class<Model>( this ) );
|
field::process( op, *matador::base_class<Model>( this ) );
|
||||||
field::attribute( op, "name", name, 511 );
|
field::attribute( op, "name", name, UniqueVarChar511 );
|
||||||
field::attribute( op, "description", description, 511 );
|
field::attribute( op, "description", description, VarChar1023 );
|
||||||
field::attribute( op, "job_name", job_name, 511 );
|
field::attribute( op, "job_name", job_name, VarChar511 );
|
||||||
field::attribute( op, "state", state );
|
field::attribute( op, "state", state );
|
||||||
field::belongs_to( op, "payload", payload, matador::utils::CascadeAllFetchLazy );
|
field::belongs_to( op, "payload", payload, utils::CascadeAllFetchLazy );
|
||||||
field::attribute( op, "job_mode", job_mode );
|
field::attribute( op, "job_mode", job_mode );
|
||||||
field::attribute( op, "start_delay", start_delay );
|
field::attribute( op, "start_delay", start_delay );
|
||||||
field::attribute( op, "interval", interval );
|
field::attribute( op, "interval", interval );
|
||||||
|
|||||||
+5
-7
@@ -1,24 +1,22 @@
|
|||||||
#ifndef MATADOR_ABSTRACT_COLLECTION_RESOLVER_HPP
|
#ifndef MATADOR_ABSTRACT_COLLECTION_RESOLVER_HPP
|
||||||
#define MATADOR_ABSTRACT_COLLECTION_RESOLVER_HPP
|
#define MATADOR_ABSTRACT_COLLECTION_RESOLVER_HPP
|
||||||
|
|
||||||
|
#include "matador/object/abstract_type_resolver.hpp"
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <typeindex>
|
#include <typeindex>
|
||||||
|
|
||||||
namespace matador::object {
|
namespace matador::object {
|
||||||
class abstract_collection_resolver {
|
class abstract_joined_resolver : public abstract_type_resolver {
|
||||||
public:
|
public:
|
||||||
virtual ~abstract_collection_resolver() = default;
|
|
||||||
|
|
||||||
[[nodiscard]] const std::type_index& root_type() const;
|
[[nodiscard]] const std::type_index& root_type() const;
|
||||||
[[nodiscard]] const std::type_index& type() const;
|
|
||||||
[[nodiscard]] const std::string& collection_name() const;
|
[[nodiscard]] const std::string& collection_name() const;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
explicit abstract_collection_resolver(const std::type_index& root_type, const std::type_index& type, std::string collection_name);
|
explicit abstract_joined_resolver(const std::type_index& root_type, const std::type_index& type, std::string collection_name);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
const std::type_index root_type_;
|
const std::type_index root_type_;
|
||||||
const std::type_index type_;
|
|
||||||
const std::string collection_name_;
|
const std::string collection_name_;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -28,20 +28,21 @@ public:
|
|||||||
attribute() = default;
|
attribute() = default;
|
||||||
attribute(std::string name,
|
attribute(std::string name,
|
||||||
utils::basic_type type,
|
utils::basic_type type,
|
||||||
const utils::field_attributes &attr = utils::null_attributes,
|
const utils::field_attributes &attr = NullAttributes,
|
||||||
null_option_type null_opt = null_option_type::NotNull);
|
null_option_type null_opt = null_option_type::NotNull);
|
||||||
|
|
||||||
[[nodiscard]] const std::string& name() const;
|
[[nodiscard]] const std::string& name() const;
|
||||||
void name(const std::string& n);
|
void name(const std::string& n);
|
||||||
[[nodiscard]] std::string full_name() const;
|
[[nodiscard]] std::string full_name() const;
|
||||||
|
[[nodiscard]] size_t index() const;
|
||||||
[[nodiscard]] const utils::field_attributes& attributes() const;
|
[[nodiscard]] const utils::field_attributes& attributes() const;
|
||||||
[[nodiscard]] utils::field_attributes& attributes();
|
[[nodiscard]] utils::field_attributes& attributes();
|
||||||
[[nodiscard]] bool is_nullable() const;
|
[[nodiscard]] bool is_nullable() const;
|
||||||
[[nodiscard]] utils::basic_type type() const;
|
[[nodiscard]] utils::basic_type type() const;
|
||||||
[[nodiscard]] std::shared_ptr<object> owner() const;
|
[[nodiscard]] std::shared_ptr<object> owner() const;
|
||||||
void change_type(utils::basic_type type, const utils::field_attributes &attr = utils::null_attributes);
|
void change_type(utils::basic_type type, const utils::field_attributes &attr = NullAttributes);
|
||||||
template < typename Type >
|
template < typename Type >
|
||||||
void change_type(const utils::field_attributes &attr = utils::null_attributes) {
|
void change_type(const utils::field_attributes &attr = NullAttributes) {
|
||||||
type_ = utils::data_type_traits<Type>::type(attr.size());
|
type_ = utils::data_type_traits<Type>::type(attr.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -68,6 +69,7 @@ private:
|
|||||||
friend class object_generator;
|
friend class object_generator;
|
||||||
|
|
||||||
std::string name_;
|
std::string name_;
|
||||||
|
size_t index_{0};
|
||||||
std::weak_ptr<object> owner_;
|
std::weak_ptr<object> owner_;
|
||||||
utils::basic_type type_{utils::basic_type::Null};
|
utils::basic_type type_{utils::basic_type::Null};
|
||||||
utils::field_attributes options_{};
|
utils::field_attributes options_{};
|
||||||
|
|||||||
@@ -27,12 +27,12 @@ public:
|
|||||||
[[nodiscard]] std::type_index type_index() const;
|
[[nodiscard]] std::type_index type_index() const;
|
||||||
[[nodiscard]] std::string name() const;
|
[[nodiscard]] std::string name() const;
|
||||||
[[nodiscard]] std::shared_ptr<class object> object() const;
|
[[nodiscard]] std::shared_ptr<class object> object() const;
|
||||||
[[nodiscard]] const std::list<attribute>& attributes() const;
|
[[nodiscard]] const std::vector<attribute>& attributes() const;
|
||||||
[[nodiscard]] const std::list<class restriction>& constraints() const;
|
[[nodiscard]] const std::list<restriction>& constraints() const;
|
||||||
|
|
||||||
[[nodiscard]] bool has_primary_key() const;
|
[[nodiscard]] bool has_primary_key() const;
|
||||||
[[nodiscard]] const utils::identifier& primary_key() const;
|
[[nodiscard]] const utils::identifier& primary_key() const;
|
||||||
[[nodiscard]] attribute* primary_key_attribute() const;
|
[[nodiscard]] const attribute* primary_key_attribute() const;
|
||||||
|
|
||||||
void update_name(const std::string& name) const;
|
void update_name(const std::string& name) const;
|
||||||
|
|
||||||
|
|||||||
@@ -95,7 +95,7 @@ public:
|
|||||||
return basic_info(std::type_index(typeid(Type)));
|
return basic_info(std::type_index(typeid(Type)));
|
||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard]] utils::result<attribute*, utils::error> primary_key_attribute(const std::type_index &ti) const;
|
[[nodiscard]] utils::result<const attribute*, utils::error> primary_key_attribute(const std::type_index &ti) const;
|
||||||
|
|
||||||
void dump(std::ostream &os) const;
|
void dump(std::ostream &os) const;
|
||||||
static void dump(std::ostream &os, const repository_node& node);
|
static void dump(std::ostream &os, const repository_node& node);
|
||||||
|
|||||||
@@ -8,9 +8,9 @@ namespace matador::object {
|
|||||||
template < class Type >
|
template < class Type >
|
||||||
class collection {
|
class collection {
|
||||||
public:
|
public:
|
||||||
using value_type = Type;
|
using value_type = typename collection_proxy<Type>::value_type;
|
||||||
using iterator = typename std::vector<value_type>::iterator;
|
using iterator = typename collection_proxy<Type>::iterator;
|
||||||
using const_iterator = typename std::vector<value_type>::const_iterator;
|
using const_iterator = typename collection_proxy<Type>::const_iterator;
|
||||||
|
|
||||||
collection()
|
collection()
|
||||||
: proxy_(std::make_shared<collection_proxy<Type>>()) {}
|
: proxy_(std::make_shared<collection_proxy<Type>>()) {}
|
||||||
@@ -21,31 +21,31 @@ public:
|
|||||||
collection(const collection& other) = default;
|
collection(const collection& other) = default;
|
||||||
|
|
||||||
void push_back(const value_type& value) {
|
void push_back(const value_type& value) {
|
||||||
proxy_->items().push_back(value);
|
proxy_->push_back(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
iterator begin() {
|
iterator begin() {
|
||||||
return proxy_->items().begin();
|
return proxy_->begin();
|
||||||
}
|
}
|
||||||
|
|
||||||
iterator end() {
|
iterator end() {
|
||||||
return proxy_->items().end();
|
return proxy_->end();
|
||||||
}
|
}
|
||||||
|
|
||||||
const_iterator begin() const {
|
const_iterator begin() const {
|
||||||
return proxy_->items().begin();
|
return proxy_->begin();
|
||||||
}
|
}
|
||||||
|
|
||||||
const_iterator end() const {
|
const_iterator end() const {
|
||||||
return proxy_->items().end();
|
return proxy_->end();
|
||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard]] size_t size() const {
|
[[nodiscard]] size_t size() const {
|
||||||
return proxy_->items().size();
|
return proxy_->size();
|
||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard]] bool empty() const {
|
[[nodiscard]] bool empty() const {
|
||||||
return proxy_->items().empty();
|
return proxy_->empty();
|
||||||
}
|
}
|
||||||
|
|
||||||
void reset(std::shared_ptr<collection_proxy<Type>> proxy) {
|
void reset(std::shared_ptr<collection_proxy<Type>> proxy) {
|
||||||
@@ -53,7 +53,7 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::shared_ptr<collection_proxy<Type>> proxy_;
|
std::shared_ptr<abstract_collection_proxy<Type>> proxy_;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
#ifndef MATADOR_COLLECTION_PROXY_HPP
|
#ifndef MATADOR_COLLECTION_PROXY_HPP
|
||||||
#define MATADOR_COLLECTION_PROXY_HPP
|
#define MATADOR_COLLECTION_PROXY_HPP
|
||||||
|
|
||||||
|
|
||||||
#include "matador/object/collection_resolver.hpp"
|
#include "matador/object/collection_resolver.hpp"
|
||||||
|
#include "matador/object/many_to_many_relation.hpp"
|
||||||
|
|
||||||
#include "matador/utils/identifier.hpp"
|
#include "matador/utils/identifier.hpp"
|
||||||
|
|
||||||
@@ -12,10 +12,100 @@
|
|||||||
|
|
||||||
namespace matador::object {
|
namespace matador::object {
|
||||||
|
|
||||||
|
// has many primitive
|
||||||
|
// relation<OwnerType, PrimitiveType>
|
||||||
|
// has many ptr
|
||||||
|
// relation<OwnerType, ObjectType>
|
||||||
|
// has many to many
|
||||||
|
// relation<OwnerType, ForeignType>
|
||||||
|
|
||||||
|
template < class RelationType >
|
||||||
|
struct relation_iterator_traits {
|
||||||
|
static RelationType& value(RelationType& item) {
|
||||||
|
return item;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template < typename Type, typename OwnerType >
|
||||||
|
struct relation_iterator_traits<relation<OwnerType, Type>> {
|
||||||
|
static Type& value(relation<OwnerType, Type>& re) {
|
||||||
|
return re.relation().value();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename Type, typename RelationType = Type>
|
||||||
|
class collection_proxy_iterator {
|
||||||
|
public:
|
||||||
|
using value_type = Type;
|
||||||
|
using reference = Type&;
|
||||||
|
using pointer = Type*;
|
||||||
|
using relation_type = RelationType;
|
||||||
|
using iterator_category = std::forward_iterator_tag;
|
||||||
|
|
||||||
|
collection_proxy_iterator() = default;
|
||||||
|
collection_proxy_iterator(typename std::vector<RelationType>::iterator it)
|
||||||
|
: it_(it) {}
|
||||||
|
|
||||||
|
reference operator*() {
|
||||||
|
return relation_iterator_traits<relation_type>::value(*it_);
|
||||||
|
}
|
||||||
|
|
||||||
|
pointer operator->() {
|
||||||
|
return &relation_iterator_traits<relation_type>::value(*it_);
|
||||||
|
}
|
||||||
|
|
||||||
|
collection_proxy_iterator& operator++() {
|
||||||
|
++it_;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
bool operator==(const collection_proxy_iterator& other) const {
|
||||||
|
return it_ == other.it_;
|
||||||
|
}
|
||||||
|
bool operator!=(const collection_proxy_iterator& other) const {
|
||||||
|
return !operator==(other);
|
||||||
|
}
|
||||||
|
|
||||||
|
// relation_type& relation() {
|
||||||
|
|
||||||
|
// return *it_;
|
||||||
|
// }
|
||||||
|
private:
|
||||||
|
typename std::vector<RelationType>::iterator it_;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename Type, typename RelationType = Type>
|
||||||
|
class abstract_collection_proxy {
|
||||||
|
public:
|
||||||
|
using value_type = Type;
|
||||||
|
using relation_type = RelationType;
|
||||||
|
// using iterator = typename std::vector<value_type>::iterator;
|
||||||
|
// using const_iterator = typename std::vector<value_type>::const_iterator;
|
||||||
|
using iterator = collection_proxy_iterator<value_type, relation_type>;
|
||||||
|
using const_iterator = collection_proxy_iterator<value_type, relation_type>;
|
||||||
|
|
||||||
|
virtual ~abstract_collection_proxy() = default;
|
||||||
|
|
||||||
|
virtual void push_back(const value_type& value) = 0;
|
||||||
|
|
||||||
|
virtual iterator begin() = 0;
|
||||||
|
virtual iterator end() = 0;
|
||||||
|
|
||||||
|
[[nodiscard]] virtual size_t size() = 0;
|
||||||
|
[[nodiscard]] virtual bool empty() = 0;
|
||||||
|
|
||||||
|
[[nodiscard]] virtual const utils::identifier& owner_id() const = 0;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
std::vector<relation_type> items_;
|
||||||
|
};
|
||||||
|
|
||||||
template<typename Type>
|
template<typename Type>
|
||||||
class collection_proxy final {
|
class collection_proxy : public abstract_collection_proxy<Type> {
|
||||||
public:
|
public:
|
||||||
|
using value_type = Type;
|
||||||
|
using iterator = typename abstract_collection_proxy<Type>::iterator;
|
||||||
|
using const_iterator = typename abstract_collection_proxy<Type>::const_iterator;
|
||||||
|
|
||||||
collection_proxy() = default;
|
collection_proxy() = default;
|
||||||
|
|
||||||
// Lazy
|
// Lazy
|
||||||
@@ -30,17 +120,32 @@ public:
|
|||||||
explicit collection_proxy(std::vector<Type> items)
|
explicit collection_proxy(std::vector<Type> items)
|
||||||
: items_(std::move(items)) {}
|
: items_(std::move(items)) {}
|
||||||
|
|
||||||
[[nodiscard]] const utils::identifier& owner_id() const {
|
[[nodiscard]] const utils::identifier& owner_id() const override {
|
||||||
return owner_id_;
|
return owner_id_;
|
||||||
}
|
}
|
||||||
const std::vector<Type>& items() const {
|
|
||||||
|
iterator begin() override {
|
||||||
resolve();
|
resolve();
|
||||||
return items_;
|
return items_.begin();
|
||||||
}
|
}
|
||||||
std::vector<Type>& items() {
|
iterator end() override {
|
||||||
resolve();
|
resolve();
|
||||||
return items_;
|
return items_.end();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void push_back(const value_type& value) override {
|
||||||
|
resolve();
|
||||||
|
items_.push_back(value);
|
||||||
|
}
|
||||||
|
[[nodiscard]] size_t size() override {
|
||||||
|
resolve();
|
||||||
|
return items_.size();
|
||||||
|
}
|
||||||
|
[[nodiscard]] bool empty() override {
|
||||||
|
resolve();
|
||||||
|
return items_.empty();
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void resolve() {
|
void resolve() {
|
||||||
if (loaded_) {
|
if (loaded_) {
|
||||||
@@ -66,5 +171,18 @@ private:
|
|||||||
std::weak_ptr<collection_resolver<Type>> resolver_{};
|
std::weak_ptr<collection_resolver<Type>> resolver_{};
|
||||||
mutable std::mutex mutex_{};
|
mutable std::mutex mutex_{};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
template<typename Type, class OwnerType>
|
||||||
|
class collection_many_to_many_proxy {
|
||||||
|
public:
|
||||||
|
using value_type = Type;
|
||||||
|
using owner_type = OwnerType;
|
||||||
|
using relation_type = many_to_many_relation<value_type, owner_type>;
|
||||||
|
using iterator = typename std::vector<relation_type>::iterator;
|
||||||
|
using const_iterator = typename std::vector<relation_type>::const_iterator;
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::vector<relation_type> relations_;
|
||||||
|
};
|
||||||
}
|
}
|
||||||
#endif //MATADOR_COLLECTION_PROXY_HPP
|
#endif //MATADOR_COLLECTION_PROXY_HPP
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
#ifndef MATADOR_COLLECTION_RESOLVER_HPP
|
#ifndef MATADOR_COLLECTION_RESOLVER_HPP
|
||||||
#define MATADOR_COLLECTION_RESOLVER_HPP
|
#define MATADOR_COLLECTION_RESOLVER_HPP
|
||||||
|
|
||||||
#include "matador/object/abstract_collection_resolver.hpp"
|
#include "matador/object/abstract_joined_resolver.hpp"
|
||||||
|
|
||||||
#include <typeindex>
|
#include <typeindex>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
@@ -12,10 +12,10 @@ class identifier;
|
|||||||
|
|
||||||
namespace matador::object {
|
namespace matador::object {
|
||||||
template<typename Type>
|
template<typename Type>
|
||||||
class collection_resolver : public abstract_collection_resolver {
|
class collection_resolver : public abstract_joined_resolver {
|
||||||
public:
|
public:
|
||||||
collection_resolver(const std::type_index& root_type, std::string collection_name)
|
collection_resolver(const std::type_index& root_type, std::string collection_name)
|
||||||
: abstract_collection_resolver(root_type, typeid(Type), std::move(collection_name)) {}
|
: abstract_joined_resolver(root_type, typeid(Type), std::move(collection_name)) {}
|
||||||
|
|
||||||
virtual std::vector<Type> resolve(const utils::identifier& id) = 0;
|
virtual std::vector<Type> resolve(const utils::identifier& id) = 0;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,28 +0,0 @@
|
|||||||
#ifndef MATADOR_CONTAINER_RESOLVER_FACTORY_HPP
|
|
||||||
#define MATADOR_CONTAINER_RESOLVER_FACTORY_HPP
|
|
||||||
|
|
||||||
#include "matador/object/collection_resolver.hpp"
|
|
||||||
|
|
||||||
namespace matador::object {
|
|
||||||
class abstract_collection_resolver_factory {
|
|
||||||
public:
|
|
||||||
virtual ~abstract_collection_resolver_factory() = default;
|
|
||||||
|
|
||||||
[[nodiscard]] virtual std::shared_ptr<abstract_collection_resolver> acquire_collection_resolver(const std::type_index &root_type, const std::type_index &element_type, const std::string &collection_name) const = 0;
|
|
||||||
virtual void register_collection_resolver(std::shared_ptr<abstract_collection_resolver> &&resolver) = 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
class collection_resolver_factory : public abstract_collection_resolver_factory {
|
|
||||||
public:
|
|
||||||
template<class Type>
|
|
||||||
[[nodiscard]] std::shared_ptr<collection_resolver<Type>> resolver(const std::type_index &root_type, const std::string &collection_name) const {
|
|
||||||
const auto res = acquire_collection_resolver(root_type, typeid(Type), collection_name);
|
|
||||||
if (!res) {
|
|
||||||
return std::dynamic_pointer_cast<collection_resolver<Type>>(res);
|
|
||||||
}
|
|
||||||
|
|
||||||
return std::dynamic_pointer_cast<collection_resolver<Type>>(res);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
#endif //MATADOR_CONTAINER_RESOLVER_FACTORY_HPP
|
|
||||||
@@ -38,17 +38,19 @@ public:
|
|||||||
completer.complete_node(node);
|
completer.complete_node(node);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template<typename BaseType>
|
||||||
|
static void on_base(const BaseType&) {}
|
||||||
template<class PrimaryKeyType>
|
template<class PrimaryKeyType>
|
||||||
static void on_primary_key(const char * /*id*/, PrimaryKeyType &/*pk*/, const utils::primary_key_attribute & /*attr*/ = utils::default_pk_attributes) {}
|
static void on_primary_key(const char * /*id*/, PrimaryKeyType &/*pk*/, const utils::primary_key_attribute & /*attr*/) {}
|
||||||
static void on_revision(const char * /*id*/, uint64_t &/*rev*/) {}
|
static void on_revision(const char * /*id*/, uint64_t &/*rev*/) {}
|
||||||
template<typename AttributeType>
|
template<typename AttributeType>
|
||||||
static void on_attribute(const char * /*id*/, AttributeType &/*val*/, const utils::field_attributes &/*attr*/ = utils::null_attributes) {}
|
static void on_attribute(const char * /*id*/, AttributeType &/*val*/, const utils::field_attributes &/*attr*/) {}
|
||||||
template<typename AttributeType>
|
template<typename AttributeType>
|
||||||
static void on_attribute(const char * /*id*/, std::optional<AttributeType> &/*val*/, const utils::field_attributes &/*attr*/ = utils::null_attributes) {}
|
static void on_attribute(const char * /*id*/, std::optional<AttributeType> &/*val*/, const utils::field_attributes &/*attr*/) {}
|
||||||
template<class ForeignPointerType>
|
template<class ForeignPointerType>
|
||||||
void on_belongs_to(const char *id, ForeignPointerType &/*obj*/, const utils::foreign_attributes &/*attr*/);
|
void on_belongs_to(const char *id, ForeignPointerType &/*obj*/, const utils::foreign_attributes &/*attr*/);
|
||||||
template<class ForeignPointerType>
|
template<class ForeignPointerType>
|
||||||
void on_has_one(const char * /*id*/, ForeignPointerType &/*obj*/, const utils::foreign_attributes &/*attr*/);
|
void on_has_one(const char * /*id*/, ForeignPointerType &/*obj*/, const char *join_column, const utils::foreign_attributes &/*attr*/);
|
||||||
template<class CollectionType>
|
template<class CollectionType>
|
||||||
void on_has_many(const char *id, CollectionType &,
|
void on_has_many(const char *id, CollectionType &,
|
||||||
const char *join_column,
|
const char *join_column,
|
||||||
@@ -132,7 +134,7 @@ void foreign_node_completer<NodeType, Observers...>::on_belongs_to(const char *
|
|||||||
|
|
||||||
template<typename NodeType, template<typename> typename ...Observers>
|
template<typename NodeType, template<typename> typename ...Observers>
|
||||||
template<class ForeignPointerType>
|
template<class ForeignPointerType>
|
||||||
void foreign_node_completer<NodeType, Observers...>::on_has_one(const char * /*id*/, ForeignPointerType &, const utils::foreign_attributes &) {
|
void foreign_node_completer<NodeType, Observers...>::on_has_one(const char * /*id*/, ForeignPointerType &, const char * /*join_column*/, const utils::foreign_attributes &) {
|
||||||
attach_node<typename ForeignPointerType::value_type>();
|
attach_node<typename ForeignPointerType::value_type>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -24,15 +24,17 @@ public:
|
|||||||
|
|
||||||
return join_columns_;
|
return join_columns_;
|
||||||
}
|
}
|
||||||
|
template<typename BaseType>
|
||||||
|
static void on_base(const BaseType&) {}
|
||||||
template < class V >
|
template < class V >
|
||||||
static void on_primary_key(const char * /*id*/, V &, const utils::primary_key_attribute& /*attr*/ = utils::default_pk_attributes) {}
|
static void on_primary_key(const char * /*id*/, V &, const utils::primary_key_attribute& /*attr*/) {}
|
||||||
static void on_revision(const char * /*id*/, uint64_t &/*rev*/) {}
|
static void on_revision(const char * /*id*/, uint64_t &/*rev*/) {}
|
||||||
template<typename Type>
|
template<typename Type>
|
||||||
static void on_attribute(const char * /*id*/, Type &, const utils::field_attributes &/*attr*/ = utils::null_attributes) {}
|
static void on_attribute(const char * /*id*/, Type &, const utils::field_attributes &/*attr*/) {}
|
||||||
template<class Pointer>
|
template<class Pointer>
|
||||||
static void on_belongs_to(const char * /*id*/, Pointer &/*obj*/, const utils::foreign_attributes &/*attr*/) {}
|
static void on_belongs_to(const char * /*id*/, Pointer &/*obj*/, const utils::foreign_attributes &/*attr*/) {}
|
||||||
template<class Pointer>
|
template<class Pointer>
|
||||||
static void on_has_one(const char * /*id*/, Pointer &/*obj*/, const utils::foreign_attributes &/*attr*/) {}
|
static void on_has_one(const char * /*id*/, Pointer &/*obj*/, const char * /*join_column*/, const utils::foreign_attributes &/*attr*/) {}
|
||||||
template<class ContainerType>
|
template<class ContainerType>
|
||||||
static void on_has_many(ContainerType &, const char */*join_column*/, const utils::foreign_attributes &/*attr*/) {}
|
static void on_has_many(ContainerType &, const char */*join_column*/, const utils::foreign_attributes &/*attr*/) {}
|
||||||
template<class ContainerType>
|
template<class ContainerType>
|
||||||
|
|||||||
@@ -0,0 +1,51 @@
|
|||||||
|
#ifndef MATADOR_CONTAINER_RESOLVER_FACTORY_HPP
|
||||||
|
#define MATADOR_CONTAINER_RESOLVER_FACTORY_HPP
|
||||||
|
|
||||||
|
#include "matador/object/collection_resolver.hpp"
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
namespace matador::object {
|
||||||
|
class abstract_collection_resolver_factory {
|
||||||
|
public:
|
||||||
|
virtual ~abstract_collection_resolver_factory() = default;
|
||||||
|
|
||||||
|
[[nodiscard]] virtual std::shared_ptr<abstract_joined_resolver> acquire_collection_resolver(const std::type_index &root_type, const std::type_index &element_type, const std::string &collection_name) const = 0;
|
||||||
|
virtual void register_collection_resolver(std::shared_ptr<abstract_joined_resolver> &&resolver) = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
class joined_collection_resolver_factory : public abstract_collection_resolver_factory {
|
||||||
|
public:
|
||||||
|
template<class Type>
|
||||||
|
[[nodiscard]] std::shared_ptr<collection_resolver<Type>> resolver(const std::type_index &root_type, const std::string &collection_name) const {
|
||||||
|
const auto res = acquire_collection_resolver(root_type, typeid(Type), collection_name);
|
||||||
|
if (!res) {
|
||||||
|
return std::dynamic_pointer_cast<collection_resolver<Type>>(res);
|
||||||
|
}
|
||||||
|
|
||||||
|
return std::dynamic_pointer_cast<collection_resolver<Type>>(res);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class abstract_joined_object_resolver_factory {
|
||||||
|
public:
|
||||||
|
virtual ~abstract_joined_object_resolver_factory() = default;
|
||||||
|
|
||||||
|
[[nodiscard]] virtual std::shared_ptr<abstract_type_resolver> acquire_joined_object_resolver(const std::type_index &root_type, const std::type_index &element_type, const std::string &collection_name) const = 0;
|
||||||
|
virtual void register_joined_object_resolver(std::shared_ptr<abstract_type_resolver> &&resolver, const std::type_index& root_type, const std::string& join_column) = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
class joined_object_resolver_factory : public abstract_joined_object_resolver_factory {
|
||||||
|
public:
|
||||||
|
template<class Type>
|
||||||
|
[[nodiscard]] std::shared_ptr<object_resolver<Type>> resolver(const std::type_index &root_type, const std::string &collection_name) const {
|
||||||
|
const auto res = acquire_joined_object_resolver(root_type, typeid(Type), collection_name);
|
||||||
|
if (!res) {
|
||||||
|
return std::dynamic_pointer_cast<object_resolver<Type>>(res);
|
||||||
|
}
|
||||||
|
|
||||||
|
return std::dynamic_pointer_cast<object_resolver<Type>>(res);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
#endif //MATADOR_CONTAINER_RESOLVER_FACTORY_HPP
|
||||||
@@ -8,6 +8,48 @@
|
|||||||
|
|
||||||
namespace matador::object {
|
namespace matador::object {
|
||||||
|
|
||||||
|
template < class LocalType, class ForeignType >
|
||||||
|
class relation {
|
||||||
|
public:
|
||||||
|
relation() = default;
|
||||||
|
relation(std::string local_name, std::string remote_name)
|
||||||
|
: local_name_(std::move(local_name))
|
||||||
|
, remote_name_(std::move(remote_name)) {}
|
||||||
|
relation(std::string local_name, std::string remote_name, const object_ptr<LocalType>& local, const ForeignType& remote)
|
||||||
|
: local_name_(std::move(local_name))
|
||||||
|
, remote_name_(std::move(remote_name))
|
||||||
|
, local_(local)
|
||||||
|
, remote_(remote)
|
||||||
|
{}
|
||||||
|
|
||||||
|
template<class Operator>
|
||||||
|
void process(Operator &op) {
|
||||||
|
namespace field = matador::access;
|
||||||
|
field::belongs_to(op, local_name_.c_str(), local_, utils::CascadeNoneFetchLazy);
|
||||||
|
foreign_field(op);
|
||||||
|
}
|
||||||
|
|
||||||
|
object_ptr<LocalType> local() const { return local_; }
|
||||||
|
const ForeignType& remote() const { return remote_; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
template<typename Operator, class RemoteType = ForeignType>
|
||||||
|
void foreign_field(Operator &op, std::enable_if_t<!is_object_ptr<RemoteType>::value>* /*unused*/) {
|
||||||
|
namespace field = matador::access;
|
||||||
|
field::attribute(op, remote_name_.c_str(), remote_);
|
||||||
|
}
|
||||||
|
template<typename Operator, class RemoteType = ForeignType>
|
||||||
|
void foreign_field(Operator &op, std::enable_if_t<is_object_ptr<RemoteType>::value>* /*unused*/) {
|
||||||
|
namespace field = matador::access;
|
||||||
|
field::belongs_to(op, remote_name_.c_str(), remote_, utils::CascadeNoneFetchLazy);
|
||||||
|
}
|
||||||
|
private:
|
||||||
|
std::string local_name_;
|
||||||
|
std::string remote_name_;
|
||||||
|
object_ptr<LocalType> local_;
|
||||||
|
ForeignType remote_;
|
||||||
|
};
|
||||||
|
|
||||||
template < class LocalType, class ForeignType >
|
template < class LocalType, class ForeignType >
|
||||||
class many_to_many_relation {
|
class many_to_many_relation {
|
||||||
public:
|
public:
|
||||||
@@ -15,6 +57,12 @@ public:
|
|||||||
many_to_many_relation(std::string local_name, std::string remote_name)
|
many_to_many_relation(std::string local_name, std::string remote_name)
|
||||||
: local_name_(std::move(local_name))
|
: local_name_(std::move(local_name))
|
||||||
, remote_name_(std::move(remote_name)) {}
|
, remote_name_(std::move(remote_name)) {}
|
||||||
|
many_to_many_relation(std::string local_name, std::string remote_name, const object_ptr<LocalType>& local, const object_ptr<ForeignType>& remote)
|
||||||
|
: local_name_(std::move(local_name))
|
||||||
|
, remote_name_(std::move(remote_name))
|
||||||
|
, local_(local)
|
||||||
|
, remote_(remote)
|
||||||
|
{}
|
||||||
|
|
||||||
template<class Operator>
|
template<class Operator>
|
||||||
void process(Operator &op) {
|
void process(Operator &op) {
|
||||||
@@ -40,6 +88,11 @@ public:
|
|||||||
many_to_relation(std::string local_name, std::string remote_name)
|
many_to_relation(std::string local_name, std::string remote_name)
|
||||||
: local_name_(std::move(local_name))
|
: local_name_(std::move(local_name))
|
||||||
, type_name_(std::move(remote_name)) {}
|
, type_name_(std::move(remote_name)) {}
|
||||||
|
many_to_relation(std::string local_name, std::string remote_name, const object_ptr<LocalType>& local, const Type& value)
|
||||||
|
: local_name_(std::move(local_name))
|
||||||
|
, type_name_(std::move(remote_name))
|
||||||
|
, local_(local)
|
||||||
|
, value_(value){}
|
||||||
|
|
||||||
template<class Operator>
|
template<class Operator>
|
||||||
void process(Operator &op) {
|
void process(Operator &op) {
|
||||||
|
|||||||
@@ -17,17 +17,21 @@ public:
|
|||||||
|
|
||||||
static const attribute& create_attribute(std::string name, const std::shared_ptr<object>& obj);
|
static const attribute& create_attribute(std::string name, const std::shared_ptr<object>& obj);
|
||||||
|
|
||||||
[[nodiscard]] attribute* primary_key_attribute() const;
|
[[nodiscard]] const attribute* primary_key_attribute() const;
|
||||||
[[nodiscard]] const utils::identifier& primary_key() const;
|
[[nodiscard]] const utils::identifier& primary_key() const;
|
||||||
[[nodiscard]] bool has_primary_key() const;
|
[[nodiscard]] bool has_primary_key() const;
|
||||||
|
|
||||||
|
[[nodiscard]] bool is_relation_object() const;
|
||||||
|
[[nodiscard]] const attribute* join_attribute() const;
|
||||||
|
[[nodiscard]] const attribute* inverse_join_attribute() const;
|
||||||
|
|
||||||
[[nodiscard]] const std::string& name() const;
|
[[nodiscard]] const std::string& name() const;
|
||||||
|
|
||||||
void update_name(const std::string& name);
|
void update_name(const std::string& name);
|
||||||
|
|
||||||
[[nodiscard]] bool has_attributes() const;
|
[[nodiscard]] bool has_attributes() const;
|
||||||
[[nodiscard]] size_t attribute_count() const;
|
[[nodiscard]] size_t attribute_count() const;
|
||||||
[[nodiscard]] const std::list<attribute>& attributes() const;
|
[[nodiscard]] const std::vector<attribute>& attributes() const;
|
||||||
|
|
||||||
[[nodiscard]] bool has_constraints() const;
|
[[nodiscard]] bool has_constraints() const;
|
||||||
[[nodiscard]] size_t constraint_count() const;
|
[[nodiscard]] size_t constraint_count() const;
|
||||||
@@ -40,9 +44,12 @@ private:
|
|||||||
friend class object_generator;
|
friend class object_generator;
|
||||||
|
|
||||||
std::string name_;
|
std::string name_;
|
||||||
attribute* pk_attribute_{nullptr};
|
int pk_column_index_{-1};
|
||||||
|
int join_column_index_{-1};
|
||||||
|
int inverse_join_column_index_{-1};
|
||||||
|
|
||||||
utils::identifier pk_identifier_;
|
utils::identifier pk_identifier_;
|
||||||
std::list<attribute> attributes_;
|
std::vector<attribute> attributes_;
|
||||||
std::list<restriction> constraints_;
|
std::list<restriction> constraints_;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
#include "matador/object/object_resolver.hpp"
|
#include "matador/object/object_resolver.hpp"
|
||||||
|
|
||||||
#include "matador/utils/identifier.hpp"
|
#include "matador/utils/identifier.hpp"
|
||||||
|
#include "matador/utils/message_bus.hpp"
|
||||||
|
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
@@ -25,16 +26,33 @@ struct cache_entry_base {
|
|||||||
[[nodiscard]] virtual bool is_dead() const noexcept = 0;
|
[[nodiscard]] virtual bool is_dead() const noexcept = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
template<typename T>
|
template<typename Type>
|
||||||
struct cache_entry : cache_entry_base {
|
struct cache_entry : cache_entry_base {
|
||||||
std::weak_ptr<object_proxy<T> > proxy;
|
std::weak_ptr<object_proxy<Type> > proxy;
|
||||||
std::weak_ptr<T> entity;
|
std::weak_ptr<Type> entity;
|
||||||
|
|
||||||
[[nodiscard]] bool is_dead() const noexcept override {
|
[[nodiscard]] bool is_dead() const noexcept override {
|
||||||
return proxy.expired() && entity.expired();
|
return proxy.expired() && entity.expired();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct object_cache_event {
|
||||||
|
std::type_index type{typeid(void)};
|
||||||
|
utils::identifier id{};
|
||||||
|
std::chrono::steady_clock::time_point timestamp{};
|
||||||
|
};
|
||||||
|
|
||||||
|
struct object_cache_accessed_event : object_cache_event {};
|
||||||
|
struct object_cache_proxy_added_event : object_cache_event {};
|
||||||
|
struct object_cache_entity_attached_event : object_cache_event {};
|
||||||
|
struct object_cache_imported_event : object_cache_event {};
|
||||||
|
struct object_cache_erased_event : object_cache_event {};
|
||||||
|
|
||||||
|
struct object_cache_sweep_event {
|
||||||
|
std::size_t removed{};
|
||||||
|
std::chrono::steady_clock::time_point timestamp{};
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Thread-sicherer Cache für Objekt-Proxies und (optional) geladene Entities.
|
* @brief Thread-sicherer Cache für Objekt-Proxies und (optional) geladene Entities.
|
||||||
*
|
*
|
||||||
@@ -54,7 +72,8 @@ public:
|
|||||||
/**
|
/**
|
||||||
* @brief Erzeugt einen leeren Cache.
|
* @brief Erzeugt einen leeren Cache.
|
||||||
*/
|
*/
|
||||||
object_cache() = default;
|
explicit object_cache(utils::message_bus &bus)
|
||||||
|
: bus_(bus) {}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Liefert einen Proxy für (T, id) und erstellt ihn bei Bedarf.
|
* @brief Liefert einen Proxy für (T, id) und erstellt ihn bei Bedarf.
|
||||||
@@ -63,8 +82,8 @@ public:
|
|||||||
* Andernfalls wird ein neuer Proxy erzeugt, im Cache abgelegt und zurückgegeben.
|
* Andernfalls wird ein neuer Proxy erzeugt, im Cache abgelegt und zurückgegeben.
|
||||||
* Falls für (T, id) bereits eine Entity vorhanden ist, wird sie an den Proxy gebunden.
|
* Falls für (T, id) bereits eine Entity vorhanden ist, wird sie an den Proxy gebunden.
|
||||||
*
|
*
|
||||||
* @tparam T Entity-Typ.
|
* @tparam Type Entity-Typ.
|
||||||
* @tparam ResolverPtr Zeiger-Typ auf einen Resolver, typischerweise
|
* @tparam ResolverPointerType Zeiger-Typ auf einen Resolver, typischerweise
|
||||||
* @c std::shared_ptr<object_resolver<T>> oder @c std::weak_ptr<object_resolver<T>>.
|
* @c std::shared_ptr<object_resolver<T>> oder @c std::weak_ptr<object_resolver<T>>.
|
||||||
* @param id Identifier/Primärschlüssel der Entity.
|
* @param id Identifier/Primärschlüssel der Entity.
|
||||||
* @param resolver_ptr Resolver (shared/weak), der zur Lazy-Auflösung durch den Proxy genutzt wird.
|
* @param resolver_ptr Resolver (shared/weak), der zur Lazy-Auflösung durch den Proxy genutzt wird.
|
||||||
@@ -72,23 +91,24 @@ public:
|
|||||||
*
|
*
|
||||||
* @note Diese Methode ist threadsafe.
|
* @note Diese Methode ist threadsafe.
|
||||||
*/
|
*/
|
||||||
template<typename T, typename ResolverPtr>
|
template<typename Type, typename ResolverPointerType>
|
||||||
std::shared_ptr<object_proxy<T>> acquire_proxy(utils::identifier id, ResolverPtr &&resolver_ptr) {
|
std::shared_ptr<object_proxy<Type>> acquire_proxy(utils::identifier id, ResolverPointerType &&resolver_ptr) {
|
||||||
const auto k = make_key<T>(id);
|
const auto k = make_key<Type>(id);
|
||||||
|
|
||||||
std::unique_lock lock(mutex_);
|
std::unique_lock lock(mutex_);
|
||||||
|
|
||||||
auto it = map_.find(k);
|
auto it = map_.find(k);
|
||||||
if (it != map_.end()) {
|
if (it != map_.end()) {
|
||||||
// found entry, return std::shared_ptr of proxy
|
// found entry, return std::shared_ptr of proxy
|
||||||
auto *entry = entry_cast_<T>(it->second.get());
|
auto *entry = entry_cast_<Type>(it->second.get());
|
||||||
if (auto proxy_ptr = entry->proxy.lock()) {
|
if (auto proxy_ptr = entry->proxy.lock()) {
|
||||||
|
bus_.publish<object_cache_accessed_event>({k.type, id, std::chrono::steady_clock::now()});
|
||||||
return proxy_ptr;
|
return proxy_ptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
// if the proxy is dead, but the entity is alive, create a new proxy and attach entity
|
// if the proxy is dead, but the entity is alive, create a new proxy and attach entity
|
||||||
auto weak_resolver = to_weak<T>(std::forward<ResolverPtr>(resolver_ptr));
|
auto weak_resolver = to_weak<Type>(std::forward<ResolverPointerType>(resolver_ptr));
|
||||||
auto proxy_ptr = std::make_shared<object_proxy<T>>(weak_resolver, id);
|
auto proxy_ptr = std::make_shared<object_proxy<Type>>(weak_resolver, id);
|
||||||
|
|
||||||
if (auto obj = entry->entity.lock()) {
|
if (auto obj = entry->entity.lock()) {
|
||||||
proxy_ptr->attach(std::move(obj));
|
proxy_ptr->attach(std::move(obj));
|
||||||
@@ -96,17 +116,19 @@ public:
|
|||||||
|
|
||||||
entry->proxy = proxy_ptr;
|
entry->proxy = proxy_ptr;
|
||||||
|
|
||||||
|
bus_.publish<object_cache_proxy_added_event>({k.type, id, std::chrono::steady_clock::now()});
|
||||||
|
|
||||||
// return the shared_ptr of the proxy
|
// return the shared_ptr of the proxy
|
||||||
return proxy_ptr;
|
return proxy_ptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
// entry couldn't be found, create a new entry
|
// entry couldn't be found, create a new entry
|
||||||
auto entry_ptr = std::make_unique<cache_entry<T>>();
|
auto entry_ptr = std::make_unique<cache_entry<Type>>();
|
||||||
auto *entry = entry_ptr.get();
|
auto *entry = entry_ptr.get();
|
||||||
|
|
||||||
// create a weak resolver and shared proxy
|
// create a weak resolver and shared proxy
|
||||||
auto weak_resolver = to_weak<T>(std::forward<ResolverPtr>(resolver_ptr));
|
auto weak_resolver = to_weak<Type>(std::forward<ResolverPointerType>(resolver_ptr));
|
||||||
auto proxy_ptr = std::make_shared<object_proxy<T>>(weak_resolver, id);
|
auto proxy_ptr = std::make_shared<object_proxy<Type>>(weak_resolver, id);
|
||||||
|
|
||||||
// lock entity and attach to proxy
|
// lock entity and attach to proxy
|
||||||
if (auto obj = entry->entity.lock()) {
|
if (auto obj = entry->entity.lock()) {
|
||||||
@@ -117,10 +139,65 @@ public:
|
|||||||
entry->proxy = proxy_ptr;
|
entry->proxy = proxy_ptr;
|
||||||
map_.emplace(k, std::move(entry_ptr));
|
map_.emplace(k, std::move(entry_ptr));
|
||||||
|
|
||||||
|
bus_.publish<object_cache_proxy_added_event>({k.type, id, std::chrono::steady_clock::now()});
|
||||||
|
|
||||||
// return the shared_ptr of the proxy
|
// return the shared_ptr of the proxy
|
||||||
return proxy_ptr;
|
return proxy_ptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template<typename Type, typename ResolverPointerType>
|
||||||
|
bool import(const utils::identifier &id, const std::shared_ptr<object_proxy<Type>> &proxy, ResolverPointerType &&resolver_ptr) {
|
||||||
|
if (!proxy) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto obj = proxy->object();
|
||||||
|
auto weak_resolver = to_weak<Type>(std::forward<ResolverPointerType>(resolver_ptr));
|
||||||
|
const auto k = make_key<Type>(id);
|
||||||
|
|
||||||
|
std::unique_lock lock(mutex_);
|
||||||
|
|
||||||
|
auto it = map_.find(k);
|
||||||
|
if (it != map_.end()) {
|
||||||
|
auto *entry = entry_cast_<Type>(it->second.get());
|
||||||
|
|
||||||
|
if (auto existing_proxy = entry->proxy.lock()) {
|
||||||
|
if (existing_proxy != proxy) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
proxy->primary_key(id);
|
||||||
|
proxy->resolver(std::move(weak_resolver));
|
||||||
|
|
||||||
|
if (obj) {
|
||||||
|
entry->entity = obj;
|
||||||
|
proxy->attach(std::move(obj));
|
||||||
|
}
|
||||||
|
|
||||||
|
entry->proxy = proxy;
|
||||||
|
bus_.publish<object_cache_imported_event>({k.type, id, std::chrono::steady_clock::now()});
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto entry_ptr = std::make_unique<cache_entry<Type>>();
|
||||||
|
auto *entry = entry_ptr.get();
|
||||||
|
|
||||||
|
proxy->primary_key(id);
|
||||||
|
proxy->resolver(std::move(weak_resolver));
|
||||||
|
|
||||||
|
if (obj) {
|
||||||
|
entry->entity = obj;
|
||||||
|
proxy->attach(std::move(obj));
|
||||||
|
}
|
||||||
|
|
||||||
|
entry->proxy = proxy;
|
||||||
|
map_.emplace(k, std::move(entry_ptr));
|
||||||
|
|
||||||
|
bus_.publish<object_cache_imported_event>({k.type, id, std::chrono::steady_clock::now()});
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
/**
|
/**
|
||||||
* @brief Verknüpft eine geladene Entity mit einem Cache-Eintrag (Type, id).
|
* @brief Verknüpft eine geladene Entity mit einem Cache-Eintrag (Type, id).
|
||||||
*
|
*
|
||||||
@@ -145,6 +222,7 @@ public:
|
|||||||
auto *entry = entry_ptr.get();
|
auto *entry = entry_ptr.get();
|
||||||
entry->entity = obj;
|
entry->entity = obj;
|
||||||
map_.emplace(k, std::move(entry_ptr));
|
map_.emplace(k, std::move(entry_ptr));
|
||||||
|
bus_.publish<object_cache_entity_attached_event>({k.type, id, std::chrono::steady_clock::now()});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -155,6 +233,8 @@ public:
|
|||||||
proxy->attach(std::move(obj));
|
proxy->attach(std::move(obj));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bus_.publish<object_cache_entity_attached_event>({k.type, id, std::chrono::steady_clock::now()});
|
||||||
|
|
||||||
prune_if_dead(it);
|
prune_if_dead(it);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -179,6 +259,9 @@ public:
|
|||||||
|
|
||||||
auto *entry = entry_cast_<Type>(it->second.get());
|
auto *entry = entry_cast_<Type>(it->second.get());
|
||||||
auto entity_ptr = entry->entity.lock();
|
auto entity_ptr = entry->entity.lock();
|
||||||
|
if (entity_ptr) {
|
||||||
|
bus_.publish<object_cache_accessed_event>({k.type, id, std::chrono::steady_clock::now()});
|
||||||
|
}
|
||||||
prune_if_dead(it);
|
prune_if_dead(it);
|
||||||
return entity_ptr;
|
return entity_ptr;
|
||||||
}
|
}
|
||||||
@@ -204,6 +287,9 @@ public:
|
|||||||
|
|
||||||
auto *entry = entry_cast_<Type>(it->second.get());
|
auto *entry = entry_cast_<Type>(it->second.get());
|
||||||
const bool loaded = !entry->entity.expired();
|
const bool loaded = !entry->entity.expired();
|
||||||
|
if (loaded) {
|
||||||
|
bus_.publish<object_cache_accessed_event>({k.type, id, std::chrono::steady_clock::now()});
|
||||||
|
}
|
||||||
prune_if_dead(it);
|
prune_if_dead(it);
|
||||||
return loaded;
|
return loaded;
|
||||||
}
|
}
|
||||||
@@ -224,6 +310,7 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
map_.erase(it);
|
map_.erase(it);
|
||||||
|
bus_.publish<object_cache_erased_event>({k.type, id, std::chrono::steady_clock::now()});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -246,6 +333,10 @@ public:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (removed > 0) {
|
||||||
|
bus_.publish<object_cache_sweep_event>({removed, std::chrono::steady_clock::now()});
|
||||||
|
}
|
||||||
|
|
||||||
return removed;
|
return removed;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -286,12 +377,10 @@ private:
|
|||||||
}
|
}
|
||||||
|
|
||||||
template<typename T, typename U>
|
template<typename T, typename U>
|
||||||
using enable_if_resolver_shared_ptr_t =
|
using enable_if_resolver_shared_ptr_t = std::enable_if_t<std::is_base_of_v<object_resolver<T>, U>, int>;
|
||||||
std::enable_if_t<std::is_base_of_v<object_resolver<T>, U>, int>;
|
|
||||||
|
|
||||||
template<typename T, typename U>
|
template<typename T, typename U>
|
||||||
using enable_if_resolver_weak_ptr_t =
|
using enable_if_resolver_weak_ptr_t = std::enable_if_t<std::is_base_of_v<object_resolver<T>, U>, int>;
|
||||||
std::enable_if_t<std::is_base_of_v<object_resolver<T>, U>, int>;
|
|
||||||
|
|
||||||
// shared_ptr<Derived> -> weak_ptr<Base>
|
// shared_ptr<Derived> -> weak_ptr<Base>
|
||||||
template<typename T, typename U, enable_if_resolver_shared_ptr_t<T, U> = 0>
|
template<typename T, typename U, enable_if_resolver_shared_ptr_t<T, U> = 0>
|
||||||
@@ -306,8 +395,8 @@ private:
|
|||||||
}
|
}
|
||||||
|
|
||||||
// (2) Opportunistischer Cleanup: entferne Entry, wenn beide weak_ptr abgelaufen sind.
|
// (2) Opportunistischer Cleanup: entferne Entry, wenn beide weak_ptr abgelaufen sind.
|
||||||
template<typename It>
|
template<typename IteratorType>
|
||||||
bool prune_if_dead(It &it) {
|
bool prune_if_dead(IteratorType &it) {
|
||||||
if (it == map_.end()) {
|
if (it == map_.end()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -319,14 +408,15 @@ private:
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename T>
|
template<typename Type>
|
||||||
void prune_if_dead_typed(typename std::unordered_map<key, std::unique_ptr<cache_entry_base>, key_hash>::iterator &it) {
|
void prune_if_dead_typed(typename std::unordered_map<key, std::unique_ptr<cache_entry_base>, key_hash>::iterator &it) {
|
||||||
(void)T{}; // T bleibt im Interface, damit bestehende Call-Sites unverändert bleiben können.
|
(void)Type{}; // T bleibt im Interface, damit bestehende Call-Sites unverändert bleiben können.
|
||||||
prune_if_dead(it);
|
prune_if_dead(it);
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
mutable std::shared_mutex mutex_{};
|
mutable std::shared_mutex mutex_{};
|
||||||
|
utils::message_bus &bus_;
|
||||||
std::unordered_map<key, std::unique_ptr<cache_entry_base>, key_hash> map_;
|
std::unordered_map<key, std::unique_ptr<cache_entry_base>, key_hash> map_;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -25,17 +25,17 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
template<typename ValueType>
|
template<typename ValueType>
|
||||||
void on_primary_key(const char *, ValueType &/*pk*/, const utils::primary_key_attribute& attr = utils::default_pk_attributes) {
|
void on_primary_key(const char *, ValueType &/*pk*/, const utils::primary_key_attribute& attr) {
|
||||||
type_ = utils::data_type_traits<ValueType>::type(attr.size());
|
type_ = utils::data_type_traits<ValueType>::type(attr.size());
|
||||||
}
|
}
|
||||||
static void on_revision(const char * /*id*/, uint64_t &/*rev*/) {}
|
static void on_revision(const char * /*id*/, uint64_t &/*rev*/) {}
|
||||||
template < class Type >
|
template < class Type >
|
||||||
static void on_attribute(const char * /*id*/, Type &/*x*/, const utils::field_attributes &/*attr*/ = utils::null_attributes) {}
|
static void on_attribute(const char * /*id*/, Type &/*x*/, const utils::field_attributes &/*attr*/) {}
|
||||||
static void on_attribute(const char * /*id*/, char * /*x*/, const utils::field_attributes &/*attr*/ = utils::null_attributes) {}
|
static void on_attribute(const char * /*id*/, char * /*x*/, const utils::field_attributes &/*attr*/) {}
|
||||||
template<class Pointer>
|
template<class Pointer>
|
||||||
static void on_belongs_to(const char * /*id*/, Pointer &/*x*/, const utils::foreign_attributes &/*attr*/) {}
|
static void on_belongs_to(const char * /*id*/, Pointer &/*x*/, const utils::foreign_attributes &/*attr*/) {}
|
||||||
template<class Pointer>
|
template<class Pointer>
|
||||||
static void on_has_one(const char * /*id*/, Pointer &/*x*/, const utils::foreign_attributes &/*attr*/) {}
|
static void on_has_one(const char * /*id*/, Pointer &/*x*/, const char * /*join_column*/, const utils::foreign_attributes &/*attr*/) {}
|
||||||
template<class ContainerType>
|
template<class ContainerType>
|
||||||
static void on_has_many(const char * /*id*/, ContainerType &, const char *, const utils::foreign_attributes &/*attr*/) {}
|
static void on_has_many(const char * /*id*/, ContainerType &, const char *, const utils::foreign_attributes &/*attr*/) {}
|
||||||
template<class ContainerType>
|
template<class ContainerType>
|
||||||
@@ -58,7 +58,11 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
template < class Type >
|
template < class Type >
|
||||||
static std::shared_ptr<object> generate(std::unique_ptr<Type>&& t, basic_repository &repo, const std::string &name) {
|
static std::shared_ptr<object> generate(std::unique_ptr<Type>&& t,
|
||||||
|
basic_repository &repo,
|
||||||
|
const std::string &name,
|
||||||
|
const std::string &join_column = "",
|
||||||
|
const std::string &inverse_join_column = "") {
|
||||||
const std::type_index ti(typeid(Type));
|
const std::type_index ti(typeid(Type));
|
||||||
if (repo.has_object_for_type(ti)) {
|
if (repo.has_object_for_type(ti)) {
|
||||||
auto obj = repo.object_for_type(ti);
|
auto obj = repo.object_for_type(ti);
|
||||||
@@ -71,18 +75,23 @@ public:
|
|||||||
std::ignore = repo.provide_object_in_advance(ti, obj);
|
std::ignore = repo.provide_object_in_advance(ti, obj);
|
||||||
object_generator gen(repo, obj);
|
object_generator gen(repo, obj);
|
||||||
access::process(gen, *t);
|
access::process(gen, *t);
|
||||||
|
if (!join_column.empty() && !inverse_join_column.empty()) {
|
||||||
|
gen.prepare_relation_table(join_column, inverse_join_column);
|
||||||
|
}
|
||||||
return obj;
|
return obj;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template<typename BaseType>
|
||||||
|
static void on_base(const BaseType&) {}
|
||||||
template < class Type >
|
template < class Type >
|
||||||
void on_primary_key(const char *, Type &x, const utils::primary_key_attribute& attr = utils::default_pk_attributes);
|
void on_primary_key(const char *, Type &x, const utils::primary_key_attribute& attr);
|
||||||
void on_revision(const char *id, uint64_t &rev);
|
void on_revision(const char *id, uint64_t &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 &x, const utils::field_attributes &attr);
|
||||||
|
|
||||||
template<typename Type>
|
template<typename Type>
|
||||||
void on_attribute(const char *id, std::optional<Type> &x, const utils::field_attributes &attr = utils::null_attributes);
|
void on_attribute(const char *id, std::optional<Type> &x, const utils::field_attributes &attr);
|
||||||
|
|
||||||
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*/) {
|
||||||
@@ -90,12 +99,13 @@ public:
|
|||||||
create_fk_constraint<typename Pointer::value_type>(id);
|
create_fk_constraint<typename Pointer::value_type>(id);
|
||||||
}
|
}
|
||||||
template<class Pointer>
|
template<class Pointer>
|
||||||
static void on_has_one(const char * /*id*/, Pointer &/*x*/, const utils::foreign_attributes &/*attr*/) {}
|
static void on_has_one(const char * /*id*/, Pointer &/*x*/, const char * /*join_column*/, const utils::foreign_attributes &/*attr*/) {}
|
||||||
|
|
||||||
template <class Pointer>
|
template <class Pointer>
|
||||||
void on_foreign_key(const char *id, Pointer &/*x*/) {
|
void on_foreign_key(const char *id, Pointer &/*x*/) {
|
||||||
const auto type = pk_type_determinator::determine<typename Pointer::value_type>();
|
const auto type = pk_type_determinator::determine<typename Pointer::value_type>();
|
||||||
auto &ref = object_->attributes_.emplace_back(id, type, utils::constraints::ForeignKey, null_option_type::NotNull);
|
auto &ref = object_->attributes_.emplace_back(id, type, utils::constraints::ForeignKey, null_option_type::NotNull);
|
||||||
|
ref.index_ = object_->attributes_.size() - 1;
|
||||||
ref.owner_ = object_;
|
ref.owner_ = object_;
|
||||||
}
|
}
|
||||||
template<class ContainerType>
|
template<class ContainerType>
|
||||||
@@ -109,6 +119,7 @@ private:
|
|||||||
template<typename ValueType>
|
template<typename ValueType>
|
||||||
attribute &emplace_attribute(const char *id, const utils::field_attributes& attr, null_option_type null_option) {
|
attribute &emplace_attribute(const char *id, const utils::field_attributes& attr, null_option_type null_option) {
|
||||||
auto &ref = object_->attributes_.emplace_back(id, utils::data_type_traits<ValueType>::type(attr.size()), attr, null_option);
|
auto &ref = object_->attributes_.emplace_back(id, utils::data_type_traits<ValueType>::type(attr.size()), attr, null_option);
|
||||||
|
ref.index_ = object_->attributes_.size() - 1;
|
||||||
ref.owner_ = object_;
|
ref.owner_ = object_;
|
||||||
return ref;
|
return ref;
|
||||||
}
|
}
|
||||||
@@ -118,9 +129,10 @@ private:
|
|||||||
void create_fk_constraint(const std::string& name) const;
|
void create_fk_constraint(const std::string& name) const;
|
||||||
void create_unique_constraint(const std::string& name) const;
|
void create_unique_constraint(const std::string& name) const;
|
||||||
|
|
||||||
[[nodiscard]] std::list<attribute>::iterator find_attribute_by_name(const std::string &name) const;
|
[[nodiscard]] std::vector<attribute>::iterator find_attribute_by_name(const std::string &name) const;
|
||||||
|
|
||||||
void prepare_primary_key(attribute &ref, utils::identifier &&pk) const;
|
void prepare_primary_key(const attribute &ref, utils::identifier &&pk) const;
|
||||||
|
void prepare_relation_table(const std::string &join_column, const std::string &inverse_join_column) const;
|
||||||
|
|
||||||
template<typename Type>
|
template<typename Type>
|
||||||
[[nodiscard]] std::shared_ptr<object> fk_object() const;
|
[[nodiscard]] std::shared_ptr<object> fk_object() const;
|
||||||
@@ -133,7 +145,11 @@ private:
|
|||||||
|
|
||||||
template<typename ValueType>
|
template<typename ValueType>
|
||||||
void object_generator::on_primary_key(const char *id, ValueType &x, const utils::primary_key_attribute& attr) {
|
void object_generator::on_primary_key(const char *id, ValueType &x, const utils::primary_key_attribute& attr) {
|
||||||
auto &ref = emplace_attribute<ValueType>(id, { attr.size(), utils::constraints::PrimaryKey }, null_option_type::NotNull);
|
auto cs = utils::constraints::PrimaryKey;
|
||||||
|
if (attr.generator() == utils::generator_type::Identity) {
|
||||||
|
cs |= utils::constraints::Identity;
|
||||||
|
}
|
||||||
|
auto &ref = emplace_attribute<ValueType>(id, { attr.size(), cs }, null_option_type::NotNull);
|
||||||
prepare_primary_key(ref, utils::identifier(x));
|
prepare_primary_key(ref, utils::identifier(x));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -154,7 +170,7 @@ void object_generator::create_fk_constraint(const std::string& name) const {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const auto obj = fk_object<Type>();
|
const auto obj = fk_object<Type>();
|
||||||
restriction pk_constraint(*pk_attr);
|
restriction pk_constraint(pk_attr->index());
|
||||||
pk_constraint.options_ |= utils::constraints::ForeignKey;
|
pk_constraint.options_ |= utils::constraints::ForeignKey;
|
||||||
pk_constraint.owner_ = object_;
|
pk_constraint.owner_ = object_;
|
||||||
pk_constraint.reference_ = obj;
|
pk_constraint.reference_ = obj;
|
||||||
|
|||||||
@@ -26,86 +26,164 @@ public:
|
|||||||
|
|
||||||
// Lazy
|
// Lazy
|
||||||
object_proxy(std::weak_ptr<object_resolver<Type>> resolver, utils::identifier id)
|
object_proxy(std::weak_ptr<object_resolver<Type>> resolver, utils::identifier id)
|
||||||
: resolver_(resolver)
|
: resolver_(std::move(resolver))
|
||||||
, pk_(std::move(id)) {
|
, pk_(std::move(id))
|
||||||
|
, state_(object_state::Persistent) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Eager
|
// Eager
|
||||||
object_proxy(std::weak_ptr<object_resolver<Type>> resolver, std::shared_ptr<Type> obj)
|
object_proxy(std::weak_ptr<object_resolver<Type>> resolver, std::shared_ptr<Type> obj)
|
||||||
: obj_(obj)
|
: obj_(std::move(obj))
|
||||||
, resolver_(resolver)
|
, resolver_(std::move(resolver))
|
||||||
, pk_(primary_key_resolver::resolve_object(*obj).pk)
|
, pk_(obj_ ? primary_key_resolver::resolve_object(*obj_).pk : utils::identifier{})
|
||||||
, state_(object_state::Persistent){
|
, state_(obj_ ? object_state::Persistent : object_state::Detached) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Transient
|
// Transient
|
||||||
explicit object_proxy(std::shared_ptr<Type> obj)
|
explicit object_proxy(std::shared_ptr<Type> obj)
|
||||||
: obj_(obj)
|
: obj_(std::move(obj))
|
||||||
, pk_(primary_key_resolver::resolve_object(*obj).pk) {
|
, pk_(obj_ ? primary_key_resolver::resolve_object(*obj_).pk : utils::identifier{}) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void attach(std::shared_ptr<Type> obj) {
|
void attach(std::shared_ptr<Type> obj) {
|
||||||
std::lock_guard lock(mutex_);
|
std::lock_guard lock(mutex_);
|
||||||
|
|
||||||
obj_ = std::move(obj);
|
obj_ = std::move(obj);
|
||||||
if (obj_) {
|
if (!obj_) {
|
||||||
pk_ = primary_key_resolver::resolve_object(*obj_).pk;
|
pk_.clear();
|
||||||
state_.store(object_state::Persistent, std::memory_order_release);
|
state_ = object_state::Detached;
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pk_ = primary_key_resolver::resolve_object(*obj_).pk;
|
||||||
|
state_ = object_state::Persistent;
|
||||||
|
}
|
||||||
|
|
||||||
|
void resolver(std::weak_ptr<object_resolver<Type>> resolver) {
|
||||||
|
std::lock_guard lock(mutex_);
|
||||||
|
resolver_ = std::move(resolver);
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] std::shared_ptr<Type> object() const {
|
||||||
|
return resolve_object();
|
||||||
}
|
}
|
||||||
|
|
||||||
void invalidate() {
|
void invalidate() {
|
||||||
std::lock_guard lock(mutex_);
|
std::lock_guard lock(mutex_);
|
||||||
obj_.reset();
|
obj_.reset();
|
||||||
resolver_.reset();
|
resolver_.reset();
|
||||||
state_.store(object_state::Detached, std::memory_order_release);
|
state_ = object_state::Detached;
|
||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard]] void *raw_pointer() const { return static_cast<void *>(pointer()); }
|
[[nodiscard]] void *raw_pointer() const { return static_cast<void *>(pointer()); }
|
||||||
|
|
||||||
Type *operator->() { return pointer(); }
|
Type *operator->() {
|
||||||
Type &operator*() { return *pointer(); }
|
auto *ptr = pointer();
|
||||||
const Type &operator*() const { return *pointer(); }
|
if (!ptr) {
|
||||||
|
throw std::runtime_error("Cannot dereference empty object proxy");
|
||||||
|
}
|
||||||
|
return ptr;
|
||||||
|
}
|
||||||
|
|
||||||
Type *pointer() const { return resolve(); }
|
const Type *operator->() const {
|
||||||
|
auto *ptr = pointer();
|
||||||
|
if (!ptr) {
|
||||||
|
throw std::runtime_error("Cannot dereference empty object proxy");
|
||||||
|
}
|
||||||
|
return ptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
Type &operator*() {
|
||||||
|
auto *ptr = pointer();
|
||||||
|
if (!ptr) {
|
||||||
|
throw std::runtime_error("Cannot dereference empty object proxy");
|
||||||
|
}
|
||||||
|
return *ptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
const Type &operator*() const {
|
||||||
|
auto *ptr = pointer();
|
||||||
|
if (!ptr) {
|
||||||
|
throw std::runtime_error("Cannot dereference empty object proxy");
|
||||||
|
}
|
||||||
|
return *ptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
Type *pointer() const {
|
||||||
|
return resolve_object().get();
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] bool empty() const {
|
||||||
|
std::lock_guard lock(mutex_);
|
||||||
|
return !obj_ && resolver_.expired();
|
||||||
|
}
|
||||||
|
|
||||||
[[nodiscard]] bool empty() const { return !obj_ && resolver_.expired(); }
|
|
||||||
[[nodiscard]] bool valid() const { return !empty(); }
|
[[nodiscard]] bool valid() const { return !empty(); }
|
||||||
[[nodiscard]] bool has_primary_key() const { return !pk_.is_null(); }
|
|
||||||
[[nodiscard]] const utils::identifier &primary_key() const { return pk_; }
|
|
||||||
void primary_key(const utils::identifier &pk) { pk_ = pk; }
|
|
||||||
|
|
||||||
bool is_persistent() const { return state_ == object_state::Persistent; }
|
[[nodiscard]] bool has_primary_key() const {
|
||||||
bool is_transient() const { return state_ == object_state::Transient; }
|
std::lock_guard lock(mutex_);
|
||||||
bool is_detached() const { return state_ == object_state::Detached; }
|
return !pk_.is_null();
|
||||||
bool is_removed() const { return state_ == object_state::Removed; }
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] utils::identifier primary_key() const {
|
||||||
|
std::lock_guard lock(mutex_);
|
||||||
|
return pk_;
|
||||||
|
}
|
||||||
|
|
||||||
|
void primary_key(const utils::identifier &pk) {
|
||||||
|
std::lock_guard lock(mutex_);
|
||||||
|
pk_ = pk;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool is_persistent() const { return is_state(object_state::Persistent); }
|
||||||
|
bool is_transient() const { return is_state(object_state::Transient); }
|
||||||
|
bool is_detached() const { return is_state(object_state::Detached); }
|
||||||
|
bool is_removed() const { return is_state(object_state::Removed); }
|
||||||
|
|
||||||
|
bool is_state(const object_state state) const {
|
||||||
|
std::lock_guard lock(mutex_);
|
||||||
|
return state_ == state;
|
||||||
|
}
|
||||||
|
|
||||||
void change_state(const object_state state) {
|
void change_state(const object_state state) {
|
||||||
state_.store(state, std::memory_order_release);
|
|
||||||
}
|
|
||||||
private:
|
|
||||||
Type* resolve() const {
|
|
||||||
if (obj_) {
|
|
||||||
return obj_.get();
|
|
||||||
}
|
|
||||||
|
|
||||||
std::lock_guard lock(mutex_);
|
std::lock_guard lock(mutex_);
|
||||||
auto resolver = resolver_.lock();
|
state_ = state;
|
||||||
if (!resolver) {
|
}
|
||||||
return nullptr;
|
private:
|
||||||
// Todo: Add states (Detached, Attached, Transient) - if attached an no resolver is available throw runtime exception
|
std::shared_ptr<Type> resolve_object() const {
|
||||||
// throw std::runtime_error("Detached proxy (session expired)");
|
std::shared_ptr<Type> current;
|
||||||
|
std::shared_ptr<object_resolver<Type>> resolver;
|
||||||
|
utils::identifier pk;
|
||||||
|
|
||||||
|
{
|
||||||
|
std::lock_guard lock(mutex_);
|
||||||
|
if (obj_) {
|
||||||
|
return obj_;
|
||||||
|
}
|
||||||
|
|
||||||
|
resolver = resolver_.lock();
|
||||||
|
if (!resolver) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
pk = pk_;
|
||||||
}
|
}
|
||||||
|
|
||||||
const_cast<std::shared_ptr<Type>&>(obj_) = resolver->resolve(pk_);
|
current = resolver->resolve(pk);
|
||||||
|
|
||||||
return obj_.get();
|
{
|
||||||
|
std::lock_guard lock(mutex_);
|
||||||
|
if (!obj_) {
|
||||||
|
obj_ = std::move(current);
|
||||||
|
}
|
||||||
|
return obj_;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::shared_ptr<Type> obj_{};
|
mutable std::shared_ptr<Type> obj_{};
|
||||||
std::weak_ptr<object_resolver<Type>> resolver_{};
|
mutable std::weak_ptr<object_resolver<Type>> resolver_{};
|
||||||
utils::identifier pk_{};
|
utils::identifier pk_{};
|
||||||
std::atomic<object_state> state_{object_state::Transient};
|
object_state state_{object_state::Transient};
|
||||||
mutable std::mutex mutex_{};
|
mutable std::mutex mutex_{};
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,58 +8,161 @@
|
|||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
||||||
namespace matador::object {
|
namespace matador::object {
|
||||||
|
struct null_object_ptr_t {
|
||||||
|
constexpr null_object_ptr_t() = default;
|
||||||
|
};
|
||||||
|
|
||||||
|
inline constexpr null_object_ptr_t nullobj{};
|
||||||
|
|
||||||
template <typename Type>
|
template <typename Type>
|
||||||
class object_ptr {
|
class object_ptr {
|
||||||
public:
|
public:
|
||||||
object_ptr()
|
object_ptr()
|
||||||
: proxy_(std::make_shared<object_proxy<Type>>()) {}
|
: proxy_(std::make_shared<object_proxy<Type>>()) {}
|
||||||
|
|
||||||
|
object_ptr(null_object_ptr_t) {}
|
||||||
|
|
||||||
explicit object_ptr(std::shared_ptr<Type> obj)
|
explicit object_ptr(std::shared_ptr<Type> obj)
|
||||||
: proxy_(std::make_shared<object_proxy<Type>>(obj)) {}
|
: proxy_(std::make_shared<object_proxy<Type>>(std::move(obj))) {}
|
||||||
|
|
||||||
explicit object_ptr(std::shared_ptr<object_proxy<Type>> obj)
|
explicit object_ptr(std::shared_ptr<object_proxy<Type>> obj)
|
||||||
: proxy_(std::move(obj)) {}
|
: proxy_(std::move(obj)) {}
|
||||||
|
|
||||||
object_ptr(const object_ptr &other) = default;
|
object_ptr(const object_ptr &other) = default;
|
||||||
object_ptr(object_ptr &&other) noexcept = default;
|
object_ptr(object_ptr &&other) noexcept = default;
|
||||||
object_ptr &operator=(const object_ptr &other) = default;
|
object_ptr& operator=(const object_ptr &other) = default;
|
||||||
object_ptr &operator=(object_ptr &&other) = default;
|
object_ptr& operator=(object_ptr &&other) noexcept = default;
|
||||||
|
|
||||||
|
object_ptr& operator=(null_object_ptr_t) {
|
||||||
|
proxy_.reset();
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
bool operator==(const object_ptr &other) const {
|
bool operator==(const object_ptr &other) const {
|
||||||
return get() == other.get();
|
if (proxy_ == other.proxy_) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!proxy_ || !other.proxy_) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (has_primary_key() && other.has_primary_key()) {
|
||||||
|
return primary_key() == other.primary_key();
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool operator==(null_object_ptr_t) const {
|
||||||
|
return empty();
|
||||||
|
}
|
||||||
|
|
||||||
bool operator!=(const object_ptr &other) const { return !operator==(other); }
|
bool operator!=(const object_ptr &other) const { return !operator==(other); }
|
||||||
|
bool operator!=(null_object_ptr_t) const { return !empty(); }
|
||||||
|
|
||||||
using value_type = Type;
|
using value_type = Type;
|
||||||
|
|
||||||
Type *operator->() const { return get(); }
|
Type *operator->() const {
|
||||||
Type &operator*() { return *get(); }
|
return checked_get();
|
||||||
const Type &operator*() const { return *get(); }
|
}
|
||||||
|
|
||||||
[[nodiscard]] bool empty() const { return get() == nullptr; }
|
Type &operator*() {
|
||||||
|
return *checked_get();
|
||||||
|
}
|
||||||
|
|
||||||
|
const Type &operator*() const {
|
||||||
|
return *checked_get();
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] bool empty() const {
|
||||||
|
return proxy_ == nullptr || proxy_->empty();
|
||||||
|
}
|
||||||
|
|
||||||
Type *get() const {
|
Type *get() const {
|
||||||
return proxy_ ? proxy_->pointer() : nullptr;
|
return proxy_ ? proxy_->pointer() : nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
void reset() { proxy_.reset(); }
|
[[nodiscard]] std::shared_ptr<Type> object() const {
|
||||||
|
return proxy_ ? proxy_->object() : nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
operator bool() const { return valid(); }
|
void reset() {
|
||||||
[[nodiscard]] bool valid() const { return proxy_ != nullptr; }
|
proxy_.reset();
|
||||||
|
}
|
||||||
|
|
||||||
[[nodiscard]] bool has_primary_key() const { return proxy_->has_primary_key(); }
|
void reset(std::shared_ptr<object_proxy<Type>> proxy) {
|
||||||
[[nodiscard]] const utils::identifier &primary_key() const { return proxy_->primary_key(); }
|
proxy_ = std::move(proxy);
|
||||||
void primary_key(const utils::identifier &pk) { proxy_->primary_key(pk); }
|
}
|
||||||
|
|
||||||
[[nodiscard]] bool is_persistent() const { return proxy_->is_persistent(); }
|
[[nodiscard]] std::shared_ptr<object_proxy<Type>> proxy() const {
|
||||||
[[nodiscard]] bool is_transient() const { return !proxy_->is_transient(); }
|
return proxy_;
|
||||||
[[nodiscard]] bool is_detached() const { return !proxy_->is_detached(); }
|
}
|
||||||
[[nodiscard]] bool is_removed() const { return !proxy_->is_removed(); }
|
|
||||||
|
|
||||||
void change_state(object_state s) {
|
explicit operator bool() const {
|
||||||
|
return valid();
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] bool valid() const {
|
||||||
|
return proxy_ != nullptr && !proxy_->empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] bool has_primary_key() const {
|
||||||
|
return proxy_ != nullptr && proxy_->has_primary_key();
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] utils::identifier primary_key() const {
|
||||||
|
return proxy_ ? proxy_->primary_key() : utils::identifier{};
|
||||||
|
}
|
||||||
|
|
||||||
|
void primary_key(const utils::identifier &pk) {
|
||||||
|
ensure_proxy();
|
||||||
|
proxy_->primary_key(pk);
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] bool is_persistent() const {
|
||||||
|
return proxy_ != nullptr && proxy_->is_persistent();
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] bool is_transient() const {
|
||||||
|
return proxy_ != nullptr && proxy_->is_transient();
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] bool is_detached() const {
|
||||||
|
return proxy_ != nullptr && proxy_->is_detached();
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] bool is_removed() const {
|
||||||
|
return proxy_ != nullptr && proxy_->is_removed();
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] bool is_state(const object_state state) const {
|
||||||
|
return proxy_ != nullptr && proxy_->is_state(state);
|
||||||
|
}
|
||||||
|
|
||||||
|
void change_state(object_state s) const {
|
||||||
if (proxy_) {
|
if (proxy_) {
|
||||||
proxy_->change_state(s);
|
proxy_->change_state(s);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::shared_ptr<object_proxy<Type> > proxy_{};
|
Type *checked_get() const {
|
||||||
|
auto *ptr = get();
|
||||||
|
if (!ptr) {
|
||||||
|
throw std::runtime_error("Cannot dereference empty object_ptr");
|
||||||
|
}
|
||||||
|
return ptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ensure_proxy() {
|
||||||
|
if (!proxy_) {
|
||||||
|
proxy_ = std::make_shared<object_proxy<Type>>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::shared_ptr<object_proxy<Type>> proxy_{};
|
||||||
};
|
};
|
||||||
|
|
||||||
template<typename>
|
template<typename>
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
#define MATADOR_OBJECT_LOADER_HPP
|
#define MATADOR_OBJECT_LOADER_HPP
|
||||||
|
|
||||||
#include "matador/object/abstract_type_resolver.hpp"
|
#include "matador/object/abstract_type_resolver.hpp"
|
||||||
|
#include "matador/object/abstract_joined_resolver.hpp"
|
||||||
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
||||||
@@ -17,5 +18,13 @@ public:
|
|||||||
|
|
||||||
virtual std::shared_ptr<Type> resolve(const utils::identifier& id) = 0;
|
virtual std::shared_ptr<Type> resolve(const utils::identifier& id) = 0;
|
||||||
};
|
};
|
||||||
|
template<typename Type>
|
||||||
|
class joined_object_resolver : public abstract_joined_resolver, public object_resolver<Type> {
|
||||||
|
public:
|
||||||
|
joined_object_resolver(const std::type_index& root_type, const std::string& join_column)
|
||||||
|
: abstract_joined_resolver(root_type, typeid(Type), join_column) {}
|
||||||
|
|
||||||
|
std::shared_ptr<Type> resolve(const utils::identifier& id) override = 0;
|
||||||
|
};
|
||||||
}
|
}
|
||||||
#endif //MATADOR_OBJECT_LOADER_HPP
|
#endif //MATADOR_OBJECT_LOADER_HPP
|
||||||
@@ -113,8 +113,8 @@ class null_observer : public observer<Type> {
|
|||||||
public:
|
public:
|
||||||
template < class OtherType >
|
template < class OtherType >
|
||||||
explicit null_observer(const null_observer<OtherType> *) {}
|
explicit null_observer(const null_observer<OtherType> *) {}
|
||||||
void on_attach(repository_node &, Type &) override {}
|
void on_attach(const repository_node &, const Type &) const override {}
|
||||||
void on_detach(repository_node &, Type &) override {}
|
void on_detach(const repository_node &, const Type &) const override {}
|
||||||
void on_insert(Type &) override {}
|
void on_insert(Type &) override {}
|
||||||
void on_update(Type &) override {}
|
void on_update(Type &) override {}
|
||||||
void on_delete(Type &) override {}
|
void on_delete(Type &) override {}
|
||||||
|
|||||||
@@ -34,8 +34,10 @@ struct pk_field_locator {
|
|||||||
explicit pk_field_locator (Type &obj)
|
explicit pk_field_locator (Type &obj)
|
||||||
: base(&obj) {}
|
: base(&obj) {}
|
||||||
|
|
||||||
|
template<typename BaseType>
|
||||||
|
static void on_base(const BaseType&) {}
|
||||||
template <typename PrimaryKeyType>
|
template <typename PrimaryKeyType>
|
||||||
void on_primary_key(const char *, PrimaryKeyType &pk, const utils::primary_key_attribute & = utils::default_pk_attributes) {
|
void on_primary_key(const char *, PrimaryKeyType &pk, const utils::primary_key_attribute & /*attr*/) {
|
||||||
// Offset bestimmen
|
// Offset bestimmen
|
||||||
const auto* obj_bytes = reinterpret_cast<std::uint8_t*>(base);
|
const auto* obj_bytes = reinterpret_cast<std::uint8_t*>(base);
|
||||||
const auto* pk_bytes = reinterpret_cast<std::uint8_t*>(&pk);
|
const auto* pk_bytes = reinterpret_cast<std::uint8_t*>(&pk);
|
||||||
@@ -86,9 +88,11 @@ struct pk_field_locator {
|
|||||||
desc.kind = pk_kind::uuid;
|
desc.kind = pk_kind::uuid;
|
||||||
|
|
||||||
desc.is_known_at = [](void *obj, const std::size_t off) -> bool {
|
desc.is_known_at = [](void *obj, const std::size_t off) -> bool {
|
||||||
auto *p = reinterpret_cast<uuid16 *>(static_cast<std::uint8_t *>(obj) + off);
|
const auto *p = reinterpret_cast<uuid16 *>(static_cast<std::uint8_t *>(obj) + off);
|
||||||
// “unknown” = all zeros
|
// “unknown” = all zeros
|
||||||
for (auto b: *p) { if (b != 0) return true; }
|
for (const auto b: *p) {
|
||||||
|
if (b != 0) return true;
|
||||||
|
}
|
||||||
return false;
|
return false;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -113,11 +117,11 @@ struct pk_field_locator {
|
|||||||
}
|
}
|
||||||
static void on_revision(const char *, std::uint64_t &) {}
|
static void on_revision(const char *, std::uint64_t &) {}
|
||||||
template <typename V>
|
template <typename V>
|
||||||
static void on_attribute(const char *, V &, const utils::field_attributes & = matador::utils::null_attributes) {}
|
static void on_attribute(const char *, V &, const utils::field_attributes &/*attr*/) {}
|
||||||
template <typename Pointer>
|
template <typename Pointer>
|
||||||
static void on_belongs_to(const char *, Pointer &, const utils::foreign_attributes &) {}
|
static void on_belongs_to(const char *, Pointer &, const utils::foreign_attributes &) {}
|
||||||
template <typename Pointer>
|
template <typename Pointer>
|
||||||
static void on_has_one(const char *, Pointer &, const utils::foreign_attributes &) {}
|
static void on_has_one(const char *, Pointer &, const char * /*join_column*/, const utils::foreign_attributes &) {}
|
||||||
template <typename Container>
|
template <typename Container>
|
||||||
static void on_has_many(const char *, Container &, const char *, const utils::foreign_attributes &) {}
|
static void on_has_many(const char *, Container &, const char *, const utils::foreign_attributes &) {}
|
||||||
template <typename Container>
|
template <typename Container>
|
||||||
|
|||||||
@@ -43,8 +43,10 @@ public:
|
|||||||
return resolver.resolve(obj);
|
return resolver.resolve(obj);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template<typename BaseType>
|
||||||
|
static void on_base(const BaseType&) {}
|
||||||
template < class Type >
|
template < class Type >
|
||||||
void on_primary_key(const char *id, Type &pk, const utils::primary_key_attribute& attr = utils::default_pk_attributes) {
|
void on_primary_key(const char *id, Type &pk, const utils::primary_key_attribute& attr) {
|
||||||
primary_key_info_.pk_column_name = id;
|
primary_key_info_.pk_column_name = id;
|
||||||
primary_key_info_.type = utils::data_type_traits<Type>::type(attr.size());
|
primary_key_info_.type = utils::data_type_traits<Type>::type(attr.size());
|
||||||
primary_key_info_.pk = pk;
|
primary_key_info_.pk = pk;
|
||||||
@@ -52,11 +54,11 @@ public:
|
|||||||
|
|
||||||
static void on_revision(const char * /*id*/, uint64_t &/*rev*/) {}
|
static void on_revision(const char * /*id*/, uint64_t &/*rev*/) {}
|
||||||
template<typename Type>
|
template<typename Type>
|
||||||
static void on_attribute(const char * /*id*/, Type &/*val*/, const utils::field_attributes &/*attr*/ = utils::null_attributes) {}
|
static void on_attribute(const char * /*id*/, Type &/*val*/, const utils::field_attributes &/*attr*/) {}
|
||||||
template<class Pointer>
|
template<class Pointer>
|
||||||
static void on_belongs_to(const char * /*id*/, Pointer &/*val*/, const utils::foreign_attributes &/*attr*/) {}
|
static void on_belongs_to(const char * /*id*/, Pointer &/*val*/, const utils::foreign_attributes &/*attr*/) {}
|
||||||
template<class Pointer>
|
template<class Pointer>
|
||||||
static void on_has_one(const char * /*id*/, Pointer &/*val*/, const utils::foreign_attributes &/*attr*/) {}
|
static void on_has_one(const char * /*id*/, Pointer &/*val*/, const char * /*join_column*/, const utils::foreign_attributes &/*attr*/) {}
|
||||||
template<class ContainerType>
|
template<class ContainerType>
|
||||||
static void on_has_many(const char * /*id*/, ContainerType &/*col*/, const char *, const utils::foreign_attributes &/*attr*/) {}
|
static void on_has_many(const char * /*id*/, ContainerType &/*col*/, const char *, const utils::foreign_attributes &/*attr*/) {}
|
||||||
template<class ContainerType>
|
template<class ContainerType>
|
||||||
|
|||||||
@@ -28,20 +28,22 @@ public:
|
|||||||
return finder.found_;
|
return finder.found_;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template<typename BaseType>
|
||||||
|
static void on_base(const BaseType&) {}
|
||||||
template<class PrimaryKeyType>
|
template<class PrimaryKeyType>
|
||||||
static void on_primary_key(const char * /*id*/, PrimaryKeyType &/*pk*/, const utils::primary_key_attribute& /*attr*/ = utils::default_pk_attributes) {}
|
static void on_primary_key(const char * /*id*/, PrimaryKeyType &/*pk*/, const utils::primary_key_attribute& /*attr*/) {}
|
||||||
static void on_revision(const char * /*id*/, uint64_t &/*rev*/) {}
|
static void on_revision(const char * /*id*/, uint64_t &/*rev*/) {}
|
||||||
template<typename AttributeType>
|
template<typename AttributeType>
|
||||||
static void on_attribute(const char * /*id*/, AttributeType &/*val*/, const utils::field_attributes &/*attr*/ = utils::null_attributes) {}
|
static void on_attribute(const char * /*id*/, AttributeType &/*val*/, const utils::field_attributes &/*attr*/) {}
|
||||||
template<typename AttributeType>
|
template<typename AttributeType>
|
||||||
static void on_attribute(const char * /*id*/, std::optional<AttributeType> &/*val*/, const utils::field_attributes &/*attr*/ = utils::null_attributes) {}
|
static void on_attribute(const char * /*id*/, std::optional<AttributeType> &/*val*/, const utils::field_attributes &/*attr*/) {}
|
||||||
|
|
||||||
template<class ForeignPointerType>
|
template<class ForeignPointerType>
|
||||||
void on_belongs_to(const char *id, ForeignPointerType &/*obj*/, const utils::foreign_attributes &/*attr*/) {
|
void on_belongs_to(const char *id, ForeignPointerType &/*obj*/, const utils::foreign_attributes &/*attr*/) {
|
||||||
found_ = requested_join_column_ == id;
|
found_ = requested_join_column_ == id;
|
||||||
}
|
}
|
||||||
template<class ForeignPointerType>
|
template<class ForeignPointerType>
|
||||||
static void on_has_one(const char * /*id*/, ForeignPointerType &/*obj*/, const utils::foreign_attributes &/*attr*/) {}
|
static void on_has_one(const char * /*id*/, ForeignPointerType &/*obj*/, const char * /*join_column*/, const utils::foreign_attributes &/*attr*/) {}
|
||||||
|
|
||||||
template<class CollectionType>
|
template<class CollectionType>
|
||||||
static void on_has_many(const char * /*id*/, CollectionType &, const char * /*join_column*/, const utils::foreign_attributes &/*attr*/) {}
|
static void on_has_many(const char * /*id*/, CollectionType &, const char * /*join_column*/, const utils::foreign_attributes &/*attr*/) {}
|
||||||
@@ -102,18 +104,20 @@ public:
|
|||||||
completer.complete_node_relations(node);
|
completer.complete_node_relations(node);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template<typename BaseType>
|
||||||
|
static void on_base(const BaseType&) {}
|
||||||
template<class PrimaryKeyType>
|
template<class PrimaryKeyType>
|
||||||
static void on_primary_key(const char * /*id*/, PrimaryKeyType &/*pk*/, const utils::primary_key_attribute& /*attr*/ = utils::default_pk_attributes) {}
|
static void on_primary_key(const char * /*id*/, PrimaryKeyType &/*pk*/, const utils::primary_key_attribute& /*attr*/) {}
|
||||||
static void on_revision(const char * /*id*/, uint64_t &/*rev*/) {}
|
static void on_revision(const char * /*id*/, uint64_t &/*rev*/) {}
|
||||||
template<typename AttributeType>
|
template<typename AttributeType>
|
||||||
static void on_attribute(const char * /*id*/, AttributeType &/*val*/, const utils::field_attributes &/*attr*/ = utils::null_attributes) {}
|
static void on_attribute(const char * /*id*/, AttributeType &/*val*/, const utils::field_attributes &/*attr*/) {}
|
||||||
template<typename AttributeType>
|
template<typename AttributeType>
|
||||||
static void on_attribute(const char * /*id*/, std::optional<AttributeType> &/*val*/, const utils::field_attributes &/*attr*/ = utils::null_attributes) {}
|
static void on_attribute(const char * /*id*/, std::optional<AttributeType> &/*val*/, const utils::field_attributes &/*attr*/) {}
|
||||||
|
|
||||||
template<class ForeignPointerType>
|
template<class ForeignPointerType>
|
||||||
void on_belongs_to(const char *id, ForeignPointerType &obj, const utils::foreign_attributes &attr);
|
void on_belongs_to(const char *id, ForeignPointerType &obj, const utils::foreign_attributes &attr);
|
||||||
template<class ForeignPointerType>
|
template<class ForeignPointerType>
|
||||||
void on_has_one(const char * /*id*/, ForeignPointerType &/*obj*/, const utils::foreign_attributes &/*attr*/);
|
void on_has_one(const char * /*id*/, ForeignPointerType &/*obj*/, const char * /*join_column*/, const utils::foreign_attributes &/*attr*/);
|
||||||
|
|
||||||
template<class CollectionType>
|
template<class CollectionType>
|
||||||
void on_has_many(const char *id, CollectionType &, const char *join_column, const utils::foreign_attributes &attr, std::enable_if_t<is_object_ptr<typename CollectionType::value_type>::value> * = nullptr);
|
void on_has_many(const char *id, CollectionType &, const char *join_column, const utils::foreign_attributes &attr, std::enable_if_t<is_object_ptr<typename CollectionType::value_type>::value> * = nullptr);
|
||||||
@@ -168,7 +172,7 @@ void relation_completer<Type, Observers...>::on_has_many(const char *id, Collect
|
|||||||
using relation_value_type = many_to_many_relation<Type, value_type>;
|
using relation_value_type = many_to_many_relation<Type, value_type>;
|
||||||
|
|
||||||
// Check if the object_ptr type is already inserted in the schema (by id)
|
// Check if the object_ptr type is already inserted in the schema (by id)
|
||||||
auto foreign_node = find_node(typeid(value_type));
|
const auto foreign_node = find_node(typeid(value_type));
|
||||||
if (!foreign_node) {
|
if (!foreign_node) {
|
||||||
// Todo: throw internal error or attach node
|
// Todo: throw internal error or attach node
|
||||||
return;
|
return;
|
||||||
@@ -187,7 +191,7 @@ void relation_completer<Type, Observers...>::on_has_many(const char *id, Collect
|
|||||||
const auto local_endpoint = std::make_shared<relation_endpoint>(id, relation_type::HasMany, *foreign_node);
|
const auto local_endpoint = std::make_shared<relation_endpoint>(id, relation_type::HasMany, *foreign_node);
|
||||||
nodes_.top()->info_->register_relation_endpoint(typeid(value_type), local_endpoint);
|
nodes_.top()->info_->register_relation_endpoint(typeid(value_type), local_endpoint);
|
||||||
} else {
|
} else {
|
||||||
// A relation table is necessary
|
// A relation table is necessary.
|
||||||
// Endpoint was not found.
|
// Endpoint was not found.
|
||||||
// Always attach a many-to-many relation type. If later a
|
// Always attach a many-to-many relation type. If later a
|
||||||
// belongs-to relation handles this relation, the many-to-many
|
// belongs-to relation handles this relation, the many-to-many
|
||||||
@@ -218,14 +222,15 @@ template<typename Type, template<typename> typename... Observers>
|
|||||||
template<class CollectionType>
|
template<class CollectionType>
|
||||||
void relation_completer<Type, Observers...>::on_has_many(const char *id, CollectionType &, const char *join_column,
|
void relation_completer<Type, Observers...>::on_has_many(const char *id, CollectionType &, const char *join_column,
|
||||||
const utils::foreign_attributes &,
|
const utils::foreign_attributes &,
|
||||||
std::enable_if_t<!is_object_ptr<typename CollectionType::value_type>::value>
|
std::enable_if_t<!is_object_ptr<typename CollectionType::value_type>::value>* /*unused*/) {
|
||||||
* /*unused*/) {
|
|
||||||
using value_type = typename CollectionType::value_type;
|
using value_type = typename CollectionType::value_type;
|
||||||
using relation_value_type = many_to_relation<Type, value_type>;
|
using relation_value_type = many_to_relation<Type, value_type>;
|
||||||
|
|
||||||
|
auto observers = internal::observer_list_copy_creator<Type, relation_value_type, Observers...>::copy_create(observers_);
|
||||||
|
|
||||||
auto node = repository_node::make_node<relation_value_type>(repo_, id, [join_column] {
|
auto node = repository_node::make_node<relation_value_type>(repo_, id, [join_column] {
|
||||||
return std::make_unique<relation_value_type>(join_column, "value");
|
return std::make_unique<relation_value_type>(join_column, "value");
|
||||||
}, {});
|
}, std::move(observers));
|
||||||
const auto result = repo_.attach_node(node.release(), "");
|
const auto result = repo_.attach_node(node.release(), "");
|
||||||
if (!result) {
|
if (!result) {
|
||||||
// Todo: throw internal exception
|
// Todo: throw internal exception
|
||||||
@@ -274,6 +279,7 @@ template<typename Type, template<typename> typename... Observers>
|
|||||||
template<class ForeignPointerType>
|
template<class ForeignPointerType>
|
||||||
void relation_completer<Type, Observers...>::on_has_one(const char *id,
|
void relation_completer<Type, Observers...>::on_has_one(const char *id,
|
||||||
ForeignPointerType &/*obj*/,
|
ForeignPointerType &/*obj*/,
|
||||||
|
const char * /*join_column*/,
|
||||||
const utils::foreign_attributes &/*attr*/) {
|
const utils::foreign_attributes &/*attr*/) {
|
||||||
using value_type = typename ForeignPointerType::value_type;
|
using value_type = typename ForeignPointerType::value_type;
|
||||||
const auto foreign_node = find_node(typeid(value_type));
|
const auto foreign_node = find_node(typeid(value_type));
|
||||||
@@ -360,7 +366,12 @@ void relation_completer<Type, Observers...>::attach_relation_node(const std::str
|
|||||||
|
|
||||||
auto observers = internal::observer_list_copy_creator<Type, relation_value_type, Observers...>::copy_create(observers_);
|
auto observers = internal::observer_list_copy_creator<Type, relation_value_type, Observers...>::copy_create(observers_);
|
||||||
|
|
||||||
auto node = repository_node::make_node<relation_value_type>(repo_, name, std::move(creator), std::move(observers));
|
auto node = repository_node::make_relation_node<relation_value_type>(repo_,
|
||||||
|
name,
|
||||||
|
join_column,
|
||||||
|
inverse_join_column,
|
||||||
|
std::move(creator),
|
||||||
|
std::move(observers));
|
||||||
auto result = repo_.attach_node(node.release(), "");
|
auto result = repo_.attach_node(node.release(), "");
|
||||||
if (!result) {
|
if (!result) {
|
||||||
// Todo: throw internal error
|
// Todo: throw internal error
|
||||||
|
|||||||
@@ -23,6 +23,13 @@ public:
|
|||||||
const std::string& name,
|
const std::string& name,
|
||||||
creator_func<Type> creator,
|
creator_func<Type> creator,
|
||||||
std::vector<std::unique_ptr<observer<Type>>>&& observers);
|
std::vector<std::unique_ptr<observer<Type>>>&& observers);
|
||||||
|
template < typename Type, template<typename> typename... Observers >
|
||||||
|
static std::unique_ptr<repository_node> make_relation_node(basic_repository& repo,
|
||||||
|
const std::string& name,
|
||||||
|
const std::string& join_column,
|
||||||
|
const std::string& inverse_join_column,
|
||||||
|
creator_func<Type> creator,
|
||||||
|
std::vector<std::unique_ptr<observer<Type>>>&& observers);
|
||||||
|
|
||||||
explicit repository_node(basic_repository& repo);
|
explicit repository_node(basic_repository& repo);
|
||||||
repository_node(const repository_node& other) = delete;
|
repository_node(const repository_node& other) = delete;
|
||||||
@@ -101,5 +108,28 @@ std::unique_ptr<repository_node> repository_node::make_node(basic_repository &re
|
|||||||
|
|
||||||
return node;
|
return node;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template<typename Type, template <typename> class ... Observers>
|
||||||
|
std::unique_ptr<repository_node> repository_node::make_relation_node(basic_repository &repo,
|
||||||
|
const std::string &name,
|
||||||
|
const std::string &join_column,
|
||||||
|
const std::string &inverse_join_column,
|
||||||
|
creator_func<Type> creator,
|
||||||
|
std::vector<std::unique_ptr<observer<Type>>> &&observers) {
|
||||||
|
const std::type_index ti(typeid(Type));
|
||||||
|
auto node = std::unique_ptr<repository_node>(new repository_node(repo, name, ti));
|
||||||
|
|
||||||
|
internal::observer_list_creator<Type, Observers...>::create_missing(observers);
|
||||||
|
|
||||||
|
auto obj = object_generator::generate<Type>(creator(), repo, name, join_column, inverse_join_column);
|
||||||
|
node->info_.reset(std::make_unique<object_info<Type>>(
|
||||||
|
*node,
|
||||||
|
obj,
|
||||||
|
std::move(observers),
|
||||||
|
std::forward<creator_func<Type>>(creator)
|
||||||
|
).release());
|
||||||
|
|
||||||
|
return node;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
#endif //REPOSITORY_NODE_HPP
|
#endif //REPOSITORY_NODE_HPP
|
||||||
|
|||||||
@@ -17,17 +17,17 @@ class object;
|
|||||||
|
|
||||||
class restriction {
|
class restriction {
|
||||||
public:
|
public:
|
||||||
explicit restriction(const class attribute& attr);
|
explicit restriction(size_t attr_index);
|
||||||
|
|
||||||
[[nodiscard]] const class attribute& attribute() const;
|
[[nodiscard]] size_t attribute_index() const;
|
||||||
[[nodiscard]] std::string column_name() const;
|
[[nodiscard]] std::string column_name() const;
|
||||||
|
[[nodiscard]] utils::constraints options() const;
|
||||||
[[nodiscard]] std::shared_ptr<object> owner() const;
|
[[nodiscard]] std::shared_ptr<object> owner() const;
|
||||||
[[nodiscard]] bool is_primary_key_constraint() const;
|
[[nodiscard]] bool is_primary_key_constraint() const;
|
||||||
[[nodiscard]] bool is_foreign_key_constraint() const;
|
[[nodiscard]] bool is_foreign_key_constraint() const;
|
||||||
[[nodiscard]] bool is_unique_constraint() const;
|
[[nodiscard]] bool is_unique_constraint() const;
|
||||||
[[nodiscard]] std::string ref_table_name() const;
|
[[nodiscard]] std::string ref_table_name() const;
|
||||||
[[nodiscard]] std::string ref_column_name() const;
|
[[nodiscard]] std::string ref_column_name() const;
|
||||||
|
|
||||||
friend std::ostream& operator<<(std::ostream& os, const restriction& c);
|
friend std::ostream& operator<<(std::ostream& os, const restriction& c);
|
||||||
|
|
||||||
[[nodiscard]] std::string type_string() const;
|
[[nodiscard]] std::string type_string() const;
|
||||||
@@ -37,7 +37,7 @@ private:
|
|||||||
friend class object_generator;
|
friend class object_generator;
|
||||||
friend class object;
|
friend class object;
|
||||||
|
|
||||||
const class attribute& attr_;
|
const size_t index_;
|
||||||
std::weak_ptr<object> owner_;
|
std::weak_ptr<object> owner_;
|
||||||
std::weak_ptr<object> reference_;
|
std::weak_ptr<object> reference_;
|
||||||
utils::constraints options_{utils::constraints::None};
|
utils::constraints options_{utils::constraints::None};
|
||||||
|
|||||||
@@ -1,35 +0,0 @@
|
|||||||
#ifndef ERROR_CODE_HPP
|
|
||||||
#define ERROR_CODE_HPP
|
|
||||||
|
|
||||||
#include <cstdint>
|
|
||||||
#include <system_error>
|
|
||||||
|
|
||||||
namespace matador::orm {
|
|
||||||
|
|
||||||
enum class error_code : uint8_t {
|
|
||||||
Ok = 0,
|
|
||||||
NoConnectionAvailable,
|
|
||||||
UnknownType,
|
|
||||||
NoPrimaryKey,
|
|
||||||
FailedToBuildQuery,
|
|
||||||
FailedToFindObject,
|
|
||||||
Failed
|
|
||||||
};
|
|
||||||
|
|
||||||
class orm_category_impl final : public std::error_category
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
[[nodiscard]] const char* name() const noexcept override;
|
|
||||||
[[nodiscard]] std::string message(int ev) const override;
|
|
||||||
};
|
|
||||||
|
|
||||||
const std::error_category& orm_category();
|
|
||||||
std::error_code make_error_code(error_code e);
|
|
||||||
std::error_condition make_error_condition(error_code e);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
template <>
|
|
||||||
struct std::is_error_code_enum<matador::orm::error_code> : true_type {};
|
|
||||||
|
|
||||||
#endif //ERROR_CODE_HPP
|
|
||||||
@@ -1,338 +0,0 @@
|
|||||||
#ifndef QUERY_SESSION_HPP
|
|
||||||
#define QUERY_SESSION_HPP
|
|
||||||
|
|
||||||
#include "matador/orm/error_code.hpp"
|
|
||||||
|
|
||||||
#include "matador/query/select_query_builder.hpp"
|
|
||||||
#include "matador/query/criteria.hpp"
|
|
||||||
#include "matador/query/insert_query_builder.hpp"
|
|
||||||
#include "matador/query/query.hpp"
|
|
||||||
#include "matador/query/generator.hpp"
|
|
||||||
#include "matador/query/schema.hpp"
|
|
||||||
|
|
||||||
#include "matador/sql/connection.hpp"
|
|
||||||
#include "matador/sql/connection_pool.hpp"
|
|
||||||
#include "matador/sql/executor.hpp"
|
|
||||||
#include "matador/sql/resolver_service.hpp"
|
|
||||||
#include "matador/sql/statement.hpp"
|
|
||||||
#include "matador/sql/statement_cache.hpp"
|
|
||||||
|
|
||||||
#include "matador/object/object_ptr.hpp"
|
|
||||||
|
|
||||||
#include <unordered_map>
|
|
||||||
#include <utility>
|
|
||||||
|
|
||||||
namespace matador::orm {
|
|
||||||
utils::error make_error(error_code ec, const std::string &msg);
|
|
||||||
|
|
||||||
struct session_context {
|
|
||||||
session_context(utils::message_bus &bus, std::string dns, const size_t count, const size_t cache_size = 500)
|
|
||||||
: bus(bus)
|
|
||||||
, dns(std::move(dns))
|
|
||||||
, connection_count(count)
|
|
||||||
, cache_size(cache_size) {
|
|
||||||
}
|
|
||||||
utils::message_bus &bus;
|
|
||||||
std::string dns;
|
|
||||||
size_t connection_count{};
|
|
||||||
size_t cache_size{500};
|
|
||||||
std::shared_ptr<sql::resolver_service> resolver_service = std::make_shared<sql::resolver_service>();
|
|
||||||
};
|
|
||||||
|
|
||||||
class session final : public sql::executor {
|
|
||||||
public:
|
|
||||||
session(session_context &&ctx, const query::schema &scm);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Insert the given object into the session.
|
|
||||||
*
|
|
||||||
* @tparam Type Type of object to insert
|
|
||||||
* @param obj Object to insert
|
|
||||||
* @return Inserted object
|
|
||||||
*/
|
|
||||||
template<typename Type>
|
|
||||||
utils::result<object::object_ptr<Type>, utils::error> insert(object::object_ptr<Type> obj);
|
|
||||||
|
|
||||||
template<typename Type>
|
|
||||||
utils::result<object::object_ptr<Type>, utils::error> update(const object::object_ptr<Type> &obj);
|
|
||||||
template<typename Type>
|
|
||||||
utils::result<void, utils::error> remove(const object::object_ptr<Type> &obj);
|
|
||||||
|
|
||||||
template<typename Type, typename PrimaryKeyType>
|
|
||||||
utils::result<object::object_ptr<Type>, utils::error> find(const PrimaryKeyType &pk);
|
|
||||||
template<typename Type>
|
|
||||||
utils::result<sql::query_result<Type>, utils::error> find(query::criteria_ptr clause = {});
|
|
||||||
|
|
||||||
template<typename Type>
|
|
||||||
utils::result<void, utils::error> drop_table() const;
|
|
||||||
utils::result<void, utils::error> drop_table(const std::string &table_name) const;
|
|
||||||
|
|
||||||
[[nodiscard]] utils::result<sql::query_result<sql::record>, utils::error> fetch_all(const sql::query_context &q) const;
|
|
||||||
[[nodiscard]] utils::result<sql::execute_result, utils::error> execute(const std::string &sql) const;
|
|
||||||
|
|
||||||
[[nodiscard]] std::vector<object::attribute> describe_table(const std::string &table_name) const;
|
|
||||||
[[nodiscard]] bool table_exists(const std::string &table_name) const;
|
|
||||||
|
|
||||||
[[nodiscard]] utils::result<std::unique_ptr<sql::query_result_impl>, utils::error> fetch(const sql::query_context &ctx) const override;
|
|
||||||
[[nodiscard]] utils::result<sql::execute_result, utils::error> execute(const sql::query_context &ctx) const override;
|
|
||||||
[[nodiscard]] utils::result<sql::statement, utils::error> prepare(const sql::query_context &ctx) override;
|
|
||||||
[[nodiscard]] std::string str(const sql::query_context &ctx) const override;
|
|
||||||
[[nodiscard]] const sql::dialect &dialect() const override;
|
|
||||||
[[nodiscard]] std::shared_ptr<sql::resolver_service> resolver() const override;
|
|
||||||
[[nodiscard]] const query::basic_schema &schema() const;
|
|
||||||
|
|
||||||
private:
|
|
||||||
sql::connection_pool pool_;
|
|
||||||
mutable sql::statement_cache cache_;
|
|
||||||
const sql::dialect &dialect_;
|
|
||||||
|
|
||||||
const query::basic_schema &schema_;
|
|
||||||
mutable std::unordered_map<std::string, std::vector<object::attribute> > prototypes_;
|
|
||||||
std::shared_ptr<sql::resolver_service> resolver_service_;
|
|
||||||
};
|
|
||||||
|
|
||||||
template<typename Type>
|
|
||||||
utils::result<object::object_ptr<Type>, utils::error> session::insert(object::object_ptr<Type> obj) {
|
|
||||||
if (obj.is_persistent()) {
|
|
||||||
return utils::ok(obj);
|
|
||||||
}
|
|
||||||
|
|
||||||
const auto it = schema_.find(typeid(Type));
|
|
||||||
if (it == schema_.end()) {
|
|
||||||
return utils::failure(make_error(error_code::UnknownType, "Failed to determine requested type."));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Build dependency-ordered insert steps (deps first, root last)
|
|
||||||
query::insert_query_builder iqb(schema_);
|
|
||||||
auto steps = iqb.build(obj);
|
|
||||||
if (!steps.is_ok()) {
|
|
||||||
return utils::failure(make_error(error_code::FailedToBuildQuery, "Failed to build insert dependency queries."));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Execute all steps; for Identity steps read RETURNING and write pk back into the object
|
|
||||||
for (auto &step : *steps) {
|
|
||||||
if (step.pk_is_unset && step.set_pk) {
|
|
||||||
if (step.pk_generator == utils::generator_type::Manually) {
|
|
||||||
if (step.pk_is_unset()) {
|
|
||||||
return utils::failure(make_error(error_code::NoPrimaryKey, "Manual primary key is required but unset."));
|
|
||||||
}
|
|
||||||
} else if (step.pk_generator == utils::generator_type::Sequence) {
|
|
||||||
if (step.pk_is_unset()) {
|
|
||||||
// hard-coded naming as you specified earlier
|
|
||||||
// <table_name>_seq (table name known in schema meta; if you prefer, store it in step too)
|
|
||||||
// For now, we derive from the schema type used for the root insert: simplistic but works if table name == entity name.
|
|
||||||
const std::string seq_name = it->second.name() + "_seq";
|
|
||||||
|
|
||||||
auto id_res = query::query::select().nextval(seq_name).fetch_value<std::uint64_t>(*this);
|
|
||||||
if (!id_res.is_ok() || !id_res.value().has_value()) {
|
|
||||||
return utils::failure(make_error(error_code::FailedToBuildQuery, "Failed to obtain next sequence value."));
|
|
||||||
}
|
|
||||||
step.set_pk(*id_res.value());
|
|
||||||
}
|
|
||||||
} else if (step.pk_generator == utils::generator_type::Table) {
|
|
||||||
if (step.pk_is_unset()) {
|
|
||||||
// TODO: implement table id generation; same idea as above:
|
|
||||||
// UPDATE <table>_tbl_seq ... RETURNING ...
|
|
||||||
return utils::failure(make_error(error_code::Failed, "Table primary key generator not implemented yet."));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// --- Execute step ---
|
|
||||||
if (std::holds_alternative<query::executable_query>(step.query)) {
|
|
||||||
const auto &q = std::get<query::executable_query>(step.query);
|
|
||||||
const auto exec_res = q.execute(*this);
|
|
||||||
if (!exec_res.is_ok()) {
|
|
||||||
return utils::failure(exec_res.err());
|
|
||||||
}
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
const auto &q = std::get<query::fetchable_query>(step.query);
|
|
||||||
auto rec_res = q.fetch_one(*this);
|
|
||||||
if (!rec_res.is_ok()) {
|
|
||||||
return utils::failure(rec_res.err());
|
|
||||||
}
|
|
||||||
if (!rec_res.value().has_value()) {
|
|
||||||
return utils::failure(make_error(error_code::FailedToFindObject, "INSERT ... RETURNING did not return a row."));
|
|
||||||
}
|
|
||||||
if (step.apply_returning) {
|
|
||||||
step.apply_returning(*rec_res.value());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
obj.change_state(object::object_state::Persistent);
|
|
||||||
return utils::ok(obj);
|
|
||||||
}
|
|
||||||
|
|
||||||
class pk_object_binder final {
|
|
||||||
public:
|
|
||||||
explicit pk_object_binder(sql::statement &stmt, const size_t position)
|
|
||||||
: stmt_(stmt)
|
|
||||||
, binding_position_(position) {
|
|
||||||
}
|
|
||||||
|
|
||||||
template<class Type>
|
|
||||||
sql::statement &bind(Type &obj) {
|
|
||||||
access::process(*this, obj);
|
|
||||||
return stmt_;
|
|
||||||
}
|
|
||||||
|
|
||||||
template<class Type>
|
|
||||||
void on_primary_key(const char * /*id*/, Type &x, const utils::primary_key_attribute & /*attr*/ = utils::default_pk_attributes) {
|
|
||||||
stmt_.bind(binding_position_, x);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void on_revision(const char * /*id*/, uint64_t &/*rev*/) {
|
|
||||||
}
|
|
||||||
|
|
||||||
template<class Type>
|
|
||||||
static void on_attribute(const char * /*id*/, Type &/*x*/, const utils::field_attributes &/*attr*/ = utils::null_attributes) {}
|
|
||||||
template<class Pointer>
|
|
||||||
static void on_belongs_to(const char * /*id*/, Pointer &/*x*/, const utils::foreign_attributes &/*attr*/ = utils::CascadeNoneFetchLazy) {}
|
|
||||||
template<class Pointer>
|
|
||||||
static void on_has_one(const char * /*id*/, Pointer &/*x*/, const utils::foreign_attributes &/*attr*/ = utils::CascadeNoneFetchLazy) {}
|
|
||||||
|
|
||||||
template<class ContainerType>
|
|
||||||
static void on_has_many(const char * /*id*/,
|
|
||||||
ContainerType &/*c*/,
|
|
||||||
const char * /*join_column*/,
|
|
||||||
const utils::foreign_attributes &/*attr*/ = utils::CascadeNoneFetchLazy) {}
|
|
||||||
|
|
||||||
template<class ContainerType>
|
|
||||||
static 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>
|
|
||||||
static void on_has_many_to_many(const char * /*id*/,
|
|
||||||
ContainerType &/*c*/,
|
|
||||||
const utils::foreign_attributes &/*attr*/) {}
|
|
||||||
|
|
||||||
private:
|
|
||||||
sql::statement &stmt_;
|
|
||||||
size_t binding_position_{0};
|
|
||||||
sql::object_pk_binder pk_binder_{};
|
|
||||||
};
|
|
||||||
|
|
||||||
template<typename Type>
|
|
||||||
utils::result<object::object_ptr<Type>, utils::error> session::update(const object::object_ptr<Type> &obj) {
|
|
||||||
const auto it = schema_.find(typeid(Type));
|
|
||||||
if (it == schema_.end()) {
|
|
||||||
return utils::failure(make_error(error_code::UnknownType, "Failed to determine requested type."));
|
|
||||||
}
|
|
||||||
using namespace matador::utils;
|
|
||||||
using namespace matador::query;
|
|
||||||
|
|
||||||
const auto col = table_column(it->second.node().info().primary_key_attribute()->name());
|
|
||||||
auto res = matador::query::query::update(it->second.name())
|
|
||||||
.set(generator::column_value_pairs<Type>())
|
|
||||||
.where(col == _)
|
|
||||||
.prepare(*this);
|
|
||||||
if (!res) {
|
|
||||||
return utils::failure(res.err());
|
|
||||||
}
|
|
||||||
|
|
||||||
res->bind(*obj);
|
|
||||||
pk_object_binder binder(res.value(), res->bind_pos());
|
|
||||||
if (const auto update_result = binder.bind(*obj).execute(); !update_result.is_ok()) {
|
|
||||||
return utils::failure(update_result.err());
|
|
||||||
}
|
|
||||||
return utils::ok(object::object_ptr{obj});
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename Type>
|
|
||||||
utils::result<void, utils::error> session::remove(const object::object_ptr<Type> &obj) {
|
|
||||||
const auto it = schema_.find(typeid(Type));
|
|
||||||
if (it == schema_.end()) {
|
|
||||||
return utils::failure(make_error(error_code::UnknownType, "Failed to determine requested type."));
|
|
||||||
}
|
|
||||||
using namespace matador::utils;
|
|
||||||
using namespace matador::query;
|
|
||||||
|
|
||||||
const auto col = table_column(it->second.node().info().primary_key_attribute()->name());
|
|
||||||
auto res = matador::query::query::remove()
|
|
||||||
.from(it->second.name())
|
|
||||||
.where(col == _)
|
|
||||||
.prepare(*this);
|
|
||||||
if (!res) {
|
|
||||||
return utils::failure(res.err());
|
|
||||||
}
|
|
||||||
|
|
||||||
pk_object_binder binder(res.value(), res->bind_pos());
|
|
||||||
if (const auto update_result = binder.bind(*obj).execute(); !update_result.is_ok()) {
|
|
||||||
return utils::failure(update_result.err());
|
|
||||||
}
|
|
||||||
return utils::ok<void>();
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename Type, typename PrimaryKeyType>
|
|
||||||
utils::result<object::object_ptr<Type>, utils::error> session::find(const PrimaryKeyType &pk) {
|
|
||||||
const auto it = schema_.find(typeid(Type));
|
|
||||||
if (it == schema_.end()) {
|
|
||||||
return utils::failure(make_error(error_code::UnknownType, "Failed to determine requested type."));
|
|
||||||
}
|
|
||||||
const auto &info = it->second.node().info();
|
|
||||||
if (!info.has_primary_key()) {
|
|
||||||
return utils::failure(make_error(error_code::NoPrimaryKey, "Type hasn't primary key."));
|
|
||||||
}
|
|
||||||
|
|
||||||
query::select_query_builder eqb(schema_);
|
|
||||||
const query::table_column c(&it->second.table(), info.primary_key_attribute()->name());
|
|
||||||
using namespace matador::query;
|
|
||||||
auto data = eqb.build<Type>(c == utils::_);
|
|
||||||
if (!data.is_ok()) {
|
|
||||||
return utils::failure(make_error(error_code::FailedToBuildQuery,
|
|
||||||
"Failed to build query for type " + info.name() + "."));
|
|
||||||
}
|
|
||||||
|
|
||||||
auto res = data->prepare(*this);
|
|
||||||
|
|
||||||
if (!res) {
|
|
||||||
return utils::failure(res.err());
|
|
||||||
}
|
|
||||||
auto stmt_result = res->bind(0, const_cast<PrimaryKeyType &>(pk))
|
|
||||||
.template fetch_one<Type>();
|
|
||||||
if (!stmt_result) {
|
|
||||||
return utils::failure(make_error(error_code::FailedToFindObject,
|
|
||||||
"Failed to find object of type " + info.name() + " with primary key " +
|
|
||||||
std::to_string(pk) + "."));
|
|
||||||
}
|
|
||||||
return utils::ok(*stmt_result);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename Type>
|
|
||||||
utils::result<sql::query_result<Type>, utils::error> session::find(query::criteria_ptr clause) {
|
|
||||||
const auto it = schema_.find(typeid(Type));
|
|
||||||
if (it == schema_.end()) {
|
|
||||||
return utils::failure(make_error(error_code::UnknownType, "Failed to determine requested type."));
|
|
||||||
}
|
|
||||||
|
|
||||||
query::select_query_builder eqb(schema_);
|
|
||||||
auto data = eqb.build<Type>(std::move(clause));
|
|
||||||
if (!data.is_ok()) {
|
|
||||||
return utils::failure(make_error(error_code::FailedToBuildQuery,
|
|
||||||
"Failed to build query for type " + it->second.name() + "."));
|
|
||||||
}
|
|
||||||
|
|
||||||
auto result = data->prepare(*this);
|
|
||||||
if (!result.is_ok()) {
|
|
||||||
return utils::failure(result.err());
|
|
||||||
}
|
|
||||||
|
|
||||||
return result->template fetch<Type>();
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename Type>
|
|
||||||
utils::result<void, utils::error> session::drop_table() const {
|
|
||||||
const auto it = schema_.find(typeid(Type));
|
|
||||||
if (it == schema_.end()) {
|
|
||||||
return utils::failure(make_error(error_code::UnknownType, "Failed to determine requested type."));
|
|
||||||
}
|
|
||||||
return drop_table(it->second.name());
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
#endif //QUERY_SESSION_HPP
|
|
||||||
@@ -3,6 +3,7 @@
|
|||||||
|
|
||||||
#include "matador/utils/error.hpp"
|
#include "matador/utils/error.hpp"
|
||||||
#include "matador/utils/result.hpp"
|
#include "matador/utils/result.hpp"
|
||||||
|
#include "matador/utils/primary_key_generator_type.hpp"
|
||||||
|
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
|
|
||||||
@@ -14,6 +15,15 @@ class abstract_pk_generator {
|
|||||||
public:
|
public:
|
||||||
virtual ~abstract_pk_generator() = default;
|
virtual ~abstract_pk_generator() = default;
|
||||||
virtual utils::result<int64_t, utils::error> next_id(const sql::executor& exec) = 0;
|
virtual utils::result<int64_t, utils::error> next_id(const sql::executor& exec) = 0;
|
||||||
|
virtual utils::result<int64_t, utils::error> current_id(const sql::executor& exec) = 0;
|
||||||
|
|
||||||
|
[[nodiscard]] utils::generator_type type() const;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
explicit abstract_pk_generator(utils::generator_type type);
|
||||||
|
|
||||||
|
private:
|
||||||
|
utils::generator_type generator_type_{};
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
#endif //MATADOR_ABSTRACT_PK_GENERATOR_HPP
|
#endif //MATADOR_ABSTRACT_PK_GENERATOR_HPP
|
||||||
@@ -3,17 +3,13 @@
|
|||||||
|
|
||||||
#include "matador/utils/attribute_writer.hpp"
|
#include "matador/utils/attribute_writer.hpp"
|
||||||
|
|
||||||
#include <optional>
|
#include "matador/sql/interface/connection_impl.hpp"
|
||||||
|
|
||||||
namespace matador::sql {
|
#include <optional>
|
||||||
class dialect;
|
|
||||||
class connection_impl;
|
|
||||||
}
|
|
||||||
|
|
||||||
namespace matador::query {
|
namespace matador::query {
|
||||||
|
|
||||||
class attribute_string_writer final : public utils::attribute_writer
|
class attribute_string_writer final : public utils::attribute_writer {
|
||||||
{
|
|
||||||
public:
|
public:
|
||||||
attribute_string_writer(const sql::dialect &d, std::optional<std::reference_wrapper<const sql::connection_impl>> conn);
|
attribute_string_writer(const sql::dialect &d, std::optional<std::reference_wrapper<const sql::connection_impl>> conn);
|
||||||
|
|
||||||
|
|||||||
@@ -1,31 +1,36 @@
|
|||||||
#ifndef MATADOR_BASIC_SCHEMA_HPP
|
#ifndef MATADOR_BASIC_SCHEMA_HPP
|
||||||
#define MATADOR_BASIC_SCHEMA_HPP
|
#define MATADOR_BASIC_SCHEMA_HPP
|
||||||
|
|
||||||
|
#include "matador/object/repository.hpp"
|
||||||
|
|
||||||
|
#include "matador/query/abstract_pk_generator.hpp"
|
||||||
|
#include "matador/query/table.hpp"
|
||||||
|
|
||||||
|
#include "matador/sql/internal/joined_collection_resolver_producer.hpp"
|
||||||
|
#include "matador/sql/internal/object_resolver_producer.hpp"
|
||||||
|
#include "matador/sql/producer_object_resolver_factory.hpp"
|
||||||
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <typeindex>
|
#include <typeindex>
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
|
|
||||||
#include "matador/object/repository.hpp"
|
namespace matador::sql {
|
||||||
|
class connection;
|
||||||
#include "matador/query/table.hpp"
|
}
|
||||||
|
|
||||||
#include "matador/sql/internal/collection_resolver_producer.hpp"
|
|
||||||
#include "matador/sql/internal/object_resolver_producer.hpp"
|
|
||||||
#include "matador/sql/producer_resolver_factory.hpp"
|
|
||||||
|
|
||||||
namespace matador::query {
|
namespace matador::query {
|
||||||
class schema_node final {
|
class schema_node final {
|
||||||
public:
|
public:
|
||||||
schema_node(class table tab, utils::generator_type generator_type, const object::repository_node& node);
|
schema_node(class table tab, std::unique_ptr<abstract_pk_generator> &&pk_generator, const object::repository_node& node);
|
||||||
|
|
||||||
[[nodiscard]] const std::string& name() const;
|
[[nodiscard]] const std::string& name() const;
|
||||||
[[nodiscard]] const class table& table() const;
|
[[nodiscard]] const class table& table() const;
|
||||||
[[nodiscard]] utils::generator_type generator_type() const;
|
[[nodiscard]] abstract_pk_generator& pk_generator() const;
|
||||||
[[nodiscard]] const object::repository_node& node() const;
|
[[nodiscard]] const object::repository_node& node() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
class table table_;
|
class table table_;
|
||||||
utils::generator_type generator_type_{};
|
std::unique_ptr<abstract_pk_generator> pk_generator_;
|
||||||
const object::repository_node& node_;
|
const object::repository_node& node_;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -33,23 +38,25 @@ class primary_key_generator_finder final {
|
|||||||
public:
|
public:
|
||||||
template< typename Type >
|
template< typename Type >
|
||||||
utils::generator_type find(const object::object_info<Type>& info) {
|
utils::generator_type find(const object::object_info<Type>& info) {
|
||||||
generator_type_ = utils::generator_type::Manually;
|
generator_type_ = utils::generator_type::Manual;
|
||||||
|
|
||||||
access::process(*this, info.prototype());
|
access::process(*this, info.prototype());
|
||||||
|
|
||||||
return generator_type_;
|
return generator_type_;
|
||||||
}
|
}
|
||||||
|
template<typename BaseType>
|
||||||
|
static void on_base(const BaseType&) {}
|
||||||
template<class V>
|
template<class V>
|
||||||
void on_primary_key(const char * /*id*/, V &/*pk*/, const utils::primary_key_attribute &attr = utils::default_pk_attributes) {
|
void on_primary_key(const char * /*id*/, V &/*pk*/, const utils::primary_key_attribute &attr) {
|
||||||
generator_type_ = attr.generator();
|
generator_type_ = attr.generator();
|
||||||
}
|
}
|
||||||
static void on_revision(const char * /*id*/, uint64_t & /*rev*/) {}
|
static void on_revision(const char * /*id*/, uint64_t & /*rev*/) {}
|
||||||
template<typename T>
|
template<typename T>
|
||||||
static void on_attribute(const char * /*id*/, T &, const utils::field_attributes & = utils::null_attributes) {}
|
static void on_attribute(const char * /*id*/, T &, const utils::field_attributes &/*attr*/) {}
|
||||||
template<class P>
|
template<class P>
|
||||||
static void on_belongs_to(const char * /*id*/, P &, const utils::foreign_attributes & ) {}
|
static void on_belongs_to(const char * /*id*/, P &, const utils::foreign_attributes & ) {}
|
||||||
template<class P>
|
template<class P>
|
||||||
static void on_has_one(const char * /*id*/, P &, const utils::foreign_attributes & ) {}
|
static void on_has_one(const char * /*id*/, P &, const char * /*join_column*/, const utils::foreign_attributes & ) {}
|
||||||
template<class C>
|
template<class C>
|
||||||
static void on_has_many(const char * /*id*/, C &, const char * /*join_column*/, const utils::foreign_attributes & ) {}
|
static void on_has_many(const char * /*id*/, C &, const char * /*join_column*/, const utils::foreign_attributes & ) {}
|
||||||
template<class C>
|
template<class C>
|
||||||
@@ -70,6 +77,14 @@ public:
|
|||||||
basic_schema();
|
basic_schema();
|
||||||
explicit basic_schema(const std::string &name);
|
explicit basic_schema(const std::string &name);
|
||||||
|
|
||||||
|
[[nodiscard]] std::string name() const;
|
||||||
|
|
||||||
|
[[nodiscard]] const object::repository &repo() const;
|
||||||
|
object::repository &repo();
|
||||||
|
|
||||||
|
[[nodiscard]] utils::result<void, utils::error> create(const sql::connection &conn) const;
|
||||||
|
[[nodiscard]] utils::result<void, utils::error> drop(const sql::connection &conn) const;
|
||||||
|
|
||||||
iterator begin();
|
iterator begin();
|
||||||
iterator end();
|
iterator end();
|
||||||
[[nodiscard]] const_iterator begin() const;
|
[[nodiscard]] const_iterator begin() const;
|
||||||
@@ -86,7 +101,9 @@ public:
|
|||||||
|
|
||||||
[[nodiscard]] bool contains(const std::type_index &index) const;
|
[[nodiscard]] bool contains(const std::type_index &index) const;
|
||||||
|
|
||||||
void initialize_executor(sql::executor &exec) const;
|
[[nodiscard]] const std::unordered_map<std::type_index, std::unique_ptr<sql::object_resolver_producer>>& resolver_producers() const;
|
||||||
|
[[nodiscard]] const std::unordered_map<object::collection_composite_key, std::unique_ptr<sql::joined_collection_resolver_producer>, object::collection_composite_key_hash>& collection_resolver_producers() const;
|
||||||
|
[[nodiscard]] const std::unordered_map<object::collection_composite_key, std::unique_ptr<sql::joined_object_resolver_producer>, object::collection_composite_key_hash>& joined_object_resolver_producers() const;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
template<typename Type>
|
template<typename Type>
|
||||||
@@ -96,7 +113,8 @@ protected:
|
|||||||
object::repository repo_;
|
object::repository repo_;
|
||||||
std::unordered_map<std::type_index, schema_node> schema_nodes_;
|
std::unordered_map<std::type_index, schema_node> schema_nodes_;
|
||||||
std::unordered_map<std::type_index, std::unique_ptr<sql::object_resolver_producer>> resolver_producers_;
|
std::unordered_map<std::type_index, std::unique_ptr<sql::object_resolver_producer>> resolver_producers_;
|
||||||
std::unordered_map<object::collection_composite_key, std::unique_ptr<sql::collection_resolver_producer>, object::collection_composite_key_hash> collection_resolver_producers_;
|
std::unordered_map<object::collection_composite_key, std::unique_ptr<sql::joined_collection_resolver_producer>, object::collection_composite_key_hash> collection_resolver_producers_;
|
||||||
|
std::unordered_map<object::collection_composite_key, std::unique_ptr<sql::joined_object_resolver_producer>, object::collection_composite_key_hash> joined_object_resolver_producers_;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
#endif //MATADOR_BASIC_SCHEMA_HPP
|
#endif //MATADOR_BASIC_SCHEMA_HPP
|
||||||
@@ -8,36 +8,39 @@
|
|||||||
#include "matador/utils/basic_types.hpp"
|
#include "matador/utils/basic_types.hpp"
|
||||||
#include "matador/utils/constraints.hpp"
|
#include "matador/utils/constraints.hpp"
|
||||||
#include "matador/utils/data_type_traits.hpp"
|
#include "matador/utils/data_type_traits.hpp"
|
||||||
|
#include "matador/utils/types.hpp"
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
namespace matador::object {
|
namespace matador::object {
|
||||||
|
|
||||||
class data_type {
|
class data_type {
|
||||||
public:
|
public:
|
||||||
explicit data_type(const utils::basic_type type, const size_t size = 0)
|
explicit data_type(const utils::basic_type type, const size_t size = 0)
|
||||||
: type_(type), size_(size) {}
|
: type_(type), size_(size) {
|
||||||
|
}
|
||||||
|
|
||||||
[[nodiscard]] const utils::basic_type& type() const { return type_; }
|
[[nodiscard]] const utils::basic_type &type() const { return type_; }
|
||||||
[[nodiscard]] size_t size() const { return size_; }
|
[[nodiscard]] size_t size() const { return size_; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
utils::basic_type type_{};
|
utils::basic_type type_{};
|
||||||
size_t size_{0};
|
size_t size_{0};
|
||||||
};
|
};
|
||||||
|
|
||||||
template<typename Type>
|
template<typename Type>
|
||||||
class typed_data_type final : public data_type {
|
class typed_data_type final : public data_type {
|
||||||
public:
|
public:
|
||||||
typed_data_type()
|
typed_data_type()
|
||||||
: data_type(utils::data_type_traits<Type>::type()) {}
|
: data_type(utils::data_type_traits<Type>::type()) {
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
template<typename Type>
|
template<typename Type>
|
||||||
class sized_typed_data_type final : public data_type {
|
class sized_typed_data_type final : public data_type {
|
||||||
public:
|
public:
|
||||||
explicit sized_typed_data_type(size_t size)
|
explicit sized_typed_data_type(size_t size)
|
||||||
: data_type(utils::data_type_traits<Type>::type(size), size) {}
|
: data_type(utils::data_type_traits<Type>::type(size), size) {
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
using TinyInt = typed_data_type<int8_t>;
|
using TinyInt = typed_data_type<int8_t>;
|
||||||
@@ -55,11 +58,14 @@ using Double = typed_data_type<double>;
|
|||||||
using Text = typed_data_type<std::string>;
|
using Text = typed_data_type<std::string>;
|
||||||
using Boolean = typed_data_type<bool>;
|
using Boolean = typed_data_type<bool>;
|
||||||
using Varchar = sized_typed_data_type<std::string>;
|
using Varchar = sized_typed_data_type<std::string>;
|
||||||
using Blob = sized_typed_data_type<std::vector<std::byte>>;
|
using Blob = sized_typed_data_type<std::vector<std::byte> >;
|
||||||
|
|
||||||
|
using Time = typed_data_type<utils::time_type_t>;
|
||||||
|
using Date = typed_data_type<utils::date_type_t>;
|
||||||
|
using Timestamp = typed_data_type<utils::timestamp_type_t>;
|
||||||
}
|
}
|
||||||
namespace matador::query {
|
|
||||||
|
|
||||||
|
namespace matador::query {
|
||||||
class column_builder {
|
class column_builder {
|
||||||
public:
|
public:
|
||||||
explicit column_builder(std::string column_name, utils::basic_type type, size_t size = 0);
|
explicit column_builder(std::string column_name, utils::basic_type type, size_t size = 0);
|
||||||
@@ -67,10 +73,10 @@ public:
|
|||||||
// ReSharper disable once CppNonExplicitConversionOperator
|
// ReSharper disable once CppNonExplicitConversionOperator
|
||||||
operator table_column() const; // NOLINT(*-explicit-constructor)
|
operator table_column() const; // NOLINT(*-explicit-constructor)
|
||||||
|
|
||||||
column_builder& not_null();
|
column_builder ¬_null();
|
||||||
column_builder& primary_key();
|
column_builder &primary_key();
|
||||||
column_builder& unique();
|
column_builder &unique();
|
||||||
column_builder& identity();
|
column_builder &identity();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::string column_name_;
|
std::string column_name_;
|
||||||
@@ -87,15 +93,15 @@ public:
|
|||||||
operator table() const; // NOLINT(*-explicit-constructor)
|
operator table() const; // NOLINT(*-explicit-constructor)
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::string table_name;
|
std::string table_name;
|
||||||
};
|
};
|
||||||
|
|
||||||
class constraint_builder {
|
class constraint_builder {
|
||||||
public:
|
public:
|
||||||
constraint_builder& constraint(std::string name);
|
constraint_builder &constraint(std::string name);
|
||||||
constraint_builder& primary_key(std::string name);
|
constraint_builder &primary_key(std::string name);
|
||||||
constraint_builder& foreign_key(std::string name);
|
constraint_builder &foreign_key(std::string name);
|
||||||
constraint_builder& references(std::string table, std::string column);
|
constraint_builder &references(std::string table, std::string column);
|
||||||
|
|
||||||
// ReSharper disable once CppNonExplicitConversionOperator
|
// ReSharper disable once CppNonExplicitConversionOperator
|
||||||
operator table_constraint() const; // NOLINT(*-explicit-constructor)
|
operator table_constraint() const; // NOLINT(*-explicit-constructor)
|
||||||
@@ -111,6 +117,5 @@ private:
|
|||||||
constraint_builder constraint(std::string name);
|
constraint_builder constraint(std::string name);
|
||||||
// table_builder table(std::string name);
|
// table_builder table(std::string name);
|
||||||
column_builder column(std::string name, utils::basic_type type, size_t size = 0);
|
column_builder column(std::string name, utils::basic_type type, size_t size = 0);
|
||||||
|
|
||||||
}
|
}
|
||||||
#endif //MATADOR_BUILDER_HPP
|
#endif //MATADOR_BUILDER_HPP
|
||||||
@@ -8,12 +8,12 @@
|
|||||||
|
|
||||||
namespace matador::query {
|
namespace matador::query {
|
||||||
enum class binary_operator {
|
enum class binary_operator {
|
||||||
EQUALS,
|
Equals,
|
||||||
NOT_EQUALS,
|
NotEquals,
|
||||||
GREATER_THAN,
|
GreaterThan,
|
||||||
GREATER_THAN_OR_EQUAL,
|
GreaterThanOrEqual,
|
||||||
LESS_THAN,
|
LessThan,
|
||||||
LESS_THAN_OR_EQUAL,
|
LessThanOrEqual,
|
||||||
};
|
};
|
||||||
|
|
||||||
class binary_criteria final : public abstract_column_criteria {
|
class binary_criteria final : public abstract_column_criteria {
|
||||||
|
|||||||
@@ -20,33 +20,33 @@ class table_column;
|
|||||||
template<class Type>
|
template<class Type>
|
||||||
std::enable_if_t<!std::is_base_of_v<fetchable_query, std::decay_t<Type>>, criteria_ptr>
|
std::enable_if_t<!std::is_base_of_v<fetchable_query, std::decay_t<Type>>, criteria_ptr>
|
||||||
operator==(const table_column &col, Type val) {
|
operator==(const table_column &col, Type val) {
|
||||||
return std::make_unique<binary_criteria>(col, binary_operator::EQUALS, utils::value(val));
|
return std::make_unique<binary_criteria>(col, binary_operator::Equals, utils::value(val));
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class Type>
|
template<class Type>
|
||||||
std::enable_if_t<!std::is_base_of_v<fetchable_query, std::decay_t<Type>>, criteria_ptr>
|
std::enable_if_t<!std::is_base_of_v<fetchable_query, std::decay_t<Type>>, criteria_ptr>
|
||||||
operator!=(const table_column &col, Type val) {
|
operator!=(const table_column &col, Type val) {
|
||||||
return std::make_unique<binary_criteria>(col, binary_operator::NOT_EQUALS, utils::value(val));
|
return std::make_unique<binary_criteria>(col, binary_operator::NotEquals, utils::value(val));
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class Type>
|
template<class Type>
|
||||||
criteria_ptr operator>(const table_column &col, Type val) {
|
criteria_ptr operator>(const table_column &col, Type val) {
|
||||||
return std::make_unique<binary_criteria>(col, binary_operator::GREATER_THAN, utils::value(val));
|
return std::make_unique<binary_criteria>(col, binary_operator::GreaterThan, utils::value(val));
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class Type>
|
template<class Type>
|
||||||
criteria_ptr operator>=(const table_column &col, Type val) {
|
criteria_ptr operator>=(const table_column &col, Type val) {
|
||||||
return std::make_unique<binary_criteria>(col, binary_operator::GREATER_THAN_OR_EQUAL, utils::value(val));
|
return std::make_unique<binary_criteria>(col, binary_operator::GreaterThanOrEqual, utils::value(val));
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class Type>
|
template<class Type>
|
||||||
criteria_ptr operator<(const table_column &col, Type val) {
|
criteria_ptr operator<(const table_column &col, Type val) {
|
||||||
return std::make_unique<binary_criteria>(col, binary_operator::LESS_THAN, utils::value(val));
|
return std::make_unique<binary_criteria>(col, binary_operator::LessThan, utils::value(val));
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class Type>
|
template<class Type>
|
||||||
criteria_ptr operator<=(const table_column &col, Type val) {
|
criteria_ptr operator<=(const table_column &col, Type val) {
|
||||||
return std::make_unique<binary_criteria>(col, binary_operator::LESS_THAN_OR_EQUAL, utils::value(val));
|
return std::make_unique<binary_criteria>(col, binary_operator::LessThanOrEqual, utils::value(val));
|
||||||
}
|
}
|
||||||
|
|
||||||
criteria_ptr operator==(const table_column &col_left, const table_column &col_right);
|
criteria_ptr operator==(const table_column &col_left, const table_column &col_right);
|
||||||
|
|||||||
@@ -0,0 +1,20 @@
|
|||||||
|
#ifndef MATADOR_DATABASE_HPP
|
||||||
|
#define MATADOR_DATABASE_HPP
|
||||||
|
|
||||||
|
#include "matador/query/schema.hpp"
|
||||||
|
#include "matador/utils/result.hpp"
|
||||||
|
#include "matador/utils/error.hpp"
|
||||||
|
|
||||||
|
namespace matador::query {
|
||||||
|
class database final {
|
||||||
|
public:
|
||||||
|
utils::result<void, utils::error> add(const std::string &name, const schema &schema);
|
||||||
|
utils::result<void, utils::error> remove(const std::string &name);
|
||||||
|
|
||||||
|
[[nodiscard]] utils::result<schema_ref, utils::error> get(const std::string &name);
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::unordered_map<std::string, schema> schema_map_;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
#endif //MATADOR_DATABASE_HPP
|
||||||
@@ -0,0 +1,272 @@
|
|||||||
|
#ifndef MATADOR_DELETE_QUERY_BUILDER_HPP
|
||||||
|
#define MATADOR_DELETE_QUERY_BUILDER_HPP
|
||||||
|
|
||||||
|
#include "matador/object/collection.hpp"
|
||||||
|
#include "matador/object/object_cache.hpp"
|
||||||
|
#include "matador/object/object_ptr.hpp"
|
||||||
|
|
||||||
|
#include "matador/query/basic_schema.hpp"
|
||||||
|
#include "matador/query/error_code.hpp"
|
||||||
|
#include "matador/query/delete_step.hpp"
|
||||||
|
#include "matador/query/query_contexts.hpp"
|
||||||
|
#include "matador/query/query_builder_exception.hpp"
|
||||||
|
#include "matador/query/query_builder_utils.hpp"
|
||||||
|
|
||||||
|
#include "matador/sql/statement.hpp"
|
||||||
|
|
||||||
|
#include "matador/utils/error.hpp"
|
||||||
|
#include "matador/utils/identifier.hpp"
|
||||||
|
#include "matador/utils/primary_key_accessor.hpp"
|
||||||
|
#include "matador/utils/result.hpp"
|
||||||
|
|
||||||
|
namespace matador::query {
|
||||||
|
template<typename ObjectType>
|
||||||
|
class delete_step_processor {
|
||||||
|
public:
|
||||||
|
explicit delete_step_processor(query_builder_context &ctx)
|
||||||
|
: ctx_(ctx) {}
|
||||||
|
|
||||||
|
utils::result<void, utils::error> build(object::object_ptr<ObjectType> ptr, const bool as_relation_step = false) {
|
||||||
|
if (!ptr) {
|
||||||
|
return utils::failure(utils::error{error_code::InvalidObject, "Object is null"});
|
||||||
|
}
|
||||||
|
|
||||||
|
ptr_ = ptr;
|
||||||
|
|
||||||
|
const auto key = make_entity_visit_key<ObjectType>(*ptr_);
|
||||||
|
if (ctx_.visited_.find(key) != ctx_.visited_.end()) {
|
||||||
|
return utils::ok<void>();
|
||||||
|
}
|
||||||
|
ctx_.visited_.insert(key);
|
||||||
|
|
||||||
|
const auto it = ctx_.schema_.find(typeid(ObjectType));
|
||||||
|
if (it == ctx_.schema_.end()) {
|
||||||
|
return utils::failure(utils::error{error_code::UnknownType, "Unknown type"});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (const auto &info = it->second.node().info(); !info.has_primary_key()) {
|
||||||
|
return utils::failure(utils::error{error_code::MissingPrimaryKey, "Type " + info.name() + " has no primary key"});
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
access::process(*this, *ptr_);
|
||||||
|
} catch (const query_builder_exception &ex) {
|
||||||
|
return utils::failure(ex.error());
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto cit = ctx_.contexts_by_type_.find(it->second.node().info().type_index());
|
||||||
|
if (cit == ctx_.contexts_by_type_.end()) {
|
||||||
|
return utils::failure(utils::error{error_code::UnknownType, "Unknown type"});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (as_relation_step) {
|
||||||
|
ctx_.relation_steps_.push_back(std::make_unique<delete_step_object<ObjectType>>(cit->second.delete_one, ptr_));
|
||||||
|
} else {
|
||||||
|
ctx_.steps_.push_back(std::make_unique<delete_step_object<ObjectType>>(cit->second.delete_one, ptr_));
|
||||||
|
}
|
||||||
|
|
||||||
|
ptr_.reset();
|
||||||
|
|
||||||
|
return utils::ok<void>();
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class PrimaryKeyType>
|
||||||
|
static void on_primary_key(const char * /*id*/, PrimaryKeyType &, const utils::primary_key_attribute & /*attr*/) {}
|
||||||
|
|
||||||
|
static void on_revision(const char * /*id*/, uint64_t & /*rev*/) {}
|
||||||
|
|
||||||
|
template<typename Type>
|
||||||
|
static void on_attribute(const char * /*id*/, Type &, const utils::field_attributes & /*attr*/) {}
|
||||||
|
|
||||||
|
template<class Pointer>
|
||||||
|
void on_belongs_to(const char * /*id*/, Pointer &obj, const utils::foreign_attributes &attr) {
|
||||||
|
on_foreign_object(obj, attr);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class Pointer>
|
||||||
|
void on_has_one(const char * /*id*/, Pointer &obj, const char * /*join_column*/, const utils::foreign_attributes &attr) {
|
||||||
|
on_foreign_object(obj, attr);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class CollectionType>
|
||||||
|
void on_has_many(const char * /*id*/,
|
||||||
|
object::collection<object::object_ptr<CollectionType>> &objects,
|
||||||
|
const char *join_column,
|
||||||
|
const utils::foreign_attributes &attr) {
|
||||||
|
if (join_column == nullptr) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!utils::is_cascade_type_set(attr.cascade(), utils::cascade_type::Remove)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
delete_step_processor<CollectionType> processor{ctx_};
|
||||||
|
for (auto &obj : objects) {
|
||||||
|
if (!obj) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto result = processor.build(obj, true);
|
||||||
|
if (!result) {
|
||||||
|
throw query_builder_exception(result.release_error());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class CollectionType>
|
||||||
|
static void on_has_many(const char * /*id*/,
|
||||||
|
object::collection<CollectionType> & /*objects*/,
|
||||||
|
const char * /*join_column*/,
|
||||||
|
const utils::foreign_attributes & /*attr*/) {
|
||||||
|
// Value-Collections bzw. Relationstabellen werden hier nicht direkt gelöscht.
|
||||||
|
// Dafür wird das Delete-Statement der jeweiligen Entity verwendet.
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class ForeignType>
|
||||||
|
void on_has_many_to_many(const char *id,
|
||||||
|
object::collection<object::object_ptr<ForeignType>> &objects,
|
||||||
|
const char *join_column,
|
||||||
|
const char *inverse_join_column,
|
||||||
|
const utils::foreign_attributes &attr) {
|
||||||
|
if (id == nullptr || join_column == nullptr || inverse_join_column == nullptr) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
using relation_value_type = object::many_to_many_relation<ObjectType, ForeignType>;
|
||||||
|
const std::type_index foreign_type{typeid(ForeignType)};
|
||||||
|
const std::type_index local_type{typeid(ObjectType)};
|
||||||
|
on_many_to_many_objects<relation_value_type>(
|
||||||
|
id,
|
||||||
|
objects,
|
||||||
|
attr,
|
||||||
|
[foreign_type, local_type](const char* relation_name) -> processing_many_to_many_key {
|
||||||
|
return {std::string{relation_name}, local_type, foreign_type};
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class ForeignType>
|
||||||
|
void on_has_many_to_many(const char *id,
|
||||||
|
object::collection<object::object_ptr<ForeignType>> &objects,
|
||||||
|
const utils::foreign_attributes &attr) {
|
||||||
|
if (id == nullptr) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
object::join_columns_collector collector;
|
||||||
|
if (auto join_columns = collector.collect<ForeignType>(); join_columns.join_column.empty() || join_columns.inverse_join_column.empty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
using relation_value_type = object::many_to_many_relation<ForeignType, ObjectType>;
|
||||||
|
const std::type_index foreign_type{typeid(ForeignType)};
|
||||||
|
const std::type_index local_type{typeid(ObjectType)};
|
||||||
|
on_many_to_many_objects<relation_value_type>(
|
||||||
|
id,
|
||||||
|
objects,
|
||||||
|
attr,
|
||||||
|
[foreign_type, local_type](const char* relation_name) -> processing_many_to_many_key {
|
||||||
|
return {std::string{relation_name}, foreign_type, local_type};
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
template<class PointerType>
|
||||||
|
void on_foreign_object(object::object_ptr<PointerType> &obj, const utils::foreign_attributes &attr) {
|
||||||
|
if (!utils::is_cascade_type_set(attr.cascade(), utils::cascade_type::Remove) || !obj) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
delete_step_processor<PointerType> processor{ctx_};
|
||||||
|
|
||||||
|
auto result = processor.build(obj);
|
||||||
|
if (!result) {
|
||||||
|
throw query_builder_exception(result.release_error());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class LocalType, class ForeignType, class RelationKeyFactory>
|
||||||
|
void on_many_to_many_objects(const char *id,
|
||||||
|
object::collection<object::object_ptr<ForeignType>> &objects,
|
||||||
|
const utils::foreign_attributes &attr,
|
||||||
|
RelationKeyFactory make_relation_key) {
|
||||||
|
if (!utils::is_cascade_type_set(attr.cascade(), utils::cascade_type::Remove)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto key = make_relation_key(id);
|
||||||
|
if (ctx_.processing_many_to_many_relations_.find(key) != ctx_.processing_many_to_many_relations_.end()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto it = ctx_.schema_.find(std::string{id});
|
||||||
|
if (it == ctx_.schema_.end()) {
|
||||||
|
throw query_builder_exception(error_code::UnknownType, "Unknown type for relation " + std::string{id});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (std::type_index(typeid(LocalType)) != it->second.node().info().type_index()) {
|
||||||
|
throw query_builder_exception(error_code::InvalidRelationType, "Invalid relation type for " + std::string{id});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (const auto cit = ctx_.contexts_by_type_.find(it->second.node().info().type_index()); cit == ctx_.contexts_by_type_.end()) {
|
||||||
|
throw query_builder_exception(error_code::UnknownType, "No query contexts for type " + it->second.node().name());
|
||||||
|
}
|
||||||
|
|
||||||
|
std::ignore = ctx_.processing_many_to_many_relations_.insert(key);
|
||||||
|
std::vector<std::unique_ptr<execute_step>> delete_relation_steps;
|
||||||
|
delete_step_processor<ForeignType> processor{ctx_};
|
||||||
|
for (auto &obj : objects) {
|
||||||
|
if (!obj) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (obj.is_persistent()) {
|
||||||
|
auto result = processor.build(obj, true);
|
||||||
|
if (!result) {
|
||||||
|
throw query_builder_exception(result.release_error());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
query_builder_context &ctx_;
|
||||||
|
object::object_ptr<ObjectType> ptr_;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<class ObjectType>
|
||||||
|
class delete_query_builder {
|
||||||
|
public:
|
||||||
|
explicit delete_query_builder(const basic_schema &schema,
|
||||||
|
const std::unordered_map<std::type_index, query_contexts> &contexts_by_type)
|
||||||
|
: schema_(schema)
|
||||||
|
, contexts_by_type_(contexts_by_type) {}
|
||||||
|
|
||||||
|
utils::result<std::vector<std::unique_ptr<execute_step>>, utils::error> build(const object::object_ptr<ObjectType> &ptr) {
|
||||||
|
if (const auto it = schema_.find(typeid(ObjectType)); it == schema_.end()) {
|
||||||
|
return utils::failure(utils::error{error_code::UnknownType, "Unknown type for delete query"});
|
||||||
|
}
|
||||||
|
|
||||||
|
query_builder_context ctx{schema_, contexts_by_type_};
|
||||||
|
delete_step_processor<ObjectType> processor{ctx};
|
||||||
|
|
||||||
|
const auto result = processor.build(ptr);
|
||||||
|
if (!result) {
|
||||||
|
return utils::failure(result.err());
|
||||||
|
}
|
||||||
|
|
||||||
|
// relation inserts must run after all entity inserts were collected
|
||||||
|
for (auto &s : ctx.steps_) {
|
||||||
|
ctx.relation_steps_.push_back(std::move(s));
|
||||||
|
}
|
||||||
|
ctx.steps_.clear();
|
||||||
|
|
||||||
|
return utils::ok(std::move(ctx.relation_steps_));
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
const basic_schema &schema_;
|
||||||
|
const std::unordered_map<std::type_index, query_contexts> &contexts_by_type_;
|
||||||
|
};
|
||||||
|
} // namespace matador::query
|
||||||
|
|
||||||
|
#endif //MATADOR_DELETE_QUERY_BUILDER_HPP
|
||||||
@@ -0,0 +1,84 @@
|
|||||||
|
#ifndef MATADOR_DELETE_STEP_HPP
|
||||||
|
#define MATADOR_DELETE_STEP_HPP
|
||||||
|
|
||||||
|
#include "matador/query/error_code.hpp"
|
||||||
|
#include "matador/query/execute_step.hpp"
|
||||||
|
|
||||||
|
#include "matador/sql/internal/identifier_statement_binder.hpp"
|
||||||
|
#include "matador/sql/statement.hpp"
|
||||||
|
|
||||||
|
#include "matador/object/object_ptr.hpp"
|
||||||
|
|
||||||
|
namespace matador::query {
|
||||||
|
template<typename ObjectType>
|
||||||
|
class delete_step_object final : public execute_step {
|
||||||
|
public:
|
||||||
|
delete_step_object(sql::query_context ctx, const object::object_ptr<ObjectType> &ptr)
|
||||||
|
: execute_step(std::move(ctx))
|
||||||
|
, ptr_(ptr) {}
|
||||||
|
|
||||||
|
utils::result<void, utils::error> prepare(sql::executor &/*conn*/) override {
|
||||||
|
id_ = ptr_.primary_key();
|
||||||
|
return utils::ok<void>();
|
||||||
|
}
|
||||||
|
|
||||||
|
utils::result<void, utils::error> execute(sql::statement &stmt) override {
|
||||||
|
if (!ptr_) {
|
||||||
|
return utils::failure(utils::error{error_code::InvalidObject, "Object is null"});
|
||||||
|
}
|
||||||
|
|
||||||
|
sql::identifier_statement_binder binder(stmt, 0);
|
||||||
|
binder.bind(id_);
|
||||||
|
|
||||||
|
if (const auto result = stmt.execute(); !result.is_ok()) {
|
||||||
|
return utils::failure(result.err());
|
||||||
|
}
|
||||||
|
|
||||||
|
return utils::ok<void>();
|
||||||
|
}
|
||||||
|
|
||||||
|
utils::result<void, utils::error> finalize(object::object_cache &cache, const resolver_service_ptr& /*resolver_service*/) override {
|
||||||
|
if (!ptr_) {
|
||||||
|
return utils::failure(utils::error{error_code::InvalidObject, "Object is null"});
|
||||||
|
}
|
||||||
|
|
||||||
|
cache.erase<ObjectType>(id_);
|
||||||
|
ptr_.change_state(object::object_state::Transient);
|
||||||
|
|
||||||
|
return utils::ok<void>();
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
object::object_ptr<ObjectType> ptr_;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename ObjectType>
|
||||||
|
class delete_step_relation : public execute_step {
|
||||||
|
public:
|
||||||
|
delete_step_relation(sql::query_context ctx, const object::object_ptr<ObjectType>& ptr)
|
||||||
|
: execute_step(std::move(ctx))
|
||||||
|
, ptr_(ptr) {}
|
||||||
|
|
||||||
|
utils::result<void, utils::error> prepare(sql::executor &) override {
|
||||||
|
return utils::ok<void>();
|
||||||
|
}
|
||||||
|
utils::result<void, utils::error> execute(sql::statement &stmt) override {
|
||||||
|
stmt.bind(*ptr_);
|
||||||
|
if (const auto exec_result = stmt.execute(); !exec_result.is_ok()) {
|
||||||
|
return utils::failure(exec_result.err());
|
||||||
|
}
|
||||||
|
|
||||||
|
return utils::ok<void>();
|
||||||
|
}
|
||||||
|
|
||||||
|
utils::result<void, utils::error> finalize(object::object_cache& /*cache*/, const resolver_service_ptr& /*resolver_service*/) override {
|
||||||
|
return utils::ok<void>();
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
object::object_ptr<ObjectType> ptr_;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif //MATADOR_DELETE_STEP_HPP
|
||||||
@@ -59,16 +59,18 @@ struct meta_registry {
|
|||||||
struct dependency_collector {
|
struct dependency_collector {
|
||||||
std::vector<fk_ref> refs;
|
std::vector<fk_ref> refs;
|
||||||
|
|
||||||
|
template<typename BaseType>
|
||||||
|
static void on_base(const BaseType&) {}
|
||||||
template<class V>
|
template<class V>
|
||||||
static void on_primary_key(const char*, V&, const utils::primary_key_attribute& /*attr*/ = utils::default_pk_attributes) {}
|
static void on_primary_key(const char*, V&, const utils::primary_key_attribute& /*attr*/) {}
|
||||||
|
|
||||||
static void on_revision(const char*, std::uint64_t&) {}
|
static void on_revision(const char*, std::uint64_t&) {}
|
||||||
|
|
||||||
template<class V>
|
template<class V>
|
||||||
static void on_attribute(const char*, V&, const utils::field_attributes &/*attr*/ = utils::null_attributes) {}
|
static void on_attribute(const char*, V&, const utils::field_attributes &/*attr*/) {}
|
||||||
|
|
||||||
template<class Pointer>
|
template<class Pointer>
|
||||||
void on_belongs_to(const char* id, Pointer& p, utils::foreign_attributes &attr) {
|
void on_belongs_to(const char* id, Pointer& p, utils::foreign_attributes &/*attr*/) {
|
||||||
// Erwartung: Pointer verhält sich wie matador::object::object_ptr<T>
|
// Erwartung: Pointer verhält sich wie matador::object::object_ptr<T>
|
||||||
// also: p.get() liefert raw ptr, und Pointer::value_type / element_type ist T.
|
// also: p.get() liefert raw ptr, und Pointer::value_type / element_type ist T.
|
||||||
auto* raw = p.get();
|
auto* raw = p.get();
|
||||||
@@ -87,16 +89,16 @@ struct dependency_collector {
|
|||||||
}
|
}
|
||||||
|
|
||||||
template<class Pointer>
|
template<class Pointer>
|
||||||
static void on_has_one(const char*, Pointer&, const auto&) {}
|
static void on_has_one(const char*, Pointer&, const char * /*join_column*/, const utils::foreign_attributes&) {}
|
||||||
|
|
||||||
template<class Container>
|
template<class Container>
|
||||||
static void on_has_many(const char*, Container&, const char*, const auto&) {}
|
static void on_has_many(const char*, Container&, const char*, const utils::foreign_attributes&) {}
|
||||||
|
|
||||||
template<class Container>
|
template<class Container>
|
||||||
static void on_has_many_to_many(const char*, Container&, const char*, const char*, const auto&) {}
|
static void on_has_many_to_many(const char*, Container&, const char*, const char*, const utils::foreign_attributes&) {}
|
||||||
|
|
||||||
template<class Container>
|
template<class Container>
|
||||||
static void on_has_many_to_many(const char*, Container&, const auto&) {}
|
static void on_has_many_to_many(const char*, Container&, const utils::foreign_attributes&) {}
|
||||||
};
|
};
|
||||||
|
|
||||||
struct flush_node {
|
struct flush_node {
|
||||||
|
|||||||
@@ -0,0 +1,46 @@
|
|||||||
|
#ifndef MATADOR_ERROR_CODE_HPP
|
||||||
|
#define MATADOR_ERROR_CODE_HPP
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
#include <system_error>
|
||||||
|
|
||||||
|
namespace matador::query {
|
||||||
|
enum class error_code {
|
||||||
|
Ok,
|
||||||
|
Failure,
|
||||||
|
InvalidQuery,
|
||||||
|
InvalidSchema,
|
||||||
|
InvalidTable,
|
||||||
|
InvalidColumn,
|
||||||
|
InvalidConstraint,
|
||||||
|
InvalidDataType,
|
||||||
|
InvalidObject,
|
||||||
|
InvalidValue,
|
||||||
|
InvalidRelationType,
|
||||||
|
MissingPrimaryKey,
|
||||||
|
UnknownType,
|
||||||
|
FailedToAcquireConnection,
|
||||||
|
FailedToFindPrimaryKey,
|
||||||
|
FailedToBuildQuery,
|
||||||
|
FailedToFindObject,
|
||||||
|
FailedToInsertObject,
|
||||||
|
FailedToAcquirePool,
|
||||||
|
UnexpectedError
|
||||||
|
};
|
||||||
|
|
||||||
|
class query_category_impl final : public std::error_category
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
[[nodiscard]] const char* name() const noexcept override;
|
||||||
|
[[nodiscard]] std::string message(int ev) const override;
|
||||||
|
};
|
||||||
|
|
||||||
|
const std::error_category& query_category();
|
||||||
|
std::error_code make_error_code(error_code e);
|
||||||
|
std::error_condition make_error_condition(error_code e);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
struct std::is_error_code_enum<matador::query::error_code> : true_type {};
|
||||||
|
#endif //MATADOR_ERROR_CODE_HPP
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
#ifndef MATADOR_EXECUTE_STEP_HPP
|
||||||
|
#define MATADOR_EXECUTE_STEP_HPP
|
||||||
|
|
||||||
|
#include "matador/utils/identifier.hpp"
|
||||||
|
#include "matador/utils/primary_key_accessor.hpp"
|
||||||
|
#include "matador/utils/error.hpp"
|
||||||
|
#include "matador/utils/result.hpp"
|
||||||
|
|
||||||
|
#include "matador/object/object_cache.hpp"
|
||||||
|
|
||||||
|
#include "matador/sql/query_context.hpp"
|
||||||
|
#include "matador/sql/executor.hpp"
|
||||||
|
|
||||||
|
#include "matador/query/abstract_pk_generator.hpp"
|
||||||
|
|
||||||
|
namespace matador::query {
|
||||||
|
using resolver_service_ptr = std::shared_ptr<sql::resolver_service>;
|
||||||
|
|
||||||
|
class execute_step {
|
||||||
|
public:
|
||||||
|
explicit execute_step(sql::query_context ctx)
|
||||||
|
: ctx_(std::move(ctx)) {}
|
||||||
|
virtual ~execute_step() = default;
|
||||||
|
|
||||||
|
virtual utils::result<void, utils::error> prepare(sql::executor &conn) = 0;
|
||||||
|
virtual utils::result<void, utils::error> execute(sql::statement &stmt) = 0;
|
||||||
|
virtual utils::result<void, utils::error> finalize(object::object_cache& cache, const resolver_service_ptr& resolver_service) = 0;
|
||||||
|
|
||||||
|
[[nodiscard]] const sql::query_context& ctx() const { return ctx_; }
|
||||||
|
|
||||||
|
protected:
|
||||||
|
utils::identifier id_;
|
||||||
|
utils::primary_key_accessor pk_accessor_;
|
||||||
|
sql::query_context ctx_;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
#endif //MATADOR_EXECUTE_STEP_HPP
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
#ifndef MATADOR_ABSTRACT_COLUMN_EXPRESSION_HPP
|
||||||
|
#define MATADOR_ABSTRACT_COLUMN_EXPRESSION_HPP
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
namespace matador::query {
|
||||||
|
class expression_visitor;
|
||||||
|
class table_column;
|
||||||
|
class abstract_column_expression {
|
||||||
|
public:
|
||||||
|
virtual ~abstract_column_expression() = default;
|
||||||
|
|
||||||
|
virtual void accept(expression_visitor& visitor) const = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
using column_expression_ptr = std::unique_ptr<abstract_column_expression>;
|
||||||
|
}
|
||||||
|
#endif //MATADOR_ABSTRACT_COLUMN_EXPRESSION_HPP
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
#ifndef MATADOR_BINARY_COLUMN_EXPRESSION_HPP
|
||||||
|
#define MATADOR_BINARY_COLUMN_EXPRESSION_HPP
|
||||||
|
|
||||||
|
#include "matador/query/expression/abstract_column_expression.hpp"
|
||||||
|
|
||||||
|
namespace matador::query {
|
||||||
|
enum class binary_expression_operator {
|
||||||
|
Plus,
|
||||||
|
Minus,
|
||||||
|
Multiply,
|
||||||
|
Divide,
|
||||||
|
Modulo
|
||||||
|
};
|
||||||
|
|
||||||
|
class binary_column_expression : public abstract_column_expression {
|
||||||
|
public:
|
||||||
|
binary_column_expression() = delete;
|
||||||
|
binary_column_expression(column_expression_ptr left_column, binary_expression_operator operand, column_expression_ptr right_column);
|
||||||
|
|
||||||
|
void accept(expression_visitor& visitor) const override;
|
||||||
|
|
||||||
|
[[nodiscard]] const column_expression_ptr& left_column() const;
|
||||||
|
[[nodiscard]] binary_expression_operator operand() const;
|
||||||
|
[[nodiscard]] const column_expression_ptr& right_column() const;
|
||||||
|
private:
|
||||||
|
column_expression_ptr left_column_;
|
||||||
|
binary_expression_operator operand_;
|
||||||
|
column_expression_ptr right_column_;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
#endif //MATADOR_BINARY_COLUMN_EXPRESSION_HPP
|
||||||
@@ -0,0 +1,55 @@
|
|||||||
|
#ifndef MATADOR_COLUMN_EXPRESSION_HPP
|
||||||
|
#define MATADOR_COLUMN_EXPRESSION_HPP
|
||||||
|
|
||||||
|
#include "matador/query/expression/table_column_expression.hpp"
|
||||||
|
#include "matador/query/expression/value_expression.hpp"
|
||||||
|
#include "matador/query/expression/placeholder_expression.hpp"
|
||||||
|
|
||||||
|
#include "matador/utils/placeholder.hpp"
|
||||||
|
#include "matador/utils/types.hpp"
|
||||||
|
|
||||||
|
namespace matador::query {
|
||||||
|
class column_expression {
|
||||||
|
public:
|
||||||
|
column_expression() = default;
|
||||||
|
explicit column_expression(column_expression_ptr expr) noexcept;
|
||||||
|
|
||||||
|
template<typename Type, typename = std::enable_if_t<std::is_convertible_v<Type, utils::database_type>>>
|
||||||
|
column_expression(Type&& value)
|
||||||
|
: column_expression(std::make_unique<value_expression>(std::forward<Type>(value))){
|
||||||
|
}
|
||||||
|
column_expression(utils::placeholder p);
|
||||||
|
column_expression(const column_expression&) = delete;
|
||||||
|
column_expression& operator=(const column_expression&) = delete;
|
||||||
|
column_expression(column_expression&&) noexcept = default;
|
||||||
|
column_expression& operator=(column_expression&&) noexcept = default;
|
||||||
|
|
||||||
|
[[nodiscard]] bool empty() const noexcept;
|
||||||
|
[[nodiscard]] const abstract_column_expression* get() const noexcept;
|
||||||
|
[[nodiscard]] const column_expression_ptr& ptr() const noexcept;
|
||||||
|
column_expression_ptr release() noexcept;
|
||||||
|
|
||||||
|
// Move out for APIs that still take column_expression_ptr
|
||||||
|
operator column_expression_ptr() && noexcept;
|
||||||
|
|
||||||
|
// The feature you want: turn an expression into a SELECT-able column with alias
|
||||||
|
[[nodiscard]] table_column as(const std::string& alias) &&;
|
||||||
|
|
||||||
|
private:
|
||||||
|
column_expression_ptr expression_{};
|
||||||
|
};
|
||||||
|
|
||||||
|
// Helper constructors (optional but convenient)
|
||||||
|
inline column_expression make_column_expression(const table_column& col) {
|
||||||
|
return column_expression{std::make_unique<table_column_expression>(col)};
|
||||||
|
}
|
||||||
|
|
||||||
|
inline column_expression make_column_expression(utils::database_type v) {
|
||||||
|
return column_expression{std::make_unique<value_expression>(std::move(v))};
|
||||||
|
}
|
||||||
|
|
||||||
|
inline column_expression make_column_expression(utils::placeholder) {
|
||||||
|
return column_expression{std::make_unique<placeholder_expression>()};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif //MATADOR_COLUMN_EXPRESSION_HPP
|
||||||
@@ -0,0 +1,118 @@
|
|||||||
|
#ifndef MATADOR_EXPRESSION_OPERATORS_HPP
|
||||||
|
#define MATADOR_EXPRESSION_OPERATORS_HPP
|
||||||
|
|
||||||
|
#include "matador/query/expression/binary_column_expression.hpp"
|
||||||
|
#include "matador/query/expression/table_column_expression.hpp"
|
||||||
|
#include "matador/query/expression/value_expression.hpp"
|
||||||
|
#include "matador/query/expression/column_expression.hpp"
|
||||||
|
|
||||||
|
#include "matador/utils/placeholder.hpp"
|
||||||
|
|
||||||
|
namespace matador::query {
|
||||||
|
template<typename Type>
|
||||||
|
column_expression operator+(const table_column& col, Type&& val) {
|
||||||
|
return column_expression{std::make_unique<binary_column_expression>(
|
||||||
|
std::make_unique<table_column_expression>(col),
|
||||||
|
binary_expression_operator::Plus,
|
||||||
|
std::make_unique<value_expression>(std::forward<Type>(val))
|
||||||
|
)};
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename Type>
|
||||||
|
column_expression operator+(Type&& val, const table_column& col) {
|
||||||
|
return column_expression{std::make_unique<binary_column_expression>(
|
||||||
|
std::make_unique<value_expression>(std::forward<Type>(val)),
|
||||||
|
binary_expression_operator::Plus,
|
||||||
|
std::make_unique<table_column_expression>(col)
|
||||||
|
)};
|
||||||
|
}
|
||||||
|
|
||||||
|
column_expression operator+(const table_column& col, utils::placeholder /*val*/);
|
||||||
|
column_expression operator+(utils::placeholder /*val*/, const table_column& col);
|
||||||
|
|
||||||
|
template<typename Type>
|
||||||
|
column_expression operator-(const table_column& col, Type&& val) {
|
||||||
|
return column_expression{std::make_unique<binary_column_expression>(
|
||||||
|
std::make_unique<table_column_expression>(col),
|
||||||
|
binary_expression_operator::Minus,
|
||||||
|
std::make_unique<value_expression>(std::forward<Type>(val))
|
||||||
|
)};
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename Type>
|
||||||
|
column_expression operator-(Type&& val, const table_column& col) {
|
||||||
|
return column_expression{std::make_unique<binary_column_expression>(
|
||||||
|
std::make_unique<value_expression>(std::forward<Type>(val)),
|
||||||
|
binary_expression_operator::Minus,
|
||||||
|
std::make_unique<table_column_expression>(col)
|
||||||
|
)};
|
||||||
|
}
|
||||||
|
|
||||||
|
column_expression operator-(const table_column& col, utils::placeholder /*val*/);
|
||||||
|
column_expression operator-(utils::placeholder /*val*/, const table_column& col);
|
||||||
|
|
||||||
|
template<typename Type>
|
||||||
|
column_expression operator*(const table_column& col, Type&& val) {
|
||||||
|
return column_expression{std::make_unique<binary_column_expression>(
|
||||||
|
std::make_unique<table_column_expression>(col),
|
||||||
|
binary_expression_operator::Multiply,
|
||||||
|
std::make_unique<value_expression>(std::forward<Type>(val))
|
||||||
|
)};
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename Type>
|
||||||
|
column_expression operator*(Type&& val, const table_column& col) {
|
||||||
|
return std::make_unique<binary_column_expression>(
|
||||||
|
std::make_unique<value_expression>(std::forward<Type>(val)),
|
||||||
|
binary_expression_operator::Multiply,
|
||||||
|
std::make_unique<table_column_expression>(col)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
column_expression operator*(const table_column& col, utils::placeholder /*val*/);
|
||||||
|
column_expression operator*(utils::placeholder /*val*/, const table_column& col);
|
||||||
|
column_expression operator*(column_expression&& left, column_expression&& right);
|
||||||
|
|
||||||
|
template<typename Type>
|
||||||
|
column_expression operator/(const table_column& col, Type&& val) {
|
||||||
|
return column_expression{std::make_unique<binary_column_expression>(
|
||||||
|
std::make_unique<table_column_expression>(col),
|
||||||
|
binary_expression_operator::Divide,
|
||||||
|
std::make_unique<value_expression>(std::forward<Type>(val))
|
||||||
|
)};
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename Type>
|
||||||
|
column_expression operator/(Type&& val, const table_column& col) {
|
||||||
|
return column_expression{std::make_unique<binary_column_expression>(
|
||||||
|
std::make_unique<value_expression>(std::forward<Type>(val)),
|
||||||
|
binary_expression_operator::Divide,
|
||||||
|
std::make_unique<table_column_expression>(col)
|
||||||
|
)};
|
||||||
|
}
|
||||||
|
|
||||||
|
column_expression operator/(const table_column& col, utils::placeholder /*val*/);
|
||||||
|
column_expression operator/(utils::placeholder /*val*/, const table_column& col);
|
||||||
|
|
||||||
|
template<typename Type>
|
||||||
|
column_expression operator%(const table_column& col, Type&& val) {
|
||||||
|
return column_expression{std::make_unique<binary_column_expression>(
|
||||||
|
std::make_unique<table_column_expression>(col),
|
||||||
|
binary_expression_operator::Modulo,
|
||||||
|
std::make_unique<value_expression>(std::forward<Type>(val))
|
||||||
|
)};
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename Type>
|
||||||
|
column_expression operator%(Type&& val, const table_column& col) {
|
||||||
|
return column_expression{std::make_unique<binary_column_expression>(
|
||||||
|
std::make_unique<value_expression>(std::forward<Type>(val)),
|
||||||
|
binary_expression_operator::Modulo,
|
||||||
|
std::make_unique<table_column_expression>(col)
|
||||||
|
)};
|
||||||
|
}
|
||||||
|
|
||||||
|
column_expression operator%(const table_column& col, utils::placeholder /*val*/);
|
||||||
|
column_expression operator%(utils::placeholder /*val*/, const table_column& col);
|
||||||
|
}
|
||||||
|
#endif //MATADOR_EXPRESSION_OPERATORS_HPP
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
#ifndef MATADOR_EXPRESSION_VISITOR_HPP
|
||||||
|
#define MATADOR_EXPRESSION_VISITOR_HPP
|
||||||
|
|
||||||
|
namespace matador::query {
|
||||||
|
class binary_column_expression;
|
||||||
|
class table_column_expression;
|
||||||
|
class value_expression;
|
||||||
|
class placeholder_expression;
|
||||||
|
|
||||||
|
class expression_visitor {
|
||||||
|
public:
|
||||||
|
virtual ~expression_visitor() = default;
|
||||||
|
|
||||||
|
virtual void visit(const binary_column_expression& node) = 0;
|
||||||
|
virtual void visit(const table_column_expression& node) = 0;
|
||||||
|
virtual void visit(const value_expression& node) = 0;
|
||||||
|
virtual void visit(const placeholder_expression& node) = 0;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
#endif //MATADOR_EXPRESSION_VISITOR_HPP
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
#ifndef MATADOR_PLACEHOLDER_EXPRESSION_HPP
|
||||||
|
#define MATADOR_PLACEHOLDER_EXPRESSION_HPP
|
||||||
|
|
||||||
|
#include "matador/query/expression/abstract_column_expression.hpp"
|
||||||
|
|
||||||
|
namespace matador::query {
|
||||||
|
class placeholder_expression : public abstract_column_expression {
|
||||||
|
public:
|
||||||
|
placeholder_expression() = default;
|
||||||
|
|
||||||
|
void accept(expression_visitor& visitor) const override;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
#endif //MATADOR_PLACEHOLDER_EXPRESSION_HPP
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
#ifndef MATADOR_TABLE_COLUMN_EXPRESSION_HPP
|
||||||
|
#define MATADOR_TABLE_COLUMN_EXPRESSION_HPP
|
||||||
|
|
||||||
|
#include "matador/query/expression/abstract_column_expression.hpp"
|
||||||
|
#include "matador/query/table_column.hpp"
|
||||||
|
|
||||||
|
namespace matador::query {
|
||||||
|
class table_column_expression : public abstract_column_expression {
|
||||||
|
public:
|
||||||
|
table_column_expression() = delete;
|
||||||
|
explicit table_column_expression(table_column col);
|
||||||
|
|
||||||
|
void accept(expression_visitor& visitor) const override;
|
||||||
|
|
||||||
|
[[nodiscard]] const table_column& col() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
table_column column_;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
#endif //MATADOR_TABLE_COLUMN_EXPRESSION_HPP
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
#ifndef MATADOR_VALUE_EXPRESSION_HPP
|
||||||
|
#define MATADOR_VALUE_EXPRESSION_HPP
|
||||||
|
|
||||||
|
#include "matador/query/expression/abstract_column_expression.hpp"
|
||||||
|
|
||||||
|
#include "matador/utils/types.hpp"
|
||||||
|
|
||||||
|
namespace matador::query {
|
||||||
|
class value_expression : public abstract_column_expression {
|
||||||
|
public:
|
||||||
|
value_expression() = delete;
|
||||||
|
explicit value_expression(utils::database_type value);
|
||||||
|
|
||||||
|
void accept(expression_visitor& visitor) const override;
|
||||||
|
|
||||||
|
[[nodiscard]] const utils::database_type& value() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
utils::database_type value_;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
#endif //MATADOR_VALUE_EXPRESSION_HPP
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
#ifndef MATADOR_EXPRESSION_EVALUATOR_HPP
|
||||||
|
#define MATADOR_EXPRESSION_EVALUATOR_HPP
|
||||||
|
|
||||||
|
#include "matador/query/expression/expression_visitor.hpp"
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace matador::sql {
|
||||||
|
class dialect;
|
||||||
|
struct query_context;
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace matador::query {
|
||||||
|
class expression_evaluator final : public expression_visitor {
|
||||||
|
public:
|
||||||
|
expression_evaluator(const sql::dialect &d, sql::query_context &query);
|
||||||
|
void visit(const binary_column_expression& node) override;
|
||||||
|
void visit(const table_column_expression& node) override;
|
||||||
|
void visit(const value_expression& node) override;
|
||||||
|
void visit(const placeholder_expression& node) override;
|
||||||
|
|
||||||
|
[[nodiscard]] const std::string& result() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
const sql::dialect &dialect_;
|
||||||
|
sql::query_context &query_;
|
||||||
|
std::string expression_;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
#endif //MATADOR_EXPRESSION_EVALUATOR_HPP
|
||||||
@@ -12,30 +12,30 @@ class foreign_attributes;
|
|||||||
|
|
||||||
namespace matador::query::detail {
|
namespace matador::query::detail {
|
||||||
|
|
||||||
class fk_value_extractor
|
class fk_value_extractor {
|
||||||
{
|
|
||||||
public:
|
public:
|
||||||
fk_value_extractor() = default;
|
fk_value_extractor() = default;
|
||||||
|
|
||||||
template<class Type>
|
template<class Type>
|
||||||
utils::database_type extract(Type &x)
|
utils::database_type extract(Type &x) {
|
||||||
{
|
|
||||||
access::process(*this, x);
|
access::process(*this, x);
|
||||||
return value_;
|
return value_;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template<typename BaseType>
|
||||||
|
static void on_base(const BaseType&) {}
|
||||||
template<typename ValueType>
|
template<typename ValueType>
|
||||||
void on_primary_key(const char *, ValueType &pk, const utils::primary_key_attribute& /*attr*/ = utils::default_pk_attributes) {
|
void on_primary_key(const char *, ValueType &pk, const utils::primary_key_attribute& /*attr*/) {
|
||||||
value_ = pk;
|
value_ = pk;
|
||||||
}
|
}
|
||||||
static void on_revision(const char * /*id*/, uint64_t &/*rev*/) {}
|
static void on_revision(const char * /*id*/, uint64_t &/*rev*/) {}
|
||||||
template < class Type >
|
template < class Type >
|
||||||
static void on_attribute(const char * /*id*/, Type &/*x*/, const utils::field_attributes &/*attr*/ = utils::null_attributes) {}
|
static void on_attribute(const char * /*id*/, Type &/*x*/, const utils::field_attributes &/*attr*/) {}
|
||||||
static void on_attribute(const char * /*id*/, char * /*x*/, const utils::field_attributes &/*attr*/ = utils::null_attributes) {}
|
static void on_attribute(const char * /*id*/, char * /*x*/, const utils::field_attributes &/*attr*/) {}
|
||||||
template<class Pointer>
|
template<class Pointer>
|
||||||
static void on_belongs_to(const char * /*id*/, Pointer &/*x*/, const utils::foreign_attributes &/*attr*/) {}
|
static void on_belongs_to(const char * /*id*/, Pointer &/*x*/, const utils::foreign_attributes &/*attr*/) {}
|
||||||
template<class Pointer>
|
template<class Pointer>
|
||||||
static void on_has_one(const char * /*id*/, Pointer &/*x*/, const utils::foreign_attributes &/*attr*/) {}
|
static void on_has_one(const char * /*id*/, Pointer &/*x*/, const char * /*join_column*/, const utils::foreign_attributes &/*attr*/) {}
|
||||||
template<class ContainerType>
|
template<class ContainerType>
|
||||||
static void on_has_many_to_many(const char *, ContainerType &, const char * /*join_column*/, const char * /*inverse_join_column*/, const utils::foreign_attributes &/*attr*/) {}
|
static void on_has_many_to_many(const char *, ContainerType &, const char * /*join_column*/, const char * /*inverse_join_column*/, const utils::foreign_attributes &/*attr*/) {}
|
||||||
template<class ContainerType>
|
template<class ContainerType>
|
||||||
|
|||||||
@@ -63,14 +63,16 @@ public:
|
|||||||
return result_;
|
return result_;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template<typename BaseType>
|
||||||
|
static void on_base(const BaseType&) {}
|
||||||
template < class V >
|
template < class V >
|
||||||
void on_primary_key(const char *id, V &, const utils::primary_key_attribute& /*attr*/ = utils::default_pk_attributes) {
|
void on_primary_key(const char *id, V &, const utils::primary_key_attribute& /*attr*/) {
|
||||||
push(id);
|
push(id);
|
||||||
}
|
}
|
||||||
void on_revision(const char *id, uint64_t &/*rev*/);
|
void on_revision(const char *id, uint64_t &/*rev*/);
|
||||||
|
|
||||||
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*/) {
|
||||||
push(id);
|
push(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -79,7 +81,7 @@ public:
|
|||||||
on_foreign_key(id, x, attr);
|
on_foreign_key(id, x, attr);
|
||||||
}
|
}
|
||||||
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 &x, const char * /*join_column*/, const utils::foreign_attributes &attr) {
|
||||||
on_foreign_key(id, x, attr);
|
on_foreign_key(id, x, attr);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -105,7 +107,7 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
template<class CollectionType>
|
template<class CollectionType>
|
||||||
void on_has_many(const char * /*id*/, CollectionType &, const char *join_column, const utils::foreign_attributes &attr, std::enable_if_t<!object::is_object_ptr<typename CollectionType::value_type>::value> * = nullptr) {
|
void on_has_many(const char * /*id*/, CollectionType &, const char * /*join_column*/, const utils::foreign_attributes &/*attr*/, std::enable_if_t<!object::is_object_ptr<typename CollectionType::value_type>::value> * = nullptr) {
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class ContainerType>
|
template<class ContainerType>
|
||||||
@@ -169,14 +171,16 @@ public:
|
|||||||
return result_;
|
return result_;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template<typename BaseType>
|
||||||
|
static void on_base(const BaseType&) {}
|
||||||
template < class V >
|
template < class V >
|
||||||
void on_primary_key(const char * /*id*/, V &/*x*/, const utils::primary_key_attribute& /*attr*/ = utils::default_pk_attributes) {
|
void on_primary_key(const char * /*id*/, V &/*x*/, const utils::primary_key_attribute& /*attr*/) {
|
||||||
result_.emplace_back(utils::_);
|
result_.emplace_back(utils::_);
|
||||||
}
|
}
|
||||||
void on_revision(const char *id, uint64_t &/*rev*/);
|
void on_revision(const char *id, uint64_t &/*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 &/*x*/, const utils::field_attributes &/*attr*/) {
|
||||||
result_.emplace_back(utils::_);
|
result_.emplace_back(utils::_);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -185,7 +189,7 @@ public:
|
|||||||
result_.emplace_back(utils::_);
|
result_.emplace_back(utils::_);
|
||||||
}
|
}
|
||||||
template<class Type, template < class ... > class Pointer>
|
template<class Type, template < class ... > class Pointer>
|
||||||
void on_has_one(const char * /*id*/, Pointer<Type> &/*x*/, const utils::foreign_attributes &/*attr*/) {
|
void on_has_one(const char * /*id*/, Pointer<Type> &/*x*/, const char * /*join_column*/, const utils::foreign_attributes &/*attr*/) {
|
||||||
result_.emplace_back(utils::_);
|
result_.emplace_back(utils::_);
|
||||||
}
|
}
|
||||||
template<class ContainerType>
|
template<class ContainerType>
|
||||||
@@ -224,17 +228,19 @@ public:
|
|||||||
result_.clear();
|
result_.clear();
|
||||||
access::process(*this, obj);
|
access::process(*this, obj);
|
||||||
|
|
||||||
return result_;
|
return std::move(result_);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template<typename BaseType>
|
||||||
|
static void on_base(const BaseType&) {}
|
||||||
template < class V >
|
template < class V >
|
||||||
void on_primary_key(const char *id, V &x, const utils::primary_key_attribute& /*attr*/ = utils::default_pk_attributes) {
|
void on_primary_key(const char *id, V &x, const utils::primary_key_attribute& /*attr*/) {
|
||||||
push_back(id, x);
|
push_back(id, x);
|
||||||
}
|
}
|
||||||
void on_revision(const char *id, uint64_t &/*rev*/);
|
void on_revision(const char *id, uint64_t &/*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 &x, const utils::field_attributes &/*attr*/) {
|
||||||
push_back(id, x);
|
push_back(id, x);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -243,7 +249,7 @@ public:
|
|||||||
push_back(id, fk_value_extractor_.extract(*x));
|
push_back(id, fk_value_extractor_.extract(*x));
|
||||||
}
|
}
|
||||||
template<class Type, template < class ... > class Pointer>
|
template<class Type, template < class ... > class Pointer>
|
||||||
void on_has_one(const char *id, Pointer<Type> &x, const utils::foreign_attributes &/*attr*/) {
|
void on_has_one(const char *id, Pointer<Type> &x, const char * /*join_column*/, const utils::foreign_attributes &/*attr*/) {
|
||||||
push_back(id, fk_value_extractor_.extract(*x));
|
push_back(id, fk_value_extractor_.extract(*x));
|
||||||
}
|
}
|
||||||
template<class ContainerType>
|
template<class ContainerType>
|
||||||
@@ -256,9 +262,9 @@ public:
|
|||||||
template<typename Type>
|
template<typename Type>
|
||||||
void push_back(const char* id, const Type& value) {
|
void push_back(const char* id, const Type& value) {
|
||||||
if (is_column_value_generator_option_set(options_, column_value_generator_options::ValuesAsPlaceHolder)) {
|
if (is_column_value_generator_option_set(options_, column_value_generator_options::ValuesAsPlaceHolder)) {
|
||||||
result_.emplace_back(id, utils::_);
|
result_.emplace_back(id, std::make_unique<placeholder_expression>());
|
||||||
} else {
|
} else {
|
||||||
result_.emplace_back(id, value);
|
result_.emplace_back(id, std::make_unique<value_expression>(value));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -280,6 +286,8 @@ std::vector<utils::placeholder> placeholders(const Type &obj) {
|
|||||||
return generator.generate(obj);
|
return generator.generate(obj);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::vector<utils::placeholder> placeholders(size_t num);
|
||||||
|
|
||||||
template<typename Type>
|
template<typename Type>
|
||||||
std::vector<internal::column_value_pair> column_value_pairs(const Type &obj, const column_value_generator_options options = column_value_generator_options::None) {
|
std::vector<internal::column_value_pair> column_value_pairs(const Type &obj, const column_value_generator_options options = column_value_generator_options::None) {
|
||||||
column_value_generator generator(options);
|
column_value_generator generator(options);
|
||||||
|
|||||||
@@ -0,0 +1,14 @@
|
|||||||
|
#ifndef MATADOR_IDENTITY_PK_GENERATOR_HPP
|
||||||
|
#define MATADOR_IDENTITY_PK_GENERATOR_HPP
|
||||||
|
|
||||||
|
#include "matador/query/abstract_pk_generator.hpp"
|
||||||
|
|
||||||
|
namespace matador::query {
|
||||||
|
class identity_pk_generator : public abstract_pk_generator {
|
||||||
|
public:
|
||||||
|
identity_pk_generator();
|
||||||
|
[[nodiscard]] utils::result<int64_t, utils::error> next_id(const sql::executor& exec) override;
|
||||||
|
[[nodiscard]] utils::result<int64_t, utils::error> current_id(const sql::executor& exec) override;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
#endif //MATADOR_IDENTITY_PK_GENERATOR_HPP
|
||||||
@@ -1,133 +1,119 @@
|
|||||||
#ifndef MATADOR_INSERT_QUERY_BUILDER_HPP
|
#ifndef MATADOR_INSERT_QUERY_BUILDER_HPP
|
||||||
#define MATADOR_INSERT_QUERY_BUILDER_HPP
|
#define MATADOR_INSERT_QUERY_BUILDER_HPP
|
||||||
|
|
||||||
|
#include "matador/object/collection.hpp"
|
||||||
|
#include "matador/object/object_ptr.hpp"
|
||||||
|
|
||||||
#include "matador/query/basic_schema.hpp"
|
#include "matador/query/basic_schema.hpp"
|
||||||
#include "matador/query/intermediates/executable_query.hpp"
|
#include "matador/query/intermediates/executable_query.hpp"
|
||||||
|
#include "matador/query/insert_step.hpp"
|
||||||
#include "matador/query/query.hpp"
|
#include "matador/query/query.hpp"
|
||||||
|
#include "matador/query/query_contexts.hpp"
|
||||||
#include "matador/query/query_builder_exception.hpp"
|
#include "matador/query/query_builder_exception.hpp"
|
||||||
|
#include "matador/query/query_builder_utils.hpp"
|
||||||
|
|
||||||
|
#include "matador/sql/statement.hpp"
|
||||||
|
|
||||||
|
#include "matador/utils/primary_key_accessor.hpp"
|
||||||
|
|
||||||
|
namespace matador::sql {
|
||||||
|
class statement_cache;
|
||||||
|
}
|
||||||
|
|
||||||
namespace matador::query {
|
namespace matador::query {
|
||||||
|
template < class ObjectType >
|
||||||
struct pk_setter {
|
class has_many_linker {
|
||||||
const std::string &name;
|
|
||||||
std::uint64_t value;
|
|
||||||
|
|
||||||
template<class V>
|
|
||||||
void on_primary_key(const char *id, V &pk, const utils::primary_key_attribute & = utils::default_pk_attributes) {
|
|
||||||
if (id != nullptr && name == id) {
|
|
||||||
pk = static_cast<V>(value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
static void on_revision(const char * /*id*/, uint64_t & /*rev*/) {}
|
|
||||||
template<typename T>
|
|
||||||
static void on_attribute(const char * /*id*/, T &, const utils::field_attributes & = utils::null_attributes) {}
|
|
||||||
template<class P>
|
|
||||||
static void on_belongs_to(const char * /*id*/, P &, const utils::foreign_attributes & ) {}
|
|
||||||
template<class P>
|
|
||||||
static void on_has_one(const char * /*id*/, P &, const utils::foreign_attributes & ) {}
|
|
||||||
template<class C>
|
|
||||||
static void on_has_many(const char * /*id*/, C &, const char * /*join_column*/, const utils::foreign_attributes & ) {}
|
|
||||||
template<class C>
|
|
||||||
static void on_has_many_to_many(const char * /*id*/, C &, const char * /*join_column*/, const char * /*inverse_join_column*/, const utils::foreign_attributes & ) {}
|
|
||||||
template<class C>
|
|
||||||
static void on_has_many_to_many(const char * /*id*/, C &, const utils::foreign_attributes & ) {}
|
|
||||||
};
|
|
||||||
|
|
||||||
struct pk_unset_checker {
|
|
||||||
const std::string &name;
|
|
||||||
bool unset{true};
|
|
||||||
|
|
||||||
template<class V>
|
|
||||||
void on_primary_key(const char *id, V &pk, const utils::primary_key_attribute & = utils::default_pk_attributes) {
|
|
||||||
if (id != nullptr && name == id) {
|
|
||||||
// Your convention: 0 means unset for integer PKs
|
|
||||||
unset = (static_cast<std::uint64_t>(pk) == 0ULL);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
static void on_revision(const char * /*id*/, uint64_t & /*rev*/) {}
|
|
||||||
template<typename T>
|
|
||||||
static void on_attribute(const char * /*id*/, T &, const utils::field_attributes & = utils::null_attributes) {}
|
|
||||||
template<class P>
|
|
||||||
static void on_belongs_to(const char * /*id*/, P &, const utils::foreign_attributes & ) {}
|
|
||||||
template<class P>
|
|
||||||
static void on_has_one(const char * /*id*/, P &, const utils::foreign_attributes & ) {}
|
|
||||||
template<class C>
|
|
||||||
static void on_has_many(const char * /*id*/, C &, const char * /*join_column*/, const utils::foreign_attributes & ) {}
|
|
||||||
template<class C>
|
|
||||||
static void on_has_many_to_many(const char * /*id*/, C &, const char * /*join_column*/, const char * /*inverse_join_column*/, const utils::foreign_attributes & ) {}
|
|
||||||
template<class C>
|
|
||||||
static void on_has_many_to_many(const char * /*id*/, C &, const utils::foreign_attributes & ) {}
|
|
||||||
};
|
|
||||||
|
|
||||||
struct pk_value_extractor {
|
|
||||||
std::uint64_t value{0};
|
|
||||||
|
|
||||||
template<class V>
|
|
||||||
void on_primary_key(const char * /*id*/, V &pk, const utils::primary_key_attribute & = utils::default_pk_attributes) {
|
|
||||||
value = static_cast<std::uint64_t>(pk);
|
|
||||||
}
|
|
||||||
static void on_revision(const char * /*id*/, uint64_t & /*rev*/) {}
|
|
||||||
template<typename T>
|
|
||||||
static void on_attribute(const char * /*id*/, T &, const utils::field_attributes & = utils::null_attributes) {}
|
|
||||||
template<class P>
|
|
||||||
static void on_belongs_to(const char * /*id*/, P &, const utils::foreign_attributes & ) {}
|
|
||||||
template<class P>
|
|
||||||
static void on_has_one(const char * /*id*/, P &, const utils::foreign_attributes & ) {}
|
|
||||||
template<class C>
|
|
||||||
static void on_has_many(const char * /*id*/, C &, const char * /*join_column*/, const utils::foreign_attributes & ) {}
|
|
||||||
template<class C>
|
|
||||||
static void on_has_many_to_many(const char * /*id*/, C &, const char * /*join_column*/, const char * /*inverse_join_column*/, const utils::foreign_attributes & ) {}
|
|
||||||
template<class C>
|
|
||||||
static void on_has_many_to_many(const char * /*id*/, C &, const utils::foreign_attributes & ) {}
|
|
||||||
};
|
|
||||||
|
|
||||||
class insert_query_builder {
|
|
||||||
public:
|
public:
|
||||||
using step_query_t = std::variant<executable_query, fetchable_query>;
|
has_many_linker(const object::object_ptr<ObjectType> &ptr, std::string join_column)
|
||||||
|
: ptr_(ptr), join_column_(std::move(join_column)) {}
|
||||||
struct insert_step {
|
template<typename BaseType>
|
||||||
step_query_t query;
|
static void on_base(const BaseType&) {}
|
||||||
|
template < class PrimaryKeyType >
|
||||||
// Session uses these to handle manual/sequence/table pre-insert PKs
|
static void on_primary_key(const char * /*id*/, PrimaryKeyType &, const utils::primary_key_attribute& /*attr*/) {}
|
||||||
utils::generator_type pk_generator{utils::generator_type::Manually};
|
static void on_revision(const char * /*id*/, uint64_t &/*rev*/) {}
|
||||||
std::string pk_name;
|
|
||||||
|
|
||||||
std::function<bool()> pk_is_unset{};
|
|
||||||
std::function<void(std::uint64_t)> set_pk{};
|
|
||||||
|
|
||||||
// Identity post-insert
|
|
||||||
std::function<void(const sql::record &)> apply_returning{};
|
|
||||||
};
|
|
||||||
|
|
||||||
public:
|
|
||||||
explicit insert_query_builder(const basic_schema &schema);
|
|
||||||
|
|
||||||
template<class EntityType>
|
|
||||||
utils::result<std::vector<insert_step>, query_build_error> build(const object::object_ptr<EntityType> &ptr) {
|
|
||||||
if (const auto it = schema_.find(typeid(EntityType)); it == schema_.end()) {
|
|
||||||
return utils::failure(query_build_error::UnknownType);
|
|
||||||
}
|
|
||||||
|
|
||||||
steps_.clear();
|
|
||||||
visited_.clear();
|
|
||||||
|
|
||||||
build_for(ptr);
|
|
||||||
|
|
||||||
// relation inserts must run after all entity inserts were collected
|
|
||||||
for (auto &s : relation_steps_) {
|
|
||||||
steps_.push_back(std::move(s));
|
|
||||||
}
|
|
||||||
relation_steps_.clear();
|
|
||||||
|
|
||||||
return utils::ok(steps_);
|
|
||||||
}
|
|
||||||
|
|
||||||
template < class V >
|
|
||||||
static void on_primary_key(const char * /*id*/, V &, const utils::primary_key_attribute& /*attr*/ = utils::default_pk_attributes) {}
|
|
||||||
static void on_revision(const char *id, uint64_t &/*rev*/);
|
|
||||||
|
|
||||||
template<typename Type>
|
template<typename Type>
|
||||||
static void on_attribute(const char * /*id*/, Type &, const utils::field_attributes &/*attr*/ = utils::null_attributes) {}
|
static void on_attribute(const char * /*id*/, Type &, const utils::field_attributes &/*attr*/) {}
|
||||||
|
template<class Pointer>
|
||||||
|
static void on_belongs_to(const char * /*id*/, Pointer &/*obj*/, const utils::foreign_attributes &/*attr*/) {}
|
||||||
|
void on_belongs_to(const char *id, object::object_ptr<ObjectType> &obj, const utils::foreign_attributes &/*attr*/) {
|
||||||
|
if (id != join_column_) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
obj = ptr_;
|
||||||
|
}
|
||||||
|
template<class Pointer>
|
||||||
|
static void on_has_one(const char * /*id*/, Pointer &/*obj*/, const char * /*join_column*/, const utils::foreign_attributes &/*attr*/) {}
|
||||||
|
template<class CollectionType>
|
||||||
|
static void on_has_many(const char * /*id*/, CollectionType &/*con*/, const char *, const utils::foreign_attributes &/*attr*/, std::enable_if_t<!object::is_object_ptr<typename CollectionType::value_type>::value> * = nullptr) {}
|
||||||
|
template<class Collection>
|
||||||
|
static void on_has_many_to_many(const char * /*id*/, Collection &/*container*/, const char * /*join_column*/, const char * /*inverse_join_column*/, const utils::foreign_attributes & ) {}
|
||||||
|
template<class Collection>
|
||||||
|
static void on_has_many_to_many(const char * /*id*/, Collection &/*container*/, const utils::foreign_attributes & ) {}
|
||||||
|
|
||||||
|
private:
|
||||||
|
object::object_ptr<ObjectType> ptr_;
|
||||||
|
std::string join_column_;
|
||||||
|
};
|
||||||
|
|
||||||
|
template < typename ObjectType >
|
||||||
|
class insert_step_processor {
|
||||||
|
public:
|
||||||
|
explicit insert_step_processor(query_builder_context &ctx)
|
||||||
|
: ctx_{ctx}
|
||||||
|
{}
|
||||||
|
|
||||||
|
utils::result<void, utils::error> build(object::object_ptr<ObjectType> ptr, const bool as_relation_step = false) {
|
||||||
|
if (!ptr) {
|
||||||
|
return utils::failure(utils::error{error_code::InvalidObject, "Object is null"});
|
||||||
|
}
|
||||||
|
ptr_ = ptr;
|
||||||
|
|
||||||
|
const auto key = make_entity_visit_key<ObjectType>(*ptr_);
|
||||||
|
if (ctx_.visited_.find(key) != ctx_.visited_.end()) {
|
||||||
|
return utils::ok<void>();
|
||||||
|
}
|
||||||
|
ctx_.visited_.insert(key);
|
||||||
|
|
||||||
|
const auto it = ctx_.schema_.find(typeid(ObjectType));
|
||||||
|
if (it == ctx_.schema_.end()) {
|
||||||
|
return utils::failure(utils::error{error_code::UnknownType, "Unknown type"});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 1) Traverse relations first => dependencies will be inserted before this object
|
||||||
|
try {
|
||||||
|
access::process(*this, *ptr_);
|
||||||
|
} catch (const query_builder_exception &ex) {
|
||||||
|
return utils::failure(ex.error());
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2) Build INSERT for this object
|
||||||
|
const auto &info = it->second.node().info();
|
||||||
|
if (!info.has_primary_key() || it->second.pk_generator().type() == utils::generator_type::None) {
|
||||||
|
return utils::failure(utils::error{error_code::MissingPrimaryKey, "Type " + info.name() + " has no primary key"});
|
||||||
|
}
|
||||||
|
const auto cit = ctx_.contexts_by_type_.find(it->second.node().info().type_index());
|
||||||
|
if (cit == ctx_.contexts_by_type_.end()) {
|
||||||
|
return utils::failure(utils::error{error_code::UnknownType, "Unknown type"});
|
||||||
|
}
|
||||||
|
|
||||||
|
auto step = create_insert_step(cit->second.insert, it->second);
|
||||||
|
if (as_relation_step) {
|
||||||
|
ctx_.relation_steps_.push_back(std::move(step));
|
||||||
|
} else {
|
||||||
|
ctx_.steps_.push_back(std::move(step));
|
||||||
|
}
|
||||||
|
|
||||||
|
ptr_.reset();
|
||||||
|
|
||||||
|
return utils::ok<void>();
|
||||||
|
}
|
||||||
|
|
||||||
|
template < class PrimaryKeyType >
|
||||||
|
static void on_primary_key(const char * /*id*/, PrimaryKeyType &, const utils::primary_key_attribute& /*attr*/) {}
|
||||||
|
static void on_revision(const char * /*id*/, uint64_t &/*rev*/) {}
|
||||||
|
template<typename Type>
|
||||||
|
static void on_attribute(const char * /*id*/, Type &, const utils::field_attributes &/*attr*/) {}
|
||||||
|
|
||||||
template<class Pointer>
|
template<class Pointer>
|
||||||
void on_belongs_to(const char * /*id*/, Pointer &obj, const utils::foreign_attributes &attr) {
|
void on_belongs_to(const char * /*id*/, Pointer &obj, const utils::foreign_attributes &attr) {
|
||||||
@@ -135,150 +121,246 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
template<class Pointer>
|
template<class Pointer>
|
||||||
void on_has_one(const char * /*id*/, Pointer &obj, const utils::foreign_attributes &attr) {
|
void on_has_one(const char * /*id*/, Pointer &obj, const char * /*join_column*/, const utils::foreign_attributes &attr) {
|
||||||
on_foreign_object(obj, attr);
|
on_foreign_object(obj, attr);
|
||||||
}
|
}
|
||||||
template<class Collection>
|
|
||||||
static void on_has_many(const char * /*id*/, Collection &con, const char * /*join_column*/, const utils::foreign_attributes & ) {}
|
template<class CollectionType>
|
||||||
template<class Collection>
|
void on_has_many(const char * /*id*/,
|
||||||
void on_has_many_to_many(const char *id, Collection &con, const char *join_column, const char *inverse_join_column, const utils::foreign_attributes & ) {
|
object::collection<object::object_ptr<CollectionType>> &objects,
|
||||||
if (id == nullptr || join_column == nullptr || inverse_join_column == nullptr) {
|
const char *join_column,
|
||||||
|
const utils::foreign_attributes &attr) {
|
||||||
|
if (join_column == nullptr) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (current_entity_pk_ == 0ULL) {
|
if (!utils::is_cascade_type_set(attr.cascade(), utils::cascade_type::Insert)) {
|
||||||
// Without a local PK we cannot create relation rows
|
|
||||||
// (PK may be assigned later by Identity; that case needs a different mechanism than requested here)
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto rel_it = schema_.find(std::string{id});
|
has_many_linker<ObjectType> linker(ptr_, join_column);
|
||||||
if (rel_it == schema_.end()) {
|
insert_step_processor<CollectionType> processor{ctx_};
|
||||||
throw query_builder_exception(query_build_error::UnknownType);
|
for (auto &obj : objects) {
|
||||||
}
|
|
||||||
const table &rel_table = rel_it->second.table();
|
|
||||||
|
|
||||||
for (auto &obj : con) {
|
|
||||||
if (!obj) {
|
if (!obj) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ensure target exists as dependency (deps first)
|
if (obj.is_transient()) {
|
||||||
if (!obj.is_persistent()) {
|
auto result = processor.build(obj, true);
|
||||||
using dep_t = std::remove_reference_t<decltype(*obj)>;
|
if (!result) {
|
||||||
build_for<dep_t>(obj);
|
throw query_builder_exception(result.release_error());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Extract FK value from the foreign object
|
access::process(linker, *obj);
|
||||||
pk_value_extractor fk{};
|
}
|
||||||
access::process(fk, *obj);
|
}
|
||||||
if (fk.value == 0ULL) {
|
|
||||||
|
template<class CollectionType>
|
||||||
|
void on_has_many(const char *id,
|
||||||
|
object::collection<CollectionType> &objects,
|
||||||
|
const char *join_column,
|
||||||
|
const utils::foreign_attributes &attr) {
|
||||||
|
if (id == nullptr || join_column == nullptr) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!utils::is_cascade_type_set(attr.cascade(), utils::cascade_type::Insert)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto it = ctx_.schema_.find(std::string{id});
|
||||||
|
if (it == ctx_.schema_.end()) {
|
||||||
|
throw query_builder_exception(error_code::UnknownType, "Unknown type " + std::string{id});
|
||||||
|
}
|
||||||
|
|
||||||
|
using relation_value_type = object::many_to_relation<ObjectType, CollectionType>;
|
||||||
|
if (std::type_index(typeid(relation_value_type)) != it->second.node().info().type_index()) {
|
||||||
|
throw query_builder_exception(error_code::InvalidRelationType, "Invalid relation type");
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto cit = ctx_.contexts_by_type_.find(it->second.node().info().type_index());
|
||||||
|
if (cit == ctx_.contexts_by_type_.end()) {
|
||||||
|
throw query_builder_exception(error_code::UnknownType, "Unknown type" + std::string{id});
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto &obj : objects) {
|
||||||
|
auto rel = object::make_object<relation_value_type>(join_column, "value", ptr_, obj);
|
||||||
|
|
||||||
|
ctx_.relation_steps_.push_back(std::make_unique<insert_step_relation<relation_value_type>>(cit->second.insert, rel));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class ForeignType>
|
||||||
|
void on_has_many_to_many(const char *id,
|
||||||
|
object::collection<object::object_ptr<ForeignType>> &objects,
|
||||||
|
const char *join_column,
|
||||||
|
const char *inverse_join_column,
|
||||||
|
const utils::foreign_attributes &attr) {
|
||||||
|
if (id == nullptr || join_column == nullptr || inverse_join_column == nullptr) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
using relation_value_type = object::many_to_many_relation<ObjectType, ForeignType>;
|
||||||
|
const std::type_index foreign_type{typeid(ForeignType)};
|
||||||
|
const std::type_index local_type{typeid(ObjectType)};
|
||||||
|
insert_many_to_many_relations<relation_value_type>(
|
||||||
|
id,
|
||||||
|
objects,
|
||||||
|
attr,
|
||||||
|
[foreign_type, local_type](const char* relation_name) -> processing_many_to_many_key {
|
||||||
|
return {std::string{relation_name}, local_type, foreign_type};
|
||||||
|
},
|
||||||
|
[this, join_column, inverse_join_column](const auto &obj) {
|
||||||
|
return object::make_object<relation_value_type>(join_column, inverse_join_column, ptr_, obj);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class ForeignType>
|
||||||
|
void on_has_many_to_many(const char *id, object::collection<object::object_ptr<ForeignType>> &objects, const utils::foreign_attributes &attr) {
|
||||||
|
if (id == nullptr) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
object::join_columns_collector collector;
|
||||||
|
auto join_columns = collector.collect<ForeignType>();
|
||||||
|
if (join_columns.join_column.empty() || join_columns.inverse_join_column.empty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
using relation_value_type = object::many_to_many_relation<ForeignType, ObjectType>;
|
||||||
|
const std::type_index foreign_type{typeid(ForeignType)};
|
||||||
|
const std::type_index local_type{typeid(ObjectType)};
|
||||||
|
insert_many_to_many_relations<relation_value_type>(
|
||||||
|
id,
|
||||||
|
objects,
|
||||||
|
attr,
|
||||||
|
[foreign_type, local_type](const char* relation_name) -> processing_many_to_many_key {
|
||||||
|
return {std::string{relation_name}, foreign_type, local_type};
|
||||||
|
},
|
||||||
|
[this, join_columns = std::move(join_columns)](const auto &obj) {
|
||||||
|
return object::make_object<relation_value_type>(join_columns.inverse_join_column, join_columns.join_column, obj, ptr_);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
template<class PointerType>
|
||||||
|
void on_foreign_object(object::object_ptr<PointerType> &obj, const utils::foreign_attributes &attr) {
|
||||||
|
if (!utils::is_cascade_type_set(attr.cascade(), utils::cascade_type::Insert) || !obj || !obj.is_transient()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
insert_step_processor<PointerType> processor{ctx_};
|
||||||
|
|
||||||
|
auto result = processor.build(obj);
|
||||||
|
if (!result) {
|
||||||
|
throw query_builder_exception(result.release_error());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class LocalType, class ForeignType, class RelationKeyFactory, class RelationFactory>
|
||||||
|
void insert_many_to_many_relations(const char *id,
|
||||||
|
object::collection<object::object_ptr<ForeignType>> &objects,
|
||||||
|
const utils::foreign_attributes &attr,
|
||||||
|
RelationKeyFactory make_relation_key,
|
||||||
|
RelationFactory make_relation) {
|
||||||
|
if (!utils::is_cascade_type_set(attr.cascade(), utils::cascade_type::Insert)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto key = make_relation_key(id);
|
||||||
|
if (ctx_.processing_many_to_many_relations_.find(key) != ctx_.processing_many_to_many_relations_.end()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto it = ctx_.schema_.find(std::string{id});
|
||||||
|
if (it == ctx_.schema_.end()) {
|
||||||
|
throw query_builder_exception(error_code::UnknownType, "Unknown type for relation " + std::string{id});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (std::type_index(typeid(LocalType)) != it->second.node().info().type_index()) {
|
||||||
|
throw query_builder_exception(error_code::InvalidRelationType, "Invalid relation type for " + std::string{id});
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto cit = ctx_.contexts_by_type_.find(it->second.node().info().type_index());
|
||||||
|
if (cit == ctx_.contexts_by_type_.end()) {
|
||||||
|
throw query_builder_exception(error_code::UnknownType, "No query contexts for type " + it->second.node().name());
|
||||||
|
}
|
||||||
|
|
||||||
|
std::ignore = ctx_.processing_many_to_many_relations_.insert(key);
|
||||||
|
std::vector<std::unique_ptr<execute_step>> insert_relation_steps;
|
||||||
|
insert_step_processor<ForeignType> processor(ctx_);
|
||||||
|
for (auto &obj : objects) {
|
||||||
|
if (!obj) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Build INSERT into relation table with the 2 FK columns
|
if (obj.is_transient()) {
|
||||||
insert_step rel_step{};
|
auto result = processor.build(obj, true);
|
||||||
rel_step.query = executable_query{
|
if (!result) {
|
||||||
query::query::insert()
|
throw query_builder_exception(result.release_error());
|
||||||
.into(rel_table, {table_column{&rel_table, join_column}, table_column{&rel_table, inverse_join_column}})
|
};
|
||||||
.values({utils::database_type{current_entity_pk_}, utils::database_type{fk.value}})
|
}
|
||||||
};
|
|
||||||
relation_steps_.push_back(std::move(rel_step));
|
auto rel = make_relation(obj);
|
||||||
|
|
||||||
|
// access::process(*this, *rel);
|
||||||
|
|
||||||
|
insert_relation_steps.push_back(std::make_unique<insert_step_relation<LocalType>>(cit->second.insert, rel));
|
||||||
}
|
}
|
||||||
|
ctx_.relation_steps_.insert(
|
||||||
|
ctx_.relation_steps_.end(),
|
||||||
|
std::make_move_iterator(insert_relation_steps.begin()),
|
||||||
|
std::make_move_iterator(insert_relation_steps.end()));
|
||||||
|
ctx_.processing_many_to_many_relations_.erase(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::unique_ptr<execute_step> create_insert_step(const sql::query_context& query_ctx, const schema_node& node) {
|
||||||
|
if (node.pk_generator().type() == utils::generator_type::Manual) {
|
||||||
|
return std::make_unique<insert_step_pk_manual<ObjectType>>(query_ctx, ptr_);
|
||||||
|
}
|
||||||
|
if (node.pk_generator().type() == utils::generator_type::Identity) {
|
||||||
|
return std::make_unique<insert_step_pk_identity<ObjectType>>(query_ctx, ptr_, node.node().info().primary_key_attribute()->name());
|
||||||
|
}
|
||||||
|
return std::make_unique<insert_step_pk_generated<ObjectType>>(query_ctx, ptr_, node.pk_generator());
|
||||||
}
|
}
|
||||||
template<class C>
|
|
||||||
static void on_has_many_to_many(const char * /*id*/, C &, const utils::foreign_attributes & ) {}
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
template<class EntityType>
|
query_builder_context& ctx_;
|
||||||
static std::pair<std::type_index, const void *> make_visit_key(const object::object_ptr<EntityType> &ptr) {
|
object::object_ptr<ObjectType> ptr_;
|
||||||
return {std::type_index(typeid(EntityType)), static_cast<const void *>(&(*ptr))};
|
};
|
||||||
|
|
||||||
|
template<class ObjectType>
|
||||||
|
class insert_query_builder {
|
||||||
|
public:
|
||||||
|
explicit insert_query_builder(const basic_schema &schema, const std::unordered_map<std::type_index, query_contexts> &contexts_by_type)
|
||||||
|
: schema_(schema)
|
||||||
|
, contexts_by_type_(contexts_by_type)
|
||||||
|
{}
|
||||||
|
|
||||||
|
utils::result<std::vector<std::unique_ptr<execute_step>>, utils::error> build(const object::object_ptr<ObjectType> &ptr) {
|
||||||
|
if (const auto it = schema_.find(typeid(ObjectType)); it == schema_.end()) {
|
||||||
|
return utils::failure(utils::error{error_code::UnknownType, "Unknown type for insert query"});
|
||||||
|
}
|
||||||
|
|
||||||
|
query_builder_context ctx{schema_, contexts_by_type_};
|
||||||
|
insert_step_processor<ObjectType> processor{ctx};
|
||||||
|
|
||||||
|
const auto result = processor.build(ptr);
|
||||||
|
if (!result) {
|
||||||
|
return utils::failure(result.err());
|
||||||
|
}
|
||||||
|
|
||||||
|
// relation inserts must run after all entity inserts were collected
|
||||||
|
for (auto &s : ctx.relation_steps_) {
|
||||||
|
ctx.steps_.push_back(std::move(s));
|
||||||
|
}
|
||||||
|
ctx.relation_steps_.clear();
|
||||||
|
|
||||||
|
return utils::ok(std::move(ctx.steps_));
|
||||||
}
|
}
|
||||||
|
|
||||||
struct visit_key_hash {
|
|
||||||
size_t operator()(const std::pair<std::type_index, const void *> &p) const noexcept {
|
|
||||||
// combine hashes (simple + sufficient here)
|
|
||||||
const size_t h1 = p.first.hash_code();
|
|
||||||
const size_t h2 = std::hash<const void *>{}(p.second);
|
|
||||||
return h1 ^ (h2 + 0x9e3779b97f4a7c15ULL + (h1 << 6) + (h1 >> 2));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
template<class EntityType>
|
|
||||||
void build_for(const object::object_ptr<EntityType> &ptr) {
|
|
||||||
if (!ptr) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const auto key = make_visit_key<EntityType>(ptr);
|
|
||||||
if (visited_.find(key) != visited_.end()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
visited_.insert(key);
|
|
||||||
|
|
||||||
const auto it = schema_.find(typeid(EntityType));
|
|
||||||
if (it == schema_.end()) {
|
|
||||||
throw query_builder_exception(query_build_error::UnknownType);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 1) Traverse relations first => dependencies will be inserted before this object
|
|
||||||
access::process(*this, *ptr);
|
|
||||||
|
|
||||||
// 2) Build INSERT for this object
|
|
||||||
const auto &info = it->second.node().info();
|
|
||||||
insert_step step{};
|
|
||||||
if (info.has_primary_key()) {
|
|
||||||
step.pk_name = info.primary_key_attribute()->name();
|
|
||||||
step.pk_generator = it->second.generator_type();
|
|
||||||
|
|
||||||
step.pk_is_unset = [ptr, name = step.pk_name]() {
|
|
||||||
pk_unset_checker chk{name};
|
|
||||||
access::process(chk, *ptr);
|
|
||||||
return chk.unset;
|
|
||||||
};
|
|
||||||
|
|
||||||
step.set_pk = [ptr, name = step.pk_name](std::uint64_t v) {
|
|
||||||
pk_setter setter{name, v};
|
|
||||||
access::process(setter, *ptr);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
if (info.has_primary_key() && step.pk_generator == utils::generator_type::Identity) {
|
|
||||||
const table_column pk_col(&it->second.table(), step.pk_name);
|
|
||||||
step.query = fetchable_query{query::query::insert().into(it->second.table()).values(*ptr).returning(pk_col)};
|
|
||||||
step.apply_returning = [ptr, pk_name = step.pk_name](const sql::record &rec) {
|
|
||||||
if (auto v = rec.at<std::uint64_t>(pk_name); v.has_value()) {
|
|
||||||
pk_setter setter{pk_name, *v};
|
|
||||||
access::process(setter, *ptr);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
} else {
|
|
||||||
step.query = executable_query{query::query::insert().into(it->second.table()).values(*ptr)};
|
|
||||||
}
|
|
||||||
steps_.push_back(std::move(step));
|
|
||||||
}
|
|
||||||
|
|
||||||
template<class Pointer>
|
|
||||||
void on_foreign_object(Pointer &obj, const utils::foreign_attributes & /*attr*/) {
|
|
||||||
if (!obj) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Dependency only matters if the referenced object must be inserted
|
|
||||||
if (obj.is_persistent()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
using dep_t = std::remove_reference_t<decltype(*obj)>;
|
|
||||||
build_for<dep_t>(obj);
|
|
||||||
}
|
|
||||||
private:
|
private:
|
||||||
const basic_schema &schema_;
|
const basic_schema &schema_;
|
||||||
|
const std::unordered_map<std::type_index, query_contexts> &contexts_by_type_;
|
||||||
std::vector<insert_step> steps_;
|
|
||||||
std::vector<insert_step> relation_steps_;
|
|
||||||
std::unordered_set<std::pair<std::type_index, const void *>, visit_key_hash> visited_;
|
|
||||||
|
|
||||||
std::uint64_t current_entity_pk_{0};
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
#endif //MATADOR_INSERT_QUERY_BUILDER_HPP
|
#endif //MATADOR_INSERT_QUERY_BUILDER_HPP
|
||||||
@@ -0,0 +1,180 @@
|
|||||||
|
#ifndef MATADOR_INSERT_STEP_HPP
|
||||||
|
#define MATADOR_INSERT_STEP_HPP
|
||||||
|
|
||||||
|
#include "matador/query/abstract_pk_generator.hpp"
|
||||||
|
#include "matador/query/execute_step.hpp"
|
||||||
|
#include "matador/query/error_code.hpp"
|
||||||
|
|
||||||
|
#include "matador/object/object_cache.hpp"
|
||||||
|
#include "matador/object/object_ptr.hpp"
|
||||||
|
|
||||||
|
#include "matador/sql/execute_result.hpp"
|
||||||
|
#include "matador/sql/resolver_service.hpp"
|
||||||
|
#include "matador/sql/statement.hpp"
|
||||||
|
|
||||||
|
namespace matador::query {
|
||||||
|
template<typename ObjectType>
|
||||||
|
utils::result<void, utils::error> finalize_inserted_object(object::object_ptr<ObjectType> &ptr,
|
||||||
|
const utils::identifier& pk,
|
||||||
|
object::object_cache &cache,
|
||||||
|
const std::shared_ptr<sql::resolver_service> &resolver_service) {
|
||||||
|
if (!ptr) {
|
||||||
|
return utils::failure(utils::error(error_code::InvalidObject, "Inserted object is null."));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!resolver_service) {
|
||||||
|
return utils::failure(utils::error(error_code::UnknownType, "Missing resolver service."));
|
||||||
|
}
|
||||||
|
|
||||||
|
auto resolver = resolver_service->template object_resolver<ObjectType>();
|
||||||
|
if (!resolver) {
|
||||||
|
return utils::failure(utils::error(error_code::UnknownType, "Missing object resolver for inserted type."));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!cache.import<ObjectType>(pk, ptr.proxy(), resolver)) {
|
||||||
|
return utils::failure(utils::error(error_code::FailedToFindObject, "Object cache already contains another live proxy for inserted object."));
|
||||||
|
}
|
||||||
|
|
||||||
|
ptr.change_state(object::object_state::Persistent);
|
||||||
|
return utils::ok<void>();
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename ObjectType>
|
||||||
|
class insert_step_pk_generated : public execute_step {
|
||||||
|
public:
|
||||||
|
insert_step_pk_generated(sql::query_context ctx, const object::object_ptr<ObjectType>& ptr, abstract_pk_generator& pk_generator)
|
||||||
|
: execute_step(std::move(ctx))
|
||||||
|
, ptr_(ptr)
|
||||||
|
, pk_generator_(pk_generator){}
|
||||||
|
|
||||||
|
utils::result<void, utils::error> prepare(sql::executor& conn) override {
|
||||||
|
auto result = pk_generator_.next_id(conn);
|
||||||
|
if (!result.is_ok()) {
|
||||||
|
return utils::failure(result.err());
|
||||||
|
}
|
||||||
|
id_ = *result;
|
||||||
|
pk_accessor_.set(*ptr_, id_);
|
||||||
|
|
||||||
|
return utils::ok<void>();
|
||||||
|
}
|
||||||
|
|
||||||
|
utils::result<void, utils::error> execute(sql::statement& stmt) override {
|
||||||
|
stmt.bind(*ptr_);
|
||||||
|
|
||||||
|
if (const auto exec_result = stmt.execute(); !exec_result.is_ok()) {
|
||||||
|
return utils::failure(exec_result.err());
|
||||||
|
}
|
||||||
|
|
||||||
|
ptr_.change_state(object::object_state::Persistent);
|
||||||
|
return utils::ok<void>();
|
||||||
|
}
|
||||||
|
|
||||||
|
utils::result<void, utils::error> finalize(object::object_cache& cache, const resolver_service_ptr& resolver_service) override {
|
||||||
|
return finalize_inserted_object(ptr_, id_, cache, resolver_service);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
object::object_ptr<ObjectType> ptr_;
|
||||||
|
abstract_pk_generator& pk_generator_;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename ObjectType>
|
||||||
|
class insert_step_pk_identity : public execute_step {
|
||||||
|
public:
|
||||||
|
insert_step_pk_identity(sql::query_context ctx, const object::object_ptr<ObjectType>& ptr, std::string pk_column_name)
|
||||||
|
: execute_step(std::move(ctx))
|
||||||
|
, ptr_(ptr)
|
||||||
|
, pk_column_name_(std::move(pk_column_name)){}
|
||||||
|
|
||||||
|
utils::result<void, utils::error> prepare(sql::executor&) override {
|
||||||
|
return utils::ok<void>();
|
||||||
|
}
|
||||||
|
|
||||||
|
utils::result<void, utils::error> execute(sql::statement& stmt) override {
|
||||||
|
stmt.bind(*ptr_);
|
||||||
|
|
||||||
|
auto result = stmt.fetch_one();
|
||||||
|
if (!result.is_ok()) {
|
||||||
|
return utils::failure(result.err());
|
||||||
|
}
|
||||||
|
if (!result.value().has_value()) {
|
||||||
|
return utils::failure(utils::error(error_code::FailedToFindObject, "Failed to insert object and retrieve identity."));
|
||||||
|
}
|
||||||
|
|
||||||
|
auto rec = result->value();
|
||||||
|
const auto& f = rec.at(pk_column_name_);
|
||||||
|
if (auto res = id_.assign(f.value()); !res.is_ok()) {
|
||||||
|
return utils::failure(res.err());
|
||||||
|
}
|
||||||
|
pk_accessor_.set(*ptr_, id_);
|
||||||
|
|
||||||
|
ptr_.change_state(object::object_state::Persistent);
|
||||||
|
return utils::ok<void>();
|
||||||
|
}
|
||||||
|
|
||||||
|
utils::result<void, utils::error> finalize(object::object_cache& cache, const resolver_service_ptr& resolver_service) override {
|
||||||
|
return finalize_inserted_object(ptr_, id_, cache, resolver_service);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
object::object_ptr<ObjectType> ptr_;
|
||||||
|
std::string pk_column_name_;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename ObjectType>
|
||||||
|
class insert_step_pk_manual : public execute_step {
|
||||||
|
public:
|
||||||
|
insert_step_pk_manual(sql::query_context ctx, const object::object_ptr<ObjectType>& ptr)
|
||||||
|
: execute_step(std::move(ctx))
|
||||||
|
, ptr_(ptr) {}
|
||||||
|
|
||||||
|
utils::result<void, utils::error> prepare(sql::executor &) override {
|
||||||
|
return utils::ok<void>();
|
||||||
|
}
|
||||||
|
utils::result<void, utils::error> execute(sql::statement &stmt) override {
|
||||||
|
stmt.bind(*ptr_);
|
||||||
|
if (const auto exec_result = stmt.execute(); !exec_result.is_ok()) {
|
||||||
|
return utils::failure(exec_result.err());
|
||||||
|
}
|
||||||
|
|
||||||
|
id_ = object::primary_key_resolver::resolve_object(*ptr_).pk;
|
||||||
|
ptr_.change_state(object::object_state::Persistent);
|
||||||
|
return utils::ok<void>();
|
||||||
|
}
|
||||||
|
|
||||||
|
utils::result<void, utils::error> finalize(object::object_cache& cache, const resolver_service_ptr& resolver_service) override {
|
||||||
|
return finalize_inserted_object(ptr_, id_, cache, resolver_service);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
object::object_ptr<ObjectType> ptr_;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename ObjectType>
|
||||||
|
class insert_step_relation : public execute_step {
|
||||||
|
public:
|
||||||
|
insert_step_relation(sql::query_context ctx, const object::object_ptr<ObjectType>& ptr)
|
||||||
|
: execute_step(std::move(ctx))
|
||||||
|
, ptr_(ptr) {}
|
||||||
|
|
||||||
|
utils::result<void, utils::error> prepare(sql::executor &) override {
|
||||||
|
return utils::ok<void>();
|
||||||
|
}
|
||||||
|
utils::result<void, utils::error> execute(sql::statement &stmt) override {
|
||||||
|
stmt.bind(*ptr_);
|
||||||
|
if (const auto exec_result = stmt.execute(); !exec_result.is_ok()) {
|
||||||
|
return utils::failure(exec_result.err());
|
||||||
|
}
|
||||||
|
|
||||||
|
return utils::ok<void>();
|
||||||
|
}
|
||||||
|
|
||||||
|
utils::result<void, utils::error> finalize(object::object_cache& /*cache*/, const resolver_service_ptr& /*resolver_service*/) override {
|
||||||
|
return utils::ok<void>();
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
object::object_ptr<ObjectType> ptr_;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
#endif //MATADOR_INSERT_STEP_HPP
|
||||||
@@ -7,6 +7,7 @@
|
|||||||
#include "../../utils/result.hpp"
|
#include "../../utils/result.hpp"
|
||||||
|
|
||||||
namespace matador::sql {
|
namespace matador::sql {
|
||||||
|
class dialect;
|
||||||
class executor;
|
class executor;
|
||||||
class statement;
|
class statement;
|
||||||
struct execute_result;
|
struct execute_result;
|
||||||
@@ -21,8 +22,8 @@ public:
|
|||||||
|
|
||||||
[[nodiscard]] utils::result<sql::execute_result, utils::error> execute(const sql::executor &exec) const;
|
[[nodiscard]] utils::result<sql::execute_result, utils::error> execute(const sql::executor &exec) const;
|
||||||
[[nodiscard]] utils::result<sql::statement, utils::error> prepare(sql::executor &exec) const;
|
[[nodiscard]] utils::result<sql::statement, utils::error> prepare(sql::executor &exec) const;
|
||||||
[[nodiscard]] sql::query_context compile(const sql::executor &exec) const;
|
[[nodiscard]] sql::query_context compile(const sql::dialect &d) const;
|
||||||
[[nodiscard]] std::string str(const sql::executor &exec) const;
|
[[nodiscard]] std::string str(const sql::dialect &d) const;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -75,7 +75,6 @@ public:
|
|||||||
|
|
||||||
[[nodiscard]] utils::result<sql::statement, utils::error> prepare(sql::executor &exec) const;
|
[[nodiscard]] utils::result<sql::statement, utils::error> prepare(sql::executor &exec) const;
|
||||||
|
|
||||||
[[nodiscard]] std::string str(const sql::executor &exec) const;
|
|
||||||
[[nodiscard]] std::string str(const sql::dialect &d) const;
|
[[nodiscard]] std::string str(const sql::dialect &d) const;
|
||||||
[[nodiscard]] sql::query_context compile(const sql::dialect &d) const;
|
[[nodiscard]] sql::query_context compile(const sql::dialect &d) const;
|
||||||
|
|
||||||
|
|||||||
@@ -11,6 +11,5 @@ public:
|
|||||||
|
|
||||||
[[nodiscard]] query_alter_table_intermediate table(const table &tab) const;
|
[[nodiscard]] query_alter_table_intermediate table(const table &tab) const;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
#endif //MATADOR_QUERY_ALTER_INTERMEDIATE_HPP
|
#endif //MATADOR_QUERY_ALTER_INTERMEDIATE_HPP
|
||||||
@@ -30,7 +30,7 @@ public:
|
|||||||
using query_intermediate::query_intermediate;
|
using query_intermediate::query_intermediate;
|
||||||
|
|
||||||
query_create_table_columns_intermediate columns(std::initializer_list<object::attribute> attributes);
|
query_create_table_columns_intermediate columns(std::initializer_list<object::attribute> attributes);
|
||||||
query_create_table_columns_intermediate columns(const std::list<object::attribute> &attributes);
|
query_create_table_columns_intermediate columns(const std::vector<object::attribute> &attributes);
|
||||||
query_create_table_columns_intermediate columns(std::initializer_list<table_column> columns);
|
query_create_table_columns_intermediate columns(std::initializer_list<table_column> columns);
|
||||||
query_create_table_columns_intermediate columns(const std::list<table_column> &columns);
|
query_create_table_columns_intermediate columns(const std::list<table_column> &columns);
|
||||||
query_create_table_columns_intermediate columns(const std::vector<table_column> &columns);
|
query_create_table_columns_intermediate columns(const std::vector<table_column> &columns);
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
#define QUERY_EXECUTE_WHERE_INTERMEDIATE_HPP
|
#define QUERY_EXECUTE_WHERE_INTERMEDIATE_HPP
|
||||||
|
|
||||||
#include "matador/query/intermediates/executable_query.hpp"
|
#include "matador/query/intermediates/executable_query.hpp"
|
||||||
|
#include "matador/query/intermediates/fetchable_query.hpp"
|
||||||
|
|
||||||
namespace matador::query {
|
namespace matador::query {
|
||||||
|
|
||||||
@@ -14,9 +15,18 @@ class query_execute_where_intermediate : public executable_query
|
|||||||
public:
|
public:
|
||||||
using executable_query::executable_query;
|
using executable_query::executable_query;
|
||||||
|
|
||||||
|
template<typename... TableColumns>
|
||||||
|
fetchable_query returning(TableColumns... table_columns) {
|
||||||
|
const std::vector<table_column> tcv { std::forward<TableColumns>(table_columns)... };
|
||||||
|
return returning(tcv);
|
||||||
|
}
|
||||||
|
|
||||||
query_execute_limit_intermediate limit(size_t limit);
|
query_execute_limit_intermediate limit(size_t limit);
|
||||||
query_execute_order_by_intermediate order_by(const table_column &col);
|
query_execute_order_by_intermediate order_by(const table_column &col);
|
||||||
query_execute_order_by_intermediate order_by(std::initializer_list<table_column> columns);
|
query_execute_order_by_intermediate order_by(std::initializer_list<table_column> columns);
|
||||||
|
|
||||||
|
private:
|
||||||
|
fetchable_query returning(const std::vector<table_column>& tables);
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,6 @@
|
|||||||
#ifndef QUERY_INTERMEDIATE_HPP
|
#ifndef QUERY_INTERMEDIATE_HPP
|
||||||
#define QUERY_INTERMEDIATE_HPP
|
#define QUERY_INTERMEDIATE_HPP
|
||||||
|
|
||||||
// #include "matador/query/query_data.hpp"
|
|
||||||
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
||||||
namespace matador::query {
|
namespace matador::query {
|
||||||
@@ -11,7 +9,7 @@ struct query_data;
|
|||||||
class query_intermediate {
|
class query_intermediate {
|
||||||
public:
|
public:
|
||||||
query_intermediate();
|
query_intermediate();
|
||||||
query_intermediate(const std::shared_ptr<query_data> &context); // NOLINT(*-explicit-constructor)
|
query_intermediate(const std::shared_ptr<query_data> &context); // NOLINT(*-explicit-constructor)
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
std::shared_ptr<query_data> context_;
|
std::shared_ptr<query_data> context_;
|
||||||
|
|||||||
@@ -2,22 +2,15 @@
|
|||||||
#define QUERY_INTO_INTERMEDIATE_HPP
|
#define QUERY_INTO_INTERMEDIATE_HPP
|
||||||
|
|
||||||
#include "matador/query/intermediates/executable_query.hpp"
|
#include "matador/query/intermediates/executable_query.hpp"
|
||||||
#include "matador/query/intermediates/fetchable_query.hpp"
|
#include "matador/query/intermediates/query_values_intermediate.hpp"
|
||||||
|
|
||||||
#include "matador/query/value_extractor.hpp"
|
#include "matador/query/value_extractor.hpp"
|
||||||
|
|
||||||
|
#include "matador/object/object_ptr.hpp"
|
||||||
|
|
||||||
#include "matador/utils/placeholder.hpp"
|
#include "matador/utils/placeholder.hpp"
|
||||||
|
|
||||||
namespace matador::query {
|
namespace matador::query {
|
||||||
class table_column;
|
|
||||||
|
|
||||||
class query_values_intermediate : public executable_query {
|
|
||||||
public:
|
|
||||||
using executable_query::executable_query;
|
|
||||||
|
|
||||||
fetchable_query returning(const table_column &col);
|
|
||||||
};
|
|
||||||
|
|
||||||
class query_into_intermediate : public query_intermediate {
|
class query_into_intermediate : public query_intermediate {
|
||||||
public:
|
public:
|
||||||
using query_intermediate::query_intermediate;
|
using query_intermediate::query_intermediate;
|
||||||
@@ -30,6 +23,10 @@ public:
|
|||||||
query_values_intermediate values(const Type &obj) {
|
query_values_intermediate values(const Type &obj) {
|
||||||
return values(value_extractor::extract(obj));
|
return values(value_extractor::extract(obj));
|
||||||
}
|
}
|
||||||
|
template<class Type>
|
||||||
|
query_values_intermediate values(const object::object_ptr<Type> &obj) {
|
||||||
|
return values(*obj);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ public:
|
|||||||
explicit query_select_intermediate(const std::vector<table_column>& columns);
|
explicit query_select_intermediate(const std::vector<table_column>& columns);
|
||||||
|
|
||||||
fetchable_query nextval(const std::string& sequence_name);
|
fetchable_query nextval(const std::string& sequence_name);
|
||||||
|
fetchable_query currval(const std::string& sequence_name);
|
||||||
|
|
||||||
template<typename... Tables>
|
template<typename... Tables>
|
||||||
query_from_intermediate from(const Tables&... tables) {
|
query_from_intermediate from(const Tables&... tables) {
|
||||||
|
|||||||
@@ -9,14 +9,11 @@
|
|||||||
|
|
||||||
namespace matador::query {
|
namespace matador::query {
|
||||||
|
|
||||||
class query_set_intermediate : public executable_query
|
class query_set_intermediate : public executable_query {
|
||||||
{
|
|
||||||
public:
|
public:
|
||||||
using executable_query::executable_query;
|
using executable_query::executable_query;
|
||||||
|
|
||||||
query_execute_where_intermediate where(std::unique_ptr<abstract_criteria> cond) {
|
query_execute_where_intermediate where(std::unique_ptr<abstract_criteria> cond);
|
||||||
return where_clause(std::move(cond));
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
query_execute_where_intermediate where_clause(std::unique_ptr<abstract_criteria> &&cond);
|
query_execute_where_intermediate where_clause(std::unique_ptr<abstract_criteria> &&cond);
|
||||||
|
|||||||
@@ -2,25 +2,38 @@
|
|||||||
#define QUERY_UPDATE_INTERMEDIATE_HPP
|
#define QUERY_UPDATE_INTERMEDIATE_HPP
|
||||||
|
|
||||||
#include "matador/query/intermediates/query_intermediate.hpp"
|
#include "matador/query/intermediates/query_intermediate.hpp"
|
||||||
|
|
||||||
#include "matador/query/intermediates/query_set_intermediate.hpp"
|
#include "matador/query/intermediates/query_set_intermediate.hpp"
|
||||||
|
|
||||||
#include "matador/query/key_value_generator.hpp"
|
#include "matador/query/expression/column_expression.hpp"
|
||||||
|
#include "matador/query/generator.hpp"
|
||||||
|
|
||||||
#include "matador/query/internal/column_value_pair.hpp"
|
#include "matador/query/internal/column_value_pair.hpp"
|
||||||
|
|
||||||
namespace matador::query {
|
namespace matador::query {
|
||||||
|
namespace internal {
|
||||||
class query_update_intermediate : public query_intermediate {
|
class query_set_part;
|
||||||
|
}
|
||||||
|
class query_update_intermediate : public executable_query {
|
||||||
public:
|
public:
|
||||||
explicit query_update_intermediate(const table& tab);
|
explicit query_update_intermediate(const table& tab);
|
||||||
|
|
||||||
query_set_intermediate set(std::initializer_list<internal::column_value_pair> columns);
|
query_update_intermediate& set(const table_column& col, column_expression&& expression);
|
||||||
query_set_intermediate set(std::vector<internal::column_value_pair> &&columns);
|
|
||||||
template<class Type>
|
template<class Type>
|
||||||
query_set_intermediate set(const Type &obj) {
|
query_set_intermediate set(const Type &obj) {
|
||||||
return set(key_value_generator::generate(obj));
|
return set(generator::column_value_pairs(obj));
|
||||||
}
|
}
|
||||||
|
template<class Type>
|
||||||
|
query_set_intermediate set() {
|
||||||
|
return set(generator::column_value_pairs<Type>());
|
||||||
|
}
|
||||||
|
query_execute_where_intermediate where(std::unique_ptr<abstract_criteria> cond);
|
||||||
|
|
||||||
|
private:
|
||||||
|
query_execute_where_intermediate where_clause(std::unique_ptr<abstract_criteria> &&cond);
|
||||||
|
query_set_intermediate set(std::vector<internal::column_value_pair> &&key_value_pairs);
|
||||||
|
|
||||||
|
private:
|
||||||
|
internal::query_set_part* query_set_part_{nullptr};
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,24 @@
|
|||||||
|
#ifndef MATADOR_QUERY_VALUES_INTERMEDIATE_H
|
||||||
|
#define MATADOR_QUERY_VALUES_INTERMEDIATE_H
|
||||||
|
|
||||||
|
#include "matador/query/intermediates/executable_query.hpp"
|
||||||
|
#include "matador/query/intermediates/fetchable_query.hpp"
|
||||||
|
|
||||||
|
namespace matador::query {
|
||||||
|
class table_column;
|
||||||
|
class query_values_intermediate : public executable_query {
|
||||||
|
public:
|
||||||
|
using executable_query::executable_query;
|
||||||
|
|
||||||
|
template<typename... TableColumns>
|
||||||
|
fetchable_query returning(const TableColumns&... table_columns) {
|
||||||
|
const std::vector<table_column> tcv { table_columns... };
|
||||||
|
return returning(tcv);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
fetchable_query returning(const std::vector<table_column>& tables);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif //MATADOR_QUERY_VALUES_INTERMEDIATE_H
|
||||||
@@ -2,6 +2,7 @@
|
|||||||
#define QUERY_KEY_VALUE_PAIR_HPP
|
#define QUERY_KEY_VALUE_PAIR_HPP
|
||||||
|
|
||||||
#include "matador/query/table_column.hpp"
|
#include "matador/query/table_column.hpp"
|
||||||
|
#include "../expression/column_expression.hpp"
|
||||||
|
|
||||||
#include "matador/utils/placeholder.hpp"
|
#include "matador/utils/placeholder.hpp"
|
||||||
#include "matador/utils/types.hpp"
|
#include "matador/utils/types.hpp"
|
||||||
@@ -10,20 +11,21 @@ namespace matador::query::internal {
|
|||||||
|
|
||||||
class column_value_pair {
|
class column_value_pair {
|
||||||
public:
|
public:
|
||||||
column_value_pair(table_column col, utils::database_type value);
|
column_value_pair() = default;
|
||||||
column_value_pair(std::string name, utils::database_type value);
|
column_value_pair(column_value_pair&& x) = default;
|
||||||
column_value_pair(const char *name, utils::database_type value);
|
column_value_pair& operator=(column_value_pair&& x) = default;
|
||||||
column_value_pair(const char *name, utils::placeholder p);
|
column_value_pair(table_column col, column_expression_ptr expression);
|
||||||
|
|
||||||
friend bool operator==(const column_value_pair &lhs, const column_value_pair &rhs);
|
friend bool operator==(const column_value_pair &lhs, const column_value_pair &rhs);
|
||||||
friend bool operator!=(const column_value_pair &lhs, const column_value_pair &rhs);
|
friend bool operator!=(const column_value_pair &lhs, const column_value_pair &rhs);
|
||||||
|
|
||||||
[[nodiscard]] const table_column& col() const;
|
[[nodiscard]] const table_column& col() const;
|
||||||
[[nodiscard]] const std::variant<utils::placeholder, utils::database_type>& value() const;
|
[[nodiscard]] const abstract_column_expression& expression() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
table_column column_;
|
table_column column_;
|
||||||
std::variant<utils::placeholder, utils::database_type> value_;
|
column_expression_ptr expression_;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,8 +15,6 @@
|
|||||||
#include <list>
|
#include <list>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
||||||
#include "matador/object/restriction.hpp"
|
|
||||||
|
|
||||||
namespace matador::query::internal {
|
namespace matador::query::internal {
|
||||||
|
|
||||||
class query_alter_part final : public query_part {
|
class query_alter_part final : public query_part {
|
||||||
@@ -139,6 +137,19 @@ private:
|
|||||||
std::string sequence_name_;
|
std::string sequence_name_;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class query_select_currval_part final : public query_part {
|
||||||
|
public:
|
||||||
|
explicit query_select_currval_part(std::string sequence_name);
|
||||||
|
|
||||||
|
[[nodiscard]] const std::string& sequence_name() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
void accept(query_part_visitor &visitor) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::string sequence_name_;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents the SQL FROM part
|
* Represents the SQL FROM part
|
||||||
@@ -374,15 +385,15 @@ private:
|
|||||||
class query_set_part final : public query_part
|
class query_set_part final : public query_part
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
explicit query_set_part(const std::vector<internal::column_value_pair>& key_value_pairs);
|
explicit query_set_part(std::vector<column_value_pair>&& key_value_pairs);
|
||||||
|
|
||||||
[[nodiscard]] const std::vector<internal::column_value_pair>& column_values() const;
|
[[nodiscard]] const std::vector<column_value_pair>& column_values() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void accept(query_part_visitor &visitor) override;
|
void accept(query_part_visitor &visitor) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::vector<internal::column_value_pair> key_value_pairs_;
|
std::vector<column_value_pair> key_value_pairs_;
|
||||||
};
|
};
|
||||||
|
|
||||||
class query_delete_part final : public query_part
|
class query_delete_part final : public query_part
|
||||||
|
|||||||
@@ -26,14 +26,16 @@ public:
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template<typename BaseType>
|
||||||
|
static void on_base(const BaseType&) {}
|
||||||
template < class V >
|
template < class V >
|
||||||
void on_primary_key(const char *id, V &x, const utils::primary_key_attribute& /*attr*/ = utils::default_pk_attributes) {
|
void on_primary_key(const char *id, V &x, const utils::primary_key_attribute& /*attr*/) {
|
||||||
result_.emplace_back(id, x);
|
result_.emplace_back(id, x);
|
||||||
}
|
}
|
||||||
void on_revision(const char *id, uint64_t &/*rev*/);
|
void on_revision(const char *id, uint64_t &/*rev*/) const;
|
||||||
|
|
||||||
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 &x, const utils::field_attributes &/*attr*/) {
|
||||||
result_.emplace_back(id, x);
|
result_.emplace_back(id, x);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -42,7 +44,7 @@ public:
|
|||||||
result_.emplace_back(id, fk_value_extractor_.extract(*x));
|
result_.emplace_back(id, fk_value_extractor_.extract(*x));
|
||||||
}
|
}
|
||||||
template<class Type, template < class ... > class Pointer>
|
template<class Type, template < class ... > class Pointer>
|
||||||
void on_has_one(const char *id, Pointer<Type> &x, const utils::foreign_attributes &/*attr*/) {
|
void on_has_one(const char *id, Pointer<Type> &x, const char * /*join_column*/, const utils::foreign_attributes &/*attr*/) {
|
||||||
result_.emplace_back(id, fk_value_extractor_.extract(*x));
|
result_.emplace_back(id, fk_value_extractor_.extract(*x));
|
||||||
}
|
}
|
||||||
template<class ContainerType>
|
template<class ContainerType>
|
||||||
|
|||||||
@@ -0,0 +1,14 @@
|
|||||||
|
#ifndef MATADOR_MANUAL_PK_GENERATOR_HPP
|
||||||
|
#define MATADOR_MANUAL_PK_GENERATOR_HPP
|
||||||
|
|
||||||
|
#include "matador/query/abstract_pk_generator.hpp"
|
||||||
|
|
||||||
|
namespace matador::query {
|
||||||
|
class manual_pk_generator : public abstract_pk_generator {
|
||||||
|
public:
|
||||||
|
manual_pk_generator();
|
||||||
|
[[nodiscard]] utils::result<int64_t, utils::error> next_id(const sql::executor& exec) override;
|
||||||
|
[[nodiscard]] utils::result<int64_t, utils::error> current_id(const sql::executor& exec) override;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
#endif //MATADOR_MANUAL_PK_GENERATOR_HPP
|
||||||
@@ -18,7 +18,7 @@ TABLE_NAME##_table()\
|
|||||||
: TABLE_NAME##_table(#TABLE_NAME) \
|
: TABLE_NAME##_table(#TABLE_NAME) \
|
||||||
{} \
|
{} \
|
||||||
TABLE_NAME##_table(const std::string& alias) \
|
TABLE_NAME##_table(const std::string& alias) \
|
||||||
: typed_table(#TABLE_NAME, alias, {MAP(FIELD_STRING, __VA_ARGS__)}) \
|
: typed_table(#TABLE_NAME, alias, {MAP(FIELD_STRING, __VA_ARGS__)}, {}, {}) \
|
||||||
MAP(INIT_FIELD, __VA_ARGS__) \
|
MAP(INIT_FIELD, __VA_ARGS__) \
|
||||||
{} \
|
{} \
|
||||||
MAP(FIELD, __VA_ARGS__) \
|
MAP(FIELD, __VA_ARGS__) \
|
||||||
|
|||||||
@@ -17,25 +17,20 @@ table_column avg(const std::string &column);
|
|||||||
table_column maximum(const std::string &column);
|
table_column maximum(const std::string &column);
|
||||||
table_column minimum(const std::string &column);
|
table_column minimum(const std::string &column);
|
||||||
|
|
||||||
class query
|
[[nodiscard]] query_create_intermediate create();
|
||||||
{
|
[[nodiscard]] query_drop_intermediate drop();
|
||||||
public:
|
[[nodiscard]] query_select_intermediate select();
|
||||||
[[nodiscard]] static query_create_intermediate create();
|
[[nodiscard]] query_select_intermediate select(std::initializer_list<table_column> columns);
|
||||||
[[nodiscard]] static query_drop_intermediate drop();
|
[[nodiscard]] query_select_intermediate select(const std::vector<table_column>& columns);
|
||||||
[[nodiscard]] static query_select_intermediate select();
|
[[nodiscard]] query_select_intermediate select(const std::vector<std::string> &column_names);
|
||||||
[[nodiscard]] static query_select_intermediate select(std::initializer_list<table_column> columns);
|
template<class Type>
|
||||||
[[nodiscard]] static query_select_intermediate select(const std::vector<table_column>& columns);
|
[[nodiscard]] query_select_intermediate select(const schema &scm) {
|
||||||
[[nodiscard]] static query_select_intermediate select(const std::vector<std::string> &column_names);
|
return select(generator::columns<Type>(scm));
|
||||||
template<class Type>
|
}
|
||||||
[[nodiscard]] static query_select_intermediate select(const schema &scm) {
|
[[nodiscard]] query_insert_intermediate insert();
|
||||||
return select(generator::columns<Type>(scm));
|
[[nodiscard]] query_update_intermediate update(const table &table);
|
||||||
}
|
[[nodiscard]] query_delete_intermediate remove();
|
||||||
[[nodiscard]] static query_insert_intermediate insert();
|
|
||||||
[[nodiscard]] static query_update_intermediate update(const table &table);
|
|
||||||
[[nodiscard]] static query_delete_intermediate remove();
|
|
||||||
|
|
||||||
[[nodiscard]] static query_alter_intermediate alter();
|
|
||||||
};
|
|
||||||
|
|
||||||
|
[[nodiscard]] query_alter_intermediate alter();
|
||||||
}
|
}
|
||||||
#endif //QUERY_QUERY_HPP
|
#endif //QUERY_QUERY_HPP
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
#include "matador/query/query_data.hpp"
|
#include "matador/query/query_data.hpp"
|
||||||
|
|
||||||
#include "matador/sql/query_context.hpp"
|
#include "matador/sql/query_context.hpp"
|
||||||
|
#include "matador/sql/interface/connection_impl.hpp"
|
||||||
|
|
||||||
#include "matador/utils/placeholder.hpp"
|
#include "matador/utils/placeholder.hpp"
|
||||||
|
|
||||||
@@ -12,7 +13,6 @@
|
|||||||
#include <optional>
|
#include <optional>
|
||||||
|
|
||||||
namespace matador::sql {
|
namespace matador::sql {
|
||||||
class connection_impl;
|
|
||||||
class dialect;
|
class dialect;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -29,11 +29,10 @@ struct value_visitor;
|
|||||||
|
|
||||||
class query_builder final : public query_part_visitor {
|
class query_builder final : public query_part_visitor {
|
||||||
public:
|
public:
|
||||||
sql::query_context compile(const query_data &data,
|
sql::query_context build(const query_data &data,
|
||||||
const sql::dialect &d,
|
const sql::dialect &d,
|
||||||
std::optional<std::reference_wrapper<const sql::connection_impl>> conn);
|
std::optional<std::reference_wrapper<const sql::connection_impl>> conn);
|
||||||
|
|
||||||
protected:
|
|
||||||
void visit(internal::query_alter_part& part) override;
|
void visit(internal::query_alter_part& part) override;
|
||||||
void visit(internal::query_alter_table_part& part) override;
|
void visit(internal::query_alter_table_part& part) override;
|
||||||
void visit(internal::query_add_key_constraint_part& part) override;
|
void visit(internal::query_add_key_constraint_part& part) override;
|
||||||
@@ -46,6 +45,7 @@ protected:
|
|||||||
|
|
||||||
void visit(internal::query_select_part &part) override;
|
void visit(internal::query_select_part &part) override;
|
||||||
void visit(internal::query_select_nextval_part &part) override;
|
void visit(internal::query_select_nextval_part &part) override;
|
||||||
|
void visit(internal::query_select_currval_part &part) override;
|
||||||
void visit(internal::query_from_part &part) override;
|
void visit(internal::query_from_part &part) override;
|
||||||
void visit(internal::query_join_table_part &part) override;
|
void visit(internal::query_join_table_part &part) override;
|
||||||
void visit(internal::query_join_query_part &part) override;
|
void visit(internal::query_join_query_part &part) override;
|
||||||
@@ -83,8 +83,10 @@ protected:
|
|||||||
|
|
||||||
static std::string build_table_name(sql::dialect_token token, const sql::dialect &d, const table& t);
|
static std::string build_table_name(sql::dialect_token token, const sql::dialect &d, const table& t);
|
||||||
static std::string build_table_name(const sql::dialect &d, const table& t);
|
static std::string build_table_name(const sql::dialect &d, const table& t);
|
||||||
|
static std::string determine_value(const sql::dialect &d, sql::query_context& ctx, const abstract_column_expression &exp);
|
||||||
static std::string determine_value(value_visitor &visitor, const std::variant<utils::placeholder, utils::database_type> &val);
|
static std::string determine_value(value_visitor &visitor, const std::variant<utils::placeholder, utils::database_type> &val);
|
||||||
|
|
||||||
|
|
||||||
[[nodiscard]] std::string build_add_constraint_string(const table_constraint& c) const;
|
[[nodiscard]] std::string build_add_constraint_string(const table_constraint& c) const;
|
||||||
[[nodiscard]] std::string build_drop_constraint_string(const table_constraint& c) const;
|
[[nodiscard]] std::string build_drop_constraint_string(const table_constraint& c) const;
|
||||||
static std::string build_constraint_name(const table_constraint& c);
|
static std::string build_constraint_name(const table_constraint& c);
|
||||||
|
|||||||
@@ -3,28 +3,20 @@
|
|||||||
|
|
||||||
#include "matador/utils/error.hpp"
|
#include "matador/utils/error.hpp"
|
||||||
|
|
||||||
|
#include "matador/query/error_code.hpp"
|
||||||
|
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
|
|
||||||
namespace matador::query {
|
namespace matador::query {
|
||||||
|
|
||||||
enum class query_build_error : std::uint8_t {
|
|
||||||
Ok = 0,
|
|
||||||
UnknownType,
|
|
||||||
MissingPrimaryKey,
|
|
||||||
MissingObject,
|
|
||||||
UnexpectedError,
|
|
||||||
QueryError
|
|
||||||
};
|
|
||||||
|
|
||||||
class query_builder_exception final : public std::exception {
|
class query_builder_exception final : public std::exception {
|
||||||
public:
|
public:
|
||||||
explicit query_builder_exception(query_build_error error, utils::error &&err = {});
|
explicit query_builder_exception(utils::error &&err);
|
||||||
|
query_builder_exception(error_code error, std::string &&msg);
|
||||||
|
|
||||||
[[nodiscard]] query_build_error error_type() const;
|
|
||||||
[[nodiscard]] const utils::error &error() const;
|
[[nodiscard]] const utils::error &error() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
const query_build_error error_type_;
|
|
||||||
utils::error error_;
|
utils::error error_;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,60 @@
|
|||||||
|
#ifndef MATADOR_QUERY_BUILDER_UTILS_HPP
|
||||||
|
#define MATADOR_QUERY_BUILDER_UTILS_HPP
|
||||||
|
|
||||||
|
#include <functional>
|
||||||
|
#include <map>
|
||||||
|
#include <typeindex>
|
||||||
|
|
||||||
|
namespace matador::query {
|
||||||
|
template<class EntityType>
|
||||||
|
static std::pair<std::type_index, const void *> make_entity_visit_key(const EntityType &ptr) {
|
||||||
|
return {std::type_index(typeid(EntityType)), static_cast<const void *>(&ptr)};
|
||||||
|
}
|
||||||
|
|
||||||
|
struct entity_visit_key_hash {
|
||||||
|
size_t operator()(const std::pair<std::type_index, const void *> &p) const noexcept {
|
||||||
|
const size_t h1 = p.first.hash_code();
|
||||||
|
const size_t h2 = std::hash<const void *>{}(p.second);
|
||||||
|
return h1 ^ (h2 + 0x9e3779b97f4a7c15ULL + (h1 << 6) + (h1 >> 2));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct processing_many_to_many_key {
|
||||||
|
std::string id;
|
||||||
|
std::type_index local_type{typeid(void)};
|
||||||
|
std::type_index foreign_type{typeid(void)};
|
||||||
|
bool operator==(processing_many_to_many_key const &other) const {
|
||||||
|
return local_type == other.local_type && foreign_type == other.foreign_type && id == other.id;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template<class LocalType, typename ForeignType>
|
||||||
|
static processing_many_to_many_key make_processing_many_to_many_key(const std::string &id) {
|
||||||
|
return {id, std::type_index(typeid(LocalType)), std::type_index(typeid(ForeignType))};
|
||||||
|
}
|
||||||
|
|
||||||
|
struct processing_many_to_many_key_hash {
|
||||||
|
size_t operator()(const processing_many_to_many_key &p) const noexcept {
|
||||||
|
size_t seed = std::hash<std::type_index>{}(p.local_type);
|
||||||
|
|
||||||
|
const size_t foreign_hash = std::hash<std::type_index>{}(p.foreign_type);
|
||||||
|
seed ^= foreign_hash + 0x9e3779b97f4a7c15ULL + (seed << 6) + (seed >> 2);
|
||||||
|
|
||||||
|
const size_t id_hash = std::hash<std::string>{}(p.id);
|
||||||
|
seed ^= id_hash + 0x9e3779b97f4a7c15ULL + (seed << 6) + (seed >> 2);
|
||||||
|
|
||||||
|
return seed;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct query_builder_context {
|
||||||
|
const basic_schema &schema_;
|
||||||
|
const std::unordered_map<std::type_index, query_contexts> &contexts_by_type_;
|
||||||
|
|
||||||
|
std::vector<std::unique_ptr<execute_step>> steps_{};
|
||||||
|
std::vector<std::unique_ptr<execute_step>> relation_steps_{};
|
||||||
|
std::unordered_set<std::pair<std::type_index, const void *>, entity_visit_key_hash> visited_{};
|
||||||
|
std::unordered_set<processing_many_to_many_key, processing_many_to_many_key_hash> processing_many_to_many_relations_{};
|
||||||
|
};
|
||||||
|
}
|
||||||
|
#endif //MATADOR_QUERY_BUILDER_UTILS_HPP
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user