From afecb526c97de8a5545775adffc8f86a8f8b23fb Mon Sep 17 00:00:00 2001 From: "F.D.Castel" Date: Tue, 24 Feb 2026 03:09:11 +0000 Subject: [PATCH] Expose descriptor metadata: name, relation, alias, owner, charSetId, subType Add six new fields to the Descriptor struct, populated from IMessageMetadata during statement preparation. This resolves the 'FIXME: more things' placeholder and provides ODBC-level metadata without requiring consumers to use the low-level Firebird API. Fields use the naming requested by the maintainer: - 'name' (from getField) instead of 'field' - 'charSetId' (from getCharSet) instead of 'charSet' Closes #37 --- src/fb-cpp/Descriptor.h | 32 ++++++++++++++++++++- src/fb-cpp/Statement.cpp | 6 ++++ src/test/Statement.cpp | 61 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 98 insertions(+), 1 deletion(-) diff --git a/src/fb-cpp/Descriptor.h b/src/fb-cpp/Descriptor.h index a2b7ae0..ed7f500 100644 --- a/src/fb-cpp/Descriptor.h +++ b/src/fb-cpp/Descriptor.h @@ -26,6 +26,7 @@ #define FBCPP_DESCRIPTOR_H #include "fb-api.h" +#include /// @@ -279,7 +280,36 @@ namespace fbcpp /// Indicates whether the column or parameter can contain null values. /// bool isNullable; - // FIXME: more things + + /// + /// Column or parameter name. + /// + std::string name; + + /// + /// Table or relation this column belongs to (empty for expressions). + /// + std::string relation; + + /// + /// Column alias as it appears in the query's SELECT list. + /// + std::string alias; + + /// + /// Owner of the relation (empty for expressions). + /// + std::string owner; + + /// + /// Character set ID for string and BLOB columns. + /// + unsigned charSetId; + + /// + /// Sub-type (BLOB sub-type or numeric sub-type). + /// + int subType; }; } // namespace fbcpp diff --git a/src/fb-cpp/Statement.cpp b/src/fb-cpp/Statement.cpp index a42a5b1..3970e74 100644 --- a/src/fb-cpp/Statement.cpp +++ b/src/fb-cpp/Statement.cpp @@ -108,6 +108,12 @@ Statement::Statement( .offset = 0, .nullOffset = 0, .isNullable = static_cast(metadata->isNullable(&statusWrapper, index)), + .name = metadata->getField(&statusWrapper, index), + .relation = metadata->getRelation(&statusWrapper, index), + .alias = metadata->getAlias(&statusWrapper, index), + .owner = metadata->getOwner(&statusWrapper, index), + .charSetId = metadata->getCharSet(&statusWrapper, index), + .subType = metadata->getSubType(&statusWrapper, index), }; switch (descriptor.originalType) diff --git a/src/test/Statement.cpp b/src/test/Statement.cpp index b802065..91b6791 100644 --- a/src/test/Statement.cpp +++ b/src/test/Statement.cpp @@ -187,6 +187,67 @@ BOOST_AUTO_TEST_CASE(constructorProvidesMetadataHandles) BOOST_CHECK(outputDescriptor.adjustedType == DescriptorAdjustedType::STRING); } +BOOST_AUTO_TEST_CASE(descriptorMetadataFields) +{ + const auto database = getTempFile("Statement-descriptorMetadataFields.fdb"); + + Attachment attachment{CLIENT, database, AttachmentOptions().setCreateDatabase(true)}; + FbDropDatabase attachmentDrop{attachment}; + + Transaction transaction{attachment}; + + Statement createTable{attachment, transaction, + "create table test_meta (" + " id integer not null," + " name varchar(100)," + " amount numeric(18, 2)," + " data blob sub_type text" + ")"}; + createTable.execute(transaction); + transaction.commit(); + + Transaction transaction2{attachment}; + + Statement select{attachment, transaction2, "select t.id as pk, t.name as label, t.amount, t.data from test_meta t"}; + + const auto& descriptors = select.getOutputDescriptors(); + BOOST_REQUIRE_EQUAL(descriptors.size(), 4U); + + // Column 0: t.id as pk + BOOST_CHECK_EQUAL(descriptors[0].name, "ID"); + BOOST_CHECK_EQUAL(descriptors[0].alias, "PK"); + BOOST_CHECK_EQUAL(descriptors[0].relation, "TEST_META"); + BOOST_CHECK(descriptors[0].subType == 0); + + // Column 1: t.name as label + BOOST_CHECK_EQUAL(descriptors[1].name, "NAME"); + BOOST_CHECK_EQUAL(descriptors[1].alias, "LABEL"); + BOOST_CHECK_EQUAL(descriptors[1].relation, "TEST_META"); + BOOST_CHECK(descriptors[1].adjustedType == DescriptorAdjustedType::STRING); + + // Column 2: t.amount (no alias, numeric) + BOOST_CHECK_EQUAL(descriptors[2].name, "AMOUNT"); + BOOST_CHECK_EQUAL(descriptors[2].alias, "AMOUNT"); + BOOST_CHECK_EQUAL(descriptors[2].relation, "TEST_META"); + + // Column 3: t.data (blob sub_type text) + BOOST_CHECK_EQUAL(descriptors[3].name, "DATA"); + BOOST_CHECK_EQUAL(descriptors[3].alias, "DATA"); + BOOST_CHECK_EQUAL(descriptors[3].relation, "TEST_META"); + BOOST_CHECK_EQUAL(descriptors[3].subType, 1); + + // Input descriptors for a parameterized query + Statement paramSelect{attachment, transaction2, "select t.id from test_meta t where t.id = ?"}; + + const auto& inDescriptors = paramSelect.getInputDescriptors(); + BOOST_REQUIRE_EQUAL(inDescriptors.size(), 1U); + + // Input parameters have empty name/relation/alias + BOOST_CHECK(inDescriptors[0].name.empty()); + BOOST_CHECK(inDescriptors[0].relation.empty()); + BOOST_CHECK(inDescriptors[0].alias.empty()); +} + BOOST_AUTO_TEST_SUITE_END()