fixed postgres tests

This commit is contained in:
Sascha Kühl 2025-02-15 12:36:21 +01:00
parent 6e64ba99de
commit ec96c15905
16 changed files with 89 additions and 58 deletions

View File

@ -96,8 +96,8 @@ utils::result<std::unique_ptr<sql::query_result_impl>, utils::error> postgres_co
std::vector<object::attribute_definition> prototype = context.prototype; std::vector<object::attribute_definition> prototype = context.prototype;
const auto num_col = PQnfields(res); const int num_col = PQnfields(res);
if (prototype.size() != num_col) { if (prototype.size() != static_cast<size_t>(num_col)) {
return utils::failure(make_error(sql::error_code::FETCH_FAILED, res, conn_, "Number of received columns doesn't match expected columns.", context.sql)); return utils::failure(make_error(sql::error_code::FETCH_FAILED, res, conn_, "Number of received columns doesn't match expected columns.", context.sql));
} }
for (int i = 0; i < num_col; ++i) { for (int i = 0; i < num_col; ++i) {
@ -170,7 +170,7 @@ utils::basic_type oid2type(const Oid oid) {
return utils::basic_type::type_int32; return utils::basic_type::type_int32;
case 20: case 20:
return utils::basic_type::type_int64; return utils::basic_type::type_int64;
case 24: case 25:
return utils::basic_type::type_text; return utils::basic_type::type_text;
case 1043: case 1043:
return utils::basic_type::type_varchar; return utils::basic_type::type_varchar;

View File

@ -2,7 +2,7 @@ CPMAddPackage("gh:catchorg/Catch2@3.7.1")
list(APPEND CMAKE_MODULE_PATH ${catch2_SOURCE_DIR}/extras) list(APPEND CMAKE_MODULE_PATH ${catch2_SOURCE_DIR}/extras)
set(POSTGRES_CONNECTION_STRING "postgres://news:news@localhost:15432/matador") set(POSTGRES_CONNECTION_STRING "postgres://test:test123!@127.0.0.1:5432/matador")
configure_file(Connection.hpp.in ${PROJECT_BINARY_DIR}/backends/postgres/test/connection.hpp @ONLY IMMEDIATE) configure_file(Connection.hpp.in ${PROJECT_BINARY_DIR}/backends/postgres/test/connection.hpp @ONLY IMMEDIATE)

View File

@ -32,20 +32,20 @@ public:
type_ = utils::data_type_traits<ValueType>::type(0); type_ = utils::data_type_traits<ValueType>::type(0);
} }
void on_primary_key(const char * /*id*/, std::string &/*pk*/, size_t size); void on_primary_key(const char * /*id*/, std::string &/*pk*/, size_t size);
void on_revision(const char * /*id*/, unsigned long long &/*rev*/) {} static void on_revision(const char * /*id*/, unsigned long long &/*rev*/) {}
template < class Type > template < class Type >
void on_attribute(const char * /*id*/, Type &/*x*/, const utils::field_attributes &/*attr*/ = utils::null_attributes) {} static void on_attribute(const char * /*id*/, Type &/*x*/, const utils::field_attributes &/*attr*/ = utils::null_attributes) {}
void on_attribute(const char * /*id*/, char * /*x*/, const utils::field_attributes &/*attr*/ = utils::null_attributes) {} static void on_attribute(const char * /*id*/, char * /*x*/, const utils::field_attributes &/*attr*/ = utils::null_attributes) {}
template<class Pointer> template<class Pointer>
void on_belongs_to(const char * /*id*/, Pointer &/*x*/, const utils::foreign_attributes &/*attr*/) {} static void on_belongs_to(const char * /*id*/, Pointer &/*x*/, const utils::foreign_attributes &/*attr*/) {}
template<class Pointer> template<class Pointer>
void on_has_one(const char * /*id*/, Pointer &/*x*/, const utils::foreign_attributes &/*attr*/) {} static void on_has_one(const char * /*id*/, Pointer &/*x*/, const utils::foreign_attributes &/*attr*/) {}
template<class ContainerType> template<class ContainerType>
void on_has_many(const char * /*id*/, ContainerType &, const char *, const utils::foreign_attributes &/*attr*/) {} static void on_has_many(const char * /*id*/, ContainerType &, const char *, const utils::foreign_attributes &/*attr*/) {}
template<class ContainerType> template<class ContainerType>
void on_has_many_to_many(const char *id, ContainerType &c, const char *join_column, const char *inverse_join_column, const utils::foreign_attributes &/*attr*/) {} static void on_has_many_to_many(const char * /*id*/, ContainerType & /*cont*/, const char * /*join_column*/, const char * /*inverse_join_column*/, const utils::foreign_attributes &/*attr*/) {}
template<class ContainerType> template<class ContainerType>
void on_has_many_to_many(const char *id, ContainerType &c, const utils::foreign_attributes &/*attr*/) {} static void on_has_many_to_many(const char * /*id*/, ContainerType & /*cont*/, const utils::foreign_attributes &/*attr*/) {}
private: private:
utils::basic_type type_{}; utils::basic_type type_{};
@ -65,7 +65,7 @@ public:
attribute_definition_generator gen(columns, repo); attribute_definition_generator gen(columns, repo);
Type obj; Type obj;
access::process(gen, obj); access::process(gen, obj);
return std::move(columns); return columns;
} }
template < class V > template < class V >
@ -92,11 +92,11 @@ public:
} }
} }
template<class ContainerType> template<class ContainerType>
void on_has_many(const char *id, ContainerType &, const char *, const utils::foreign_attributes &/*attr*/) {} static void on_has_many(const char * /*id*/, ContainerType &, const char *, const utils::foreign_attributes &/*attr*/) {}
template<class ContainerType> template<class ContainerType>
void on_has_many_to_many(const char *id, ContainerType &c, const char *join_column, const char *inverse_join_column, const utils::foreign_attributes &/*attr*/) {} static void on_has_many_to_many(const char * /*id*/, ContainerType & /*cont*/, const char * /*join_column*/, const char * /*inverse_join_column*/, const utils::foreign_attributes &/*attr*/) {}
template<class ContainerType> template<class ContainerType>
void on_has_many_to_many(const char *id, ContainerType &c, const utils::foreign_attributes &/*attr*/) {} static void on_has_many_to_many(const char * /*id*/, ContainerType & /*cont*/, const utils::foreign_attributes &/*attr*/) {}
private: private:
[[nodiscard]] utils::result<std::shared_ptr<attribute_definition>, utils::error> determine_foreign_ref(const std::type_index &ti) const; [[nodiscard]] utils::result<std::shared_ptr<attribute_definition>, utils::error> determine_foreign_ref(const std::type_index &ti) const;
@ -122,7 +122,7 @@ void attribute_definition_generator::on_attribute(const char *id, Type &x, const
} }
template<typename Type> template<typename Type>
void attribute_definition_generator::on_attribute(const char *id, std::optional<Type> &x, const utils::field_attributes &attr) void attribute_definition_generator::on_attribute(const char *id, std::optional<Type> & /*x*/, const utils::field_attributes &attr)
{ {
columns_.emplace_back(id, utils::data_type_traits<Type>::type(attr.size()), attr, null_option::NULLABLE); columns_.emplace_back(id, utils::data_type_traits<Type>::type(attr.size()), attr, null_option::NULLABLE);
} }

