You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
After SQLPrepare, reading a result column through the IRD (SQLGetDescField with SQL_DESC_TYPE, SQL_DESC_CONCISE_TYPE, SQL_DESC_NAME, ...) returns SQL_SUCCESS with the record's constructor defaults: type 99 (SQL_C_DEFAULT) and an empty name, for every column type. SQLDescribeCol and SQLColAttribute on the same column answer correctly. Once the column has been bound with SQLBindCol, or the statement executed, the descriptor answers correctly as well.
Environment
Windows x64, master 35fae3e with #305 and #315 applied (the code involved is not touched by either), Firebird 5.0.3, Microsoft driver manager. The code involved is platform-independent.
Reproduction
SELECT CAST(1 AS INTEGER) AS A FROM RDB$DATABASE, SQLPrepare, SQLGetStmtAttr(SQL_ATTR_IMP_ROW_DESC), then SQLGetDescField(hIrd, 1, SQL_DESC_TYPE, ...) and SQL_DESC_NAME:
before the descriptor read
SQL_DESC_TYPE
SQL_DESC_NAME
prepare only
99
empty
prepare, then SQLDescribeCol
99
empty
prepare, then SQLBindCol
4
A
prepare, then SQLExecute
4
A
Same 99 for BIGINT, VARCHAR and a table's SMALLINT column, while SQLDescribeCol and SQLColAttribute(SQL_DESC_TYPE) give -5, -9 and 5. SQL_DESC_CONCISE_TYPE behaves the same way.
Cause
SQLPrepare sizes the IRD (setDefaultImplDesc allocates headCount records, so SQL_DESC_COUNT is right) but does not fill the records: each starts with type = conciseType = SQL_C_DEFAULT and no name (DescRecord.cpp). defFromMetaDataOut fills a record when the column is bound (bindOutputColumn) or when the statement executes. SQLDescribeCol and SQLColAttribute read the statement metadata directly and do not fill the record. OdbcDesc::sqlGetDescField (and sqlGetDescRec) fetch the record and read its fields as they are.
Expected
The IRD describes the result set from SQLPrepare on; SQLDescribeCol and SQLColAttribute are defined as views onto it. Reading an undefined IRD record through the descriptor API should fill it from the metadata first, the way SQLBindCol does.
Second part: the type fields once the record is filled
Even after the record is filled (after SQLBindCol on master), the descriptor getters hand out the record's raw fields, and the record keeps the concise SQL type in type and the converter's C type in conciseType. Measured on the #314 driver after SQLBindCol: SQLColAttribute (TYPE / CONCISE_TYPE) versus SQLGetDescField (TYPE / CONCISE_TYPE / DATETIME_INTERVAL_CODE) and SQLGetDescRec (type / subtype):
column
SQLColAttribute
SQLGetDescField
SQLGetDescRec
INTEGER
4 / 4
4 / -16 / 0
4 / 0
BIGINT
-5 / -5
-5 / -25 / 0
-5 / 0
NUMERIC(9,2)
2 / 2
2 / -16 / 0
2 / 0
VARCHAR(5)
-9 / -9
-9 / -8 / 0
-9 / 0
DATE
91 / 91
91 / 91 / 0
91 / 0
TIMESTAMP
93 / 93
93 / 93 / 0
93 / 0
BLOB SUB_TYPE TEXT
-1 / -1
-1 / 1 / 0
-1 / 0
SQL_DESC_CONCISE_TYPE is the C type (SQL_C_SLONG, SQL_C_SBIGINT, SQL_C_WCHAR, SQL_C_CHAR), right for the datetime types only because the C and SQL codes coincide. ODBC expects the concise SQL type there, SQL_DATETIME (9) in SQL_DESC_TYPE for datetime columns with SQL_DESC_DATETIME_INTERVAL_CODE 1, 2 or 3, and the same pair from SQLGetDescRec.
Full sweep of the metadata paths
A probe over seventeen Firebird column types (SMALLINT, INTEGER, BIGINT, NUMERIC(9,2), DECIMAL(18,4), FLOAT, DOUBLE PRECISION, CHAR(5), VARCHAR(5), DATE, TIME, TIMESTAMP, text and binary BLOB, BOOLEAN, CHAR(4) and VARCHAR(4)CHARACTER SET OCTETS), comparing SQLDescribeCol, SQLColAttribute, SQLGetDescField and SQLGetDescRec on the IRD before and after SQLBindCol, SQLCopyDesc, and SQLDescribeParam against the IPD, in CHARSET=UTF8 and NONE, on the #314 driver. Beyond the two parts above it found:
The IPD has both defects as well: records at the constructor defaults until the parameter is bound (type 99, nullable 0, scale 0 while SQLDescribeParam says SQL_NULLABLE and 2), then the raw type fields.
SQLColAttribute(SQL_DESC_TYPE) answers the concise code for datetime columns (91 for a DATE) and SQLColAttribute(SQL_DESC_DATETIME_INTERVAL_CODE) is rejected with HY091.
SQLGetDescField writes SQL_DESC_LENGTH, SQL_DESC_OCTET_LENGTH and SQL_DESC_DISPLAY_SIZE as 32-bit integers; on a 64-bit build the upper half of the application's SQLLEN keeps its previous content (a variable preset to -1 reads back as -4294967291 for a length of 5).
Conventions the sweep shows and that are not defects of the descriptor code, listed so they are not rediscovered:
SQL_DESC_LENGTH and SQL_DESC_OCTET_LENGTH for numeric and datetime columns are crossed between the two APIs: SQLColAttribute gives display size / precision (6 / 5 for SMALLINT), the descriptor gives precision / display size (5 / 6); the specification's transfer octet length would be 2 (SQL_SMALLINT), 6 (SQL_TYPE_DATE), 16 (SQL_TYPE_TIMESTAMP).
CHARACTER SET OCTETS columns are reported as SQL_CHAR / SQL_VARCHAR, not SQL_BINARY / SQL_VARBINARY (a 16-byte one is SQL_GUID).
SQL_DESC_PRECISION of a BLOB is INT_MAX through SQLColAttribute and -1 through the descriptor, whose field is an SQLSMALLINT.
Text columns follow their own character set: a VARCHAR column created without one in a NONE database is SQL_VARCHAR even on a CHARSET=UTF8 connection.
Linux: SQLColAttribute and SQLColAttributeW were never exported
Found while the CI ran the sweep's tests under unixODBC. The two entry points declare their numeric attribute as SQLPOINTER on every platform but 64-bit Windows (an #ifdef _WIN64 mirroring Microsoft's header), while unixODBC's sql.h declares SQLLEN *NumericAttribute. The compiler treats the driver's definition as an overload and gives it C++ linkage: an ELF scan of the Linux CI build shows _Z15SQLColAttributePvttS_sPsS_ and _Z16SQLColAttributeWPvttS_sPsS_ and no C-linkage SQLColAttribute / SQLColAttributeW; the other 112 entry points are fine. unixODBC then falls back to the ODBC 2 SQLColAttributesW for every SQLColAttribute call, mapping SQL_DESC_TYPE to SQL_COLUMN_TYPE (so the verbose type could never be observed there), and the same #ifdef writes the numeric attribute as a 32-bit integer on Linux x64, so a negative type code read from a zeroed SQLLEN comes back as a large positive number (4294967287 for SQL_WVARCHAR).
Summary
After
SQLPrepare, reading a result column through the IRD (SQLGetDescFieldwithSQL_DESC_TYPE,SQL_DESC_CONCISE_TYPE,SQL_DESC_NAME, ...) returnsSQL_SUCCESSwith the record's constructor defaults: type 99 (SQL_C_DEFAULT) and an empty name, for every column type.SQLDescribeColandSQLColAttributeon the same column answer correctly. Once the column has been bound withSQLBindCol, or the statement executed, the descriptor answers correctly as well.Environment
Windows x64, master
35fae3ewith #305 and #315 applied (the code involved is not touched by either), Firebird 5.0.3, Microsoft driver manager. The code involved is platform-independent.Reproduction
SELECT CAST(1 AS INTEGER) AS A FROM RDB$DATABASE,SQLPrepare,SQLGetStmtAttr(SQL_ATTR_IMP_ROW_DESC), thenSQLGetDescField(hIrd, 1, SQL_DESC_TYPE, ...)andSQL_DESC_NAME:SQL_DESC_TYPESQL_DESC_NAMESQLDescribeColSQLBindColASQLExecuteASame 99 for
BIGINT,VARCHARand a table'sSMALLINTcolumn, whileSQLDescribeColandSQLColAttribute(SQL_DESC_TYPE)give -5, -9 and 5.SQL_DESC_CONCISE_TYPEbehaves the same way.Cause
SQLPreparesizes the IRD (setDefaultImplDescallocatesheadCountrecords, soSQL_DESC_COUNTis right) but does not fill the records: each starts withtype = conciseType = SQL_C_DEFAULTand no name (DescRecord.cpp).defFromMetaDataOutfills a record when the column is bound (bindOutputColumn) or when the statement executes.SQLDescribeColandSQLColAttributeread the statement metadata directly and do not fill the record.OdbcDesc::sqlGetDescField(andsqlGetDescRec) fetch the record and read its fields as they are.Expected
The IRD describes the result set from
SQLPrepareon;SQLDescribeColandSQLColAttributeare defined as views onto it. Reading an undefined IRD record through the descriptor API should fill it from the metadata first, the waySQLBindColdoes.Found while writing the tests for #307.
Second part: the type fields once the record is filled
Even after the record is filled (after
SQLBindColon master), the descriptor getters hand out the record's raw fields, and the record keeps the concise SQL type intypeand the converter's C type inconciseType. Measured on the #314 driver afterSQLBindCol:SQLColAttribute(TYPE / CONCISE_TYPE) versusSQLGetDescField(TYPE / CONCISE_TYPE / DATETIME_INTERVAL_CODE) andSQLGetDescRec(type / subtype):SQL_DESC_CONCISE_TYPEis the C type (SQL_C_SLONG,SQL_C_SBIGINT,SQL_C_WCHAR,SQL_C_CHAR), right for the datetime types only because the C and SQL codes coincide. ODBC expects the concise SQL type there,SQL_DATETIME(9) inSQL_DESC_TYPEfor datetime columns withSQL_DESC_DATETIME_INTERVAL_CODE1, 2 or 3, and the same pair fromSQLGetDescRec.Full sweep of the metadata paths
A probe over seventeen Firebird column types (
SMALLINT,INTEGER,BIGINT,NUMERIC(9,2),DECIMAL(18,4),FLOAT,DOUBLE PRECISION,CHAR(5),VARCHAR(5),DATE,TIME,TIMESTAMP, text and binaryBLOB,BOOLEAN,CHAR(4)andVARCHAR(4)CHARACTER SET OCTETS), comparingSQLDescribeCol,SQLColAttribute,SQLGetDescFieldandSQLGetDescRecon the IRD before and afterSQLBindCol,SQLCopyDesc, andSQLDescribeParamagainst the IPD, inCHARSET=UTF8andNONE, on the #314 driver. Beyond the two parts above it found:SQLDescribeParamsaysSQL_NULLABLEand 2), then the raw type fields.SQLColAttribute(SQL_DESC_TYPE)answers the concise code for datetime columns (91 for aDATE) andSQLColAttribute(SQL_DESC_DATETIME_INTERVAL_CODE)is rejected withHY091.SQLGetDescFieldwritesSQL_DESC_LENGTH,SQL_DESC_OCTET_LENGTHandSQL_DESC_DISPLAY_SIZEas 32-bit integers; on a 64-bit build the upper half of the application'sSQLLENkeeps its previous content (a variable preset to -1 reads back as -4294967291 for a length of 5).SQLGetDescRecon an unprepared IRD answersHY091whereSQLGetDescFieldanswersHY007since Keep SQL_ATTR_KEYSET_SIZE apart from the rowset size; HY007 for an unprepared IRD (#307) #314.Conventions the sweep shows and that are not defects of the descriptor code, listed so they are not rediscovered:
SQL_DESC_LENGTHandSQL_DESC_OCTET_LENGTHfor numeric and datetime columns are crossed between the two APIs:SQLColAttributegives display size / precision (6 / 5 forSMALLINT), the descriptor gives precision / display size (5 / 6); the specification's transfer octet length would be 2 (SQL_SMALLINT), 6 (SQL_TYPE_DATE), 16 (SQL_TYPE_TIMESTAMP).CHARACTER SET OCTETScolumns are reported asSQL_CHAR/SQL_VARCHAR, notSQL_BINARY/SQL_VARBINARY(a 16-byte one isSQL_GUID).SQL_DESC_PRECISIONof aBLOBisINT_MAXthroughSQLColAttributeand -1 through the descriptor, whose field is anSQLSMALLINT.VARCHARcolumn created without one in aNONEdatabase isSQL_VARCHAReven on aCHARSET=UTF8connection.Linux: SQLColAttribute and SQLColAttributeW were never exported
Found while the CI ran the sweep's tests under unixODBC. The two entry points declare their numeric attribute as
SQLPOINTERon every platform but 64-bit Windows (an#ifdef _WIN64mirroring Microsoft's header), while unixODBC'ssql.hdeclaresSQLLEN *NumericAttribute. The compiler treats the driver's definition as an overload and gives it C++ linkage: an ELF scan of the Linux CI build shows_Z15SQLColAttributePvttS_sPsS_and_Z16SQLColAttributeWPvttS_sPsS_and no C-linkageSQLColAttribute/SQLColAttributeW; the other 112 entry points are fine. unixODBC then falls back to the ODBC 2SQLColAttributesWfor everySQLColAttributecall, mappingSQL_DESC_TYPEtoSQL_COLUMN_TYPE(so the verbose type could never be observed there), and the same#ifdefwrites the numeric attribute as a 32-bit integer on Linux x64, so a negative type code read from a zeroedSQLLENcomes back as a large positive number (4294967287 forSQL_WVARCHAR).