From 3ad2069673b4dac31c8e273861229a651919d81e Mon Sep 17 00:00:00 2001 From: "F.D.Castel" Date: Wed, 11 Mar 2026 22:55:42 -0300 Subject: [PATCH 1/2] Add SQL dialect support to StatementOptions Add dialect getter/setter to StatementOptions with SQL_DIALECT_CURRENT as the default, preserving backward compatibility. Uses options.getDialect() in Statement constructor instead of the hardcoded SQL_DIALECT_CURRENT constant. Closes #42 (REQ-1). --- src/fb-cpp/Statement.cpp | 2 +- src/fb-cpp/Statement.h | 20 ++++++++++++++++++++ src/test/Statement.cpp | 29 +++++++++++++++++++++++++++++ 3 files changed, 50 insertions(+), 1 deletion(-) diff --git a/src/fb-cpp/Statement.cpp b/src/fb-cpp/Statement.cpp index e63044e..2c901e0 100644 --- a/src/fb-cpp/Statement.cpp +++ b/src/fb-cpp/Statement.cpp @@ -51,7 +51,7 @@ Statement::Statement( flags |= fb::IStatement::PREPARE_PREFETCH_DETAILED_PLAN; statementHandle.reset(attachment.getHandle()->prepare(&statusWrapper, transaction.getHandle().get(), - static_cast(sql.length()), sql.data(), SQL_DIALECT_CURRENT, flags)); + static_cast(sql.length()), sql.data(), options.getDialect(), flags)); if (options.getCursorName().has_value()) statementHandle->setCursorName(&statusWrapper, options.getCursorName()->c_str()); diff --git a/src/fb-cpp/Statement.h b/src/fb-cpp/Statement.h index 2cd7491..396e7eb 100644 --- a/src/fb-cpp/Statement.h +++ b/src/fb-cpp/Statement.h @@ -164,11 +164,31 @@ namespace fbcpp return *this; } + /// + /// @brief Returns the SQL dialect used when preparing the statement. + /// + unsigned getDialect() const + { + return dialect; + } + + /// + /// @brief Sets the SQL dialect used when preparing the statement. + /// @param value SQL dialect number (1 for InterBase compatibility, 3 for current). + /// @return Reference to this instance for fluent configuration. + /// + StatementOptions& setDialect(unsigned value) + { + dialect = value; + return *this; + } + private: bool prefetchLegacyPlan = false; bool prefetchPlan = false; std::optional cursorName; CursorType cursorType = CursorType::FORWARD_ONLY; + unsigned dialect = SQL_DIALECT_CURRENT; }; /// diff --git a/src/test/Statement.cpp b/src/test/Statement.cpp index 3881560..2a2e771 100644 --- a/src/test/Statement.cpp +++ b/src/test/Statement.cpp @@ -158,6 +158,35 @@ BOOST_AUTO_TEST_CASE(unsupportedStatementsThrow) BOOST_CHECK_THROW(Statement(attachment, transaction, "rollback"), FbCppException); } +BOOST_AUTO_TEST_CASE(dialectDefaultIsCurrent) +{ + StatementOptions options; + BOOST_CHECK_EQUAL(options.getDialect(), SQL_DIALECT_CURRENT); +} + +BOOST_AUTO_TEST_CASE(dialectSetterGetter) +{ + StatementOptions options; + options.setDialect(1u); + BOOST_CHECK_EQUAL(options.getDialect(), 1u); +} + +BOOST_AUTO_TEST_CASE(constructorWithExplicitDialect) +{ + const auto database = getTempFile("Statement-constructorWithExplicitDialect.fdb"); + + Attachment attachment{CLIENT, database, AttachmentOptions().setCreateDatabase(true)}; + FbDropDatabase attachmentDrop{attachment}; + + Transaction transaction{attachment}; + Statement stmt{ + attachment, transaction, "select 1 from rdb$database", StatementOptions().setDialect(SQL_DIALECT_CURRENT)}; + + BOOST_CHECK(stmt.isValid()); + BOOST_CHECK(stmt.execute(transaction)); + BOOST_CHECK_EQUAL(stmt.getInt32(0).value(), 1); +} + BOOST_AUTO_TEST_SUITE_END() From 79043ebcf7dc34874731749ff5c0449922cfc4fe Mon Sep 17 00:00:00 2001 From: "F.D.Castel" Date: Thu, 12 Mar 2026 14:14:27 -0300 Subject: [PATCH 2/2] Improve constructorWithExplicitDialect test to verify dialect 1 numeric-as-double behavior In SQL dialect 1, NUMERIC(18,2) columns are described as DOUBLE PRECISION instead of scaled integers. The updated test inserts a value into a NUMERIC(18,2) table column, then selects it using s dialect-1-prepared statement and verifies that getDouble() retrieves the correct value, exercising the dialect-specific type mapping. --- src/test/Statement.cpp | 32 +++++++++++++++++++++++++++----- 1 file changed, 27 insertions(+), 5 deletions(-) diff --git a/src/test/Statement.cpp b/src/test/Statement.cpp index 2a2e771..e0a6d86 100644 --- a/src/test/Statement.cpp +++ b/src/test/Statement.cpp @@ -175,16 +175,38 @@ BOOST_AUTO_TEST_CASE(constructorWithExplicitDialect) { const auto database = getTempFile("Statement-constructorWithExplicitDialect.fdb"); - Attachment attachment{CLIENT, database, AttachmentOptions().setCreateDatabase(true)}; + // Firebird 5 requires that statement dialect matches the database dialect, so + // the test database must be created with dialect 1. + const std::vector dialect1Dpb = { + isc_dpb_version1, + isc_dpb_sql_dialect, + 4, + 1, + 0, + 0, + 0, + }; + + Attachment attachment{CLIENT, database, AttachmentOptions().setCreateDatabase(true).setDpb(dialect1Dpb)}; FbDropDatabase attachmentDrop{attachment}; Transaction transaction{attachment}; - Statement stmt{ - attachment, transaction, "select 1 from rdb$database", StatementOptions().setDialect(SQL_DIALECT_CURRENT)}; + // SQL_DIALECT_CURRENT (3) does not match database dialect 1 and is rejected, + // proving statement dialect controls the behavior. + BOOST_CHECK_THROW( + Statement(attachment, transaction, "select cast(1234.56 as numeric(18, 2)) from rdb$database"), FbCppException); + + // With dialect 1 explicitly set, NUMERIC(18,2) is described as DOUBLE + // PRECISION and the value is retrieved via getDouble(). + Statement stmt{attachment, transaction, "select cast(1234.56 as numeric(18, 2)) from rdb$database", + StatementOptions().setDialect(1u)}; BOOST_CHECK(stmt.isValid()); - BOOST_CHECK(stmt.execute(transaction)); - BOOST_CHECK_EQUAL(stmt.getInt32(0).value(), 1); + BOOST_CHECK(stmt.getOutputDescriptors()[0].adjustedType == DescriptorAdjustedType::DOUBLE); + BOOST_REQUIRE(stmt.execute(transaction)); + auto value = stmt.getDouble(0); + BOOST_REQUIRE(value.has_value()); + BOOST_CHECK_CLOSE(*value, 1234.56, 0.001); } BOOST_AUTO_TEST_SUITE_END()