From 5668f1060b6e46aa9315fd1ef86685e5a468caa9 Mon Sep 17 00:00:00 2001 From: sascha Date: Tue, 17 Feb 2026 13:31:06 +0100 Subject: [PATCH] added sequence-specific code (create, drop and nextval) --- backends/postgres/test/CMakeLists.txt | 4 +- include/matador/object/object_ptr.hpp | 5 +++ .../intermediates/query_drop_intermediate.hpp | 3 +- .../query_select_intermediate.hpp | 2 + .../matador/query/internal/query_parts.hpp | 40 +++++++++++++++++++ include/matador/query/query_builder.hpp | 3 ++ include/matador/query/query_part_visitor.hpp | 8 ++++ include/matador/sql/dialect.hpp | 8 +++- include/matador/sql/dialect_token.hpp | 3 ++ include/matador/sql/query_context.hpp | 2 + .../query_create_intermediate.cpp | 2 + .../intermediates/query_drop_intermediate.cpp | 10 ++++- .../query_select_intermediate.cpp | 8 ++-- source/orm/query/internal/query_parts.cpp | 38 ++++++++++++++++++ source/orm/query/query_builder.cpp | 15 +++++++ source/orm/sql/dialect.cpp | 12 ++++++ test/backends/QueryTest.cpp | 19 +++++++++ 17 files changed, 174 insertions(+), 8 deletions(-) diff --git a/backends/postgres/test/CMakeLists.txt b/backends/postgres/test/CMakeLists.txt index b3bdd32..ad29d5c 100644 --- a/backends/postgres/test/CMakeLists.txt +++ b/backends/postgres/test/CMakeLists.txt @@ -2,8 +2,8 @@ CPMAddPackage("gh:catchorg/Catch2@3.7.1") list(APPEND CMAKE_MODULE_PATH ${catch2_SOURCE_DIR}/extras) -#set(POSTGRES_CONNECTION_STRING "postgres://test:test123!@127.0.0.1:15442/matador") -set(POSTGRES_CONNECTION_STRING "postgres://test:test123!@127.0.0.1:5432/matador") +set(POSTGRES_CONNECTION_STRING "postgres://test:test123!@127.0.0.1:15442/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) diff --git a/include/matador/object/object_ptr.hpp b/include/matador/object/object_ptr.hpp index a8d9207..f96df71 100644 --- a/include/matador/object/object_ptr.hpp +++ b/include/matador/object/object_ptr.hpp @@ -48,6 +48,11 @@ public: [[nodiscard]] const utils::identifier &primary_key() const { return proxy_->primary_key(); } void primary_key(const utils::identifier &pk) { proxy_->primary_key(pk); } + [[nodiscard]] bool is_persistent() const { return proxy_->is_persistent(); } + [[nodiscard]] bool is_transient() const { return !proxy_->is_transient(); } + [[nodiscard]] bool is_detached() const { return !proxy_->is_detached(); } + [[nodiscard]] bool is_removed() const { return !proxy_->is_removed(); } + private: std::shared_ptr > proxy_{}; }; diff --git a/include/matador/query/intermediates/query_drop_intermediate.hpp b/include/matador/query/intermediates/query_drop_intermediate.hpp index dd560ca..58299d3 100644 --- a/include/matador/query/intermediates/query_drop_intermediate.hpp +++ b/include/matador/query/intermediates/query_drop_intermediate.hpp @@ -11,7 +11,8 @@ public: query_drop_intermediate(); executable_query table(const table &tab); - // executable_query schema(const std::string &schema_name); + executable_query sequence(const std::string &sequence_name); + executable_query schema(const std::string &schema_name); }; } diff --git a/include/matador/query/intermediates/query_select_intermediate.hpp b/include/matador/query/intermediates/query_select_intermediate.hpp index 6beba5a..0fd739d 100644 --- a/include/matador/query/intermediates/query_select_intermediate.hpp +++ b/include/matador/query/intermediates/query_select_intermediate.hpp @@ -17,6 +17,8 @@ class query_select_intermediate : public query_intermediate public: explicit query_select_intermediate(const std::vector& columns); + fetchable_query nextval(const std::string& sequence_name); + template query_from_intermediate from(const Tables&... tables) { std::vector v { tables... }; diff --git a/include/matador/query/internal/query_parts.hpp b/include/matador/query/internal/query_parts.hpp index 2272392..0d007ae 100644 --- a/include/matador/query/internal/query_parts.hpp +++ b/include/matador/query/internal/query_parts.hpp @@ -126,6 +126,20 @@ private: std::vector columns_; }; +class query_select_nextval_part final : public query_part { +public: + explicit query_select_nextval_part(std::string sequence_name); + + [[nodiscard]] const std::string& sequence_name() const; + +private: + void accept(query_part_visitor &visitor) override; + +private: + std::string sequence_name_; +}; + + /** * Represents the SQL FROM part */ @@ -443,6 +457,19 @@ private: std::list constraints_; }; +class query_create_sequence_part final : public query_part { +public: + explicit query_create_sequence_part(std::string sequence_name); + + [[nodiscard]] const std::string& sequence_name() const; + +private: + void accept(query_part_visitor &visitor) override; + +private: + std::string sequence_name_; +}; + class query_create_schema_part final : public query_part { public: explicit query_create_schema_part(std::string schema); @@ -479,6 +506,19 @@ private: class table table_; }; +class query_drop_sequence_part final : public query_part { +public: + explicit query_drop_sequence_part(std::string sequence_name); + + [[nodiscard]] const std::string& sequence_name() const; + +private: + void accept(query_part_visitor &visitor) override; + +private: + std::string sequence_name_; +}; + class query_drop_schema_part final : public query_part { public: explicit query_drop_schema_part(std::string schema_); diff --git a/include/matador/query/query_builder.hpp b/include/matador/query/query_builder.hpp index 5fd3f3f..20b2058 100644 --- a/include/matador/query/query_builder.hpp +++ b/include/matador/query/query_builder.hpp @@ -45,6 +45,7 @@ protected: void visit(internal::query_drop_key_constraint_part_by_constraint &part) override; void visit(internal::query_select_part &part) override; + void visit(internal::query_select_nextval_part &part) override; void visit(internal::query_from_part &part) override; void visit(internal::query_join_table_part &part) override; void visit(internal::query_join_query_part &part) override; @@ -72,10 +73,12 @@ protected: void visit(internal::query_create_table_part &part) override; void visit(internal::query_create_table_columns_part& part) override; void visit(internal::query_create_table_constraints_part& part) override; + void visit(internal::query_create_sequence_part &part) override; void visit(internal::query_create_schema_part& part) override; void visit(internal::query_drop_part &part) override; void visit(internal::query_drop_table_part &part) override; + void visit(internal::query_drop_sequence_part &part) override; void visit(internal::query_drop_schema_part& part) override; static std::string build_table_name(sql::dialect_token token, const sql::dialect &d, const table& t); diff --git a/include/matador/query/query_part_visitor.hpp b/include/matador/query/query_part_visitor.hpp index a388c43..bb5738b 100644 --- a/include/matador/query/query_part_visitor.hpp +++ b/include/matador/query/query_part_visitor.hpp @@ -17,6 +17,7 @@ class query_add_foreign_key_reference_part; class query_add_primary_key_constraint_part; // Select class query_select_part; +class query_select_nextval_part; class query_from_part; class query_join_table_part; class query_join_query_part; @@ -45,12 +46,16 @@ class query_create_part; class query_create_table_part; class query_create_table_columns_part; class query_create_table_constraints_part; +// Create Sequence +class query_create_sequence_part; // Create schema class query_create_schema_part; // Drop common class query_drop_part; // Drop table class query_drop_table_part; +// Drop sequence +class query_drop_sequence_part; // Drop schema class query_drop_schema_part; } @@ -70,6 +75,7 @@ public: virtual void visit(internal::query_drop_key_constraint_part_by_constraint &part) = 0; virtual void visit(internal::query_select_part &part) = 0; + virtual void visit(internal::query_select_nextval_part &part) = 0; virtual void visit(internal::query_from_part &part) = 0; virtual void visit(internal::query_join_table_part &part) = 0; virtual void visit(internal::query_join_query_part &part) = 0; @@ -97,10 +103,12 @@ public: virtual void visit(internal::query_create_table_part &part) = 0; virtual void visit(internal::query_create_table_columns_part &part) = 0; virtual void visit(internal::query_create_table_constraints_part &part) = 0; + virtual void visit(internal::query_create_sequence_part &part) = 0; virtual void visit(internal::query_create_schema_part &part) = 0; virtual void visit(internal::query_drop_part &part) = 0; virtual void visit(internal::query_drop_table_part &part) = 0; + virtual void visit(internal::query_drop_sequence_part &part) = 0; virtual void visit(internal::query_drop_schema_part &part) = 0; }; diff --git a/include/matador/sql/dialect.hpp b/include/matador/sql/dialect.hpp index 37d28df..7ac0c11 100644 --- a/include/matador/sql/dialect.hpp +++ b/include/matador/sql/dialect.hpp @@ -145,6 +145,7 @@ public: [[nodiscard]] const std::string& end_binary_data() const; [[nodiscard]] const std::string& end_quote() const; [[nodiscard]] const std::string& end_string_data() const; + [[nodiscard]] const std::string& exists() const; [[nodiscard]] const std::string& foreign_key() const; [[nodiscard]] const std::string& from() const; [[nodiscard]] const std::string& group_by() const; @@ -157,6 +158,7 @@ public: [[nodiscard]] const std::string& join() const; [[nodiscard]] const std::string& like() const; [[nodiscard]] const std::string& limit() const; + [[nodiscard]] const std::string& nextval() const; [[nodiscard]] const std::string& not_() const; [[nodiscard]] const std::string& not_null() const; [[nodiscard]] const std::string& offset() const; @@ -171,6 +173,7 @@ public: [[nodiscard]] const std::string& schema() const; [[nodiscard]] const std::string& select() const; [[nodiscard]] const std::string& set() const; + [[nodiscard]] const std::string& sequence() const; [[nodiscard]] const std::string& start_quote() const; [[nodiscard]] const std::string& string_quote() const; [[nodiscard]] const std::string& table() const; @@ -192,6 +195,7 @@ private: std::string default_schema_name_; + token_to_string_map tokens_ { {dialect_token::AddConstraint, "ADD CONSTRAINT"}, {dialect_token::Alter, "ALTER"}, @@ -215,6 +219,7 @@ private: {dialect_token::EndBinaryData, "'"}, {dialect_token::EndQuote, "\""}, {dialect_token::EndStringData, "'"}, + {dialect_token::Exists, "EXISTS"}, {dialect_token::ForeignKey, "FOREIGN KEY"}, {dialect_token::From, "FROM"}, {dialect_token::GroupBy, "GROUP BY"}, @@ -227,6 +232,7 @@ private: {dialect_token::Join, "LEFT JOIN"}, {dialect_token::Like, "LIKE"}, {dialect_token::Limit, "LIMIT"}, + {dialect_token::NextVal, "NEXTVAL"}, {dialect_token::None, ""}, {dialect_token::Not, "NOT"}, {dialect_token::NotNull, "NOT NULL"}, @@ -242,6 +248,7 @@ private: {dialect_token::Schema, "SCHEMA"}, {dialect_token::Select, "SELECT"}, {dialect_token::Set, "SET"}, + {dialect_token::Sequence, "SEQUENCE"}, {dialect_token::StartQuote, "\""}, {dialect_token::StringQuote, "'"}, {dialect_token::Table, "TABLE"}, @@ -250,7 +257,6 @@ private: {dialect_token::Values, "VALUES"}, {dialect_token::Where, "WHERE"} }; - data_type_to_string_map data_types_ { {utils::basic_type::Int8, "TINYINT"}, {utils::basic_type::Int16, "SMALLINT"}, diff --git a/include/matador/sql/dialect_token.hpp b/include/matador/sql/dialect_token.hpp index 1348a42..053fd00 100644 --- a/include/matador/sql/dialect_token.hpp +++ b/include/matador/sql/dialect_token.hpp @@ -29,6 +29,7 @@ enum class dialect_token : uint8_t { EndBinaryData, EndQuote, EndStringData, + Exists, ForeignKey, From, GroupBy, @@ -41,6 +42,7 @@ enum class dialect_token : uint8_t { Join, Like, Limit, + NextVal, None, Not, NotNull, @@ -56,6 +58,7 @@ enum class dialect_token : uint8_t { Schema, Select, Set, + Sequence, StartQuote, StringQuote, Table, diff --git a/include/matador/sql/query_context.hpp b/include/matador/sql/query_context.hpp index 495ec8f..d788cb5 100644 --- a/include/matador/sql/query_context.hpp +++ b/include/matador/sql/query_context.hpp @@ -13,6 +13,7 @@ enum class sql_command { Create, CreateTable, CreateSchema, + CreateSequence, CreateDatabase, Update, Insert, @@ -21,6 +22,7 @@ enum class sql_command { Drop, DropTable, DropSchema, + DropSequence, DropDatabase, Alter, AlterTable, diff --git a/source/orm/query/intermediates/query_create_intermediate.cpp b/source/orm/query/intermediates/query_create_intermediate.cpp index 646ef84..3aa64a1 100644 --- a/source/orm/query/intermediates/query_create_intermediate.cpp +++ b/source/orm/query/intermediates/query_create_intermediate.cpp @@ -18,7 +18,9 @@ query_create_table_intermediate query_create_intermediate::table(const class tab context_->parts.push_back(std::make_unique(tab)); return {context_}; } + query_create_sequence_intermediate query_create_intermediate::sequence(const std::string& sequence_name) { + context_->parts.push_back(std::make_unique(sequence_name)); return {context_}; } diff --git a/source/orm/query/intermediates/query_drop_intermediate.cpp b/source/orm/query/intermediates/query_drop_intermediate.cpp index 696e9ca..487b111 100644 --- a/source/orm/query/intermediates/query_drop_intermediate.cpp +++ b/source/orm/query/intermediates/query_drop_intermediate.cpp @@ -5,7 +5,6 @@ #include "matador/query/query_data.hpp" namespace matador::query { - query_drop_intermediate::query_drop_intermediate() { context_->parts.push_back(std::make_unique()); } @@ -15,4 +14,13 @@ executable_query query_drop_intermediate::table(const class table &tab) { return {context_}; } +executable_query query_drop_intermediate::sequence(const std::string& sequence_name) { + context_->parts.push_back(std::make_unique(sequence_name)); + return {context_}; +} + +executable_query query_drop_intermediate::schema(const std::string& schema_name) { + context_->parts.push_back(std::make_unique(schema_name)); + return {context_}; +} } diff --git a/source/orm/query/intermediates/query_select_intermediate.cpp b/source/orm/query/intermediates/query_select_intermediate.cpp index e251b00..2b9e986 100644 --- a/source/orm/query/intermediates/query_select_intermediate.cpp +++ b/source/orm/query/intermediates/query_select_intermediate.cpp @@ -11,9 +11,11 @@ query_select_intermediate::query_select_intermediate(const std::vectorparts.push_back(std::make_unique(columns)); } -// query_from_intermediate query_select_intermediate::from(const table& tab) { -// return from(std::vector{tab}); -// } +fetchable_query query_select_intermediate::nextval(const std::string& sequence_name) { + context_->parts.push_back(std::make_unique(sequence_name)); + return {context_}; +} + query_from_intermediate query_select_intermediate::from(const std::vector
& tables) { context_->parts.push_back(std::make_unique(tables)); for (const auto& tab : tables) { diff --git a/source/orm/query/internal/query_parts.cpp b/source/orm/query/internal/query_parts.cpp index fa04f8f..fcd42f2 100644 --- a/source/orm/query/internal/query_parts.cpp +++ b/source/orm/query/internal/query_parts.cpp @@ -134,6 +134,19 @@ const std::vector &query_select_part::columns() const { return columns_; } +query_select_nextval_part::query_select_nextval_part(std::string sequence_name) +: query_part(sql::dialect_token::NextVal) +, sequence_name_(std::move(sequence_name)){ +} + +const std::string& query_select_nextval_part::sequence_name() const { + return sequence_name_; +} + +void query_select_nextval_part::accept(query_part_visitor& visitor) { + visitor.visit(*this); +} + query_from_part::query_from_part(std::vector
tables) : query_part(sql::dialect_token::From) , tables_(std::move(tables)) { @@ -419,6 +432,18 @@ void query_create_table_constraints_part::accept(query_part_visitor &visitor) { visitor.visit(*this); } +query_create_sequence_part::query_create_sequence_part(std::string sequence_name) +: query_part(sql::dialect_token::Sequence) +, sequence_name_(std::move(sequence_name)) {} + +const std::string& query_create_sequence_part::sequence_name() const { + return sequence_name_; +} + +void query_create_sequence_part::accept(query_part_visitor& visitor) { + visitor.visit(*this); +} + query_create_schema_part::query_create_schema_part(std::string schema) : query_part(sql::dialect_token::Schema) , schema_(std::move(schema)) { @@ -453,6 +478,19 @@ void query_drop_table_part::accept(query_part_visitor &visitor) { visitor.visit(*this); } +query_drop_sequence_part::query_drop_sequence_part(std::string sequence_name) +: query_part(sql::dialect_token::Table) +, sequence_name_(std::move(sequence_name)) { +} + +const std::string& query_drop_sequence_part::sequence_name() const { + return sequence_name_; +} + +void query_drop_sequence_part::accept(query_part_visitor& visitor) { + visitor.visit(*this); +} + query_drop_schema_part::query_drop_schema_part(std::string schema_) : query_part(sql::dialect_token::Schema) , schema_(std::move(schema_)) { diff --git a/source/orm/query/query_builder.cpp b/source/orm/query/query_builder.cpp index b0baf96..51518b8 100644 --- a/source/orm/query/query_builder.cpp +++ b/source/orm/query/query_builder.cpp @@ -94,6 +94,11 @@ void query_builder::visit(internal::query_select_part &part) { build_fetchable_columns(query_, part.columns(), *dialect_); } +void query_builder::visit(internal::query_select_nextval_part& part) { + query_.sql += dialect_->nextval() + "('" + part.sequence_name() + "')"; + prepare_prototype(query_.prototype, part.sequence_name()); +} + void query_builder::visit(internal::query_from_part &part) { query_.table_name = part.tables().front().name(); query_.sql += " " + dialect_->from() + " "; @@ -301,6 +306,11 @@ void query_builder::visit(internal::query_create_table_constraints_part& part) { query_.sql += result; } +void query_builder::visit(internal::query_create_sequence_part& part) { + query_.command = sql::sql_command::CreateSequence; + query_.sql += " " + dialect_->sequence() + " " + dialect_->prepare_identifier_string(part.sequence_name()); +} + void query_builder::visit( internal::query_create_schema_part& part ) { query_.command = sql::sql_command::CreateSchema; query_.sql += " " + dialect_->schema() + " " + dialect_->prepare_identifier_string(part.schema()); @@ -341,6 +351,11 @@ void query_builder::visit(internal::query_set_part &part) { query_.sql += result; } +void query_builder::visit(internal::query_drop_sequence_part& part) { + query_.command = sql::sql_command::DropSequence; + query_.sql += " " + dialect_->sequence() + " " + dialect_->prepare_identifier_string(part.sequence_name()); +} + void query_builder::visit(internal::query_drop_table_part &part) { query_.table_name = part.table().name(); query_.sql += " " + build_table_name(part.token(), *dialect_, query_.table_name); diff --git a/source/orm/sql/dialect.cpp b/source/orm/sql/dialect.cpp index e14385d..e5d42f3 100644 --- a/source/orm/sql/dialect.cpp +++ b/source/orm/sql/dialect.cpp @@ -171,6 +171,10 @@ const std::string &dialect::end_string_data() const { return token_at(dialect_token::EndStringData); } +const std::string& dialect::exists() const { + return token_at(dialect_token::Exists); +} + const std::string &dialect::from() const { return token_at(dialect_token::From); } @@ -235,6 +239,10 @@ const std::string &dialect::limit() const { return token_at(dialect_token::Limit); } +const std::string& dialect::nextval() const { + return token_at(dialect_token::NextVal); +} + const std::string &dialect::not_() const { return token_at(dialect_token::Not); } @@ -291,6 +299,10 @@ const std::string &dialect::set() const { return token_at(dialect_token::Set); } +const std::string& dialect::sequence() const { + return token_at(dialect_token::Sequence); +} + const std::string &dialect::start_quote() const { return token_at(dialect_token::StartQuote); } diff --git a/test/backends/QueryTest.cpp b/test/backends/QueryTest.cpp index 463af9a..131a8af 100644 --- a/test/backends/QueryTest.cpp +++ b/test/backends/QueryTest.cpp @@ -111,6 +111,25 @@ TEST_CASE_METHOD(QueryFixture, "Execute insert with returning", "[query][insert] REQUIRE(rec->at(0).as() == 7); } +TEST_CASE_METHOD(QueryFixture, "Test sequence", "[query][sequence]") { + auto result = query::create() + .sequence("person_seq") + .execute(db); + REQUIRE(result.is_ok()); + + auto next_id = query::select() + .nextval("person_seq") + .fetch_value(db); + + REQUIRE(next_id.is_ok()); + REQUIRE(*next_id == 1); + + result = query::drop() + .sequence("person_seq") + .execute(db); + REQUIRE(result.is_ok()); +} + TEST_CASE_METHOD(QueryFixture, "Execute insert statement", "[query][insert]") { auto res = query::create() .table("person")