discord-bot/libs/sqlite_orm-1.8.2/tests/tests2.cpp

635 lines
20 KiB
C++

#include <sqlite_orm/sqlite_orm.h>
#include <catch2/catch_all.hpp>
#ifdef SQLITE_ORM_OPTIONAL_SUPPORTED
#include <optional> // std::optional
#endif // SQLITE_ORM_OPTIONAL_SUPPORTED
#include <memory> // std::default_delete
#include <stdlib.h> // free()
using namespace sqlite_orm;
using std::default_delete;
using std::unique_ptr;
TEST_CASE("Empty storage") {
auto storage = make_storage("empty.sqlite");
storage.table_exists("table");
}
TEST_CASE("Remove") {
struct Object {
int id;
std::string name;
};
{
auto storage = make_storage(
"test_remove.sqlite",
make_table("objects", make_column("id", &Object::id, primary_key()), make_column("name", &Object::name)));
storage.sync_schema();
storage.remove_all<Object>();
auto id1 = storage.insert(Object{0, "Skillet"});
REQUIRE(storage.count<Object>() == 1);
storage.remove<Object>(id1);
REQUIRE(storage.count<Object>() == 0);
}
{
auto storage = make_storage("test_remove.sqlite",
make_table("objects",
make_column("id", &Object::id),
make_column("name", &Object::name),
primary_key(&Object::id)));
storage.sync_schema();
storage.remove_all<Object>();
auto id1 = storage.insert(Object{0, "Skillet"});
REQUIRE(storage.count<Object>() == 1);
storage.remove<Object>(id1);
REQUIRE(storage.count<Object>() == 0);
}
{
auto storage = make_storage("",
make_table("objects",
make_column("id", &Object::id),
make_column("name", &Object::name),
primary_key(&Object::id, &Object::name)));
storage.sync_schema();
storage.replace(Object{1, "Skillet"});
REQUIRE(storage.count<Object>() == 1);
storage.remove<Object>(1, "Skillet");
REQUIRE(storage.count<Object>() == 0);
storage.replace(Object{1, "Skillet"});
storage.replace(Object{2, "Paul Cless"});
REQUIRE(storage.count<Object>() == 2);
storage.remove<Object>(1, "Skillet");
REQUIRE(storage.count<Object>() == 1);
}
}
#if SQLITE_VERSION_NUMBER >= 3031000
TEST_CASE("insert with generated column") {
struct Product {
std::string name;
double price = 0;
double discount = 0;
double tax = 0;
double netPrice = 0;
#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED
Product() = default;
Product(std::string name, double price, double discount, double tax, double netPrice) :
name{std::move(name)}, price{price}, discount{discount}, tax{tax}, netPrice{netPrice} {}
#endif
bool operator==(const Product& other) const {
return this->name == other.name && this->price == other.price && this->discount == other.discount &&
this->tax == other.tax && this->netPrice == other.netPrice;
}
};
auto storage =
make_storage({},
make_table("products",
make_column("name", &Product::name),
make_column("price", &Product::price),
make_column("discount", &Product::discount),
make_column("tax", &Product::tax),
make_column("net_price",
&Product::netPrice,
generated_always_as(c(&Product::price) * (1 - c(&Product::discount)) *
(1 + c(&Product::tax))))));
storage.sync_schema();
Product product{"ABC Widget", 100, 0.05, 0.07, -100};
SECTION("insert") {
storage.insert(product);
}
SECTION("replace") {
storage.replace(product);
}
auto allProducts = storage.get_all<Product>();
decltype(allProducts) expectedProducts;
expectedProducts.push_back({"ABC Widget", 100, 0.05, 0.07, 101.65});
REQUIRE(allProducts == expectedProducts);
}
#endif
TEST_CASE("insert") {
struct Object {
int id;
std::string name;
};
struct ObjectWithoutRowid {
int id;
std::string name;
};
auto storage = make_storage(
"test_insert.sqlite",
make_table("objects", make_column("id", &Object::id, primary_key()), make_column("name", &Object::name)),
make_table("objects_without_rowid",
make_column("id", &ObjectWithoutRowid::id, primary_key()),
make_column("name", &ObjectWithoutRowid::name))
.without_rowid());
storage.sync_schema();
storage.remove_all<Object>();
storage.remove_all<ObjectWithoutRowid>();
for(auto i = 0; i < 100; ++i) {
storage.insert(Object{
0,
"Skillet",
});
REQUIRE(storage.count<Object>() == i + 1);
}
auto initList = {
Object{
0,
"Insane",
},
Object{
0,
"Super",
},
Object{
0,
"Sun",
},
};
auto countBefore = storage.count<Object>();
SECTION("straight") {
storage.insert_range(initList.begin(), initList.end());
REQUIRE(storage.count<Object>() == countBefore + static_cast<int>(initList.size()));
// test empty container
std::vector<Object> emptyVector;
REQUIRE_NOTHROW(storage.insert_range(emptyVector.begin(), emptyVector.end()));
}
SECTION("pointers") {
std::vector<unique_ptr<Object>> pointers;
pointers.reserve(initList.size());
std::transform(initList.begin(), initList.end(), std::back_inserter(pointers), [](const Object& object) {
return std::make_unique<Object>(Object{object});
});
storage.insert_range(pointers.begin(), pointers.end(), &std::unique_ptr<Object>::operator*);
// test empty container
std::vector<unique_ptr<Object>> emptyVector;
REQUIRE_NOTHROW(
storage.insert_range(emptyVector.begin(), emptyVector.end(), &std::unique_ptr<Object>::operator*));
}
// test insert without rowid
storage.insert(ObjectWithoutRowid{10, "Life"});
REQUIRE(storage.get<ObjectWithoutRowid>(10).name == "Life");
storage.insert(ObjectWithoutRowid{20, "Death"});
REQUIRE(storage.get<ObjectWithoutRowid>(20).name == "Death");
}
struct SqrtFunction {
static int callsCount;
double operator()(double arg) const {
++callsCount;
return std::sqrt(arg);
}
static const char* name() {
return "SQRT_CUSTOM";
}
};
int SqrtFunction::callsCount = 0;
struct HasPrefixFunction {
static int callsCount;
static int objectsCount;
HasPrefixFunction() {
++objectsCount;
}
HasPrefixFunction(const HasPrefixFunction&) {
++objectsCount;
}
HasPrefixFunction(HasPrefixFunction&&) {
++objectsCount;
}
~HasPrefixFunction() {
--objectsCount;
}
bool operator()(const std::string& str, const std::string& prefix) {
++callsCount;
return str.compare(0, prefix.size(), prefix) == 0;
}
static std::string name() {
return "HAS_PREFIX";
}
};
int HasPrefixFunction::callsCount = 0;
int HasPrefixFunction::objectsCount = 0;
struct MeanFunction {
double total = 0;
int count = 0;
static int objectsCount;
MeanFunction() {
++objectsCount;
}
MeanFunction(const MeanFunction&) {
++objectsCount;
}
MeanFunction(MeanFunction&&) {
++objectsCount;
}
~MeanFunction() {
--objectsCount;
}
void step(double value) {
total += value;
++count;
}
double fin() const {
return total / count;
}
static std::string name() {
return "MEAN";
}
};
int MeanFunction::objectsCount = 0;
struct FirstFunction {
static int objectsCount;
static int callsCount;
FirstFunction() {
++objectsCount;
}
FirstFunction(const MeanFunction&) {
++objectsCount;
}
FirstFunction(MeanFunction&&) {
++objectsCount;
}
~FirstFunction() {
--objectsCount;
}
std::string operator()(const arg_values& args) const {
++callsCount;
std::string res;
res.reserve(args.size());
for(auto value: args) {
auto stringValue = value.get<std::string>();
if(!stringValue.empty()) {
res += stringValue.front();
}
}
return res;
}
static const char* name() {
return "FIRST";
}
};
struct MultiSum {
double sum = 0;
static int objectsCount;
MultiSum() {
++objectsCount;
}
MultiSum(const MeanFunction&) {
++objectsCount;
}
MultiSum(MeanFunction&&) {
++objectsCount;
}
~MultiSum() {
--objectsCount;
}
void step(const arg_values& args) {
for(auto it = args.begin(); it != args.end(); ++it) {
if(!it->empty() && (it->is_integer() || it->is_float())) {
this->sum += it->get<double>();
}
}
}
double fin() const {
return this->sum;
}
static const char* name() {
return "MULTI_SUM";
}
};
int MultiSum::objectsCount = 0;
int FirstFunction::objectsCount = 0;
int FirstFunction::callsCount = 0;
TEST_CASE("custom functions") {
using Catch::Matchers::ContainsSubstring;
SqrtFunction::callsCount = 0;
HasPrefixFunction::callsCount = 0;
FirstFunction::callsCount = 0;
std::string path;
SECTION("in memory") {
path = {};
}
SECTION("file") {
path = "custom_function.sqlite";
::remove(path.c_str());
}
struct User {
int id = 0;
#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED
User() = default;
User(int id) : id{id} {}
#endif
};
auto storage = make_storage(path, make_table("users", make_column("id", &User::id)));
storage.sync_schema();
// call before creation
REQUIRE_THROWS_WITH(storage.select(func<SqrtFunction>(4)), ContainsSubstring("no such function"));
// create function
REQUIRE(SqrtFunction::callsCount == 0);
storage.create_scalar_function<SqrtFunction>();
REQUIRE(SqrtFunction::callsCount == 0);
// call after creation
{
auto rows = storage.select(func<SqrtFunction>(4));
REQUIRE(SqrtFunction::callsCount == 1);
decltype(rows) expected;
expected.push_back(2);
REQUIRE(rows == expected);
}
// create function
REQUIRE(HasPrefixFunction::callsCount == 0);
REQUIRE(HasPrefixFunction::objectsCount == 0);
storage.create_scalar_function<HasPrefixFunction>();
REQUIRE(HasPrefixFunction::callsCount == 0);
REQUIRE(HasPrefixFunction::objectsCount == 0);
// call after creation
{
auto rows = storage.select(func<HasPrefixFunction>("one", "o"));
decltype(rows) expected;
expected.push_back(true);
REQUIRE(rows == expected);
}
REQUIRE(HasPrefixFunction::callsCount == 1);
REQUIRE(HasPrefixFunction::objectsCount == 0);
{
auto rows = storage.select(func<HasPrefixFunction>("two", "b"));
decltype(rows) expected;
expected.push_back(false);
REQUIRE(rows == expected);
}
REQUIRE(HasPrefixFunction::callsCount == 2);
REQUIRE(HasPrefixFunction::objectsCount == 0);
// delete function
storage.delete_scalar_function<HasPrefixFunction>();
// delete function
storage.delete_scalar_function<SqrtFunction>();
storage.create_aggregate_function<MeanFunction>();
storage.replace(User{1});
storage.replace(User{2});
storage.replace(User{3});
REQUIRE(storage.count<User>() == 3);
{
REQUIRE(MeanFunction::objectsCount == 0);
auto rows = storage.select(func<MeanFunction>(&User::id));
REQUIRE(MeanFunction::objectsCount == 0);
decltype(rows) expected;
expected.push_back(2);
REQUIRE(rows == expected);
}
storage.delete_aggregate_function<MeanFunction>();
storage.create_scalar_function<FirstFunction>();
{
auto rows = storage.select(func<FirstFunction>("Vanotek", "Tinashe", "Pitbull"));
decltype(rows) expected;
expected.push_back("VTP");
REQUIRE(rows == expected);
REQUIRE(FirstFunction::objectsCount == 0);
REQUIRE(FirstFunction::callsCount == 1);
}
{
auto rows = storage.select(func<FirstFunction>("Charli XCX", "Rita Ora"));
decltype(rows) expected;
expected.push_back("CR");
REQUIRE(rows == expected);
REQUIRE(FirstFunction::objectsCount == 0);
REQUIRE(FirstFunction::callsCount == 2);
}
{
auto rows = storage.select(func<FirstFunction>("Ted"));
decltype(rows) expected;
expected.push_back("T");
REQUIRE(rows == expected);
REQUIRE(FirstFunction::objectsCount == 0);
REQUIRE(FirstFunction::callsCount == 3);
}
{
auto rows = storage.select(func<FirstFunction>());
decltype(rows) expected;
expected.push_back("");
REQUIRE(rows == expected);
REQUIRE(FirstFunction::objectsCount == 0);
REQUIRE(FirstFunction::callsCount == 4);
}
storage.delete_scalar_function<FirstFunction>();
storage.create_aggregate_function<MultiSum>();
{
REQUIRE(MultiSum::objectsCount == 0);
auto rows = storage.select(func<MultiSum>(&User::id, 5));
decltype(rows) expected;
expected.push_back(21);
REQUIRE(rows == expected);
REQUIRE(MultiSum::objectsCount == 0);
}
storage.delete_aggregate_function<MultiSum>();
}
// Wrap std::default_delete in a function
#ifndef SQLITE_ORM_BROKEN_VARIADIC_PACK_EXPANSION
template<typename T>
void delete_default(std::conditional_t<std::is_array<T>::value, std::decay_t<T>, T*> o) noexcept(
noexcept(std::default_delete<T>{}(o))) {
std::default_delete<T>{}(o);
}
// Integral function constant for default deletion
template<typename T>
using delete_default_t = std::integral_constant<decltype(&delete_default<T>), delete_default<T>>;
// Integral function constant variable for default deletion
template<typename T>
SQLITE_ORM_INLINE_VAR constexpr delete_default_t<T> delete_default_f{};
#endif
using free_t = std::integral_constant<decltype(&free), free>;
SQLITE_ORM_INLINE_VAR constexpr free_t free_f{};
TEST_CASE("obtain_xdestroy_for") {
using internal::xdestroy_proxy;
// class yielding a 'xDestroy' function pointer
struct xdestroy_holder {
xdestroy_fn_t xDestroy = free;
constexpr operator xdestroy_fn_t() const noexcept {
return xDestroy;
}
};
// class yielding a function pointer not of type xdestroy_fn_t
struct int_destroy_holder {
using destroy_fn_t = void (*)(int*);
destroy_fn_t destroy = nullptr;
constexpr operator destroy_fn_t() const noexcept {
return destroy;
}
};
{
constexpr int* int_nullptr = nullptr;
#if !defined(SQLITE_ORM_BROKEN_VARIADIC_PACK_EXPANSION) || \
(__cpp_constexpr >= 201907L) // Trivial default initialization in constexpr functions
constexpr const int* const_int_nullptr = nullptr;
#endif
// null_xdestroy_f(int*)
constexpr xdestroy_fn_t xDestroy1 = obtain_xdestroy_for(null_xdestroy_f, int_nullptr);
STATIC_REQUIRE(xDestroy1 == nullptr);
REQUIRE(xDestroy1 == nullptr);
// free(int*)
constexpr xdestroy_fn_t xDestroy2 = obtain_xdestroy_for(free, int_nullptr);
STATIC_REQUIRE(xDestroy2 == &free);
REQUIRE(xDestroy2 == &free);
// free_f(int*)
constexpr xdestroy_fn_t xDestroy3 = obtain_xdestroy_for(free_f, int_nullptr);
STATIC_REQUIRE(xDestroy3 == &free);
REQUIRE(xDestroy3 == &free);
#if __cpp_constexpr >= 201603L // constexpr lambda
// [](void* p){}
constexpr auto lambda4_1 = [](void*) {};
constexpr xdestroy_fn_t xDestroy4_1 = obtain_xdestroy_for(lambda4_1, int_nullptr);
STATIC_REQUIRE(xDestroy4_1 == lambda4_1);
REQUIRE(xDestroy4_1 == lambda4_1);
#else
#if !defined(_MSC_VER) || (_MSC_VER >= 1914) // conversion of lambda closure to function pointer using `+`
// [](void* p){}
auto lambda4_1 = [](void*) {};
xdestroy_fn_t xDestroy4_1 = obtain_xdestroy_for(lambda4_1, int_nullptr);
REQUIRE(xDestroy4_1 == lambda4_1);
#endif
#endif
// [](int* p) { delete p; }
#if __cplusplus >= 202002L // default-constructible non-capturing lambdas
constexpr auto lambda4_2 = [](int* p) {
delete p;
};
using lambda4_2_t = std::remove_const_t<decltype(lambda4_2)>;
constexpr xdestroy_fn_t xDestroy4_2 = obtain_xdestroy_for(lambda4_2, int_nullptr);
STATIC_REQUIRE(xDestroy4_2 == &xdestroy_proxy<lambda4_2_t, int>);
REQUIRE((xDestroy4_2 == &xdestroy_proxy<lambda4_2_t, int>));
#endif
// default_delete<int>(int*)
constexpr xdestroy_fn_t xDestroy5 = obtain_xdestroy_for(default_delete<int>{}, int_nullptr);
STATIC_REQUIRE(xDestroy5 == &xdestroy_proxy<default_delete<int>, int>);
REQUIRE((xDestroy5 == &xdestroy_proxy<default_delete<int>, int>));
#ifndef SQLITE_ORM_BROKEN_VARIADIC_PACK_EXPANSION
// delete_default_f<int>(int*)
constexpr xdestroy_fn_t xDestroy6 = obtain_xdestroy_for(delete_default_f<int>, int_nullptr);
STATIC_REQUIRE(xDestroy6 == &xdestroy_proxy<delete_default_t<int>, int>);
REQUIRE((xDestroy6 == &xdestroy_proxy<delete_default_t<int>, int>));
// delete_default_f<int>(const int*)
constexpr xdestroy_fn_t xDestroy7 = obtain_xdestroy_for(delete_default_f<int>, const_int_nullptr);
STATIC_REQUIRE(xDestroy7 == &xdestroy_proxy<delete_default_t<int>, const int>);
REQUIRE((xDestroy7 == &xdestroy_proxy<delete_default_t<int>, const int>));
#endif
#if __cpp_constexpr >= 201907L // Trivial default initialization in constexpr functions
// xdestroy_holder{ free }(int*)
constexpr xdestroy_fn_t xDestroy8 = obtain_xdestroy_for(xdestroy_holder{free}, int_nullptr);
STATIC_REQUIRE(xDestroy8 == &free);
REQUIRE(xDestroy8 == &free);
// xdestroy_holder{ free }(const int*)
constexpr xdestroy_fn_t xDestroy9 = obtain_xdestroy_for(xdestroy_holder{free}, const_int_nullptr);
STATIC_REQUIRE(xDestroy9 == &free);
REQUIRE(xDestroy9 == &free);
// xdestroy_holder{ nullptr }(const int*)
constexpr xdestroy_fn_t xDestroy10 = obtain_xdestroy_for(xdestroy_holder{nullptr}, const_int_nullptr);
STATIC_REQUIRE(xDestroy10 == nullptr);
REQUIRE(xDestroy10 == nullptr);
#endif
// expressions that do not work
#if 0
// can't use functions that differ from xdestroy_fn_t
constexpr xdestroy_fn_t xDestroy = obtain_xdestroy_for(delete_default<int>, int_nullptr);
// can't use object yielding a function pointer that differs from xdestroy_fn_t
constexpr xdestroy_fn_t xDestroy = obtain_xdestroy_for(int_destroy_holder{}, int_nullptr);
// successfully takes default_delete<void>, but default_delete statically asserts on a non-complete type `void*`
constexpr xdestroy_fn_t xDestroy = obtain_xdestroy_for(default_delete<void>{}, int_nullptr);
// successfully takes default_delete<int>, but xdestroy_proxy can't call the deleter with a `const int*`
constexpr xdestroy_fn_t xDestroy = obtain_xdestroy_for(default_delete<int>{}, const_int_nullptr);
#endif
}
}