Add auto-incrementing primary keys (#23)

This commit is contained in:
Dr. Patrick Urbanke (劉自成)
2025-06-24 22:34:50 +02:00
committed by GitHub
parent e4b821138a
commit aa1a96f1c0
13 changed files with 275 additions and 37 deletions
+27 -17
View File
@@ -1,30 +1,40 @@
#ifndef SQLGEN_PRIMARY_KEY_HPP_
#define SQLGEN_PRIMARY_KEY_HPP_
#include <type_traits>
#include "transpilation/is_nullable.hpp"
namespace sqlgen {
template <class T>
inline constexpr bool auto_incr = true;
template <class T, bool _auto_incr = false>
struct PrimaryKey {
using ReflectionType = T;
static constexpr bool auto_incr = _auto_incr;
static_assert(!transpilation::is_nullable_v<T>,
"A primary key cannot be nullable.");
static_assert(
!transpilation::is_nullable_v<T>,
"A primary key cannot be nullable. Please use a non-nullable type.");
static_assert(!_auto_incr || std::is_integral_v<T>,
"The type of an auto-incrementing primary key must be "
"integral. Please use an integral type or remove auto_incr.");
PrimaryKey() : value_(0) {}
PrimaryKey() : value_(T()) {}
PrimaryKey(const T& _value) : value_(_value) {}
PrimaryKey(PrimaryKey<T>&& _other) noexcept = default;
PrimaryKey(PrimaryKey&& _other) noexcept = default;
PrimaryKey(const PrimaryKey<T>& _other) = default;
PrimaryKey(const PrimaryKey& _other) = default;
template <class U>
PrimaryKey(const PrimaryKey<U>& _other) : value_(_other.get()) {}
template <class U, bool _other_auto_incr>
PrimaryKey(const PrimaryKey<U, _other_auto_incr>& _other)
: value_(_other.get()) {}
template <class U>
PrimaryKey(PrimaryKey<U>&& _other) : value_(_other.get()) {}
template <class U, bool _other_auto_incr>
PrimaryKey(PrimaryKey<U, _other_auto_incr>&& _other) : value_(_other.get()) {}
template <class U,
typename std::enable_if<std::is_convertible_v<U, ReflectionType>,
@@ -71,22 +81,22 @@ struct PrimaryKey {
}
/// Assigns the underlying object.
PrimaryKey<T>& operator=(const PrimaryKey<T>& _other) = default;
PrimaryKey& operator=(const PrimaryKey& _other) = default;
/// Assigns the underlying object.
PrimaryKey<T>& operator=(PrimaryKey<T>&& _other) = default;
PrimaryKey& operator=(PrimaryKey&& _other) = default;
/// Assigns the underlying object.
template <class U>
auto& operator=(const PrimaryKey<U>& _other) {
template <class U, bool _other_auto_incr>
auto& operator=(const PrimaryKey<U, _other_auto_incr>& _other) {
value_ = _other.get();
return *this;
}
/// Assigns the underlying object.
template <class U>
auto& operator=(PrimaryKey<U>&& _other) {
value_ = std::forward<T>(_other.value_);
template <class U, bool _other_auto_incr>
auto& operator=(PrimaryKey<U, _other_auto_incr>&& _other) {
value_ = std::move(_other.value_);
return *this;
}
+1
View File
@@ -7,6 +7,7 @@
namespace sqlgen::dynamic::types {
struct Properties {
bool auto_incr = false;
bool primary = false;
bool nullable = false;
};
@@ -0,0 +1,60 @@
#ifndef SQLGEN_INTERNAL_REMOVE_AUTO_INCR_PRIMARY_HPP_
#define SQLGEN_INTERNAL_REMOVE_AUTO_INCR_PRIMARY_HPP_
#include <rfl.hpp>
#include <type_traits>
#include "../PrimaryKey.hpp"
#include "../transpilation/is_primary_key.hpp"
namespace sqlgen::internal {
namespace remove_auto_incr {
template <class FieldType>
struct FieldWrapper {};
template <class NamedTupleType>
struct NamedTupleWrapper;
template <class... Fields>
struct NamedTupleWrapper<rfl::NamedTuple<Fields...>> {
using Type = rfl::NamedTuple<Fields...>;
template <class NewField>
friend constexpr auto operator+(const NamedTupleWrapper&,
const FieldWrapper<NewField>&) {
if constexpr (transpilation::is_primary_key_v<
std::remove_pointer_t<typename NewField::Type>>) {
if constexpr (std::remove_pointer_t<typename NewField::Type>::auto_incr) {
return NamedTupleWrapper<rfl::NamedTuple<Fields...>>{};
} else {
return NamedTupleWrapper<rfl::NamedTuple<Fields..., NewField>>{};
}
} else {
return NamedTupleWrapper<rfl::NamedTuple<Fields..., NewField>>{};
}
}
};
template <class NamedTupleType>
struct RemoveAutoIncrPrimary;
template <class... Fields>
struct RemoveAutoIncrPrimary<rfl::NamedTuple<Fields...>> {
static constexpr auto wrapper =
(NamedTupleWrapper<rfl::NamedTuple<>>{} + ... + FieldWrapper<Fields>{});
using Type = decltype(wrapper)::Type;
};
} // namespace remove_auto_incr
template <class NamedTupleType>
using remove_auto_incr_primary_t =
typename remove_auto_incr::RemoveAutoIncrPrimary<
std::remove_cvref_t<NamedTupleType>>::Type;
} // namespace sqlgen::internal
#endif
+4 -1
View File
@@ -7,17 +7,20 @@
#include <type_traits>
#include <vector>
#include "remove_auto_incr_primary_t.hpp"
#include "to_str.hpp"
namespace sqlgen::internal {
template <class T>
std::vector<std::optional<std::string>> to_str_vec(const T& _t) {
const auto view = rfl::to_view(_t);
using ViewType = remove_auto_incr_primary_t<decltype(view)>;
return rfl::apply(
[](auto... _ptrs) {
return std::vector<std::optional<std::string>>({to_str(*_ptrs)...});
},
rfl::to_view(_t).values());
ViewType(view).values());
}
} // namespace sqlgen::internal
+13 -7
View File
@@ -11,23 +11,29 @@
namespace sqlgen::parsing {
template <class T>
struct Parser<PrimaryKey<T>> {
static Result<PrimaryKey<T>> read(
template <class T, bool _auto_incr>
struct Parser<PrimaryKey<T, _auto_incr>> {
static Result<PrimaryKey<T, _auto_incr>> read(
const std::optional<std::string>& _str) noexcept {
return Parser<std::remove_cvref_t<T>>::read(_str).transform(
[](auto&& _t) -> PrimaryKey<T> {
return PrimaryKey<T>(std::move(_t));
[](auto&& _t) -> PrimaryKey<T, _auto_incr> {
return PrimaryKey<T, _auto_incr>(std::move(_t));
});
}
static std::optional<std::string> write(const PrimaryKey<T>& _p) noexcept {
return Parser<std::remove_cvref_t<T>>::write(_p.value());
static std::optional<std::string> write(
const PrimaryKey<T, _auto_incr>& _p) noexcept {
if constexpr (_auto_incr) {
return std::nullopt;
} else {
return Parser<std::remove_cvref_t<T>>::write(_p.value());
}
}
static dynamic::Type to_type() noexcept {
return Parser<std::remove_cvref_t<T>>::to_type().visit(
[](auto _t) -> dynamic::Type {
_t.properties.auto_incr = _auto_incr;
_t.properties.primary = true;
return _t;
});
@@ -13,8 +13,8 @@ class is_primary_key;
template <class T>
class is_primary_key : public std::false_type {};
template <class T>
class is_primary_key<PrimaryKey<T>> : public std::true_type {};
template <class T, bool _auto_incr>
class is_primary_key<PrimaryKey<T, _auto_incr>> : public std::true_type {};
template <class T>
constexpr bool is_primary_key_v = is_primary_key<std::remove_cvref_t<T>>::value;
@@ -9,6 +9,7 @@
#include "../dynamic/CreateTable.hpp"
#include "../dynamic/Table.hpp"
#include "../internal/remove_auto_incr_primary_t.hpp"
#include "get_schema.hpp"
#include "get_tablename.hpp"
#include "make_columns.hpp"
@@ -10,6 +10,7 @@
#include "../dynamic/Table.hpp"
#include "../internal/collect/vector.hpp"
#include "../internal/remove_auto_incr_primary_t.hpp"
#include "get_schema.hpp"
#include "get_tablename.hpp"
#include "make_columns.hpp"
@@ -22,7 +23,8 @@ template <class T, class InsertOrWrite>
InsertOrWrite to_insert_or_write() {
using namespace std::ranges::views;
using NamedTupleType = rfl::named_tuple_t<std::remove_cvref_t<T>>;
using NamedTupleType = sqlgen::internal::remove_auto_incr_primary_t<
rfl::named_tuple_t<std::remove_cvref_t<T>>>;
using Fields = typename NamedTupleType::Fields;
const auto columns = make_columns<Fields>(