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/criteria.hpp"
|
||||
#include "matador/query/insert_query_builder.hpp"
|
||||
#include "matador/query/query.hpp"
|
||||
#include "matador/query/generator.hpp"
|
||||
#include "matador/query/schema.hpp"
|
||||
|
|
@ -153,12 +154,8 @@ public:
|
|||
* @param obj Object to insert
|
||||
* @return Inserted object
|
||||
*/
|
||||
// template<typename Type>
|
||||
// utils::result<object::object_ptr<Type>, utils::error> insert(Type *obj);
|
||||
template<typename Type>
|
||||
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>
|
||||
utils::result<object::object_ptr<Type>, utils::error> update(const object::object_ptr<Type> &obj);
|
||||
|
|
@ -205,6 +202,54 @@ private:
|
|||
|
||||
template<typename Type>
|
||||
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));
|
||||
if (it == schema_.end()) {
|
||||
return utils::failure(make_error(error_code::UnknownType, "Failed to determine requested type."));
|
||||
|
|
|
|||
|
|
@ -16,17 +16,51 @@
|
|||
namespace matador::query {
|
||||
class schema_node final {
|
||||
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 class table& table() const;
|
||||
[[nodiscard]] utils::generator_type generator_type() const;
|
||||
[[nodiscard]] const object::repository_node& node() const;
|
||||
|
||||
private:
|
||||
class table table_;
|
||||
utils::generator_type generator_type_{};
|
||||
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 {
|
||||
public:
|
||||
using schema_node_map = std::unordered_map<std::type_index, schema_node>;
|
||||
|
|
|
|||
|
|
@ -114,7 +114,7 @@ private:
|
|||
// 2) Build INSERT for this object
|
||||
// For Identity PK strategy: append RETURNING(pk_column)
|
||||
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 table_column pk_col(&it->second.table(), pk_name);
|
||||
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_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:
|
||||
template<typename Type>
|
||||
|
|
@ -289,7 +289,10 @@ utils::result<void, utils::error> schema::drop_table(const sql::connection &conn
|
|||
|
||||
template <typename Type>
|
||||
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()) {
|
||||
return;
|
||||
|
|
|
|||
|
|
@ -122,6 +122,7 @@ add_library(matador-core STATIC
|
|||
utils/version.cpp
|
||||
../../include/matador/object/collection_utils.hpp
|
||||
object/collection_utils.cpp
|
||||
../../include/matador/object/pk_field_locator.hpp
|
||||
)
|
||||
|
||||
target_link_libraries(matador-core ${CMAKE_DL_LIBS})
|
||||
|
|
|
|||
|
|
@ -189,6 +189,7 @@ add_library(matador-orm STATIC
|
|||
sql/statement.cpp
|
||||
sql/statement_cache.cpp
|
||||
../../include/matador/query/insert_query_builder.hpp
|
||||
query/abstract_pk_generator.hpp
|
||||
)
|
||||
|
||||
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"
|
||||
|
||||
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))
|
||||
, generator_type_(generator_type)
|
||||
, node_(std::move(node)) {
|
||||
}
|
||||
|
||||
|
|
@ -16,6 +17,10 @@ const table &schema_node::table() const {
|
|||
return table_;
|
||||
}
|
||||
|
||||
utils::generator_type schema_node::generator_type() const {
|
||||
return generator_type_;
|
||||
}
|
||||
|
||||
const object::repository_node& schema_node::node() const {
|
||||
return node_;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -165,11 +165,11 @@ sql::query_context schema::build_drop_constraint_context(const object::repositor
|
|||
.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;
|
||||
for (const auto &attr: node.info().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
|
||||
|
|
|
|||
Loading…
Reference in New Issue