View File

@ -53,7 +53,6 @@ public:
template <typename Type> template <typename Type>
object_info_ref<Type> info() const { object_info_ref<Type> info() const {
auto ref = std::ref(static_cast<const object_info<Type>&>(*info_));
return std::ref(static_cast<const object_info<Type>&>(*info_)); return std::ref(static_cast<const object_info<Type>&>(*info_));
} }

View File

@ -26,7 +26,7 @@ public:
explicit session(sql::connection_pool<sql::connection> &pool); explicit session(sql::connection_pool<sql::connection> &pool);
template<typename Type> template<typename Type>
[[nodiscard]] utils::result<void, utils::error> attach(const std::string &table_name); [[nodiscard]] utils::result<void, utils::error> attach(const std::string &table_name) const;
utils::result<void, utils::error> create_schema() const; utils::result<void, utils::error> create_schema() const;
@ -58,6 +58,9 @@ public:
auto obj = build_select_query(data.release()).template fetch_one<Type>(*c); auto obj = build_select_query(data.release()).template fetch_one<Type>(*c);
if (!obj) { if (!obj) {
return utils::failure(obj.err());
}
if (!obj->get()) {
return utils::failure(make_error(error_code::FailedToFindObject, "Failed to find object of type " + info->get().name() + " with primary key " + std::to_string(pk) + ".")); return utils::failure(make_error(error_code::FailedToFindObject, "Failed to find object of type " + info->get().name() + " with primary key " + std::to_string(pk) + "."));
} }
return utils::ok(object::object_ptr<Type>{ obj.release() }); return utils::ok(object::object_ptr<Type>{ obj.release() });
@ -104,8 +107,8 @@ public:
} }
template<typename Type> template<typename Type>
void drop_table(); utils::result<void, utils::error> drop_table();
void drop_table(const std::string &table_name) const; utils::result<void, utils::error> drop_table(const std::string &table_name) const;
[[nodiscard]] utils::result<sql::query_result<sql::record>, utils::error> fetch(const sql::query_context &q) const; [[nodiscard]] utils::result<sql::query_result<sql::record>, utils::error> fetch(const sql::query_context &q) const;
// [[nodiscard]] query_result<record> fetch(const std::string &sql) const; // [[nodiscard]] query_result<record> fetch(const std::string &sql) const;
@ -133,8 +136,7 @@ private:
}; };
template<typename Type> template<typename Type>
[[nodiscard]] utils::result<void, utils::error> session::attach(const std::string &table_name) [[nodiscard]] utils::result<void, utils::error> session::attach(const std::string &table_name) const {
{
return schema_->attach<Type>(table_name); return schema_->attach<Type>(table_name);
} }
@ -159,13 +161,14 @@ utils::result<object::object_ptr<Type>, utils::error> session::insert(Type *obj)
} }
template<typename Type> template<typename Type>
void session::drop_table() utils::result<void, utils::error> session::drop_table() {
{
auto info = schema_->info<Type>(); auto info = schema_->info<Type>();
if (info) { if (info) {
return drop_table(info.name); return drop_table(info->get().name());
} }
return utils::failure(info.err());
} }
} }
#endif //QUERY_SESSION_HPP #endif //QUERY_SESSION_HPP

