diff --git a/backends/postgres/test/CMakeLists.txt b/backends/postgres/test/CMakeLists.txt index ade1ec1..87ceb6e 100644 --- a/backends/postgres/test/CMakeLists.txt +++ b/backends/postgres/test/CMakeLists.txt @@ -37,6 +37,9 @@ set(TEST_SOURCES ../../../test/backends/SequenceFixture.hpp ../../../test/backends/SequenceFixture.cpp ../../../test/backends/SequenceTest.cpp + ../../../test/backends/TableSequenceFixture.hpp + ../../../test/backends/TableSequenceFixture.cpp + ../../../test/backends/TableSequenceTest.cpp ) set(LIBRARY_TEST_TARGET PostgresTests) diff --git a/demo/work/admin/LoginHistory.hpp b/demo/work/admin/LoginHistory.hpp index 4cd32ed..0892495 100644 --- a/demo/work/admin/LoginHistory.hpp +++ b/demo/work/admin/LoginHistory.hpp @@ -52,6 +52,7 @@ struct LoginHistory : core::Model { template void process( Operator& op ) { namespace field = matador::access; + field::process_base( op, *this ); field::process( op, *matador::base_class( this ) ); field::belongs_to( op, "client", client, matador::utils::CascadeAllFetchLazy ); field::belongs_to( op, "scenario", scenario, matador::utils::CascadeAllFetchLazy ); diff --git a/include/matador/query/table_column.hpp b/include/matador/query/table_column.hpp index fe9bafd..7c065b5 100644 --- a/include/matador/query/table_column.hpp +++ b/include/matador/query/table_column.hpp @@ -13,6 +13,7 @@ namespace matador::query { class table; +class column_expression; // ReSharper disable CppNonExplicitConvertingConstructor class table_column { @@ -35,6 +36,7 @@ public: table_column(const std::shared_ptr& expression) noexcept; + table_column(column_expression&& expression) noexcept; table_column& operator=(const table_column& other); table_column(const table_column& other) = default; table_column(table_column&& other) noexcept = default; diff --git a/include/matador/utils/access.hpp b/include/matador/utils/access.hpp index ba910b6..df38974 100644 --- a/include/matador/utils/access.hpp +++ b/include/matador/utils/access.hpp @@ -25,6 +25,20 @@ void process(Operator &op, const Type &object) { process(op, const_cast(object)); } +template +void process_base(Operator &op, const Derived &object) { + static_assert(!std::is_same_v, "class Base must not be of same type as class Derived"); + static_assert(std::is_base_of_v, "class Base must be base of class Derived"); + process(op, static_cast(object)); +} + +template +void process_base(Operator &op, Derived &object) { + static_assert(!std::is_same_v, "class Base must not be of same type as class Derived"); + static_assert(std::is_base_of_v, "class Base must be base of class Derived"); + process(op, static_cast(object)); +} + template< class Operator, class Type > void primary_key(Operator &op, const char *id, Type &value, const utils::primary_key_attribute &attr) { op.on_primary_key(id, value, attr); diff --git a/include/matador/utils/base_class.hpp b/include/matador/utils/base_class.hpp index e823463..b9d7c9d 100644 --- a/include/matador/utils/base_class.hpp +++ b/include/matador/utils/base_class.hpp @@ -8,33 +8,31 @@ namespace matador { /** * @brief Safely casts a given derived class to its base class * - * @tparam B The base class type - * @tparam D The class type of the derived class + * @tparam Base The base class type + * @tparam Derived The class type of the derived class * @param derived The derived object * @return The cast object */ -template < class B, class D> -const B* base_class(const D *derived) -{ - static_assert(!std::is_same_v, "class B must not be of same type as class D"); - static_assert(std::is_base_of_v, "class B must be base of class D"); - return static_cast(derived); +template < class Base, class Derived> +const Base* base_class(const Derived *derived) { + static_assert(!std::is_same_v, "class Base must not be of same type as class Derived"); + static_assert(std::is_base_of_v, "class Base must be base of class Derived"); + return static_cast(derived); } /** * @brief Safely casts a given derived class to its base class * - * @tparam B The base class type - * @tparam D The class type of the derived class + * @tparam Base The base class type + * @tparam Derived The class type of the derived class * @param derived The derived object * @return The cast object */ -template < class B, class D> -B* base_class(D *derived) -{ - static_assert(!std::is_same_v, "class B must not be of same type as class D"); - static_assert(std::is_base_of_v, "class B must be base of class D"); - return static_cast(derived); +template < class Base, class Derived> +Base* base_class(Derived *derived) { + static_assert(!std::is_same_v, "class Base must not be of same type as class Derived"); + static_assert(std::is_base_of_v, "class Base must be base of class Derived"); + return static_cast(derived); } } diff --git a/source/orm/query/query_utils.cpp b/source/orm/query/query_utils.cpp index 9f2d72a..64a24a1 100644 --- a/source/orm/query/query_utils.cpp +++ b/source/orm/query/query_utils.cpp @@ -15,6 +15,7 @@ void prepare_column(sql::query_context& ctx, const sql::dialect& d, const table_ attribute_string_writer writer(d, {}); expression_evaluator v(d, ctx); col.expression()->accept(v); + ctx.sql += v.result(); if (col.has_alias()) { ctx.sql.append(" ").append(d.as()).append(" ").append(col.alias()); diff --git a/source/orm/query/table_column.cpp b/source/orm/query/table_column.cpp index 2c21637..3294e2a 100644 --- a/source/orm/query/table_column.cpp +++ b/source/orm/query/table_column.cpp @@ -5,6 +5,8 @@ #include #include +#include "matador/query/expression/column_expression.hpp" + namespace matador::query { table_column operator ""_col(const char *name, const size_t len) { @@ -67,6 +69,10 @@ table_column::table_column(const class table* tab, table_column::table_column(const std::shared_ptr& expression) noexcept : table_column(nullptr, "", "", utils::basic_type::Unknown, {}, sql::sql_function_t::None, expression) {} +table_column::table_column(column_expression&& expression) noexcept +: table_column(std::shared_ptr(expression.release())) { +} + table_column & table_column::operator=(const table_column &other) { if (this == &other) { return *this; @@ -95,7 +101,7 @@ bool table_column::equals(const table_column &x) const { } table_column table_column::as(const std::string& alias) const { - return {table_, column_name_, alias, type_, attributes_, function_}; + return {table_, column_name_, alias, type_, attributes_, function_, expression_}; } const std::string& table_column::name() const { diff --git a/test/backends/SequenceFixture.hpp b/test/backends/SequenceFixture.hpp index 3be0fea..42f2583 100644 --- a/test/backends/SequenceFixture.hpp +++ b/test/backends/SequenceFixture.hpp @@ -1,8 +1,6 @@ #ifndef MATADOR_SEQUENCE_FIXTURE_HPP #define MATADOR_SEQUENCE_FIXTURE_HPP -#include "matador/query/schema.hpp" - #include "matador/sql/connection.hpp" #include @@ -10,18 +8,18 @@ namespace matador::test { class SequenceFixture { public: - SequenceFixture(); - ~SequenceFixture(); + SequenceFixture(); + ~SequenceFixture(); - void check_sequence_exists(const std::string &sequence_name) const; - void check_sequence_not_exists(const std::string &sequence_name) const; + void check_sequence_exists(const std::string &sequence_name) const; + void check_sequence_not_exists(const std::string &sequence_name) const; protected: - sql::connection db; - std::stack sequences_to_drop; + sql::connection db; + std::stack sequences_to_drop; private: - void drop_sequence_if_exists(const std::string &sequence_name) const; + void drop_sequence_if_exists(const std::string &sequence_name) const; }; } diff --git a/test/backends/TableSequenceFixture.cpp b/test/backends/TableSequenceFixture.cpp new file mode 100644 index 0000000..dff072f --- /dev/null +++ b/test/backends/TableSequenceFixture.cpp @@ -0,0 +1,30 @@ +#include "TableSequenceFixture.hpp" + +#include "matador/query/query.hpp" +#include "matador/query/builder.hpp" + +#include "connection.hpp" + +#include "catch2/catch_test_macros.hpp" + +namespace matador::test { +TableSequenceFixture::TableSequenceFixture() +: db(connection::dns) { + REQUIRE(db.open()); + + REQUIRE(query::query::create() + .table(sequence_table_name) + .columns({ + query::column("name", utils::basic_type::Varchar, 255), + query::column("next_id", utils::basic_type::Int64) + }) + .execute(db)); +} + +TableSequenceFixture::~TableSequenceFixture() { + REQUIRE(query::query::drop() + .table(sequence_table_name) + .execute(db)); + REQUIRE(db.close()); +} +} diff --git a/test/backends/TableSequenceFixture.hpp b/test/backends/TableSequenceFixture.hpp new file mode 100644 index 0000000..33285b6 --- /dev/null +++ b/test/backends/TableSequenceFixture.hpp @@ -0,0 +1,18 @@ +#ifndef MATADOR_TABLE_SEQUENCE_FIXTURE_HPP +#define MATADOR_TABLE_SEQUENCE_FIXTURE_HPP + +#include "matador/sql/connection.hpp" + +namespace matador::test { +class TableSequenceFixture { +public: + TableSequenceFixture(); + ~TableSequenceFixture(); + +protected: + sql::connection db; + std::string sequence_table_name{"test_seq_table"}; +}; +} + +#endif //MATADOR_TABLE_SEQUENCE_FIXTURE_HPP \ No newline at end of file diff --git a/test/backends/TableSequenceTest.cpp b/test/backends/TableSequenceTest.cpp new file mode 100644 index 0000000..118330f --- /dev/null +++ b/test/backends/TableSequenceTest.cpp @@ -0,0 +1,40 @@ +#include "catch2/catch_test_macros.hpp" + +#include "matador/query/criteria.hpp" +#include "matador/query/expression/expression_operators.hpp" +#include "matador/query/query.hpp" +#include "matador/query/table_column.hpp" + +#include "TableSequenceFixture.hpp" + +using namespace matador::query; +using namespace matador::test; + +TEST_CASE_METHOD(TableSequenceFixture, "test create and drop table sequence", "[table_sequence][create][drop]") { + const auto next_id_col = "next_id"_col; + auto result = query::query::insert() + .into(sequence_table_name, { "name", next_id_col}) + .values({ "test_seq", 1 }) + .execute(db); + REQUIRE(result); + REQUIRE(result->affected_rows == 1); + + table_column exp(next_id_col - 1); + auto id_result = query::query::update(sequence_table_name) + .set(next_id_col, next_id_col + 1) + .where("name"_col == "test_seq") + .returning((next_id_col - 1).as("id")) + .fetch_value(db); + + REQUIRE(id_result); + REQUIRE(id_result->has_value()); + REQUIRE(id_result->value() == 1); + + id_result = query::query::select({next_id_col}) + .from(sequence_table_name) + .where("name"_col == "test_seq") + .fetch_value(db); + REQUIRE(id_result); + REQUIRE(id_result->has_value()); + REQUIRE(id_result->value() == 2); +} \ No newline at end of file