Implementation descriptors and SQLColAttribute report the columns and parameters they describe (#316) - #317
Open
fdcastel wants to merge 13 commits into
Open
Conversation
KeysetSizeDoesNotChangeRowArraySize sets SQL_ATTR_KEYSET_SIZE and reads both it and SQL_ATTR_ROW_ARRAY_SIZE back, then sets SQL_ROWSET_SIZE and checks the two stay independent. IrdHeaderFieldsReadableBeforePrepare reads SQL_DESC_ROWS_PROCESSED_PTR, SQL_DESC_ARRAY_STATUS_PTR and SQL_DESC_ALLOC_TYPE from the IRD of a statement that has not been prepared. IrdRecordFieldsBeforePrepareAreHY007 expects HY007 for SQL_DESC_COUNT and a record field in that state, and success for both once the statement is prepared.
sqlSetStmtAttr handled SQL_ATTR_KEYSET_SIZE in the same case as the ODBC 2 SQL_ROWSET_SIZE and stored it in the ARD's array size. An application that set a keyset size (ODBC 2 applications and generic layers do, with values such as 100) silently got a rowset of that size, and the next SQLFetch wrote that many rows into buffers bound for one, with no rows-fetched counter or status array to warn it. The value now goes to its own field, read back by sqlGetStmtAttr; the driver has no keyset-driven cursors, so the number is stored and returned as the specification allows.
sqlGetDescField refused every field of an undefined descriptor with HY091 "Invalid descriptor field identifier". For the IRD that state means "not prepared yet", and the header fields (the array-status and rows-processed pointers among them, which the application itself set through SQLSetStmtAttr) do not depend on the result set at all. The header fields are now returned regardless of the prepared state. SQL_DESC_COUNT and the record fields of an unprepared IRD answer HY007 "Associated statement is not prepared"; other descriptor types keep the HY091 they gave before.
…pare IrdRecordsDescribeColumnsAfterPrepare reads SQL_DESC_TYPE, SQL_DESC_CONCISE_TYPE and SQL_DESC_NAME with SQLGetDescField, and the same record with SQLGetDescRec, before anything binds or executes the columns, and compares with SQLDescribeCol. CopyDescFromIrdAfterPrepare copies the IRD to an explicit descriptor at the same point and reads the type back from the copy.
SQLPrepare sizes the IRD but leaves its records at their constructor defaults (type SQL_C_DEFAULT, no name); defFromMetaDataOut fills a record only when the column is bound or the statement executes. SQLDescribeCol and SQLColAttribute read the statement metadata directly, so they were right while SQLGetDescField, SQLGetDescRec and SQLCopyDesc on the same IRD handed back 99 and an empty name with SQL_SUCCESS. The three descriptor paths now fill an undefined IRD record before reading or copying it, the same call SQLBindCol makes. Fixes FirebirdSQL#316
IrdTypeFieldsFollowOdbc reads SQL_DESC_TYPE, SQL_DESC_CONCISE_TYPE and SQL_DESC_DATETIME_INTERVAL_CODE with SQLGetDescField, and type and subtype with SQLGetDescRec, for INTEGER, VARCHAR, DATE and TIMESTAMP columns: the concise type is what SQLDescribeCol reports, the verbose type is SQL_DATETIME with codes 1 and 3 for the two datetime columns. IrdRecordsDescribeColumnsAfterPrepare now compares the concise type with SQLDescribeCol as well.
An implementation row record keeps the concise SQL type in `type` and the converter's C type in `conciseType`, and the descriptor getters handed those fields out as they are: SQL_DESC_CONCISE_TYPE answered -16 for an INTEGER column and -8 for a VARCHAR while SQLColAttribute answered 4 and -9, SQL_DESC_TYPE answered the concise code for datetime columns, and SQL_DESC_DATETIME_INTERVAL_CODE was always 0. sqlGetDescField and sqlGetDescRec now derive the verbose type, the concise type and the datetime interval code from the SQL type for implementation row records; the record fields the converter reads are unchanged.
…epare, SQLColAttribute type fields IpdRecordsDescribeParametersAfterPrepare reads type, concise type, interval code, scale and nullability of four parameters through SQLGetDescField and SQLGetDescRec right after SQLPrepare and compares them with SQLDescribeParam. IrdLengthFieldsAreFullWidth reads SQL_DESC_LENGTH, SQL_DESC_OCTET_LENGTH and SQL_DESC_DISPLAY_SIZE into a zeroed and into an all-ones SQLLEN and expects the same value. IrdGetDescRecBeforePrepareIsHY007 expects HY007 from SQLGetDescRec on an unprepared IRD. ColAttributeTypeFieldsFollowOdbc expects the verbose type, the concise type and the datetime interval code from SQLColAttribute for INTEGER, VARCHAR, DATE and TIMESTAMP columns.
…E at full width sqlGetDescField wrote these three fields as 32-bit integers. Their ODBC types are SQLULEN and SQLLEN, 64-bit on 64-bit builds, so the upper half of the application's variable kept whatever it held before the call.
The IPD had the same two defects as the IRD: its records stayed at their constructor defaults until the parameter was bound, and the type fields were the record's raw fields. defineImplRecord now fills an undefined record of either implementation descriptor (input parameters from the statement's input metadata, procedure output parameters from the output metadata) before SQLGetDescField, SQLGetDescRec or SQLCopyDesc read it, and the three type helpers become static members of OdbcDesc so that the statement code can share them. An application may have stored the verbose type with an interval code in an IPD record; conciseSqlType folds that back to the concise code before the other two are derived. SQLGetDescRec on an unprepared IRD answers HY007, as sqlGetDescField does since the SQL_ATTR_KEYSET_SIZE change.
…ribute SQL_DESC_TYPE answered the concise code for datetime columns (91 for a DATE) where the specification asks for SQL_DATETIME, and SQL_DESC_DATETIME_INTERVAL_CODE was rejected as an unknown field. Both now come from the same helpers the descriptor API uses; SQL_DESC_CONCISE_TYPE is unchanged.
ColAttributeNumericAttributeIsFullWidth reads SQL_DESC_CONCISE_TYPE of a VARCHAR column into a zeroed and into an all-ones SQLLEN and expects SQLDescribeCol's type from both; a 32-bit write shows a negative code as a large positive in the first and a corrupted upper half in the second.
The two entry points declared their numeric attribute as SQLPOINTER on every platform but 64-bit Windows, mirroring Microsoft's header. unixODBC declares it as SQLLEN *, so on Linux the compiler saw an overload, not the extern "C" function from the header, and exported both under C++ names (_Z15SQLColAttributePvttS_sPsS_). unixODBC could not find them and routed every SQLColAttribute call through the ODBC 2 SQLColAttributesW with the field identifiers mapped to their ODBC 2 counterparts, so SQL_DESC_TYPE always came back as the concise type there. The same #ifdef made the numeric attribute a 32-bit write on Linux x64: a negative type code read from a zeroed SQLLEN came back as a large positive number. The parameter is SQLLEN * except on 32-bit Windows, where Microsoft's header keeps SQLPOINTER, and the value is written as an SQLLEN everywhere.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #316. Builds on #314: its three commits show here until it merges (same function, same test file). This PR is the last ten commits.
A sweep of every path that reports column and parameter metadata (
SQLDescribeCol,SQLColAttribute,SQLGetDescField,SQLGetDescRec,SQLCopyDesc,SQLDescribeParam, the IRD and the IPD) over seventeen Firebird column types, before and after binding, in both charsets, found the defects below. All of them are fixed here; the three items under "not changed" are conventions, listed with the reason.1. Implementation records were empty until the first bind or execute
SQLPreparesizes the IRD and the IPD but leaves their records at the constructor defaults: typeSQL_C_DEFAULT(99) and no name.defFromMetaDataOut/defFromMetaDataInfill a record only when the column or parameter is bound or the statement executes.SQLDescribeCol,SQLColAttributeandSQLDescribeParamread the statement metadata directly, so they were right, whileSQLGetDescField,SQLGetDescRecandSQLCopyDescon the same descriptors handed back 99, an empty name, nullable 0 and scale 0 withSQL_SUCCESS.defineImplRecordfills an undefined record of either descriptor before any of the three paths reads or copies it, with the callsSQLBindColandSQLBindParameteralready make.2. The type fields were the record's raw fields
An implementation record keeps the concise SQL type in
typeand the converter's C type inconciseType, and the getters handed those out as they are. Measured on the #314 driver after a bind:SQL_DESC_CONCISE_TYPEanswered -16 forINTEGER, -25 forBIGINT, -8 for a UTF8VARCHARand 1 for a textBLOBwhileSQLColAttributeanswered 4, -5, -9 and -1;SQL_DESC_TYPEanswered the concise code (91, 93) for datetime columns instead ofSQL_DATETIME;SQL_DESC_DATETIME_INTERVAL_CODEwas always 0;SQLGetDescRecreturned the same pair. The full matrix is in #316.sqlGetDescFieldandsqlGetDescRecderive the verbose type, the concise type and the datetime interval code from the SQL type for both implementation descriptors. The record fields the converter reads are unchanged.3. SQLColAttribute had the same two gaps
SQL_DESC_TYPEanswered 91 for aDATEwhere the specification asks forSQL_DATETIME, andSQL_DESC_DATETIME_INTERVAL_CODEwas rejected as an unknown field. Both now use the same helpers;SQL_DESC_CONCISE_TYPEis unchanged.4. Three length fields were written as 32-bit integers
SQLGetDescFieldwroteSQL_DESC_LENGTH,SQL_DESC_OCTET_LENGTHandSQL_DESC_DISPLAY_SIZEas 32-bit integers into the application'sSQLULEN/SQLLEN, leaving the upper half untouched on 64-bit builds. They are written at full width now.5. SQLGetDescRec on an unprepared IRD answered HY091
Now
HY007, asSQLGetDescFielddoes since #314.6. SQLColAttribute and SQLColAttributeW were not exported on Linux
The two entry points declared their numeric attribute as
SQLPOINTERon every platform but 64-bit Windows, mirroring Microsoft's header. unixODBC declares it asSQLLEN *, so on Linux the compiler saw an overload rather than theextern "C"function from the header and exported both under C++ names; an ELF symbol scan of the CI build shows 112 of the 114 entry points with C linkage and exactly these two mangled. unixODBC therefore routed everySQLColAttributecall through the ODBC 2SQLColAttributesWwithSQL_DESC_TYPEmapped toSQL_COLUMN_TYPE, and the same#ifdefmade the numeric attribute a 32-bit write on Linux x64:SQL_DESC_CONCISE_TYPEof a UTF8VARCHARread from a zeroedSQLLENcame back as 4294967287 instead of -9. The parameter isSQLLEN *except on 32-bit Windows now, and the value is written as anSQLLENeverywhere.Tests
Eight cases in
test_descriptor.cpp:IrdRecordsDescribeColumnsAfterPrepare,CopyDescFromIrdAfterPrepare,IrdTypeFieldsFollowOdbc,IpdRecordsDescribeParametersAfterPrepare,IrdLengthFieldsAreFullWidth,IrdGetDescRecBeforePrepareIsHY007,ColAttributeTypeFieldsFollowOdbc,ColAttributeNumericAttributeIsFullWidth. The first seven fail against the #314 driver on Windows, the last one fails on Linux x64 against any earlier build, and pass with this branch. Full suite with this branch,CHARSET=UTF8andNONE: 399 ran, 234 passed, 165 skipped, 0 failed.Not changed, and why
SQLCopyDescfrom an implementation descriptor still copies the converter's C type into the target's concise type. ODBC's SQL and C codes coincide only for some types (SQL_BIGINTandSQL_VARCHARhave no C twin), so an application that copies the IRD into an ARD and fetches through it works today because of that; the copy'sSQL_DESC_TYPEis the SQL type.SQL_DESC_LENGTHandSQL_DESC_OCTET_LENGTHfor numeric and datetime columns are crossed between the two APIs:SQLColAttributeanswers the display size for LENGTH (6 forSMALLINT) and the precision for OCTET_LENGTH (5), the descriptor answers the precision for LENGTH (5) and the display size for OCTET_LENGTH (6), and the specification's transfer octet length would be 2. Unifying them changes values applications size buffers with, so it needs a decision rather than a quiet fix; the descriptor side matchesSQLDescribeCol's column size today.CHARandVARCHARcolumns withCHARACTER SET OCTETSare reported asSQL_CHAR/SQL_VARCHAR(a 16-byte one asSQL_GUID, Fix SQL_C_GUID parameter binding sending corrupted data on the wire (#295) #296). Reporting them asSQL_BINARY/SQL_VARBINARYis the Firebird meaning, and an application-visible type change; same reason.