Added support for varchars

This commit is contained in:
Dr. Patrick Urbanke
2025-04-27 08:30:44 +02:00
parent a262437a37
commit 79558f0cd4
5 changed files with 178 additions and 0 deletions

View File

@@ -9,6 +9,7 @@
#include "sqlgen/Range.hpp"
#include "sqlgen/Ref.hpp"
#include "sqlgen/Result.hpp"
#include "sqlgen/Varchar.hpp"
#include "sqlgen/read.hpp"
#include "sqlgen/write.hpp"

101
include/sqlgen/Varchar.hpp Normal file
View File

@@ -0,0 +1,101 @@
#ifndef SQLGEN_VARCHAR_HPP_
#define SQLGEN_VARCHAR_HPP_
#include <stdexcept>
#include <string>
#include "Result.hpp"
namespace sqlgen {
template <size_t _size>
class Varchar {
public:
using ReflectionType = std::string;
static constexpr size_t size_ = _size;
Varchar() : value_("") {}
Varchar(const std::string& _value) : value_(check_size(_value)) {}
Varchar(const char* _value) : value_(check_size(_value)) {}
Varchar(Varchar<_size>&& _other) noexcept = default;
Varchar(const Varchar<_size>& _other) = default;
template <size_t _other_size>
Varchar(const Varchar<_other_size>& _other)
: value_(check_size(_other.get())) {}
template <size_t _other_size>
Varchar(Varchar<_other_size>&& _other) : value_(check_size(_other.get())) {}
~Varchar() = default;
static Result<Varchar<size_>> make(const std::string& _value) noexcept {
try {
return Varchar<size_>(_value);
} catch (std::exception& e) {
return error(e.what());
}
}
/// Returns the underlying object.
const ReflectionType& get() const { return value_; }
/// Returns the underlying object.
const ReflectionType& operator()() const { return value_; }
/// Assigns the underlying object.
auto& operator=(const char* _value) {
value_ = check_size(_value);
return *this;
}
/// Assigns the underlying object.
auto& operator=(const std::string& _value) {
value_ = check_size(_value);
return *this;
}
/// Assigns the underlying object.
Varchar<_size>& operator=(const Varchar<_size>& _other) = default;
/// Assigns the underlying object.
Varchar<_size>& operator=(Varchar<_size>&& _other) = default;
/// Assigns the underlying object.
template <size_t _other_size>
auto& operator=(const Varchar<_other_size>& _other) {
value_ = check_size(_other.get());
return *this;
}
/// Necessary for the automated transpilation to work.
const std::string& reflection() const { return value_; }
static constexpr size_t size() { return size_; }
/// Returns the underlying object.
const std::string& value() const { return value_; }
private:
static std::string check_size(const std::string& _str) {
if (_str.size() > size_) {
throw std::runtime_error(
"String '" + _str + "' too long: " + std::to_string(_str.size()) +
" exceeds the maximum length of " + std::to_string(size_) + ".");
}
return _str;
}
private:
/// The underlying value.
std::string value_;
};
} // namespace sqlgen
#endif

View File

@@ -8,5 +8,6 @@
#include "Parser_shared_ptr.hpp"
#include "Parser_string.hpp"
#include "Parser_unique_ptr.hpp"
#include "Parser_varchar.hpp"
#endif

View File

@@ -0,0 +1,36 @@
#ifndef SQLGEN_PARSING_PARSER_VARCHAR_HPP_
#define SQLGEN_PARSING_PARSER_VARCHAR_HPP_
#include <string>
#include <type_traits>
#include "../Result.hpp"
#include "../Varchar.hpp"
#include "../dynamic/Type.hpp"
#include "../dynamic/types.hpp"
#include "Parser_base.hpp"
namespace sqlgen::parsing {
template <size_t _size>
struct Parser<Varchar<_size>> {
static Result<Varchar<_size>> read(
const std::optional<std::string>& _str) noexcept {
return Parser<std::string>::read(_str).and_then(
[](auto&& _t) -> Result<Varchar<_size>> {
return Varchar<_size>::make(std::move(_t));
});
}
static std::optional<std::string> write(const Varchar<_size>& _v) noexcept {
return Parser<std::string>::write(_v.value());
}
static dynamic::Type to_type() noexcept {
return dynamic::types::VarChar{.length = _size};
}
};
} // namespace sqlgen::parsing
#endif

View File

@@ -0,0 +1,39 @@
#include <gtest/gtest.h>
#include <rfl.hpp>
#include <rfl/json.hpp>
#include <sqlgen.hpp>
#include <sqlgen/sqlite.hpp>
#include <vector>
namespace test_varchar {
struct Person {
sqlgen::PrimaryKey<uint32_t> id;
sqlgen::Varchar<6> first_name;
sqlgen::Varchar<7> last_name;
int age;
};
TEST(sqlite, test_varchar) {
const auto people1 = std::vector<Person>(
{Person{
.id = 0, .first_name = "Homer", .last_name = "Simpson", .age = 45},
Person{.id = 1, .first_name = "Bart", .last_name = "Simpson", .age = 10},
Person{.id = 2, .first_name = "Lisa", .last_name = "Simpson", .age = 8},
Person{
.id = 3, .first_name = "Maggie", .last_name = "Simpson", .age = 0}});
const auto conn = sqlgen::sqlite::connect();
sqlgen::write(conn, people1);
const auto people2 = sqlgen::read<std::vector<Person>>(conn).value();
const auto json1 = rfl::json::write(people1);
const auto json2 = rfl::json::write(people2);
EXPECT_EQ(json1, json2);
}
} // namespace test_varchar