Skip to content

SQL_ATTR_KEYSET_SIZE overwrites the rowset size; SQLGetDescField on an unprepared IRD answers HY091 instead of HY007 #307

Open

Description

@fdcastel

Two small deviations noticed while working on block cursors (#303, #304, #305). Both verified on Windows x64 against master 35fae3e (+ #303), Firebird 5.0.3, Microsoft driver manager; the code involved is platform-independent.

1. SQL_ATTR_KEYSET_SIZE overwrites the rowset size

OdbcStatement::sqlSetStmtAttr handles SQL_ATTR_KEYSET_SIZE in the same case as the ODBC 2 SQL_ROWSET_SIZE and stores it in the ARD's array size:

case SQL_ATTR_KEYSET_SIZE:           // 8
case SQL_ROWSET_SIZE:                // 9
    applicationRowDescriptor->headArraySize = (intptr_t) ptr;

Observed:

SQLGetStmtAttr(SQL_ATTR_ROW_ARRAY_SIZE)            -> 1        (default)
SQLSetStmtAttr(SQL_ATTR_KEYSET_SIZE, 7)            -> SQL_SUCCESS
SQLGetStmtAttr(SQL_ATTR_ROW_ARRAY_SIZE)            -> 7
SQLGetStmtAttr(SQL_ATTR_KEYSET_SIZE)               -> SQL_ERROR, HYC00

SQL_ROWSET_SIZE (9) is the ODBC 2 name of the row array size, so that half is right. SQL_ATTR_KEYSET_SIZE (8) is a different attribute: the number of rows in the keyset of a keyset-driven cursor. An application that sets it (ODBC 2 applications and generic layers do, typically with values such as 100 or more) silently gets a rowset of that size, and the next SQLFetch writes that many rows into buffers bound for one row, with no rows-fetched counter or status array to warn it. That is a buffer overrun in the application's memory.

Expected: SQL_ATTR_KEYSET_SIZE does not touch SQL_ATTR_ROW_ARRAY_SIZE. A driver without keyset-driven cursors can store the value and return it from SQLGetStmtAttr (or answer 01S02 with the value it substituted); the getter case follows the shape of #304.

2. SQLGetDescField on an unprepared IRD answers HY091

OdbcDesc::sqlGetDescField starts with

if ( bDefined == false )
    return sqlReturn (SQL_ERROR, "HY091", "Invalid descriptor field identifier");

and for the IRD bDefined stays false until the statement is prepared. So before SQLPrepare every IRD field reads back as "invalid field identifier", including the header fields the application itself just set through SQLSetStmtAttr:

before SQLPrepare:
  SQLGetDescField(IRD, SQL_DESC_ROWS_PROCESSED_PTR)  -> SQL_ERROR, HY091
  SQLGetDescField(IRD, SQL_DESC_ARRAY_STATUS_PTR)    -> SQL_ERROR, HY091
after SQLPrepare:
  both return the pointers that were set

The SQLSTATE for that state is HY007 "Associated statement is not prepared" (the field identifiers are valid). Returning the header fields regardless of the prepared state would be friendlier still, since they do not depend on the result set; SQL_DESC_COUNT and the record fields are the ones that need a prepared statement.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions