mirror of
https://github.com/getml/sqlgen.git
synced 2026-02-06 16:58:55 -06:00
Add enum support (#49)
This commit is contained in:
2
.gitmodules
vendored
2
.gitmodules
vendored
@@ -1,3 +1,3 @@
|
||||
[submodule "vcpkg"]
|
||||
path = vcpkg
|
||||
url = git@github.com:microsoft/vcpkg.git
|
||||
url = https://github.com/microsoft/vcpkg.git
|
||||
|
||||
@@ -38,6 +38,7 @@ Welcome to the sqlgen documentation. This guide provides detailed information ab
|
||||
- [Type Conversion Operations](type_conversion_operations.md) - How to convert between types safely in queries (e.g., cast int to double).
|
||||
- [Null Handling Operations](null_handling_operations.md) - How to handle nullable values and propagate nullability correctly (e.g., with coalesce and nullability rules).
|
||||
- [Timestamp and Date/Time Functions](timestamp_operations.md) - How to work with timestamps, dates, and times (e.g., extract parts, perform arithmetic, convert formats).
|
||||
- [Enums](enum.md) - How to work with enums sqlgen
|
||||
|
||||
## Data Types and Validation
|
||||
|
||||
@@ -61,4 +62,4 @@ Welcome to the sqlgen documentation. This guide provides detailed information ab
|
||||
- [PostgreSQL](postgres.md) - How to interact with PostgreSQL and compatible databases (Redshift, Aurora, Greenplum, CockroachDB, ...)
|
||||
- [SQLite](sqlite.md) - How to interact with SQLite3
|
||||
|
||||
For installation instructions, quick start guide, and usage examples, please refer to the [main README](../README.md).
|
||||
For installation instructions, quick start guide, and usage examples, please refer to the [main README](../README.md).
|
||||
37
docs/enum.md
Normal file
37
docs/enum.md
Normal file
@@ -0,0 +1,37 @@
|
||||
# `Enums`
|
||||
|
||||
Enums can directly be used in sqlgen structs. They are mapped to native enum types in PostgreSQL and MySQL. In sqlite, which does not support native enum types, they are stored as `TEXT` by default.
|
||||
|
||||
## Usage
|
||||
|
||||
### Basic Definition
|
||||
|
||||
```cpp
|
||||
enum class Color : int { RED = 1, GREEN = 2, BLUE = 3 };
|
||||
struct Flower {
|
||||
sqlgen::PrimaryKey<std::string> name;
|
||||
Color color;
|
||||
}
|
||||
|
||||
const auto red_rose = Flower{
|
||||
.name = "Rose",
|
||||
.color = Color::RED,
|
||||
};
|
||||
```
|
||||
|
||||
This generates the following SQL schema:
|
||||
```sql
|
||||
CREATE TYPE IF NOT EXISTS "Color" AS ENUM ('RED', 'GREEN', 'BLUE');
|
||||
CREATE TABLE IF NOT EXISTS "Flower"(
|
||||
"name" TEXT NOT NULL,
|
||||
"color" Color NOT NULL,
|
||||
PRIMARY_KEY("name")
|
||||
);
|
||||
```
|
||||
|
||||
## Notes
|
||||
- Due to naming restrictions in PostgreSQL, the namespace operator `::` is replaced with `__`. Thus, an enum defined as `namespace1::some_struct::MyEnum` will be created in the database as `namespace1__some_struct__MyEnum`. This mangled name can at most be 63 characters long.
|
||||
- Enums are specific types in PostgreSQL. They are only created once and shared between tables.
|
||||
- In MySQL, enums are stored as native types but they are not shared between tables.
|
||||
- In sqlite, enums are stored as `TEXT` by default. If you need to use integers, you can specialize the `Parser<T>` struct as explained [here](dynamic.md).
|
||||
- You can use `rfl::enum_to_string` and `rfl::string_to_enum` to convert between enum values and their string representations.
|
||||
@@ -14,7 +14,7 @@ using Type =
|
||||
types::Int32, types::Int64, types::JSON, types::UInt8,
|
||||
types::UInt16, types::UInt32, types::UInt64, types::Text,
|
||||
types::Date, types::Timestamp, types::TimestampWithTZ,
|
||||
types::VarChar>;
|
||||
types::VarChar, types::Enum>;
|
||||
|
||||
} // namespace sqlgen::dynamic
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
#include <cstdint>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace sqlgen::dynamic::types {
|
||||
|
||||
@@ -78,6 +79,12 @@ struct UInt64 {
|
||||
Properties properties;
|
||||
};
|
||||
|
||||
struct Enum {
|
||||
std::string name;
|
||||
std::vector<std::string> values;
|
||||
Properties properties;
|
||||
};
|
||||
|
||||
struct Text {
|
||||
Properties properties;
|
||||
};
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#ifndef SQLGEN_PARSING_PARSER_DEFAULT_HPP_
|
||||
#define SQLGEN_PARSING_PARSER_DEFAULT_HPP_
|
||||
|
||||
#include <ranges>
|
||||
#include <rfl.hpp>
|
||||
#include <string>
|
||||
#include <type_traits>
|
||||
@@ -33,8 +34,14 @@ struct Parser {
|
||||
return static_cast<Type>(std::stod(*_str));
|
||||
} else if constexpr (std::is_integral_v<Type>) {
|
||||
return static_cast<Type>(std::stoll(*_str));
|
||||
} else if (std::is_same_v<Type, bool>) {
|
||||
} else if constexpr (std::is_same_v<Type, bool>) {
|
||||
return std::stoi(*_str) != 0;
|
||||
} else if constexpr (std::is_enum_v<Type>) {
|
||||
if (auto res = rfl::string_to_enum<Type>(*_str)) {
|
||||
return Type{*res};
|
||||
} else {
|
||||
return error(res.error());
|
||||
}
|
||||
} else {
|
||||
static_assert(rfl::always_false_v<Type>, "Unsupported type");
|
||||
}
|
||||
@@ -49,6 +56,8 @@ struct Parser {
|
||||
if constexpr (transpilation::has_reflection_method<Type>) {
|
||||
return Parser<std::remove_cvref_t<typename Type::ReflectionType>>::write(
|
||||
_t.reflection());
|
||||
} else if constexpr (std::is_enum_v<Type>) {
|
||||
return rfl::enum_to_string(_t);
|
||||
} else {
|
||||
return std::to_string(_t);
|
||||
}
|
||||
@@ -108,6 +117,22 @@ struct Parser {
|
||||
"Unsupported floating point value.");
|
||||
}
|
||||
|
||||
} else if constexpr (std::is_enum_v<T>) {
|
||||
constexpr auto values = rfl::get_enumerator_array<T>();
|
||||
std::array<std::string_view, std::size(values)> enum_names{};
|
||||
for (std::size_t i = 0; i < std::size(values); ++i) {
|
||||
enum_names[i] = values[i].first;
|
||||
}
|
||||
constexpr auto org_name = rfl::internal::get_type_name_str_view<T>();
|
||||
static_assert(org_name.size() < 64,
|
||||
"Enum type exceeds type level. If it's defined in a nested "
|
||||
"namespace, consider moving it up to a higher level.");
|
||||
// Transform '::' to '__' to avoid postgres limitations
|
||||
std::string trf_name(org_name);
|
||||
std::ranges::replace(trf_name, ':', '_');
|
||||
return sqlgen::dynamic::types::Enum{
|
||||
std::move(trf_name),
|
||||
std::vector<std::string>(enum_names.begin(), enum_names.end())};
|
||||
} else {
|
||||
static_assert(rfl::always_false_v<T>, "Unsupported type.");
|
||||
}
|
||||
|
||||
@@ -25,6 +25,9 @@ dynamic::Value to_value(const T& _t) {
|
||||
} else if constexpr (has_reflection_method<Type>) {
|
||||
return to_value(_t.reflection());
|
||||
|
||||
} else if constexpr (std::is_enum_v<Type>) {
|
||||
return dynamic::Value{dynamic::String{.val = rfl::enum_to_string(_t)}};
|
||||
|
||||
} else {
|
||||
static_assert(rfl::always_false_v<T>, "Unsupported type");
|
||||
}
|
||||
|
||||
@@ -82,6 +82,10 @@ inline std::string wrap_in_quotes(const std::string& _name) noexcept {
|
||||
return "`" + _name + "`";
|
||||
}
|
||||
|
||||
inline std::string wrap_in_single_quotes(const std::string& _name) noexcept {
|
||||
return "'" + _name + "'";
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
std::string aggregation_to_sql(
|
||||
@@ -154,6 +158,8 @@ std::string cast_type_to_sql(const dynamic::Type& _type) noexcept {
|
||||
} else if constexpr (std::is_same_v<T, dynamic::types::Unknown>) {
|
||||
return "CHAR";
|
||||
|
||||
} else if constexpr (std::is_same_v<T, dynamic::types::Enum>) {
|
||||
return "ENUM";
|
||||
} else {
|
||||
static_assert(rfl::always_false_v<T>, "Not all cases were covered.");
|
||||
}
|
||||
@@ -872,6 +878,14 @@ std::string type_to_sql(const dynamic::Type& _type) noexcept {
|
||||
std::is_same_v<T, dynamic::types::UInt64>) {
|
||||
return "BIGINT";
|
||||
|
||||
} else if constexpr (std::is_same_v<T, dynamic::types::Enum>) {
|
||||
return "ENUM(" +
|
||||
internal::strings::join(
|
||||
", ", internal::collect::vector(_t.values |
|
||||
std::ranges::views::transform(
|
||||
wrap_in_single_quotes))) +
|
||||
")";
|
||||
|
||||
} else if constexpr (std::is_same_v<T, dynamic::types::Float32> ||
|
||||
std::is_same_v<T, dynamic::types::Float64>) {
|
||||
return "DECIMAL";
|
||||
|
||||
@@ -42,6 +42,9 @@ std::string field_to_str(const dynamic::SelectFrom::Field& _field) noexcept;
|
||||
std::vector<std::string> get_primary_keys(
|
||||
const dynamic::CreateTable& _stmt) noexcept;
|
||||
|
||||
std::vector<std::pair<std::string, std::vector<std::string>>> get_enum_types(
|
||||
const dynamic::CreateTable& _stmt) noexcept;
|
||||
|
||||
std::string insert_to_sql(const dynamic::Insert& _stmt) noexcept;
|
||||
|
||||
std::string join_to_sql(const dynamic::Join& _stmt) noexcept;
|
||||
@@ -66,10 +69,26 @@ std::string write_to_sql(const dynamic::Write& _stmt) noexcept;
|
||||
|
||||
inline std::string get_name(const dynamic::Column& _col) { return _col.name; }
|
||||
|
||||
inline std::pair<std::string, std::vector<std::string>> get_enum_mapping(
|
||||
const dynamic::Column& _col) {
|
||||
return _col.type.visit(
|
||||
[&](const auto& _t) -> std::pair<std::string, std::vector<std::string>> {
|
||||
using T = std::remove_cvref_t<decltype(_t)>;
|
||||
if constexpr (std::is_same_v<T, dynamic::types::Enum>) {
|
||||
return {type_to_sql(_t), _t.values};
|
||||
}
|
||||
return {};
|
||||
});
|
||||
}
|
||||
|
||||
inline std::string wrap_in_quotes(const std::string& _name) noexcept {
|
||||
return "\"" + _name + "\"";
|
||||
}
|
||||
|
||||
inline std::string wrap_in_single_quotes(const std::string& _name) noexcept {
|
||||
return "'" + _name + "'";
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
std::string aggregation_to_sql(
|
||||
@@ -254,6 +273,21 @@ std::string create_table_to_sql(const dynamic::CreateTable& _stmt) noexcept {
|
||||
};
|
||||
|
||||
std::stringstream stream;
|
||||
|
||||
for (const auto& [enum_name, enum_values] : get_enum_types(_stmt)) {
|
||||
if (_stmt.if_not_exists) {
|
||||
stream << "DO $$ BEGIN ";
|
||||
}
|
||||
stream << "CREATE TYPE " << enum_name << " AS ENUM ("
|
||||
<< internal::strings::join(
|
||||
", ", internal::collect::vector(
|
||||
enum_values | transform(wrap_in_single_quotes)))
|
||||
<< "); ";
|
||||
if (_stmt.if_not_exists) {
|
||||
stream << "EXCEPTION WHEN duplicate_object THEN NULL; END $$;";
|
||||
}
|
||||
}
|
||||
|
||||
stream << "CREATE TABLE ";
|
||||
|
||||
if (_stmt.if_not_exists) {
|
||||
@@ -385,6 +419,20 @@ std::vector<std::string> get_primary_keys(
|
||||
transform(wrap_in_quotes));
|
||||
}
|
||||
|
||||
std::vector<std::pair<std::string, std::vector<std::string>>> get_enum_types(
|
||||
const dynamic::CreateTable& _stmt) noexcept {
|
||||
using namespace std::ranges::views;
|
||||
|
||||
const auto is_enum = [](const dynamic::Column& _col) -> bool {
|
||||
return _col.type.visit([&](const auto& _t) -> bool {
|
||||
using T = std::remove_cvref_t<decltype(_t)>;
|
||||
return std::is_same_v<T, dynamic::types::Enum>;
|
||||
});
|
||||
};
|
||||
return internal::collect::vector(_stmt.columns | filter(is_enum) |
|
||||
transform(get_enum_mapping));
|
||||
}
|
||||
|
||||
std::string insert_to_sql(const dynamic::Insert& _stmt) noexcept {
|
||||
using namespace std::ranges::views;
|
||||
|
||||
@@ -751,7 +799,8 @@ std::string type_to_sql(const dynamic::Type& _type) noexcept {
|
||||
} else if constexpr (std::is_same_v<T, dynamic::types::Int64> ||
|
||||
std::is_same_v<T, dynamic::types::UInt64>) {
|
||||
return "BIGINT";
|
||||
|
||||
} else if constexpr (std::is_same_v<T, dynamic::types::Enum>) {
|
||||
return _t.name;
|
||||
} else if constexpr (std::is_same_v<T, dynamic::types::Float32> ||
|
||||
std::is_same_v<T, dynamic::types::Float64>) {
|
||||
return "NUMERIC";
|
||||
|
||||
@@ -769,7 +769,8 @@ std::string type_to_sql(const dynamic::Type& _type) noexcept {
|
||||
|
||||
} else if constexpr (std::is_same_v<T, dynamic::types::Dynamic>) {
|
||||
return _t.type_name;
|
||||
|
||||
} else if constexpr (std::is_same_v<T, dynamic::types::Enum>) {
|
||||
return "TEXT";
|
||||
} else {
|
||||
static_assert(rfl::always_false_v<T>, "Not all cases were covered.");
|
||||
}
|
||||
|
||||
107
tests/mysql/test_enum_crosstable.cpp
Normal file
107
tests/mysql/test_enum_crosstable.cpp
Normal file
@@ -0,0 +1,107 @@
|
||||
#ifndef SQLGEN_BUILD_DRY_TESTS_ONLY
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <rfl.hpp>
|
||||
#include <rfl/json.hpp>
|
||||
#include <sqlgen.hpp>
|
||||
#include <sqlgen/mysql.hpp>
|
||||
#include <vector>
|
||||
|
||||
namespace test_enum_cross_table {
|
||||
enum class AccessRestriction { PUBLIC = 1, INTERNAL = 2, CONFIDENTIAL = 3 };
|
||||
struct Employee {
|
||||
sqlgen::PrimaryKey<uint32_t, sqlgen::auto_incr> id;
|
||||
std::string first_name;
|
||||
std::string last_name;
|
||||
AccessRestriction access_level;
|
||||
};
|
||||
|
||||
struct Document {
|
||||
sqlgen::PrimaryKey<uint32_t, sqlgen::auto_incr> id;
|
||||
AccessRestriction min_access_level;
|
||||
std::string name;
|
||||
std::string path;
|
||||
};
|
||||
|
||||
TEST(mysql, test_enum_cross_table) {
|
||||
using namespace sqlgen;
|
||||
using namespace sqlgen::literals;
|
||||
|
||||
auto employees = std::vector<Employee>({
|
||||
Employee{.first_name = "Homer",
|
||||
.last_name = "Simpson",
|
||||
.access_level = AccessRestriction::PUBLIC},
|
||||
Employee{.first_name = "Waylon",
|
||||
.last_name = "Smithers",
|
||||
.access_level = AccessRestriction::INTERNAL},
|
||||
Employee{.first_name = "Montgomery",
|
||||
.last_name = "Burns",
|
||||
.access_level = AccessRestriction::CONFIDENTIAL},
|
||||
});
|
||||
auto documents = std::vector<Document>({
|
||||
Document{.min_access_level = AccessRestriction::PUBLIC,
|
||||
.name = "Power Plant Safety Manual",
|
||||
.path = "/documents/powerplant/safety_manual.txt"},
|
||||
Document{.min_access_level = AccessRestriction::INTERNAL,
|
||||
.name = "Staff Memo",
|
||||
.path = "/documents/powerplant/staff_memo.txt"},
|
||||
Document{.min_access_level = AccessRestriction::CONFIDENTIAL,
|
||||
.name = "Operations Report",
|
||||
.path = "/documents/powerplant/operations_report.pdf"},
|
||||
Document{.min_access_level = AccessRestriction::PUBLIC,
|
||||
.name = "Project Plan",
|
||||
.path = "/documents/powerplant/project_plan.md"},
|
||||
Document{.min_access_level = AccessRestriction::INTERNAL,
|
||||
.name = "Budget Q1",
|
||||
.path = "/documents/powerplant/budget_q1.pdf"},
|
||||
Document{.min_access_level = AccessRestriction::CONFIDENTIAL,
|
||||
.name = "HR Policies",
|
||||
.path = "/documents/powerplant/hr_policies.pdf"},
|
||||
Document{.min_access_level = AccessRestriction::PUBLIC,
|
||||
.name = "Team Photo",
|
||||
.path = "/documents/powerplant/team.jpg"},
|
||||
Document{.min_access_level = AccessRestriction::CONFIDENTIAL,
|
||||
.name = "Executive Summary",
|
||||
.path = "/documents/powerplant/executive_summary.docx"},
|
||||
Document{.min_access_level = AccessRestriction::INTERNAL,
|
||||
.name = "Release Notes",
|
||||
.path = "/documents/powerplant/release_notes.txt"},
|
||||
});
|
||||
|
||||
const auto credentials = sqlgen::mysql::Credentials{.host = "localhost",
|
||||
.user = "sqlgen",
|
||||
.password = "password",
|
||||
.dbname = "mysql"};
|
||||
const auto conn = mysql::connect(credentials);
|
||||
conn.and_then(drop<Employee> | if_exists)
|
||||
.and_then(drop<Document> | if_exists);
|
||||
|
||||
write(conn, employees);
|
||||
write(conn, documents);
|
||||
|
||||
const auto smithers = conn.and_then(sqlgen::read<Employee> |
|
||||
where("last_name"_c == "Smithers" and
|
||||
"first_name"_c == "Waylon"))
|
||||
.value();
|
||||
|
||||
const auto smithers_level = smithers.access_level;
|
||||
const auto smithers_documents =
|
||||
conn.and_then(sqlgen::read<std::vector<Document>> |
|
||||
where("min_access_level"_c == smithers_level ||
|
||||
"min_access_level"_c == AccessRestriction::PUBLIC) |
|
||||
order_by("name"_c))
|
||||
.value();
|
||||
|
||||
const auto expected_ids = std::set<uint32_t>{1, 2, 4, 5, 7, 9};
|
||||
std::set<uint32_t> actual_ids;
|
||||
for (const auto &d : smithers_documents) {
|
||||
actual_ids.emplace(d.id());
|
||||
}
|
||||
|
||||
EXPECT_EQ(expected_ids, actual_ids);
|
||||
}
|
||||
|
||||
} // namespace test_enum_cross_table
|
||||
|
||||
#endif
|
||||
91
tests/mysql/test_enum_lookup.cpp
Normal file
91
tests/mysql/test_enum_lookup.cpp
Normal file
@@ -0,0 +1,91 @@
|
||||
#ifndef SQLGEN_BUILD_DRY_TESTS_ONLY
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <rfl.hpp>
|
||||
#include <rfl/json.hpp>
|
||||
#include <sqlgen.hpp>
|
||||
#include <sqlgen/mysql.hpp>
|
||||
#include <vector>
|
||||
|
||||
namespace test_enum_lookup {
|
||||
|
||||
enum class AccessRestriction { PUBLIC = 1, INTERNAL = 2, CONFIDENTIAL = 3 };
|
||||
struct Document {
|
||||
sqlgen::PrimaryKey<uint32_t, sqlgen::auto_incr> id;
|
||||
AccessRestriction min_access_level;
|
||||
std::string name;
|
||||
std::string path;
|
||||
};
|
||||
|
||||
TEST(mysql, test_enum_lookup) {
|
||||
auto documents = std::vector<Document>({
|
||||
Document{.min_access_level = AccessRestriction::PUBLIC,
|
||||
.name = "Power Plant Safety Manual",
|
||||
.path = "/documents/powerplant/safety_manual.txt"},
|
||||
Document{.min_access_level = AccessRestriction::INTERNAL,
|
||||
.name = "Staff Memo",
|
||||
.path = "/documents/powerplant/staff_memo.txt"},
|
||||
Document{.min_access_level = AccessRestriction::CONFIDENTIAL,
|
||||
.name = "Operations Report",
|
||||
.path = "/documents/powerplant/operations_report.pdf"},
|
||||
Document{.min_access_level = AccessRestriction::PUBLIC,
|
||||
.name = "Project Plan",
|
||||
.path = "/documents/powerplant/project_plan.md"},
|
||||
Document{.min_access_level = AccessRestriction::INTERNAL,
|
||||
.name = "Budget Q1",
|
||||
.path = "/documents/powerplant/budget_q1.pdf"},
|
||||
Document{.min_access_level = AccessRestriction::CONFIDENTIAL,
|
||||
.name = "HR Policies",
|
||||
.path = "/documents/powerplant/hr_policies.pdf"},
|
||||
Document{.min_access_level = AccessRestriction::PUBLIC,
|
||||
.name = "Team Photo",
|
||||
.path = "/documents/powerplant/team.jpg"},
|
||||
Document{.min_access_level = AccessRestriction::CONFIDENTIAL,
|
||||
.name = "Executive Summary",
|
||||
.path = "/documents/powerplant/executive_summary.docx"},
|
||||
Document{.min_access_level = AccessRestriction::INTERNAL,
|
||||
.name = "Release Notes",
|
||||
.path = "/documents/powerplant/release_notes.txt"},
|
||||
});
|
||||
|
||||
const auto credentials = sqlgen::mysql::Credentials{.host = "localhost",
|
||||
.user = "sqlgen",
|
||||
.password = "password",
|
||||
.dbname = "mysql"};
|
||||
|
||||
using namespace sqlgen;
|
||||
using namespace sqlgen::literals;
|
||||
|
||||
const auto public_documents =
|
||||
mysql::connect(credentials)
|
||||
.and_then(drop<Document> | if_exists)
|
||||
.and_then(write(std::ref(documents)))
|
||||
.and_then(sqlgen::read<std::vector<Document>> |
|
||||
where("min_access_level"_c == AccessRestriction::PUBLIC) |
|
||||
order_by("name"_c.desc()))
|
||||
.value();
|
||||
|
||||
const auto expected = std::vector<Document>({
|
||||
Document{.id = 7,
|
||||
.min_access_level = AccessRestriction::PUBLIC,
|
||||
.name = "Team Photo",
|
||||
.path = "/documents/powerplant/team.jpg"},
|
||||
Document{.id = 4,
|
||||
.min_access_level = AccessRestriction::PUBLIC,
|
||||
.name = "Project Plan",
|
||||
.path = "/documents/powerplant/project_plan.md"},
|
||||
Document{.id = 1,
|
||||
.min_access_level = AccessRestriction::PUBLIC,
|
||||
.name = "Power Plant Safety Manual",
|
||||
.path = "/documents/powerplant/safety_manual.txt"},
|
||||
});
|
||||
|
||||
const auto json1 = rfl::json::write(expected);
|
||||
const auto json2 = rfl::json::write(public_documents);
|
||||
EXPECT_EQ(json1, json2);
|
||||
}
|
||||
|
||||
} // namespace test_enum_lookup
|
||||
|
||||
#endif
|
||||
66
tests/mysql/test_enum_namespace.cpp
Normal file
66
tests/mysql/test_enum_namespace.cpp
Normal file
@@ -0,0 +1,66 @@
|
||||
#ifndef SQLGEN_BUILD_DRY_TESTS_ONLY
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <rfl.hpp>
|
||||
#include <rfl/json.hpp>
|
||||
#include <sqlgen.hpp>
|
||||
#include <sqlgen/mysql.hpp>
|
||||
#include <vector>
|
||||
|
||||
namespace test_enum_namespace {
|
||||
namespace first {
|
||||
enum class IdenticallyNamed { VALUE0, VALUE1, VALUE2 };
|
||||
|
||||
}
|
||||
namespace second {
|
||||
enum class IdenticallyNamed { VALUE3, VALUE4, VALUE5 };
|
||||
}
|
||||
|
||||
struct MultiStruct {
|
||||
sqlgen::PrimaryKey<uint32_t, sqlgen::auto_incr> id;
|
||||
first::IdenticallyNamed enum_one;
|
||||
second::IdenticallyNamed enum_two;
|
||||
};
|
||||
|
||||
TEST(mysql, test_enum_namespace) {
|
||||
using namespace sqlgen;
|
||||
using namespace sqlgen::literals;
|
||||
|
||||
auto objects = std::vector<MultiStruct>({
|
||||
MultiStruct{.enum_one = first::IdenticallyNamed::VALUE0,
|
||||
.enum_two = second::IdenticallyNamed::VALUE3},
|
||||
MultiStruct{.enum_one = first::IdenticallyNamed::VALUE1,
|
||||
.enum_two = second::IdenticallyNamed::VALUE4},
|
||||
MultiStruct{.enum_one = first::IdenticallyNamed::VALUE2,
|
||||
.enum_two = second::IdenticallyNamed::VALUE5},
|
||||
});
|
||||
|
||||
const auto credentials = sqlgen::mysql::Credentials{.host = "localhost",
|
||||
.user = "sqlgen",
|
||||
.password = "password",
|
||||
.dbname = "mysql"};
|
||||
const auto conn = mysql::connect(credentials);
|
||||
conn.and_then(drop<MultiStruct> | if_exists);
|
||||
|
||||
write(conn, objects);
|
||||
|
||||
const auto read_objects =
|
||||
sqlgen::read<std::vector<MultiStruct>>(conn).value();
|
||||
std::vector<uint32_t> actual_ids;
|
||||
for (const auto& obj : read_objects) {
|
||||
if (obj.enum_one == first::IdenticallyNamed::VALUE0) {
|
||||
EXPECT_EQ(obj.enum_two, second::IdenticallyNamed::VALUE3);
|
||||
} else if (obj.enum_one == first::IdenticallyNamed::VALUE1) {
|
||||
EXPECT_EQ(obj.enum_two, second::IdenticallyNamed::VALUE4);
|
||||
} else if (obj.enum_one == first::IdenticallyNamed::VALUE2) {
|
||||
EXPECT_EQ(obj.enum_two, second::IdenticallyNamed::VALUE5);
|
||||
} else {
|
||||
FAIL() << "Unexpected enum value";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace test_enum_namespace
|
||||
|
||||
#endif
|
||||
108
tests/postgres/test_enum_crosstable.cpp
Normal file
108
tests/postgres/test_enum_crosstable.cpp
Normal file
@@ -0,0 +1,108 @@
|
||||
#ifndef SQLGEN_BUILD_DRY_TESTS_ONLY
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <rfl.hpp>
|
||||
#include <rfl/json.hpp>
|
||||
#include <sqlgen.hpp>
|
||||
#include <sqlgen/postgres.hpp>
|
||||
#include <vector>
|
||||
|
||||
namespace test_enum_cross_table {
|
||||
enum class AccessRestriction { PUBLIC = 1, INTERNAL = 2, CONFIDENTIAL = 3 };
|
||||
struct Employee {
|
||||
sqlgen::PrimaryKey<uint32_t, sqlgen::auto_incr> id;
|
||||
std::string first_name;
|
||||
std::string last_name;
|
||||
AccessRestriction access_level;
|
||||
};
|
||||
|
||||
struct Document {
|
||||
sqlgen::PrimaryKey<uint32_t, sqlgen::auto_incr> id;
|
||||
AccessRestriction min_access_level;
|
||||
std::string name;
|
||||
std::string path;
|
||||
};
|
||||
|
||||
TEST(postgres, test_enum_cross_table) {
|
||||
using namespace sqlgen;
|
||||
using namespace sqlgen::literals;
|
||||
|
||||
auto employees = std::vector<Employee>({
|
||||
Employee{.first_name = "Homer",
|
||||
.last_name = "Simpson",
|
||||
.access_level = AccessRestriction::PUBLIC},
|
||||
Employee{.first_name = "Waylon",
|
||||
.last_name = "Smithers",
|
||||
.access_level = AccessRestriction::INTERNAL},
|
||||
Employee{.first_name = "Montgomery",
|
||||
.last_name = "Burns",
|
||||
.access_level = AccessRestriction::CONFIDENTIAL},
|
||||
});
|
||||
auto documents = std::vector<Document>({
|
||||
Document{.min_access_level = AccessRestriction::PUBLIC,
|
||||
.name = "Power Plant Safety Manual",
|
||||
.path = "/documents/powerplant/safety_manual.txt"},
|
||||
Document{.min_access_level = AccessRestriction::INTERNAL,
|
||||
.name = "Staff Memo",
|
||||
.path = "/documents/powerplant/staff_memo.txt"},
|
||||
Document{.min_access_level = AccessRestriction::CONFIDENTIAL,
|
||||
.name = "Operations Report",
|
||||
.path = "/documents/powerplant/operations_report.pdf"},
|
||||
Document{.min_access_level = AccessRestriction::PUBLIC,
|
||||
.name = "Project Plan",
|
||||
.path = "/documents/powerplant/project_plan.md"},
|
||||
Document{.min_access_level = AccessRestriction::INTERNAL,
|
||||
.name = "Budget Q1",
|
||||
.path = "/documents/powerplant/budget_q1.pdf"},
|
||||
Document{.min_access_level = AccessRestriction::CONFIDENTIAL,
|
||||
.name = "HR Policies",
|
||||
.path = "/documents/powerplant/hr_policies.pdf"},
|
||||
Document{.min_access_level = AccessRestriction::PUBLIC,
|
||||
.name = "Team Photo",
|
||||
.path = "/documents/powerplant/team.jpg"},
|
||||
Document{.min_access_level = AccessRestriction::CONFIDENTIAL,
|
||||
.name = "Executive Summary",
|
||||
.path = "/documents/powerplant/executive_summary.docx"},
|
||||
Document{.min_access_level = AccessRestriction::INTERNAL,
|
||||
.name = "Release Notes",
|
||||
.path = "/documents/powerplant/release_notes.txt"},
|
||||
});
|
||||
|
||||
const auto credentials = sqlgen::postgres::Credentials{.user = "postgres",
|
||||
.password = "password",
|
||||
.host = "localhost",
|
||||
.dbname = "postgres"};
|
||||
const auto conn = postgres::connect(credentials);
|
||||
conn.and_then(drop<Employee> | if_exists)
|
||||
.and_then(drop<Document> | if_exists);
|
||||
|
||||
write(conn, employees);
|
||||
write(conn, documents);
|
||||
|
||||
const auto smithers = conn.and_then(sqlgen::read<Employee> |
|
||||
where("last_name"_c == "Smithers" and
|
||||
"first_name"_c == "Waylon"))
|
||||
.value();
|
||||
// EXPECT_FALSE(smithers.empty());
|
||||
|
||||
const auto smithers_level = smithers.access_level;
|
||||
const auto smithers_documents =
|
||||
conn.and_then(sqlgen::read<std::vector<Document>> |
|
||||
where("min_access_level"_c == smithers_level ||
|
||||
"min_access_level"_c == AccessRestriction::PUBLIC) |
|
||||
order_by("name"_c))
|
||||
.value();
|
||||
|
||||
const auto expected_ids = std::set<uint32_t>{1, 2, 4, 5, 7, 9};
|
||||
std::set<uint32_t> actual_ids;
|
||||
for (const auto &d : smithers_documents) {
|
||||
actual_ids.emplace(d.id());
|
||||
}
|
||||
|
||||
EXPECT_EQ(expected_ids, actual_ids);
|
||||
}
|
||||
|
||||
} // namespace test_enum_cross_table
|
||||
|
||||
#endif
|
||||
91
tests/postgres/test_enum_lookup.cpp
Normal file
91
tests/postgres/test_enum_lookup.cpp
Normal file
@@ -0,0 +1,91 @@
|
||||
#ifndef SQLGEN_BUILD_DRY_TESTS_ONLY
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <rfl.hpp>
|
||||
#include <rfl/json.hpp>
|
||||
#include <sqlgen.hpp>
|
||||
#include <sqlgen/postgres.hpp>
|
||||
#include <vector>
|
||||
|
||||
namespace test_enum_lookup {
|
||||
|
||||
enum class AccessRestriction { PUBLIC = 1, INTERNAL = 2, CONFIDENTIAL = 3 };
|
||||
struct Document {
|
||||
sqlgen::PrimaryKey<uint32_t, sqlgen::auto_incr> id;
|
||||
AccessRestriction min_access_level;
|
||||
std::string name;
|
||||
std::string path;
|
||||
};
|
||||
|
||||
TEST(postgres, test_enum_lookup) {
|
||||
auto documents = std::vector<Document>({
|
||||
Document{.min_access_level = AccessRestriction::PUBLIC,
|
||||
.name = "Power Plant Safety Manual",
|
||||
.path = "/documents/powerplant/safety_manual.txt"},
|
||||
Document{.min_access_level = AccessRestriction::INTERNAL,
|
||||
.name = "Staff Memo",
|
||||
.path = "/documents/powerplant/staff_memo.txt"},
|
||||
Document{.min_access_level = AccessRestriction::CONFIDENTIAL,
|
||||
.name = "Operations Report",
|
||||
.path = "/documents/powerplant/operations_report.pdf"},
|
||||
Document{.min_access_level = AccessRestriction::PUBLIC,
|
||||
.name = "Project Plan",
|
||||
.path = "/documents/powerplant/project_plan.md"},
|
||||
Document{.min_access_level = AccessRestriction::INTERNAL,
|
||||
.name = "Budget Q1",
|
||||
.path = "/documents/powerplant/budget_q1.pdf"},
|
||||
Document{.min_access_level = AccessRestriction::CONFIDENTIAL,
|
||||
.name = "HR Policies",
|
||||
.path = "/documents/powerplant/hr_policies.pdf"},
|
||||
Document{.min_access_level = AccessRestriction::PUBLIC,
|
||||
.name = "Team Photo",
|
||||
.path = "/documents/powerplant/team.jpg"},
|
||||
Document{.min_access_level = AccessRestriction::CONFIDENTIAL,
|
||||
.name = "Executive Summary",
|
||||
.path = "/documents/powerplant/executive_summary.docx"},
|
||||
Document{.min_access_level = AccessRestriction::INTERNAL,
|
||||
.name = "Release Notes",
|
||||
.path = "/documents/powerplant/release_notes.txt"},
|
||||
});
|
||||
|
||||
const auto credentials = sqlgen::postgres::Credentials{.user = "postgres",
|
||||
.password = "password",
|
||||
.host = "localhost",
|
||||
.dbname = "postgres"};
|
||||
|
||||
using namespace sqlgen;
|
||||
using namespace sqlgen::literals;
|
||||
|
||||
const auto public_documents =
|
||||
postgres::connect(credentials)
|
||||
.and_then(drop<Document> | if_exists)
|
||||
.and_then(write(std::ref(documents)))
|
||||
.and_then(sqlgen::read<std::vector<Document>> |
|
||||
where("min_access_level"_c == AccessRestriction::PUBLIC) |
|
||||
order_by("name"_c.desc()))
|
||||
.value();
|
||||
|
||||
const auto expected = std::vector<Document>({
|
||||
Document{.id = 7,
|
||||
.min_access_level = AccessRestriction::PUBLIC,
|
||||
.name = "Team Photo",
|
||||
.path = "/documents/powerplant/team.jpg"},
|
||||
Document{.id = 4,
|
||||
.min_access_level = AccessRestriction::PUBLIC,
|
||||
.name = "Project Plan",
|
||||
.path = "/documents/powerplant/project_plan.md"},
|
||||
Document{.id = 1,
|
||||
.min_access_level = AccessRestriction::PUBLIC,
|
||||
.name = "Power Plant Safety Manual",
|
||||
.path = "/documents/powerplant/safety_manual.txt"},
|
||||
});
|
||||
|
||||
const auto json1 = rfl::json::write(expected);
|
||||
const auto json2 = rfl::json::write(public_documents);
|
||||
EXPECT_EQ(json1, json2);
|
||||
}
|
||||
|
||||
} // namespace test_enum_lookup
|
||||
|
||||
#endif
|
||||
66
tests/postgres/test_enum_namespace.cpp
Normal file
66
tests/postgres/test_enum_namespace.cpp
Normal file
@@ -0,0 +1,66 @@
|
||||
#ifndef SQLGEN_BUILD_DRY_TESTS_ONLY
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <rfl.hpp>
|
||||
#include <rfl/json.hpp>
|
||||
#include <sqlgen.hpp>
|
||||
#include <sqlgen/postgres.hpp>
|
||||
#include <vector>
|
||||
|
||||
namespace test_enum_namespace {
|
||||
namespace first {
|
||||
enum class IdenticallyNamed { VALUE0, VALUE1, VALUE2 };
|
||||
|
||||
}
|
||||
namespace second {
|
||||
enum class IdenticallyNamed { VALUE3, VALUE4, VALUE5 };
|
||||
}
|
||||
|
||||
struct MultiStruct {
|
||||
sqlgen::PrimaryKey<uint32_t, sqlgen::auto_incr> id;
|
||||
first::IdenticallyNamed enum_one;
|
||||
second::IdenticallyNamed enum_two;
|
||||
};
|
||||
|
||||
TEST(mysql, test_enum_namespace) {
|
||||
using namespace sqlgen;
|
||||
using namespace sqlgen::literals;
|
||||
|
||||
auto objects = std::vector<MultiStruct>({
|
||||
MultiStruct{.enum_one = first::IdenticallyNamed::VALUE0,
|
||||
.enum_two = second::IdenticallyNamed::VALUE3},
|
||||
MultiStruct{.enum_one = first::IdenticallyNamed::VALUE1,
|
||||
.enum_two = second::IdenticallyNamed::VALUE4},
|
||||
MultiStruct{.enum_one = first::IdenticallyNamed::VALUE2,
|
||||
.enum_two = second::IdenticallyNamed::VALUE5},
|
||||
});
|
||||
|
||||
const auto credentials = sqlgen::postgres::Credentials{.user = "postgres",
|
||||
.password = "password",
|
||||
.host = "localhost",
|
||||
.dbname = "postgres"};
|
||||
const auto conn = postgres::connect(credentials);
|
||||
conn.and_then(drop<MultiStruct> | if_exists);
|
||||
|
||||
write(conn, objects);
|
||||
|
||||
const auto read_objects =
|
||||
sqlgen::read<std::vector<MultiStruct>>(conn).value();
|
||||
std::vector<uint32_t> actual_ids;
|
||||
for (const auto& obj : read_objects) {
|
||||
if (obj.enum_one == first::IdenticallyNamed::VALUE0) {
|
||||
EXPECT_EQ(obj.enum_two, second::IdenticallyNamed::VALUE3);
|
||||
} else if (obj.enum_one == first::IdenticallyNamed::VALUE1) {
|
||||
EXPECT_EQ(obj.enum_two, second::IdenticallyNamed::VALUE4);
|
||||
} else if (obj.enum_one == first::IdenticallyNamed::VALUE2) {
|
||||
EXPECT_EQ(obj.enum_two, second::IdenticallyNamed::VALUE5);
|
||||
} else {
|
||||
FAIL() << "Unexpected enum value";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace test_enum_namespace
|
||||
|
||||
#endif
|
||||
103
tests/sqlite/test_enum_crosstable.cpp
Normal file
103
tests/sqlite/test_enum_crosstable.cpp
Normal file
@@ -0,0 +1,103 @@
|
||||
#ifndef SQLGEN_BUILD_DRY_TESTS_ONLY
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <rfl.hpp>
|
||||
#include <rfl/json.hpp>
|
||||
#include <sqlgen.hpp>
|
||||
#include <sqlgen/sqlite.hpp>
|
||||
#include <vector>
|
||||
|
||||
namespace test_enum_cross_table {
|
||||
enum class AccessRestriction { PUBLIC = 1, INTERNAL = 2, CONFIDENTIAL = 3 };
|
||||
struct Employee {
|
||||
sqlgen::PrimaryKey<uint32_t, sqlgen::auto_incr> id;
|
||||
std::string first_name;
|
||||
std::string last_name;
|
||||
AccessRestriction access_level;
|
||||
};
|
||||
|
||||
struct Document {
|
||||
sqlgen::PrimaryKey<uint32_t, sqlgen::auto_incr> id;
|
||||
AccessRestriction min_access_level;
|
||||
std::string name;
|
||||
std::string path;
|
||||
};
|
||||
|
||||
TEST(sqlite, test_enum_cross_table) {
|
||||
using namespace sqlgen;
|
||||
using namespace sqlgen::literals;
|
||||
|
||||
auto employees = std::vector<Employee>({
|
||||
Employee{.first_name = "Homer",
|
||||
.last_name = "Simpson",
|
||||
.access_level = AccessRestriction::PUBLIC},
|
||||
Employee{.first_name = "Waylon",
|
||||
.last_name = "Smithers",
|
||||
.access_level = AccessRestriction::INTERNAL},
|
||||
Employee{.first_name = "Montgomery",
|
||||
.last_name = "Burns",
|
||||
.access_level = AccessRestriction::CONFIDENTIAL},
|
||||
});
|
||||
auto documents = std::vector<Document>({
|
||||
Document{.min_access_level = AccessRestriction::PUBLIC,
|
||||
.name = "Power Plant Safety Manual",
|
||||
.path = "/documents/powerplant/safety_manual.txt"},
|
||||
Document{.min_access_level = AccessRestriction::INTERNAL,
|
||||
.name = "Staff Memo",
|
||||
.path = "/documents/powerplant/staff_memo.txt"},
|
||||
Document{.min_access_level = AccessRestriction::CONFIDENTIAL,
|
||||
.name = "Operations Report",
|
||||
.path = "/documents/powerplant/operations_report.pdf"},
|
||||
Document{.min_access_level = AccessRestriction::PUBLIC,
|
||||
.name = "Project Plan",
|
||||
.path = "/documents/powerplant/project_plan.md"},
|
||||
Document{.min_access_level = AccessRestriction::INTERNAL,
|
||||
.name = "Budget Q1",
|
||||
.path = "/documents/powerplant/budget_q1.pdf"},
|
||||
Document{.min_access_level = AccessRestriction::CONFIDENTIAL,
|
||||
.name = "HR Policies",
|
||||
.path = "/documents/powerplant/hr_policies.pdf"},
|
||||
Document{.min_access_level = AccessRestriction::PUBLIC,
|
||||
.name = "Team Photo",
|
||||
.path = "/documents/powerplant/team.jpg"},
|
||||
Document{.min_access_level = AccessRestriction::CONFIDENTIAL,
|
||||
.name = "Executive Summary",
|
||||
.path = "/documents/powerplant/executive_summary.docx"},
|
||||
Document{.min_access_level = AccessRestriction::INTERNAL,
|
||||
.name = "Release Notes",
|
||||
.path = "/documents/powerplant/release_notes.txt"},
|
||||
});
|
||||
|
||||
const auto conn = sqlite::connect();
|
||||
conn.and_then(drop<Employee> | if_exists)
|
||||
.and_then(drop<Document> | if_exists);
|
||||
|
||||
write(conn, employees);
|
||||
write(conn, documents);
|
||||
|
||||
const auto smithers = conn.and_then(sqlgen::read<Employee> |
|
||||
where("last_name"_c == "Smithers" and
|
||||
"first_name"_c == "Waylon"))
|
||||
.value();
|
||||
|
||||
const auto smithers_level = smithers.access_level;
|
||||
const auto smithers_documents =
|
||||
conn.and_then(sqlgen::read<std::vector<Document>> |
|
||||
where("min_access_level"_c == smithers_level ||
|
||||
"min_access_level"_c == AccessRestriction::PUBLIC) |
|
||||
order_by("name"_c))
|
||||
.value();
|
||||
|
||||
const auto expected_ids = std::set<uint32_t>{1, 2, 4, 5, 7, 9};
|
||||
std::set<uint32_t> actual_ids;
|
||||
for (const auto &d : smithers_documents) {
|
||||
actual_ids.emplace(d.id());
|
||||
}
|
||||
|
||||
EXPECT_EQ(expected_ids, actual_ids);
|
||||
}
|
||||
|
||||
} // namespace test_enum_cross_table
|
||||
|
||||
#endif
|
||||
86
tests/sqlite/test_enum_lookup.cpp
Normal file
86
tests/sqlite/test_enum_lookup.cpp
Normal file
@@ -0,0 +1,86 @@
|
||||
#ifndef SQLGEN_BUILD_DRY_TESTS_ONLY
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <rfl.hpp>
|
||||
#include <rfl/json.hpp>
|
||||
#include <sqlgen.hpp>
|
||||
#include <sqlgen/sqlite.hpp>
|
||||
#include <vector>
|
||||
|
||||
namespace test_enum_lookup {
|
||||
|
||||
enum class AccessRestriction { PUBLIC = 1, INTERNAL = 2, CONFIDENTIAL = 3 };
|
||||
struct Document {
|
||||
sqlgen::PrimaryKey<uint32_t, sqlgen::auto_incr> id;
|
||||
AccessRestriction min_access_level;
|
||||
std::string name;
|
||||
std::string path;
|
||||
};
|
||||
|
||||
TEST(sqlite, test_enum_lookup) {
|
||||
auto documents = std::vector<Document>({
|
||||
Document{.min_access_level = AccessRestriction::PUBLIC,
|
||||
.name = "Power Plant Safety Manual",
|
||||
.path = "/documents/powerplant/safety_manual.txt"},
|
||||
Document{.min_access_level = AccessRestriction::INTERNAL,
|
||||
.name = "Staff Memo",
|
||||
.path = "/documents/powerplant/staff_memo.txt"},
|
||||
Document{.min_access_level = AccessRestriction::CONFIDENTIAL,
|
||||
.name = "Operations Report",
|
||||
.path = "/documents/powerplant/operations_report.pdf"},
|
||||
Document{.min_access_level = AccessRestriction::PUBLIC,
|
||||
.name = "Project Plan",
|
||||
.path = "/documents/powerplant/project_plan.md"},
|
||||
Document{.min_access_level = AccessRestriction::INTERNAL,
|
||||
.name = "Budget Q1",
|
||||
.path = "/documents/powerplant/budget_q1.pdf"},
|
||||
Document{.min_access_level = AccessRestriction::CONFIDENTIAL,
|
||||
.name = "HR Policies",
|
||||
.path = "/documents/powerplant/hr_policies.pdf"},
|
||||
Document{.min_access_level = AccessRestriction::PUBLIC,
|
||||
.name = "Team Photo",
|
||||
.path = "/documents/powerplant/team.jpg"},
|
||||
Document{.min_access_level = AccessRestriction::CONFIDENTIAL,
|
||||
.name = "Executive Summary",
|
||||
.path = "/documents/powerplant/executive_summary.docx"},
|
||||
Document{.min_access_level = AccessRestriction::INTERNAL,
|
||||
.name = "Release Notes",
|
||||
.path = "/documents/powerplant/release_notes.txt"},
|
||||
});
|
||||
|
||||
using namespace sqlgen;
|
||||
using namespace sqlgen::literals;
|
||||
|
||||
const auto public_documents =
|
||||
sqlite::connect()
|
||||
.and_then(drop<Document> | if_exists)
|
||||
.and_then(write(std::ref(documents)))
|
||||
.and_then(sqlgen::read<std::vector<Document>> |
|
||||
where("min_access_level"_c == AccessRestriction::PUBLIC) |
|
||||
order_by("name"_c.desc()))
|
||||
.value();
|
||||
|
||||
const auto expected = std::vector<Document>({
|
||||
Document{.id = 7,
|
||||
.min_access_level = AccessRestriction::PUBLIC,
|
||||
.name = "Team Photo",
|
||||
.path = "/documents/powerplant/team.jpg"},
|
||||
Document{.id = 4,
|
||||
.min_access_level = AccessRestriction::PUBLIC,
|
||||
.name = "Project Plan",
|
||||
.path = "/documents/powerplant/project_plan.md"},
|
||||
Document{.id = 1,
|
||||
.min_access_level = AccessRestriction::PUBLIC,
|
||||
.name = "Power Plant Safety Manual",
|
||||
.path = "/documents/powerplant/safety_manual.txt"},
|
||||
});
|
||||
|
||||
const auto json1 = rfl::json::write(expected);
|
||||
const auto json2 = rfl::json::write(public_documents);
|
||||
EXPECT_EQ(json1, json2);
|
||||
}
|
||||
|
||||
} // namespace test_enum_lookup
|
||||
|
||||
#endif
|
||||
62
tests/sqlite/test_enum_namespace.cpp
Normal file
62
tests/sqlite/test_enum_namespace.cpp
Normal file
@@ -0,0 +1,62 @@
|
||||
#ifndef SQLGEN_BUILD_DRY_TESTS_ONLY
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <rfl.hpp>
|
||||
#include <rfl/json.hpp>
|
||||
#include <sqlgen.hpp>
|
||||
#include <sqlgen/sqlite.hpp>
|
||||
#include <vector>
|
||||
|
||||
namespace test_enum_namespace {
|
||||
namespace first {
|
||||
enum class IdenticallyNamed { VALUE0, VALUE1, VALUE2 };
|
||||
|
||||
}
|
||||
namespace second {
|
||||
enum class IdenticallyNamed { VALUE3, VALUE4, VALUE5 };
|
||||
}
|
||||
|
||||
struct MultiStruct {
|
||||
sqlgen::PrimaryKey<uint32_t, sqlgen::auto_incr> id;
|
||||
first::IdenticallyNamed enum_one;
|
||||
second::IdenticallyNamed enum_two;
|
||||
};
|
||||
|
||||
TEST(mysql, test_enum_namespace) {
|
||||
using namespace sqlgen;
|
||||
using namespace sqlgen::literals;
|
||||
|
||||
auto objects = std::vector<MultiStruct>({
|
||||
MultiStruct{.enum_one = first::IdenticallyNamed::VALUE0,
|
||||
.enum_two = second::IdenticallyNamed::VALUE3},
|
||||
MultiStruct{.enum_one = first::IdenticallyNamed::VALUE1,
|
||||
.enum_two = second::IdenticallyNamed::VALUE4},
|
||||
MultiStruct{.enum_one = first::IdenticallyNamed::VALUE2,
|
||||
.enum_two = second::IdenticallyNamed::VALUE5},
|
||||
});
|
||||
|
||||
const auto conn = sqlite::connect();
|
||||
conn.and_then(drop<MultiStruct> | if_exists);
|
||||
|
||||
write(conn, objects);
|
||||
|
||||
const auto read_objects =
|
||||
sqlgen::read<std::vector<MultiStruct>>(conn).value();
|
||||
std::vector<uint32_t> actual_ids;
|
||||
for (const auto& obj : read_objects) {
|
||||
if (obj.enum_one == first::IdenticallyNamed::VALUE0) {
|
||||
EXPECT_EQ(obj.enum_two, second::IdenticallyNamed::VALUE3);
|
||||
} else if (obj.enum_one == first::IdenticallyNamed::VALUE1) {
|
||||
EXPECT_EQ(obj.enum_two, second::IdenticallyNamed::VALUE4);
|
||||
} else if (obj.enum_one == first::IdenticallyNamed::VALUE2) {
|
||||
EXPECT_EQ(obj.enum_two, second::IdenticallyNamed::VALUE5);
|
||||
} else {
|
||||
FAIL() << "Unexpected enum value";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace test_enum_namespace
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user