View File

@ -62,10 +62,10 @@ private:
struct entity_query_data { struct entity_query_data {
std::string root_table_name; std::string root_table_name;
std::string pk_column_; std::string pk_column_{};
std::vector<sql::column> columns; std::vector<sql::column> columns{};
std::vector<query::join_data> joins; std::vector<query::join_data> joins{};
std::unique_ptr<query::basic_condition> where_clause; std::unique_ptr<query::basic_condition> where_clause{};
}; };
enum class query_build_error : std::uint8_t { enum class query_build_error : std::uint8_t {
@ -175,7 +175,7 @@ public:
} }
template<class ContainerType> template<class ContainerType>
void on_has_many(const char * id, ContainerType &, const char *join_column, const utils::foreign_attributes &attr) { void on_has_many(const char * /*id*/, ContainerType &, const char *join_column, const utils::foreign_attributes &attr) {
if (attr.fetch() == utils::fetch_type::EAGER) { if (attr.fetch() == utils::fetch_type::EAGER) {
const auto info = schema_.info<typename ContainerType::value_type::value_type>(); const auto info = schema_.info<typename ContainerType::value_type::value_type>();
if (!info) { if (!info) {

View File

@ -15,6 +15,10 @@ class dialect;
namespace matador::query { namespace matador::query {
namespace internal {
struct basic_type_to_string_visitor;
}
struct query_data; struct query_data;
struct value_visitor; struct value_visitor;
@ -56,6 +60,7 @@ protected:
static std::string build_table_name(sql::dialect_token token, const sql::dialect &d, const sql::table& t); static std::string build_table_name(sql::dialect_token token, const sql::dialect &d, const sql::table& t);
std::string determine_value(value_visitor &visitor, const std::variant<utils::placeholder, utils::database_type> &val); std::string determine_value(value_visitor &visitor, const std::variant<utils::placeholder, utils::database_type> &val);
std::string determine_set_value(internal::basic_type_to_string_visitor &visitor, const utils::database_type &val);
protected: protected:
const query_data *data_{}; const query_data *data_{};

View File

@ -37,7 +37,7 @@ public:
column_generator gen(columns, scm, info.value().get().name(), force_lazy); column_generator gen(columns, scm, info.value().get().name(), force_lazy);
Type obj; Type obj;
access::process(gen, obj); access::process(gen, obj);
return std::move(columns); return columns;
} }
template < class V > template < class V >
@ -113,10 +113,10 @@ public:
} }
template<class ContainerType> template<class ContainerType>
static void on_has_many_to_many(const char *id, ContainerType &c, const char *join_column, const char *inverse_join_column, const utils::foreign_attributes &attr) {} static void on_has_many_to_many(const char * /*id*/, ContainerType & /*cont*/, const char * /*join_column*/, const char * /*inverse_join_column*/, const utils::foreign_attributes & /*attr*/) {}
template<class ContainerType> template<class ContainerType>
static void on_has_many_to_many(const char *id, ContainerType &c, const utils::foreign_attributes &attr) {} static void on_has_many_to_many(const char * /*id*/, ContainerType & /*cont*/, const utils::foreign_attributes &/*attr*/) {}
private: private:
void push(const std::string &column_name); void push(const std::string &column_name);

View File

@ -222,6 +222,9 @@ result<DestType, conversion_error> to(const SourceType &/*source*/, std::enable_
template < typename DestType, typename SourceType > template < typename DestType, typename SourceType >
result<DestType, conversion_error> to(const SourceType &source, std::enable_if_t<std::is_same_v<SourceType, const char*> &&std::is_same_v<DestType, std::string>>* = nullptr) { result<DestType, conversion_error> to(const SourceType &source, std::enable_if_t<std::is_same_v<SourceType, const char*> &&std::is_same_v<DestType, std::string>>* = nullptr) {
if (source == nullptr) {
return ok(std::string{});
}
return ok(std::string(source)); return ok(std::string(source));
} }

View File

@ -30,13 +30,20 @@ utils::result<void, utils::error> session::create_schema() const {
return utils::ok<void>(); return utils::ok<void>();
} }
void session::drop_table(const std::string &table_name) const { utils::result<void, utils::error> session::drop_table(const std::string &table_name) const {
auto c = pool_.acquire(); auto c = pool_.acquire();
if (!c.valid()) { if (!c.valid()) {
throw std::logic_error("no database connection available"); throw std::logic_error("no database connection available");
} }
// c->query(*schema_).drop().table(table_name).execute(); auto result = query::query::drop()
.table(table_name)
.execute(*c);
if (result.is_error()) {
return utils::failure(result.err());
}
return utils::ok<void>();
} }
utils::result<sql::query_result<sql::record>, utils::error> session::fetch(const sql::query_context &q) const utils::result<sql::query_result<sql::record>, utils::error> session::fetch(const sql::query_context &q) const

View File

@ -183,6 +183,7 @@ std::string query_compiler::determine_value(value_visitor &visitor, const std::v
query_.bind_vars.emplace_back(std::string("value_") + std::to_string(query_.bind_vars.size() + 1)); query_.bind_vars.emplace_back(std::string("value_") + std::to_string(query_.bind_vars.size() + 1));
return dialect_->next_placeholder(query_.bind_vars); return dialect_->next_placeholder(query_.bind_vars);
} }
void query_compiler::visit(internal::query_values_part &values_part) { void query_compiler::visit(internal::query_values_part &values_part) {
query_.sql += " " + dialect_->token_at(sql::dialect_token::VALUES); query_.sql += " " + dialect_->token_at(sql::dialect_token::VALUES);
@ -289,32 +290,45 @@ void query_compiler::visit(internal::query_drop_part &/*drop_part*/)
query_.sql = dialect_->token_at(sql::dialect_token::DROP); query_.sql = dialect_->token_at(sql::dialect_token::DROP);
} }
std::string query_compiler::determine_set_value(internal::basic_type_to_string_visitor &visitor, const utils::database_type &val) {
if (data_->mode == query_mode::Direct) {
std::visit(visitor, val);
return visitor.result;
}
query_.bind_vars.emplace_back(std::string("value_") + std::to_string(query_.bind_vars.size() + 1));
return dialect_->next_placeholder(query_.bind_vars);
}
void query_compiler::visit(internal::query_set_part &set_part) void query_compiler::visit(internal::query_set_part &set_part)
{ {
query_.sql += " " + dialect_->token_at(sql::dialect_token::SET) + " "; query_.sql += " " + dialect_->token_at(sql::dialect_token::SET) + " ";
attribute_string_writer writer(*dialect_, connection_); attribute_string_writer writer(*dialect_, connection_);
internal::basic_type_to_string_visitor value_to_string(writer, query_); internal::basic_type_to_string_visitor visitor(writer, query_);
std::string result; std::string result;
if (set_part.key_values().size() < 2) { if (set_part.key_values().size() < 2) {
for (const auto &col: set_part.key_values()) { for (const auto &col: set_part.key_values()) {
result.append(dialect_->prepare_identifier_string(col.name()) + "="); result.append(dialect_->prepare_identifier_string(col.name()) + "=");
auto var = col.value(); result.append(determine_set_value(visitor, col.value()));
std::visit(value_to_string, var); // auto var = col.value();
result.append(value_to_string.result); // std::visit(visitor, var);
// result.append(visitor.result);
} }
} else { } else {
auto it = set_part.key_values().begin(); auto it = set_part.key_values().begin();
result.append(dialect_->prepare_identifier_string(it->name()) + "="); result.append(dialect_->prepare_identifier_string(it->name()) + "=");
auto var = (it++)->value(); result.append(determine_set_value(visitor, (it++)->value()));
std::visit(value_to_string, var); // auto var = (it++)->value();
result.append(value_to_string.result); // std::visit(visitor, var);
// result.append(visitor.result);
for (; it != set_part.key_values().end(); ++it) { for (; it != set_part.key_values().end(); ++it) {
result.append(", "); result.append(", ");
result.append(dialect_->prepare_identifier_string(it->name()) + "="); result.append(dialect_->prepare_identifier_string(it->name()) + "=");
var = it->value(); result.append(determine_set_value(visitor, it->value()));
std::visit(value_to_string, var); // var = it->value();
result.append(value_to_string.result); // std::visit(visitor, var);
// result.append(visitor.result);
} }
} }

View File

@ -291,8 +291,8 @@ TEST_CASE_METHOD(QueryFixture, "Test describe table", "[query][describe][table]"
std::vector<std::string> column_names = { "id", std::vector<std::string> column_names = { "id",
"val_char", "val_float", "val_double", "val_short", "val_char", "val_float", "val_double", "val_short",
"val_int", "val_long", "val_long_long", "val_unsigned_char", "val_int", "val_long_long", "val_unsigned_char",
"val_unsigned_short", "val_unsigned_int", "val_unsigned_long", "val_unsigned_long_long", "val_unsigned_short", "val_unsigned_int", "val_unsigned_long_long",
"val_bool", "val_cstr", "val_string", "val_varchar", "val_bool", "val_cstr", "val_string", "val_varchar",
// "val_date", "val_time", // "val_date", "val_time",
"val_binary"}; "val_binary"};
@ -307,8 +307,8 @@ TEST_CASE_METHOD(QueryFixture, "Test describe table", "[query][describe][table]"
[](const attribute_definition &cf) { return cf.is_integer(); }, [](const attribute_definition &cf) { return cf.is_integer(); },
[](const attribute_definition &cf) { return cf.is_integer(); }, [](const attribute_definition &cf) { return cf.is_integer(); },
[](const attribute_definition &cf) { return cf.is_integer(); }, [](const attribute_definition &cf) { return cf.is_integer(); },
[](const attribute_definition &cf) { return cf.is_integer(); }, // [](const attribute_definition &cf) { return cf.is_integer(); },
[](const attribute_definition &cf) { return cf.is_integer(); }, // [](const attribute_definition &cf) { return cf.is_integer(); },
[](const attribute_definition &cf) { return cf.is_integer(); }, [](const attribute_definition &cf) { return cf.is_integer(); },
[](const attribute_definition &cf) { return cf.is_bool(); }, [](const attribute_definition &cf) { return cf.is_bool(); },
[](const attribute_definition &cf) { return cf.is_varchar(); }, [](const attribute_definition &cf) { return cf.is_varchar(); },

View File

@ -48,11 +48,11 @@ TEST_CASE_METHOD(SessionFixture, "Use session to find object with id", "[session
auto a380 = ses.insert<airplane>(1, "Boeing", "A380"); auto a380 = ses.insert<airplane>(1, "Boeing", "A380");
REQUIRE(a380.is_ok()); REQUIRE(a380.is_ok());
auto res = ses.find<airplane>(2); auto find_result = ses.find<airplane>(2);
REQUIRE(!result.is_ok()); REQUIRE(!find_result.is_ok());
REQUIRE((result.err().ec() == orm::error_code::FailedToFindObject)); REQUIRE((find_result.err().ec() == orm::error_code::FailedToFindObject));
auto find_result = ses.find<airplane>(1); find_result = ses.find<airplane>(1);
REQUIRE(find_result); REQUIRE(find_result);
auto read_a380 = find_result.value(); auto read_a380 = find_result.value();

View File

@ -30,7 +30,7 @@ struct author {
field::attribute(op, "date_of_birth", date_of_birth, 31); field::attribute(op, "date_of_birth", date_of_birth, 31);
field::attribute(op, "year_of_birth", year_of_birth); field::attribute(op, "year_of_birth", year_of_birth);
field::attribute(op, "distinguished", distinguished); field::attribute(op, "distinguished", distinguished);
field::has_many(op, "books", books, "author_id", utils::fetch_type::EAGER); field::has_many(op, "books", books, "author_id", utils::fetch_type::LAZY);
} }
}; };

View File

@ -13,7 +13,7 @@ struct person {
unsigned int id{}; unsigned int id{};
std::string name; std::string name;
unsigned int age{}; unsigned int age{};
utils::blob image; utils::blob image{};
template<class Operator> template<class Operator>
void process(Operator &op) { void process(Operator &op) {

View File

@ -16,7 +16,7 @@ struct ingredient
{ {
unsigned int id{}; unsigned int id{};
std::string name; std::string name;
std::vector<object::object_ptr<recipe>> recipes; std::vector<object::object_ptr<recipe>> recipes{};
template<class Operator> template<class Operator>
void process(Operator &op) { void process(Operator &op) {
@ -31,7 +31,7 @@ struct recipe
{ {
unsigned int id{}; unsigned int id{};
std::string name; std::string name;
std::vector<object::object_ptr<ingredient>> ingredients; std::vector<object::object_ptr<ingredient>> ingredients{};
template<class Operator> template<class Operator>
void process(Operator &op) { void process(Operator &op) {