diff --git a/OdbcDesc.cpp b/OdbcDesc.cpp index fdc252e2..8c5c32a6 100644 --- a/OdbcDesc.cpp +++ b/OdbcDesc.cpp @@ -336,7 +336,22 @@ SQLRETURN OdbcDesc::sqlGetDescField(int recNumber, int fieldId, SQLPOINTER ptr, DescRecord *record = NULL; if ( bDefined == false ) - return sqlReturn (SQL_ERROR, "HY091", "Invalid descriptor field identifier"); + { + switch (fieldId) + { + case SQL_DESC_ALLOC_TYPE: + case SQL_DESC_ARRAY_SIZE: + case SQL_DESC_ARRAY_STATUS_PTR: + case SQL_DESC_BIND_OFFSET_PTR: + case SQL_DESC_BIND_TYPE: + case SQL_DESC_ROWS_PROCESSED_PTR: + break; // header fields do not depend on the result set + default: + if ( headType == odtImplementationRow ) + return sqlReturn (SQL_ERROR, "HY007", "Associated statement is not prepared"); + return sqlReturn (SQL_ERROR, "HY091", "Invalid descriptor field identifier"); + } + } if ( recNumber > headCount ) return sqlReturn (SQL_NO_DATA_FOUND, "HY021", "Inconsistent descriptor information"); diff --git a/OdbcStatement.cpp b/OdbcStatement.cpp index 028a40d9..8a18ac6a 100644 --- a/OdbcStatement.cpp +++ b/OdbcStatement.cpp @@ -174,6 +174,7 @@ OdbcStatement::OdbcStatement(OdbcConnection *connect, int statementNumber) statement = connection->connection->createInternalStatement(); bulkInsert = NULL; execute = &OdbcStatement::executeStatement; + keysetSize = 0; fetchNext = &ResultSet::nextFetch; schemaFetchData = true; metaData = NULL; @@ -2577,6 +2578,11 @@ SQLRETURN OdbcStatement::sqlGetStmtAttr(int attribute, SQLPOINTER ptr, int buffe TRACE02(SQL_ROWSET_SIZE,value); break; + case SQL_ATTR_KEYSET_SIZE: + value = keysetSize; + TRACE02(SQL_ATTR_KEYSET_SIZE,value); + break; + case SQL_ATTR_MAX_ROWS: // SQL_MAX_ROWS 1 value = maxRows; TRACE02(SQL_ATTR_MAX_ROWS,value); @@ -3434,6 +3440,10 @@ SQLRETURN OdbcStatement::sqlSetStmtAttr(int attribute, SQLPOINTER ptr, int lengt break; case SQL_ATTR_KEYSET_SIZE: // 8 + keysetSize = (uintptr_t) ptr; + TRACE02(SQL_ATTR_KEYSET_SIZE,(intptr_t) ptr); + break; + case SQL_ROWSET_SIZE: // 9 applicationRowDescriptor->headArraySize = (intptr_t) ptr; TRACE02(SQL_ROWSET_SIZE,(intptr_t) ptr); diff --git a/OdbcStatement.h b/OdbcStatement.h index aca8696a..9c90e575 100644 --- a/OdbcStatement.h +++ b/OdbcStatement.h @@ -192,6 +192,7 @@ class OdbcStatement : public OdbcObject bool asyncEnable; int rowNumber; int rowNumberParamArray; + SQLULEN keysetSize; int lastRowsetSize; SQLLEN indicatorRowNumber; int maxRows; diff --git a/tests/test_descriptor.cpp b/tests/test_descriptor.cpp index 3060b8ff..ec0180a6 100644 --- a/tests/test_descriptor.cpp +++ b/tests/test_descriptor.cpp @@ -366,3 +366,59 @@ TEST_F(CopyDescCrashTest, SetDescCountToZeroUnbindsAll) { SQLFreeHandle(SQL_HANDLE_DESC, hDesc); } + +// ===== IRD before SQLPrepare (#307) ===== +TEST_F(DescriptorTest, IrdHeaderFieldsReadableBeforePrepare) { + SQLHDESC hIrd = SQL_NULL_HDESC; + ASSERT_TRUE(SQL_SUCCEEDED(SQLGetStmtAttr(hStmt, SQL_ATTR_IMP_ROW_DESC, &hIrd, 0, NULL))); + ASSERT_NE(hIrd, (SQLHDESC)SQL_NULL_HDESC); + + SQLULEN rowsFetched = 0; + SQLUSMALLINT rowStatus[4] = {}; + ASSERT_TRUE(SQL_SUCCEEDED(SQLSetStmtAttr(hStmt, SQL_ATTR_ROWS_FETCHED_PTR, &rowsFetched, 0))); + ASSERT_TRUE(SQL_SUCCEEDED(SQLSetStmtAttr(hStmt, SQL_ATTR_ROW_STATUS_PTR, rowStatus, 0))); + + SQLPOINTER ptr = NULL; + SQLRETURN ret = SQLGetDescField(hIrd, 0, SQL_DESC_ROWS_PROCESSED_PTR, &ptr, 0, NULL); + if (ret == SQL_ERROR && GetSqlState(SQL_HANDLE_DESC, hIrd) == "HY007") { + // unixODBC answers HY007 for an unprepared IRD before the driver sees the call + GTEST_SKIP() << "the driver manager enforces HY007 itself: " << GetOdbcError(SQL_HANDLE_DESC, hIrd); + } + ASSERT_TRUE(SQL_SUCCEEDED(ret)) << GetOdbcError(SQL_HANDLE_DESC, hIrd); + EXPECT_EQ(ptr, (SQLPOINTER)&rowsFetched); + + ptr = NULL; + ret = SQLGetDescField(hIrd, 0, SQL_DESC_ARRAY_STATUS_PTR, &ptr, 0, NULL); + ASSERT_TRUE(SQL_SUCCEEDED(ret)) << GetOdbcError(SQL_HANDLE_DESC, hIrd); + EXPECT_EQ(ptr, (SQLPOINTER)rowStatus); + + SQLSMALLINT allocType = 0; + ret = SQLGetDescField(hIrd, 0, SQL_DESC_ALLOC_TYPE, &allocType, 0, NULL); + ASSERT_TRUE(SQL_SUCCEEDED(ret)) << GetOdbcError(SQL_HANDLE_DESC, hIrd); + EXPECT_EQ(allocType, SQL_DESC_ALLOC_AUTO); +} + +TEST_F(DescriptorTest, IrdRecordFieldsBeforePrepareAreHY007) { + SQLHDESC hIrd = SQL_NULL_HDESC; + ASSERT_TRUE(SQL_SUCCEEDED(SQLGetStmtAttr(hStmt, SQL_ATTR_IMP_ROW_DESC, &hIrd, 0, NULL))); + ASSERT_NE(hIrd, (SQLHDESC)SQL_NULL_HDESC); + + SQLSMALLINT count = -1; + SQLRETURN ret = SQLGetDescField(hIrd, 0, SQL_DESC_COUNT, &count, 0, NULL); + EXPECT_EQ(ret, SQL_ERROR); + EXPECT_EQ(GetSqlState(SQL_HANDLE_DESC, hIrd), "HY007"); + + SQLSMALLINT type = 0; + ret = SQLGetDescField(hIrd, 1, SQL_DESC_TYPE, &type, 0, NULL); + EXPECT_EQ(ret, SQL_ERROR); + EXPECT_EQ(GetSqlState(SQL_HANDLE_DESC, hIrd), "HY007"); + + // Both answer once the statement is prepared + ret = SQLPrepare(hStmt, (SQLCHAR*)"SELECT 1 AS A FROM RDB$DATABASE", SQL_NTS); + ASSERT_TRUE(SQL_SUCCEEDED(ret)) << GetOdbcError(SQL_HANDLE_STMT, hStmt); + ret = SQLGetDescField(hIrd, 0, SQL_DESC_COUNT, &count, 0, NULL); + ASSERT_TRUE(SQL_SUCCEEDED(ret)) << GetOdbcError(SQL_HANDLE_DESC, hIrd); + EXPECT_EQ(count, 1); + ret = SQLGetDescField(hIrd, 1, SQL_DESC_TYPE, &type, 0, NULL); + ASSERT_TRUE(SQL_SUCCEEDED(ret)) << GetOdbcError(SQL_HANDLE_DESC, hIrd); +} diff --git a/tests/test_scrollable_cursor.cpp b/tests/test_scrollable_cursor.cpp index 7f3d2dbe..46da644a 100644 --- a/tests/test_scrollable_cursor.cpp +++ b/tests/test_scrollable_cursor.cpp @@ -193,3 +193,28 @@ TEST_F(ScrollableCursorTest, RewindAfterEnd) { ASSERT_TRUE(SQL_SUCCEEDED(ret)); EXPECT_EQ(FetchID(), 1); } + +// ===== SQL_ATTR_KEYSET_SIZE is its own attribute, not the rowset size (#307) ===== +TEST_F(ScrollableCursorTest, KeysetSizeDoesNotChangeRowArraySize) { + SQLULEN v = 0; + ASSERT_TRUE(SQL_SUCCEEDED(SQLGetStmtAttr(hStmt, SQL_ATTR_ROW_ARRAY_SIZE, &v, 0, NULL))); + EXPECT_EQ(v, 1u); + + ASSERT_TRUE(SQL_SUCCEEDED(SQLSetStmtAttr(hStmt, SQL_ATTR_KEYSET_SIZE, (SQLPOINTER)7, 0))); + v = 0; + ASSERT_TRUE(SQL_SUCCEEDED(SQLGetStmtAttr(hStmt, SQL_ATTR_ROW_ARRAY_SIZE, &v, 0, NULL))); + EXPECT_EQ(v, 1u) << "SQL_ATTR_KEYSET_SIZE must not touch the rowset size"; + v = 0; + SQLRETURN ret = SQLGetStmtAttr(hStmt, SQL_ATTR_KEYSET_SIZE, &v, 0, NULL); + ASSERT_TRUE(SQL_SUCCEEDED(ret)) << GetOdbcError(SQL_HANDLE_STMT, hStmt); + EXPECT_EQ(v, 7u); + + // The ODBC 2 name of the rowset size still sets it, and leaves the keyset size alone + ASSERT_TRUE(SQL_SUCCEEDED(SQLSetStmtAttr(hStmt, SQL_ROWSET_SIZE, (SQLPOINTER)3, 0))); + v = 0; + ASSERT_TRUE(SQL_SUCCEEDED(SQLGetStmtAttr(hStmt, SQL_ATTR_ROW_ARRAY_SIZE, &v, 0, NULL))); + EXPECT_EQ(v, 3u); + v = 0; + ASSERT_TRUE(SQL_SUCCEEDED(SQLGetStmtAttr(hStmt, SQL_ATTR_KEYSET_SIZE, &v, 0, NULL))); + EXPECT_EQ(v, 7u); +}