primary key generator progress: added primary_key_generator_finder
This commit is contained in:
parent
38dc42ade8
commit
0876535b44
|
|
@ -0,0 +1,129 @@
|
||||||
|
#ifndef MATADOR_PK_FIELD_LOCATOR_HPP
|
||||||
|
#define MATADOR_PK_FIELD_LOCATOR_HPP
|
||||||
|
|
||||||
|
#include "matador/utils/primary_key_attribute.hpp"
|
||||||
|
#include "matador/utils/field_attributes.hpp"
|
||||||
|
#include "matador/utils/foreign_attributes.hpp"
|
||||||
|
|
||||||
|
#include <variant>
|
||||||
|
#include <array>
|
||||||
|
#include <stdexcept>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace matador::object::detail {
|
||||||
|
using uuid16 = std::array<std::uint8_t, 16>;
|
||||||
|
using pk_value = std::variant<std::int64_t, std::string, uuid16>;
|
||||||
|
|
||||||
|
enum class pk_kind { integer, string, uuid };
|
||||||
|
|
||||||
|
struct pk_field_descriptor {
|
||||||
|
std::size_t offset{};
|
||||||
|
pk_kind kind{pk_kind::integer};
|
||||||
|
|
||||||
|
bool (*is_known_at)(const void* obj, std::size_t offset) = nullptr;
|
||||||
|
pk_value (*get_at)(const void* obj, std::size_t offset) = nullptr;
|
||||||
|
void (*set_at)(void* obj, std::size_t offset, const pk_value& v) = nullptr;
|
||||||
|
};
|
||||||
|
|
||||||
|
template < typename Type >
|
||||||
|
struct pk_field_locator {
|
||||||
|
Type *base{nullptr};
|
||||||
|
pk_field_descriptor desc{};
|
||||||
|
bool found{false};
|
||||||
|
|
||||||
|
explicit pk_field_locator (Type &obj)
|
||||||
|
: base(&obj) {}
|
||||||
|
|
||||||
|
template <typename PrimaryKeyType>
|
||||||
|
void on_primary_key(const char *, PrimaryKeyType &pk, const utils::primary_key_attribute & = utils::default_pk_attributes) {
|
||||||
|
// Offset bestimmen
|
||||||
|
const auto* obj_bytes = reinterpret_cast<std::uint8_t*>(base);
|
||||||
|
const auto* pk_bytes = reinterpret_cast<std::uint8_t*>(&pk);
|
||||||
|
desc.offset = static_cast<std::size_t>(pk_bytes - obj_bytes);
|
||||||
|
if constexpr (std::is_integral_v<PrimaryKeyType> || std::is_enum_v<PrimaryKeyType>) {
|
||||||
|
desc.kind = pk_kind::integer;
|
||||||
|
|
||||||
|
desc.is_known_at = [](void *obj, const std::size_t off) -> bool {
|
||||||
|
auto *p = reinterpret_cast<PrimaryKeyType *>(static_cast<std::uint8_t *>(obj) + off);
|
||||||
|
return *p != PrimaryKeyType{};
|
||||||
|
};
|
||||||
|
|
||||||
|
desc.get_at = [](void *obj, const std::size_t off) -> pk_value {
|
||||||
|
auto *p = reinterpret_cast<PrimaryKeyType *>(static_cast<std::uint8_t *>(obj) + off);
|
||||||
|
return static_cast<std::int64_t>(*p);
|
||||||
|
};
|
||||||
|
|
||||||
|
desc.set_at = [](void *obj, const std::size_t off, const pk_value &v) {
|
||||||
|
auto *p = reinterpret_cast<PrimaryKeyType *>(static_cast<std::uint8_t *>(obj) + off);
|
||||||
|
if (auto *i = std::get_if<std::int64_t>(&v)) {
|
||||||
|
*p = static_cast<PrimaryKeyType>(*i);
|
||||||
|
} else {
|
||||||
|
throw std::logic_error("PK type mismatch: expected integer");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
} else if constexpr (std::is_same_v<PrimaryKeyType, std::string>) {
|
||||||
|
desc.kind = pk_kind::string;
|
||||||
|
|
||||||
|
desc.is_known_at = [](void *obj, const std::size_t off) -> bool {
|
||||||
|
const auto *p = reinterpret_cast<std::string *>(static_cast<std::uint8_t *>(obj) + off);
|
||||||
|
return !p->empty();
|
||||||
|
};
|
||||||
|
|
||||||
|
desc.get_at = [](void *obj, const std::size_t off) -> pk_value {
|
||||||
|
auto *p = reinterpret_cast<std::string *>(static_cast<std::uint8_t *>(obj) + off);
|
||||||
|
return *p;
|
||||||
|
};
|
||||||
|
|
||||||
|
desc.set_at = [](void *obj, const std::size_t off, const pk_value &v) {
|
||||||
|
auto *p = reinterpret_cast<std::string *>(static_cast<std::uint8_t *>(obj) + off);
|
||||||
|
if (auto *s = std::get_if<std::string>(&v)) {
|
||||||
|
*p = *s;
|
||||||
|
} else {
|
||||||
|
throw std::logic_error("PK type mismatch: expected string");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
} else if constexpr (std::is_same_v<PrimaryKeyType, uuid16>) {
|
||||||
|
desc.kind = pk_kind::uuid;
|
||||||
|
|
||||||
|
desc.is_known_at = [](void *obj, const std::size_t off) -> bool {
|
||||||
|
auto *p = reinterpret_cast<uuid16 *>(static_cast<std::uint8_t *>(obj) + off);
|
||||||
|
// “unknown” = all zeros
|
||||||
|
for (auto b: *p) { if (b != 0) return true; }
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
|
desc.get_at = [](void *obj, const std::size_t off) -> pk_value {
|
||||||
|
auto *p = reinterpret_cast<uuid16 *>(static_cast<std::uint8_t *>(obj) + off);
|
||||||
|
return *p;
|
||||||
|
};
|
||||||
|
|
||||||
|
desc.set_at = [](void *obj, const std::size_t off, const pk_value &v) {
|
||||||
|
auto *p = reinterpret_cast<uuid16 *>(static_cast<std::uint8_t *>(obj) + off);
|
||||||
|
if (auto *u = std::get_if<uuid16>(&v)) {
|
||||||
|
*p = *u;
|
||||||
|
} else {
|
||||||
|
throw std::logic_error("PK type mismatch: expected uuid16");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
throw std::logic_error("Unsupported primary key field type");
|
||||||
|
}
|
||||||
|
|
||||||
|
found = true;
|
||||||
|
}
|
||||||
|
static void on_revision(const char *, std::uint64_t &) {}
|
||||||
|
template <typename V>
|
||||||
|
static void on_attribute(const char *, V &, const utils::field_attributes & = matador::utils::null_attributes) {}
|
||||||
|
template <typename Pointer>
|
||||||
|
static void on_belongs_to(const char *, Pointer &, const utils::foreign_attributes &) {}
|
||||||
|
template <typename Pointer>
|
||||||
|
static void on_has_one(const char *, Pointer &, const utils::foreign_attributes &) {}
|
||||||
|
template <typename Container>
|
||||||
|
static void on_has_many(const char *, Container &, const char *, const utils::foreign_attributes &) {}
|
||||||
|
template <typename Container>
|
||||||
|
static void on_has_many_to_many(const char *, Container &, const char *, const char *,const utils::foreign_attributes &) {}
|
||||||
|
template <typename Container>
|
||||||
|
static void on_has_many_to_many(const char *, Container &, const utils::foreign_attributes &) {}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
#endif //MATADOR_PK_FIELD_LOCATOR_HPP
|
||||||
|
|
@ -5,6 +5,7 @@
|
||||||
|
|
||||||
#include "matador/query/select_query_builder.hpp"
|
#include "matador/query/select_query_builder.hpp"
|
||||||
#include "matador/query/criteria.hpp"
|
#include "matador/query/criteria.hpp"
|
||||||
|
#include "matador/query/insert_query_builder.hpp"
|
||||||
#include "matador/query/query.hpp"
|
#include "matador/query/query.hpp"
|
||||||
#include "matador/query/generator.hpp"
|
#include "matador/query/generator.hpp"
|
||||||
#include "matador/query/schema.hpp"
|
#include "matador/query/schema.hpp"
|
||||||
|
|
@ -153,12 +154,8 @@ public:
|
||||||
* @param obj Object to insert
|
* @param obj Object to insert
|
||||||
* @return Inserted object
|
* @return Inserted object
|
||||||
*/
|
*/
|
||||||
// template<typename Type>
|
|
||||||
// utils::result<object::object_ptr<Type>, utils::error> insert(Type *obj);
|
|
||||||
template<typename Type>
|
template<typename Type>
|
||||||
utils::result<object::object_ptr<Type>, utils::error> insert(object::object_ptr<Type> obj);
|
utils::result<object::object_ptr<Type>, utils::error> insert(object::object_ptr<Type> obj);
|
||||||
// template<class Type, typename... Args>
|
|
||||||
// utils::result<object::object_ptr<Type>, utils::error> insert(Args &&... args);
|
|
||||||
|
|
||||||
template<typename Type>
|
template<typename Type>
|
||||||
utils::result<object::object_ptr<Type>, utils::error> update(const object::object_ptr<Type> &obj);
|
utils::result<object::object_ptr<Type>, utils::error> update(const object::object_ptr<Type> &obj);
|
||||||
|
|
@ -205,6 +202,54 @@ private:
|
||||||
|
|
||||||
template<typename Type>
|
template<typename Type>
|
||||||
utils::result<object::object_ptr<Type>, utils::error> session::insert(object::object_ptr<Type> obj) {
|
utils::result<object::object_ptr<Type>, utils::error> session::insert(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."));
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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.has_returning) {
|
||||||
|
const auto exec_res = step.query.execute(*this);
|
||||||
|
if (!exec_res.is_ok()) {
|
||||||
|
return utils::failure(exec_res.err());
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto stmt_res = step.query.prepare(*this);
|
||||||
|
if (!stmt_res.is_ok()) {
|
||||||
|
return utils::failure(stmt_res.err());
|
||||||
|
}
|
||||||
|
|
||||||
|
// RETURNING produces a result set; fetch the first row (single-row insert)
|
||||||
|
auto rec_res = stmt_res->fetch_one(); // const overload => std::optional<sql::record>
|
||||||
|
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());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return utils::ok(obj);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
const auto it = schema_.find(typeid(Type));
|
const auto it = schema_.find(typeid(Type));
|
||||||
if (it == schema_.end()) {
|
if (it == schema_.end()) {
|
||||||
return utils::failure(make_error(error_code::UnknownType, "Failed to determine requested type."));
|
return utils::failure(make_error(error_code::UnknownType, "Failed to determine requested type."));
|
||||||
|
|
|
||||||
|
|
@ -16,17 +16,51 @@
|
||||||
namespace matador::query {
|
namespace matador::query {
|
||||||
class schema_node final {
|
class schema_node final {
|
||||||
public:
|
public:
|
||||||
schema_node(class table tab, const object::repository_node& node);
|
schema_node(class table tab, utils::generator_type generator_type, 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]] 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_{};
|
||||||
const object::repository_node& node_;
|
const object::repository_node& node_;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class primary_key_generator_finder final {
|
||||||
|
public:
|
||||||
|
template< typename Type >
|
||||||
|
utils::generator_type find(const object::object_info<Type>& info) {
|
||||||
|
generator_type_ = utils::generator_type::Manually;
|
||||||
|
|
||||||
|
access::process(*this, info.prototype());
|
||||||
|
|
||||||
|
return generator_type_;
|
||||||
|
}
|
||||||
|
template<class V>
|
||||||
|
void on_primary_key(const char * /*id*/, V &/*pk*/, const utils::primary_key_attribute &attr = utils::default_pk_attributes) {
|
||||||
|
generator_type_ = attr.generator();
|
||||||
|
}
|
||||||
|
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 & ) {}
|
||||||
|
|
||||||
|
private:
|
||||||
|
utils::generator_type generator_type_{};
|
||||||
|
};
|
||||||
|
|
||||||
class basic_schema {
|
class basic_schema {
|
||||||
public:
|
public:
|
||||||
using schema_node_map = std::unordered_map<std::type_index, schema_node>;
|
using schema_node_map = std::unordered_map<std::type_index, schema_node>;
|
||||||
|
|
|
||||||
|
|
@ -114,7 +114,7 @@ private:
|
||||||
// 2) Build INSERT for this object
|
// 2) Build INSERT for this object
|
||||||
// For Identity PK strategy: append RETURNING(pk_column)
|
// For Identity PK strategy: append RETURNING(pk_column)
|
||||||
const auto &info = it->second.node().info();
|
const auto &info = it->second.node().info();
|
||||||
if (info.has_primary_key() && info.primary_key_attribute()->generator() == utils::generator_type::Identity) {
|
if (info.has_primary_key() && it->second.generator_type() == utils::generator_type::Identity) {
|
||||||
const std::string pk_name = info.primary_key_attribute()->name();
|
const std::string pk_name = info.primary_key_attribute()->name();
|
||||||
const table_column pk_col(&it->second.table(), pk_name);
|
const table_column pk_col(&it->second.table(), pk_name);
|
||||||
insert_step step {
|
insert_step step {
|
||||||
|
|
|
||||||
|
|
@ -270,7 +270,7 @@ private:
|
||||||
[[nodiscard]] static sql::query_context build_add_constraint_context(const object::repository_node& node, const object::restriction& cons, const sql::connection &conn) ;
|
[[nodiscard]] static sql::query_context build_add_constraint_context(const object::repository_node& node, const object::restriction& cons, const sql::connection &conn) ;
|
||||||
[[nodiscard]] static sql::query_context build_drop_constraint_context(const object::repository_node& node, const object::restriction& cons, const sql::connection &conn) ;
|
[[nodiscard]] static sql::query_context build_drop_constraint_context(const object::repository_node& node, const object::restriction& cons, const sql::connection &conn) ;
|
||||||
|
|
||||||
iterator insert_table(const std::type_index& ti, const object::repository_node &node);
|
iterator insert_table(const std::type_index& ti, const object::repository_node &node, utils::generator_type generator_type);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
template<typename Type>
|
template<typename Type>
|
||||||
|
|
@ -289,7 +289,10 @@ utils::result<void, utils::error> schema::drop_table(const sql::connection &conn
|
||||||
|
|
||||||
template <typename Type>
|
template <typename Type>
|
||||||
void schema_observer<Type>::on_attach(const object::repository_node &node, const Type &/*prototype*/) const {
|
void schema_observer<Type>::on_attach(const object::repository_node &node, const Type &/*prototype*/) const {
|
||||||
const auto it = schema_.insert_table(typeid(Type), node);
|
primary_key_generator_finder finder;
|
||||||
|
const auto generator_type = finder.find(node.info<Type>().get());
|
||||||
|
|
||||||
|
const auto it = schema_.insert_table(typeid(Type), node, generator_type);
|
||||||
|
|
||||||
if (!it->second.node().info().has_primary_key()) {
|
if (!it->second.node().info().has_primary_key()) {
|
||||||
return;
|
return;
|
||||||
|
|
|
||||||
|
|
@ -122,6 +122,7 @@ add_library(matador-core STATIC
|
||||||
utils/version.cpp
|
utils/version.cpp
|
||||||
../../include/matador/object/collection_utils.hpp
|
../../include/matador/object/collection_utils.hpp
|
||||||
object/collection_utils.cpp
|
object/collection_utils.cpp
|
||||||
|
../../include/matador/object/pk_field_locator.hpp
|
||||||
)
|
)
|
||||||
|
|
||||||
target_link_libraries(matador-core ${CMAKE_DL_LIBS})
|
target_link_libraries(matador-core ${CMAKE_DL_LIBS})
|
||||||
|
|
|
||||||
|
|
@ -189,6 +189,7 @@ add_library(matador-orm STATIC
|
||||||
sql/statement.cpp
|
sql/statement.cpp
|
||||||
sql/statement_cache.cpp
|
sql/statement_cache.cpp
|
||||||
../../include/matador/query/insert_query_builder.hpp
|
../../include/matador/query/insert_query_builder.hpp
|
||||||
|
query/abstract_pk_generator.hpp
|
||||||
)
|
)
|
||||||
|
|
||||||
target_include_directories(matador-orm
|
target_include_directories(matador-orm
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,11 @@
|
||||||
|
#ifndef MATADOR_ABSTRACT_PK_GENERATOR_HPP
|
||||||
|
#define MATADOR_ABSTRACT_PK_GENERATOR_HPP
|
||||||
|
|
||||||
|
namespace matador::query {
|
||||||
|
class abstract_pk_generator {
|
||||||
|
public:
|
||||||
|
virtual ~abstract_pk_generator() = default;
|
||||||
|
virtual unsigned int generate() = 0;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
#endif //MATADOR_ABSTRACT_PK_GENERATOR_HPP
|
||||||
|
|
@ -3,8 +3,9 @@
|
||||||
#include "matador/sql/executor.hpp"
|
#include "matador/sql/executor.hpp"
|
||||||
|
|
||||||
namespace matador::query {
|
namespace matador::query {
|
||||||
schema_node::schema_node(class table tab, const object::repository_node& node)
|
schema_node::schema_node(class table tab, utils::generator_type generator_type, const object::repository_node& node)
|
||||||
: table_(std::move(tab))
|
: table_(std::move(tab))
|
||||||
|
, generator_type_(generator_type)
|
||||||
, node_(std::move(node)) {
|
, node_(std::move(node)) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -16,6 +17,10 @@ const table &schema_node::table() const {
|
||||||
return table_;
|
return table_;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
utils::generator_type schema_node::generator_type() const {
|
||||||
|
return generator_type_;
|
||||||
|
}
|
||||||
|
|
||||||
const object::repository_node& schema_node::node() const {
|
const object::repository_node& schema_node::node() const {
|
||||||
return node_;
|
return node_;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -165,11 +165,11 @@ sql::query_context schema::build_drop_constraint_context(const object::repositor
|
||||||
.compile(conn);
|
.compile(conn);
|
||||||
}
|
}
|
||||||
|
|
||||||
schema::iterator schema::insert_table(const std::type_index &ti, const object::repository_node &node) {
|
schema::iterator schema::insert_table(const std::type_index &ti, const object::repository_node &node, const utils::generator_type generator_type) {
|
||||||
std::vector<table_column> columns;
|
std::vector<table_column> columns;
|
||||||
for (const auto &attr: node.info().attributes()) {
|
for (const auto &attr: node.info().attributes()) {
|
||||||
columns.emplace_back(nullptr, attr.name(), attr.type(), attr.attributes());
|
columns.emplace_back(nullptr, attr.name(), attr.type(), attr.attributes());
|
||||||
}
|
}
|
||||||
return schema_nodes_.insert({ti, schema_node{table(node.name(), columns), node}}).first;
|
return schema_nodes_.insert({ti, schema_node{table(node.name(), columns), generator_type, node}}).first;
|
||||||
}
|
}
|
||||||
} // namespace matador::query
|
} // namespace matador::query
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue