From 4516e4c40eae1e6fa12ce4beddfd86e901b86317 Mon Sep 17 00:00:00 2001 From: rscales Date: Fri, 4 Jul 2025 00:53:00 +0100 Subject: [PATCH 01/18] Implement SQLGetStmtAttr and SQLSetStmtAttr --- cpp/src/arrow/flight/sql/odbc/entry_points.cc | 8 +- cpp/src/arrow/flight/sql/odbc/odbc_api.cc | 28 +++- cpp/src/arrow/flight/sql/odbc/odbc_api.h | 2 + .../odbc_impl/odbc_statement.h | 8 +- .../odbc_impl/odbc_statement.cc | 123 +++++++++--------- 5 files changed, 94 insertions(+), 75 deletions(-) diff --git a/cpp/src/arrow/flight/sql/odbc/entry_points.cc b/cpp/src/arrow/flight/sql/odbc/entry_points.cc index 1eb421812ab0..8cdf45b33c2e 100644 --- a/cpp/src/arrow/flight/sql/odbc/entry_points.cc +++ b/cpp/src/arrow/flight/sql/odbc/entry_points.cc @@ -279,13 +279,9 @@ SQLRETURN SQL_API SQLPrimaryKeys(SQLHSTMT statementHandle, SQLWCHAR* catalogName return SQL_ERROR; } -SQLRETURN SQL_API SQLSetStmtAttr(SQLHSTMT statementHandle, SQLINTEGER attribute, +SQLRETURN SQL_API SQLSetStmtAttr(SQLHSTMT stmt, SQLINTEGER attribute, SQLPOINTER valuePtr, SQLINTEGER stringLength) { - LOG_DEBUG( - "SQLSetStmtAttrW called with statementHandle: {}, attribute: {}, valuePtr: {}, " - "stringLength: {}", - statementHandle, attribute, valuePtr, stringLength); - return SQL_ERROR; + return arrow::SQLSetStmtAttr(stmt, attribute, valuePtr, stringLength); } SQLRETURN SQL_API SQLTables(SQLHSTMT statementHandle, SQLWCHAR* catalogName, diff --git a/cpp/src/arrow/flight/sql/odbc/odbc_api.cc b/cpp/src/arrow/flight/sql/odbc/odbc_api.cc index 352072ef21ab..d799b5f94f5a 100644 --- a/cpp/src/arrow/flight/sql/odbc/odbc_api.cc +++ b/cpp/src/arrow/flight/sql/odbc/odbc_api.cc @@ -874,7 +874,7 @@ SQLRETURN SQLGetInfo(SQLHDBC conn, SQLUSMALLINT infoType, SQLPOINTER infoValuePt SQLRETURN SQLGetStmtAttr(SQLHSTMT stmt, SQLINTEGER attribute, SQLPOINTER valuePtr, SQLINTEGER bufferLength, SQLINTEGER* stringLengthPtr) { using ODBC::ODBCStatement; - // TODO: complete implementation of SQLGetStmtAttrW and write tests + LOG_DEBUG( "SQLGetStmtAttrW called with stmt: {}, attribute: {}, valuePtr: {}, " "bufferLength: {}, stringLengthPtr: {}", @@ -882,11 +882,29 @@ SQLRETURN SQLGetStmtAttr(SQLHSTMT stmt, SQLINTEGER attribute, SQLPOINTER valuePt return ODBCStatement::ExecuteWithDiagnostics(stmt, SQL_ERROR, [=]() { ODBCStatement* statement = reinterpret_cast(stmt); + bool isUnicode = true; - statement->GetStmtAttr(attribute, valuePtr, bufferLength, stringLengthPtr, isUnicode); - // TODO: change GetStmtAttr to return SQLRETURN instead of void, - // and return value from GetStmtAttr instead - return SQL_SUCCESS; + + return statement->GetStmtAttr(attribute, valuePtr, bufferLength, stringLengthPtr, + isUnicode); + }); +} + +SQLRETURN SQLSetStmtAttr(SQLHSTMT stmt, SQLINTEGER attribute, + SQLPOINTER valuePtr, SQLINTEGER stringLength) { + using ODBC::ODBCStatement; + + LOG_DEBUG( + "SQLSetStmtAttrW called with stmt: {}, attribute: {}, valuePtr: {}, " + "stringLength: {}", + stmt, attribute, valuePtr, stringLength); + + return ODBCStatement::ExecuteWithDiagnostics(stmt, SQL_ERROR, [=]() { + ODBCStatement* statement = reinterpret_cast(stmt); + + bool isUnicode = true; + + return statement->SetStmtAttr(attribute, valuePtr, stringLength, isUnicode); }); } diff --git a/cpp/src/arrow/flight/sql/odbc/odbc_api.h b/cpp/src/arrow/flight/sql/odbc/odbc_api.h index 07614ec84ba7..f820e4c71587 100644 --- a/cpp/src/arrow/flight/sql/odbc/odbc_api.h +++ b/cpp/src/arrow/flight/sql/odbc/odbc_api.h @@ -62,6 +62,8 @@ SQLRETURN SQLGetInfo(SQLHDBC conn, SQLUSMALLINT infoType, SQLPOINTER infoValuePt SQLSMALLINT bufLen, SQLSMALLINT* length); SQLRETURN SQLGetStmtAttr(SQLHSTMT stmt, SQLINTEGER attribute, SQLPOINTER valuePtr, SQLINTEGER bufferLength, SQLINTEGER* stringLengthPtr); +SQLRETURN SQLSetStmtAttr(SQLHSTMT stmt, SQLINTEGER attribute, + SQLPOINTER valuePtr, SQLINTEGER stringLength); SQLRETURN SQLExecDirect(SQLHSTMT stmt, SQLWCHAR* queryText, SQLINTEGER textLength); SQLRETURN SQLPrepare(SQLHSTMT stmt, SQLWCHAR* queryText, SQLINTEGER textLength); SQLRETURN SQLExecute(SQLHSTMT stmt); diff --git a/cpp/src/arrow/flight/sql/odbc/odbcabstraction/include/odbcabstraction/odbc_impl/odbc_statement.h b/cpp/src/arrow/flight/sql/odbc/odbcabstraction/include/odbcabstraction/odbc_impl/odbc_statement.h index df5ca5e34abe..dba2134666b1 100644 --- a/cpp/src/arrow/flight/sql/odbc/odbcabstraction/include/odbcabstraction/odbc_impl/odbc_statement.h +++ b/cpp/src/arrow/flight/sql/odbc/odbcabstraction/include/odbcabstraction/odbc_impl/odbc_statement.h @@ -70,10 +70,10 @@ class ODBCStatement : public ODBCHandle { bool Fetch(size_t rows); bool isPrepared() const; - void GetStmtAttr(SQLINTEGER statementAttribute, SQLPOINTER output, - SQLINTEGER bufferSize, SQLINTEGER* strLenPtr, bool isUnicode); - void SetStmtAttr(SQLINTEGER statementAttribute, SQLPOINTER value, SQLINTEGER bufferSize, - bool isUnicode); + SQLRETURN GetStmtAttr(SQLINTEGER statementAttribute, SQLPOINTER output, + SQLINTEGER bufferSize, SQLINTEGER* strLenPtr, bool isUnicode); + SQLRETURN SetStmtAttr(SQLINTEGER statementAttribute, SQLPOINTER value, + SQLINTEGER bufferSize, bool isUnicode); void RevertAppDescriptor(bool isApd); diff --git a/cpp/src/arrow/flight/sql/odbc/odbcabstraction/odbc_impl/odbc_statement.cc b/cpp/src/arrow/flight/sql/odbc/odbcabstraction/odbc_impl/odbc_statement.cc index 49ad527bab0d..210341095834 100644 --- a/cpp/src/arrow/flight/sql/odbc/odbcabstraction/odbc_impl/odbc_statement.cc +++ b/cpp/src/arrow/flight/sql/odbc/odbcabstraction/odbc_impl/odbc_statement.cc @@ -360,7 +360,7 @@ bool ODBCStatement::Fetch(size_t rows) { return rowsFetched != 0; } -void ODBCStatement::GetStmtAttr(SQLINTEGER statementAttribute, SQLPOINTER output, +SQLRETURN ODBCStatement::GetStmtAttr(SQLINTEGER statementAttribute, SQLPOINTER output, SQLINTEGER bufferSize, SQLINTEGER* strLenPtr, bool isUnicode) { using driver::odbcabstraction::Statement; @@ -369,63 +369,63 @@ void ODBCStatement::GetStmtAttr(SQLINTEGER statementAttribute, SQLPOINTER output // Descriptor accessor attributes case SQL_ATTR_APP_PARAM_DESC: DescriptorToHandle(output, m_currentApd, strLenPtr); - return; + return SQL_SUCCESS; case SQL_ATTR_APP_ROW_DESC: DescriptorToHandle(output, m_currentArd, strLenPtr); - return; + return SQL_SUCCESS; case SQL_ATTR_IMP_PARAM_DESC: DescriptorToHandle(output, m_ipd.get(), strLenPtr); - return; + return SQL_SUCCESS; case SQL_ATTR_IMP_ROW_DESC: DescriptorToHandle(output, m_ird.get(), strLenPtr); - return; + return SQL_SUCCESS; // Attributes that are descriptor fields case SQL_ATTR_PARAM_BIND_OFFSET_PTR: m_currentApd->GetHeaderField(SQL_DESC_BIND_OFFSET_PTR, output, bufferSize, strLenPtr); - return; + return SQL_SUCCESS; case SQL_ATTR_PARAM_BIND_TYPE: m_currentApd->GetHeaderField(SQL_DESC_BIND_TYPE, output, bufferSize, strLenPtr); - return; + return SQL_SUCCESS; case SQL_ATTR_PARAM_OPERATION_PTR: m_currentApd->GetHeaderField(SQL_DESC_ARRAY_STATUS_PTR, output, bufferSize, strLenPtr); - return; + return SQL_SUCCESS; case SQL_ATTR_PARAM_STATUS_PTR: m_ipd->GetHeaderField(SQL_DESC_ARRAY_STATUS_PTR, output, bufferSize, strLenPtr); - return; + return SQL_SUCCESS; case SQL_ATTR_PARAMS_PROCESSED_PTR: m_ipd->GetHeaderField(SQL_DESC_ROWS_PROCESSED_PTR, output, bufferSize, strLenPtr); - return; + return SQL_SUCCESS; case SQL_ATTR_PARAMSET_SIZE: m_currentApd->GetHeaderField(SQL_DESC_ARRAY_SIZE, output, bufferSize, strLenPtr); - return; + return SQL_SUCCESS; case SQL_ATTR_ROW_ARRAY_SIZE: m_currentArd->GetHeaderField(SQL_DESC_ARRAY_SIZE, output, bufferSize, strLenPtr); - return; + return SQL_SUCCESS; case SQL_ATTR_ROW_BIND_OFFSET_PTR: m_currentArd->GetHeaderField(SQL_DESC_BIND_OFFSET_PTR, output, bufferSize, strLenPtr); - return; + return SQL_SUCCESS; case SQL_ATTR_ROW_BIND_TYPE: m_currentArd->GetHeaderField(SQL_DESC_BIND_TYPE, output, bufferSize, strLenPtr); - return; + return SQL_SUCCESS; case SQL_ATTR_ROW_OPERATION_PTR: m_currentArd->GetHeaderField(SQL_DESC_ARRAY_STATUS_PTR, output, bufferSize, strLenPtr); - return; + return SQL_SUCCESS; case SQL_ATTR_ROW_STATUS_PTR: m_ird->GetHeaderField(SQL_DESC_ARRAY_STATUS_PTR, output, bufferSize, strLenPtr); - return; + return SQL_SUCCESS; case SQL_ATTR_ROWS_FETCHED_PTR: m_ird->GetHeaderField(SQL_DESC_ROWS_PROCESSED_PTR, output, bufferSize, strLenPtr); - return; + return SQL_SUCCESS; case SQL_ATTR_ASYNC_ENABLE: GetAttribute(static_cast(SQL_ASYNC_ENABLE_OFF), output, bufferSize, strLenPtr); - return; + return SQL_SUCCESS; #ifdef SQL_ATTR_ASYNC_STMT_EVENT case SQL_ATTR_ASYNC_STMT_EVENT: @@ -442,51 +442,51 @@ void ODBCStatement::GetStmtAttr(SQLINTEGER statementAttribute, SQLPOINTER output case SQL_ATTR_CURSOR_SCROLLABLE: GetAttribute(static_cast(SQL_NONSCROLLABLE), output, bufferSize, strLenPtr); - return; + return SQL_SUCCESS; case SQL_ATTR_CURSOR_SENSITIVITY: GetAttribute(static_cast(SQL_UNSPECIFIED), output, bufferSize, strLenPtr); - return; + return SQL_SUCCESS; case SQL_ATTR_CURSOR_TYPE: GetAttribute(static_cast(SQL_CURSOR_FORWARD_ONLY), output, bufferSize, strLenPtr); - return; + return SQL_SUCCESS; case SQL_ATTR_ENABLE_AUTO_IPD: GetAttribute(static_cast(SQL_FALSE), output, bufferSize, strLenPtr); - return; + return SQL_SUCCESS; case SQL_ATTR_FETCH_BOOKMARK_PTR: GetAttribute(static_cast(NULL), output, bufferSize, strLenPtr); - return; + return SQL_SUCCESS; case SQL_ATTR_KEYSET_SIZE: GetAttribute(static_cast(0), output, bufferSize, strLenPtr); - return; + return SQL_SUCCESS; case SQL_ATTR_ROW_NUMBER: GetAttribute(static_cast(m_rowNumber), output, bufferSize, strLenPtr); - return; + return SQL_SUCCESS; case SQL_ATTR_SIMULATE_CURSOR: GetAttribute(static_cast(SQL_SC_UNIQUE), output, bufferSize, strLenPtr); - return; + return SQL_SUCCESS; case SQL_ATTR_USE_BOOKMARKS: GetAttribute(static_cast(SQL_UB_OFF), output, bufferSize, strLenPtr); - return; + return SQL_SUCCESS; case SQL_ATTR_CONCURRENCY: GetAttribute(static_cast(SQL_CONCUR_READ_ONLY), output, bufferSize, strLenPtr); - return; + return SQL_SUCCESS; case SQL_ATTR_MAX_ROWS: GetAttribute(static_cast(m_maxRows), output, bufferSize, strLenPtr); - return; + return SQL_SUCCESS; case SQL_ATTR_RETRIEVE_DATA: GetAttribute(static_cast(SQL_RD_ON), output, bufferSize, strLenPtr); - return; + return SQL_SUCCESS; case SQL_ROWSET_SIZE: GetAttribute(static_cast(m_rowsetSize), output, bufferSize, strLenPtr); - return; + return SQL_SUCCESS; // Driver-level statement attributes. These are all SQLULEN attributes. case SQL_ATTR_MAX_LENGTH: @@ -509,15 +509,15 @@ void ODBCStatement::GetStmtAttr(SQLINTEGER statementAttribute, SQLPOINTER output if (spiAttribute) { GetAttribute(static_cast(boost::get(*spiAttribute)), output, bufferSize, strLenPtr); - return; + return SQL_SUCCESS; } throw DriverException( "Invalid statement attribute: " + std::to_string(statementAttribute), "HY092"); } -void ODBCStatement::SetStmtAttr(SQLINTEGER statementAttribute, SQLPOINTER value, - SQLINTEGER bufferSize, bool isUnicode) { +SQLRETURN ODBCStatement::SetStmtAttr(SQLINTEGER statementAttribute, SQLPOINTER value, + SQLINTEGER bufferSize, bool isUnicode) { size_t attributeToWrite = 0; bool successfully_written = false; @@ -533,7 +533,7 @@ void ODBCStatement::SetStmtAttr(SQLINTEGER statementAttribute, SQLPOINTER value, desc->RegisterToStatement(this, true); } } - return; + return SQL_SUCCESS; } case SQL_ATTR_APP_ROW_DESC: { ODBCDescriptor* desc = static_cast(value); @@ -546,7 +546,7 @@ void ODBCStatement::SetStmtAttr(SQLINTEGER statementAttribute, SQLPOINTER value, desc->RegisterToStatement(this, false); } } - return; + return SQL_SUCCESS; } case SQL_ATTR_IMP_PARAM_DESC: throw DriverException("Cannot assign implementation descriptor.", "HY017"); @@ -555,40 +555,40 @@ void ODBCStatement::SetStmtAttr(SQLINTEGER statementAttribute, SQLPOINTER value, // Attributes that are descriptor fields case SQL_ATTR_PARAM_BIND_OFFSET_PTR: m_currentApd->SetHeaderField(SQL_DESC_BIND_OFFSET_PTR, value, bufferSize); - return; + return SQL_SUCCESS; case SQL_ATTR_PARAM_BIND_TYPE: m_currentApd->SetHeaderField(SQL_DESC_BIND_TYPE, value, bufferSize); - return; + return SQL_SUCCESS; case SQL_ATTR_PARAM_OPERATION_PTR: m_currentApd->SetHeaderField(SQL_DESC_ARRAY_STATUS_PTR, value, bufferSize); - return; + return SQL_SUCCESS; case SQL_ATTR_PARAM_STATUS_PTR: m_ipd->SetHeaderField(SQL_DESC_ARRAY_STATUS_PTR, value, bufferSize); - return; + return SQL_SUCCESS; case SQL_ATTR_PARAMS_PROCESSED_PTR: m_ipd->SetHeaderField(SQL_DESC_ROWS_PROCESSED_PTR, value, bufferSize); - return; + return SQL_SUCCESS; case SQL_ATTR_PARAMSET_SIZE: m_currentApd->SetHeaderField(SQL_DESC_ARRAY_SIZE, value, bufferSize); - return; + return SQL_SUCCESS; case SQL_ATTR_ROW_ARRAY_SIZE: m_currentArd->SetHeaderField(SQL_DESC_ARRAY_SIZE, value, bufferSize); - return; + return SQL_SUCCESS; case SQL_ATTR_ROW_BIND_OFFSET_PTR: m_currentArd->SetHeaderField(SQL_DESC_BIND_OFFSET_PTR, value, bufferSize); - return; + return SQL_SUCCESS; case SQL_ATTR_ROW_BIND_TYPE: m_currentArd->SetHeaderField(SQL_DESC_BIND_TYPE, value, bufferSize); - return; + return SQL_SUCCESS; case SQL_ATTR_ROW_OPERATION_PTR: m_currentArd->SetHeaderField(SQL_DESC_ARRAY_STATUS_PTR, value, bufferSize); - return; + return SQL_SUCCESS; case SQL_ATTR_ROW_STATUS_PTR: m_ird->SetHeaderField(SQL_DESC_ARRAY_STATUS_PTR, value, bufferSize); - return; + return SQL_SUCCESS; case SQL_ATTR_ROWS_FETCHED_PTR: m_ird->SetHeaderField(SQL_DESC_ROWS_PROCESSED_PTR, value, bufferSize); - return; + return SQL_SUCCESS; case SQL_ATTR_ASYNC_ENABLE: throw DriverException("Unsupported attribute", "HYC00"); @@ -607,43 +607,43 @@ void ODBCStatement::SetStmtAttr(SQLINTEGER statementAttribute, SQLPOINTER value, case SQL_ATTR_CONCURRENCY: CheckIfAttributeIsSetToOnlyValidValue(value, static_cast(SQL_CONCUR_READ_ONLY)); - return; + return SQL_SUCCESS; case SQL_ATTR_CURSOR_SCROLLABLE: CheckIfAttributeIsSetToOnlyValidValue(value, static_cast(SQL_NONSCROLLABLE)); - return; + return SQL_SUCCESS; case SQL_ATTR_CURSOR_SENSITIVITY: CheckIfAttributeIsSetToOnlyValidValue(value, static_cast(SQL_UNSPECIFIED)); - return; + return SQL_SUCCESS; case SQL_ATTR_CURSOR_TYPE: CheckIfAttributeIsSetToOnlyValidValue( value, static_cast(SQL_CURSOR_FORWARD_ONLY)); - return; + return SQL_SUCCESS; case SQL_ATTR_ENABLE_AUTO_IPD: CheckIfAttributeIsSetToOnlyValidValue(value, static_cast(SQL_FALSE)); - return; + return SQL_SUCCESS; case SQL_ATTR_FETCH_BOOKMARK_PTR: if (value != NULL) { throw DriverException("Optional feature not implemented", "HYC00"); } - return; + return SQL_SUCCESS; case SQL_ATTR_KEYSET_SIZE: CheckIfAttributeIsSetToOnlyValidValue(value, static_cast(0)); - return; + return SQL_SUCCESS; case SQL_ATTR_ROW_NUMBER: throw DriverException("Cannot set read-only attribute", "HY092"); case SQL_ATTR_SIMULATE_CURSOR: CheckIfAttributeIsSetToOnlyValidValue(value, static_cast(SQL_SC_UNIQUE)); - return; + return SQL_SUCCESS; case SQL_ATTR_USE_BOOKMARKS: CheckIfAttributeIsSetToOnlyValidValue(value, static_cast(SQL_UB_OFF)); - return; + return SQL_SUCCESS; case SQL_ATTR_RETRIEVE_DATA: CheckIfAttributeIsSetToOnlyValidValue(value, static_cast(SQL_TRUE)); - return; + return SQL_SUCCESS; case SQL_ROWSET_SIZE: SetAttribute(value, m_rowsetSize); - return; + return SQL_SUCCESS; case SQL_ATTR_MAX_ROWS: throw DriverException("Cannot set read-only attribute", "HY092"); @@ -673,9 +673,12 @@ void ODBCStatement::SetStmtAttr(SQLINTEGER statementAttribute, SQLPOINTER value, throw DriverException("Invalid attribute: " + std::to_string(attributeToWrite), "HY092"); } - if (!successfully_written) { + if (successfully_written) { + return SQL_SUCCESS; + } else { GetDiagnostics().AddWarning("Optional value changed.", "01S02", driver::odbcabstraction::ODBCErrorCodes_GENERAL_WARNING); + return SQL_SUCCESS_WITH_INFO; } } From 12a059f5ac18b0c51c498304c7f062e1c9680c88 Mon Sep 17 00:00:00 2001 From: rscales Date: Fri, 4 Jul 2025 02:39:24 +0100 Subject: [PATCH 02/18] Reformat files --- cpp/src/arrow/flight/sql/odbc/entry_points.cc | 4 ++-- cpp/src/arrow/flight/sql/odbc/odbc_api.cc | 4 ++-- cpp/src/arrow/flight/sql/odbc/odbc_api.h | 4 ++-- .../sql/odbc/odbcabstraction/odbc_impl/odbc_statement.cc | 4 ++-- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/cpp/src/arrow/flight/sql/odbc/entry_points.cc b/cpp/src/arrow/flight/sql/odbc/entry_points.cc index 8cdf45b33c2e..7e9fd406f951 100644 --- a/cpp/src/arrow/flight/sql/odbc/entry_points.cc +++ b/cpp/src/arrow/flight/sql/odbc/entry_points.cc @@ -279,8 +279,8 @@ SQLRETURN SQL_API SQLPrimaryKeys(SQLHSTMT statementHandle, SQLWCHAR* catalogName return SQL_ERROR; } -SQLRETURN SQL_API SQLSetStmtAttr(SQLHSTMT stmt, SQLINTEGER attribute, - SQLPOINTER valuePtr, SQLINTEGER stringLength) { +SQLRETURN SQL_API SQLSetStmtAttr(SQLHSTMT stmt, SQLINTEGER attribute, SQLPOINTER valuePtr, + SQLINTEGER stringLength) { return arrow::SQLSetStmtAttr(stmt, attribute, valuePtr, stringLength); } diff --git a/cpp/src/arrow/flight/sql/odbc/odbc_api.cc b/cpp/src/arrow/flight/sql/odbc/odbc_api.cc index d799b5f94f5a..884c0888c610 100644 --- a/cpp/src/arrow/flight/sql/odbc/odbc_api.cc +++ b/cpp/src/arrow/flight/sql/odbc/odbc_api.cc @@ -890,8 +890,8 @@ SQLRETURN SQLGetStmtAttr(SQLHSTMT stmt, SQLINTEGER attribute, SQLPOINTER valuePt }); } -SQLRETURN SQLSetStmtAttr(SQLHSTMT stmt, SQLINTEGER attribute, - SQLPOINTER valuePtr, SQLINTEGER stringLength) { +SQLRETURN SQLSetStmtAttr(SQLHSTMT stmt, SQLINTEGER attribute, SQLPOINTER valuePtr, + SQLINTEGER stringLength) { using ODBC::ODBCStatement; LOG_DEBUG( diff --git a/cpp/src/arrow/flight/sql/odbc/odbc_api.h b/cpp/src/arrow/flight/sql/odbc/odbc_api.h index f820e4c71587..504a8f545f88 100644 --- a/cpp/src/arrow/flight/sql/odbc/odbc_api.h +++ b/cpp/src/arrow/flight/sql/odbc/odbc_api.h @@ -62,8 +62,8 @@ SQLRETURN SQLGetInfo(SQLHDBC conn, SQLUSMALLINT infoType, SQLPOINTER infoValuePt SQLSMALLINT bufLen, SQLSMALLINT* length); SQLRETURN SQLGetStmtAttr(SQLHSTMT stmt, SQLINTEGER attribute, SQLPOINTER valuePtr, SQLINTEGER bufferLength, SQLINTEGER* stringLengthPtr); -SQLRETURN SQLSetStmtAttr(SQLHSTMT stmt, SQLINTEGER attribute, - SQLPOINTER valuePtr, SQLINTEGER stringLength); +SQLRETURN SQLSetStmtAttr(SQLHSTMT stmt, SQLINTEGER attribute, SQLPOINTER valuePtr, + SQLINTEGER stringLength); SQLRETURN SQLExecDirect(SQLHSTMT stmt, SQLWCHAR* queryText, SQLINTEGER textLength); SQLRETURN SQLPrepare(SQLHSTMT stmt, SQLWCHAR* queryText, SQLINTEGER textLength); SQLRETURN SQLExecute(SQLHSTMT stmt); diff --git a/cpp/src/arrow/flight/sql/odbc/odbcabstraction/odbc_impl/odbc_statement.cc b/cpp/src/arrow/flight/sql/odbc/odbcabstraction/odbc_impl/odbc_statement.cc index 210341095834..15da9838f7da 100644 --- a/cpp/src/arrow/flight/sql/odbc/odbcabstraction/odbc_impl/odbc_statement.cc +++ b/cpp/src/arrow/flight/sql/odbc/odbcabstraction/odbc_impl/odbc_statement.cc @@ -361,8 +361,8 @@ bool ODBCStatement::Fetch(size_t rows) { } SQLRETURN ODBCStatement::GetStmtAttr(SQLINTEGER statementAttribute, SQLPOINTER output, - SQLINTEGER bufferSize, SQLINTEGER* strLenPtr, - bool isUnicode) { + SQLINTEGER bufferSize, SQLINTEGER* strLenPtr, + bool isUnicode) { using driver::odbcabstraction::Statement; boost::optional spiAttribute; switch (statementAttribute) { From 04962f3de70b70fc55395c4de2a9a683fe13cedb Mon Sep 17 00:00:00 2001 From: rscales Date: Tue, 8 Jul 2025 06:22:09 +0100 Subject: [PATCH 03/18] Add initial version of statement attr tests --- .../odbc_impl/odbc_statement.cc | 2 +- .../flight/sql/odbc/tests/CMakeLists.txt | 1 + .../sql/odbc/tests/statement_attr_test.cc | 1262 +++++++++++++++++ 3 files changed, 1264 insertions(+), 1 deletion(-) create mode 100644 cpp/src/arrow/flight/sql/odbc/tests/statement_attr_test.cc diff --git a/cpp/src/arrow/flight/sql/odbc/odbcabstraction/odbc_impl/odbc_statement.cc b/cpp/src/arrow/flight/sql/odbc/odbcabstraction/odbc_impl/odbc_statement.cc index 15da9838f7da..8b507fdb64e8 100644 --- a/cpp/src/arrow/flight/sql/odbc/odbcabstraction/odbc_impl/odbc_statement.cc +++ b/cpp/src/arrow/flight/sql/odbc/odbcabstraction/odbc_impl/odbc_statement.cc @@ -639,7 +639,7 @@ SQLRETURN ODBCStatement::SetStmtAttr(SQLINTEGER statementAttribute, SQLPOINTER v CheckIfAttributeIsSetToOnlyValidValue(value, static_cast(SQL_UB_OFF)); return SQL_SUCCESS; case SQL_ATTR_RETRIEVE_DATA: - CheckIfAttributeIsSetToOnlyValidValue(value, static_cast(SQL_TRUE)); + CheckIfAttributeIsSetToOnlyValidValue(value, static_cast(SQL_RD_ON)); return SQL_SUCCESS; case SQL_ROWSET_SIZE: SetAttribute(value, m_rowsetSize); diff --git a/cpp/src/arrow/flight/sql/odbc/tests/CMakeLists.txt b/cpp/src/arrow/flight/sql/odbc/tests/CMakeLists.txt index 1a3da7fad317..952b1d36cc88 100644 --- a/cpp/src/arrow/flight/sql/odbc/tests/CMakeLists.txt +++ b/cpp/src/arrow/flight/sql/odbc/tests/CMakeLists.txt @@ -33,6 +33,7 @@ add_arrow_test(flight_sql_odbc_test SOURCES connection_attr_test.cc connection_info_test.cc + statement_attr_test.cc statement_test.cc # Connection test needs to be put last to resolve segfault issue connection_test.cc diff --git a/cpp/src/arrow/flight/sql/odbc/tests/statement_attr_test.cc b/cpp/src/arrow/flight/sql/odbc/tests/statement_attr_test.cc new file mode 100644 index 000000000000..f4bc05d0e2f3 --- /dev/null +++ b/cpp/src/arrow/flight/sql/odbc/tests/statement_attr_test.cc @@ -0,0 +1,1262 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +#include "arrow/flight/sql/odbc/tests/odbc_test_suite.h" +#include "arrow/flight/sql/odbc/odbcabstraction/include/odbcabstraction/odbc_impl/odbc_statement.h" +#include "arrow/flight/sql/odbc/odbcabstraction/include/odbcabstraction/spi/statement.h" + +#ifdef _WIN32 +# include +#endif + +#include +#include +#include + +#include "gtest/gtest.h" + +namespace arrow::flight::sql::odbc { + +// Helper Functions + +// Validate unsigned length SQLULEN return value +void validateGetStmtAttr(SQLHSTMT statement, SQLUSMALLINT attribute, + SQLULEN expected_value) { + SQLULEN value = 0; + SQLINTEGER stringLengthPtr; + + SQLRETURN ret = SQLGetStmtAttr(statement, attribute, &value, 0, &stringLengthPtr); + + if (ret != SQL_SUCCESS) { + // TODO remove later + std::cerr << GetOdbcErrorMessage(SQL_HANDLE_STMT, statement) << std::endl; + } + + EXPECT_EQ(ret, SQL_SUCCESS); + + EXPECT_EQ(value, expected_value); +} + +// Validate unsigned length SQLULEN return value is greater than +void validateGetStmtAttrGreaterThan(SQLHSTMT statement, SQLUSMALLINT attribute, + SQLULEN compared_value) { + SQLULEN value = 0; + SQLINTEGER stringLengthPtr; + + SQLRETURN ret = SQLGetStmtAttr(statement, attribute, &value, 0, &stringLengthPtr); + + if (ret != SQL_SUCCESS) { + // TODO remove later + std::cerr << GetOdbcErrorMessage(SQL_HANDLE_STMT, statement) << std::endl; + } + + EXPECT_EQ(ret, SQL_SUCCESS); + + EXPECT_GT(value, compared_value); +} + +// Validate error return value and code +void validateGetStmtAttrErrorCode(SQLHSTMT statement, SQLUSMALLINT attribute, + std::string_view error_code) { + SQLULEN value = 0; + SQLINTEGER stringLengthPtr; + + SQLRETURN ret = SQLGetStmtAttr(statement, attribute, &value, 0, &stringLengthPtr); + + EXPECT_EQ(ret, SQL_ERROR); + + if (ret != SQL_SUCCESS) { + // TODO remove later + std::cerr << GetOdbcErrorMessage(SQL_HANDLE_STMT, statement) << std::endl; + } + + VerifyOdbcErrorState(SQL_HANDLE_STMT, statement, error_code); +} + +// Validate unsigned length SQLULEN return value +void validateSetStmtAttr(SQLHSTMT statement, SQLUSMALLINT attribute, SQLULEN new_value) { + SQLINTEGER stringLengthPtr = sizeof(SQLULEN); + + SQLRETURN ret = SQLSetStmtAttr(statement, attribute, &new_value, stringLengthPtr); + + if (ret != SQL_SUCCESS) { + // TODO remove later + std::cerr << GetOdbcErrorMessage(SQL_HANDLE_STMT, statement) << std::endl; + } + + EXPECT_EQ(ret, SQL_SUCCESS); +} + +// Validate error return value and code +void validateSetStmtAttrErrorCode(SQLHSTMT statement, SQLUSMALLINT attribute, + SQLULEN new_value, std::string_view error_code) { + // SQLINTEGER stringLengthPtr; + + SQLRETURN ret = SQLSetStmtAttr(statement, attribute, &new_value, 0); + + if (ret != SQL_SUCCESS) { + // TODO remove later + std::cerr << GetOdbcErrorMessage(SQL_HANDLE_STMT, statement) << std::endl; + } + + EXPECT_EQ(ret, SQL_ERROR); + + VerifyOdbcErrorState(SQL_HANDLE_STMT, statement, error_code); +} + +TYPED_TEST(FlightSQLODBCTestBase, TestSQLGetStmtAttrAppParamDesc) { + this->connect(); + + validateGetStmtAttrGreaterThan(this->stmt, SQL_ATTR_APP_PARAM_DESC, + static_cast(0)); + + this->disconnect(); +} + +TYPED_TEST(FlightSQLODBCTestBase, TestSQLGetStmtAttrAppRowDesc) { + this->connect(); + + validateGetStmtAttrGreaterThan(this->stmt, SQL_ATTR_APP_ROW_DESC, + static_cast(0)); + + this->disconnect(); +} + +TYPED_TEST(FlightSQLODBCTestBase, TestSQLGetStmtAttrAsyncEnable) { + this->connect(); + + validateGetStmtAttr(this->stmt, SQL_ATTR_ASYNC_ENABLE, + static_cast(SQL_ASYNC_ENABLE_OFF)); + + this->disconnect(); +} + +// #ifdef SQL_ATTR_ASYNC_STMT_EVENT +// case SQL_ATTR_ASYNC_STMT_EVENT: +// throw DriverException("Unsupported attribute", "HYC00"); +// #endif +// +#ifdef SQL_ATTR_ASYNC_STMT_EVENT +TYPED_TEST(FlightSQLODBCTestBase, TestSQLGetStmtAttrAsyncStmtEventUnsupported) { + this->connect(); + + // Optional feature not implemented + validateGetStmtAttrErrorCode(this->stmt, SQL_ATTR_ASYNC_STMT_EVENT, error_state_HYC00); + + this->disconnect(); +} +#endif + +// #ifdef SQL_ATTR_ASYNC_STMT_PCALLBACK +// case SQL_ATTR_ASYNC_STMT_PCALLBACK: +// throw DriverException("Unsupported attribute", "HYC00"); +// #endif +// +#ifdef SQL_ATTR_ASYNC_STMT_PCALLBACK +TYPED_TEST(FlightSQLODBCTestBase, TestSQLGetStmtAttrAsyncStmtPCCallbackUnsupported) { + this->connect(); + + // Optional feature not implemented + validateGetStmtAttrErrorCode(this->stmt, SQL_ATTR_ASYNC_STMT_PCALLBACK, + error_state_HYC00); + + this->disconnect(); +} +#endif + +// #ifdef SQL_ATTR_ASYNC_STMT_PCONTEXT +// case SQL_ATTR_ASYNC_STMT_PCONTEXT: +// throw DriverException("Unsupported attribute", "HYC00"); +// #endif +// +#ifdef SQL_ATTR_ASYNC_STMT_PCONTEXT +TYPED_TEST(FlightSQLODBCTestBase, TestSQLGetStmtAttrAsyncStmtPCContextUnsupported) { + this->connect(); + + // Optional feature not implemented + validateGetStmtAttrErrorCode(this->stmt, SQL_ATTR_ASYNC_STMT_PCONTEXT, + error_state_HYC00); + + this->disconnect(); +} +#endif + +TYPED_TEST(FlightSQLODBCTestBase, TestSQLGetStmtAttrConcurrency) { + this->connect(); + + validateGetStmtAttr(this->stmt, SQL_ATTR_CONCURRENCY, + static_cast(SQL_CONCUR_READ_ONLY)); + + this->disconnect(); +} + +// case SQL_ATTR_CURSOR_SCROLLABLE: +// GetAttribute(static_cast(SQL_NONSCROLLABLE), output, bufferSize, strLenPtr); +// +TYPED_TEST(FlightSQLODBCTestBase, TestSQLGetStmtAttrCursorScrollable) { + // TODO HY092: [Apache Arrow][Flight SQL] (100) Invalid statement attribute: 65535 + GTEST_SKIP(); + this->connect(); + + validateGetStmtAttr(this->stmt, SQL_ATTR_CURSOR_SCROLLABLE, + static_cast(SQL_NONSCROLLABLE)); + + this->disconnect(); +} + +// case SQL_ATTR_CURSOR_SENSITIVITY: +// GetAttribute(static_cast(SQL_UNSPECIFIED), output, bufferSize, strLenPtr); +// +TYPED_TEST(FlightSQLODBCTestBase, TestSQLGetStmtAttrCursorSensitivity) { + // TODO HY092: [Apache Arrow][Flight SQL] (100) Invalid statement attribute: 65534 + GTEST_SKIP(); + this->connect(); + + validateGetStmtAttr(this->stmt, SQL_ATTR_CURSOR_SENSITIVITY, + static_cast(SQL_UNSPECIFIED)); + + this->disconnect(); +} + +TYPED_TEST(FlightSQLODBCTestBase, TestSQLGetStmtAttrCursorType) { + this->connect(); + + validateGetStmtAttr(this->stmt, SQL_ATTR_CURSOR_TYPE, + static_cast(SQL_CURSOR_FORWARD_ONLY)); + + this->disconnect(); +} + +TYPED_TEST(FlightSQLODBCTestBase, TestSQLGetStmtAttrEnableAutoIPD) { + this->connect(); + + validateGetStmtAttr(this->stmt, SQL_ATTR_ENABLE_AUTO_IPD, + static_cast(SQL_FALSE)); + + this->disconnect(); +} + +TYPED_TEST(FlightSQLODBCTestBase, TestSQLGetStmtAttrFetchBookmarkPointer) { + this->connect(); + + // TODO static_cast(NULL) does not compile, but was used as default in + // odbc_statement.cc + // validateGetStmtAttr(this->stmt, SQL_ATTR_FETCH_BOOKMARK_PTR, + // static_cast(NULL)); + validateGetStmtAttr(this->stmt, SQL_ATTR_FETCH_BOOKMARK_PTR, static_cast(NULL)); + + this->disconnect(); +} + +TYPED_TEST(FlightSQLODBCTestBase, TestSQLGetStmtAttrIMPParamDesc) { + this->connect(); + + validateGetStmtAttrGreaterThan(this->stmt, SQL_ATTR_IMP_PARAM_DESC, + static_cast(0)); + + this->disconnect(); +} + +TYPED_TEST(FlightSQLODBCTestBase, TestSQLGetStmtAttrIMPRowDesc) { + this->connect(); + + validateGetStmtAttrGreaterThan(this->stmt, SQL_ATTR_IMP_ROW_DESC, + static_cast(0)); + + this->disconnect(); +} + +TYPED_TEST(FlightSQLODBCTestBase, TestSQLGetStmtAttrKeysetSize) { + this->connect(); + + validateGetStmtAttr(this->stmt, SQL_ATTR_KEYSET_SIZE, static_cast(0)); + + this->disconnect(); +} + +TYPED_TEST(FlightSQLODBCTestBase, TestSQLGetStmtAttrMaxLength) { + this->connect(); + + validateGetStmtAttr(this->stmt, SQL_ATTR_MAX_LENGTH, static_cast(0)); + + this->disconnect(); +} + +TYPED_TEST(FlightSQLODBCTestBase, TestSQLGetStmtAttrMaxRows) { + this->connect(); + + validateGetStmtAttr(this->stmt, SQL_ATTR_MAX_ROWS, static_cast(0)); + + this->disconnect(); +} + +TYPED_TEST(FlightSQLODBCTestBase, TestSQLGetStmtAttrMetadataID) { + this->connect(); + + validateGetStmtAttr(this->stmt, SQL_ATTR_METADATA_ID, static_cast(SQL_FALSE)); + + this->disconnect(); +} + +TYPED_TEST(FlightSQLODBCTestBase, TestSQLGetStmtAttrNoscan) { + this->connect(); + + validateGetStmtAttr(this->stmt, SQL_ATTR_NOSCAN, static_cast(SQL_NOSCAN_OFF)); + + this->disconnect(); +} + +TYPED_TEST(FlightSQLODBCTestBase, TestSQLGetStmtAttrParamBindOffsetPtr) { + this->connect(); + + validateGetStmtAttr(this->stmt, SQL_ATTR_PARAM_BIND_OFFSET_PTR, + static_cast(0)); + + this->disconnect(); +} + +TYPED_TEST(FlightSQLODBCTestBase, TestSQLGetStmtAttrParamBindType) { + this->connect(); + + validateGetStmtAttr(this->stmt, SQL_ATTR_PARAM_BIND_TYPE, + static_cast(SQL_PARAM_BIND_BY_COLUMN)); + + this->disconnect(); +} + +TYPED_TEST(FlightSQLODBCTestBase, TestSQLGetStmtAttrParamOperationPtr) { + this->connect(); + + validateGetStmtAttr(this->stmt, SQL_ATTR_PARAM_OPERATION_PTR, static_cast(0)); + + this->disconnect(); +} + +TYPED_TEST(FlightSQLODBCTestBase, TestSQLGetStmtAttrParamStatusPtr) { + this->connect(); + + validateGetStmtAttr(this->stmt, SQL_ATTR_PARAM_STATUS_PTR, static_cast(0)); + + this->disconnect(); +} + +TYPED_TEST(FlightSQLODBCTestBase, TestSQLGetStmtAttrParamsProcessedPtr) { + this->connect(); + + validateGetStmtAttr(this->stmt, SQL_ATTR_PARAMS_PROCESSED_PTR, static_cast(0)); + + this->disconnect(); +} + +TYPED_TEST(FlightSQLODBCTestBase, TestSQLGetStmtAttrParamsetSize) { + this->connect(); + + validateGetStmtAttr(this->stmt, SQL_ATTR_PARAMSET_SIZE, static_cast(1)); + + this->disconnect(); +} + +TYPED_TEST(FlightSQLODBCTestBase, TestSQLGetStmtAttrQueryTimeout) { + this->connect(); + + validateGetStmtAttr(this->stmt, SQL_ATTR_QUERY_TIMEOUT, static_cast(0)); + + this->disconnect(); +} + +TYPED_TEST(FlightSQLODBCTestBase, TestSQLGetStmtAttrRetrieveData) { + this->connect(); + + validateGetStmtAttr(this->stmt, SQL_ATTR_RETRIEVE_DATA, + static_cast(SQL_RD_ON)); + + this->disconnect(); +} + +TYPED_TEST(FlightSQLODBCTestBase, TestSQLGetStmtAttrRowArraySize) { + this->connect(); + + validateGetStmtAttr(this->stmt, SQL_ATTR_ROW_ARRAY_SIZE, static_cast(1)); + + this->disconnect(); +} + +TYPED_TEST(FlightSQLODBCTestBase, TestSQLGetStmtAttrRowBindOffsetPtr) { + this->connect(); + + validateGetStmtAttr(this->stmt, SQL_ATTR_ROW_BIND_OFFSET_PTR, static_cast(0)); + + this->disconnect(); +} + +TYPED_TEST(FlightSQLODBCTestBase, TestSQLGetStmtAttrRowBindType) { + this->connect(); + + validateGetStmtAttr(this->stmt, SQL_ATTR_ROW_BIND_TYPE, static_cast(0)); + + this->disconnect(); +} + +// An SQLULEN value that is the number of the current row in the entire result set.If the +// number of the current row cannot be determined or +// there is no current row, +// the driver returns 0. +// +// This attribute can be retrieved by a call to SQLGetStmtAttr but not set by a call to +// SQLSetStmtAttr. +// +TYPED_TEST(FlightSQLODBCTestBase, TestSQLGetStmtAttrRowNumber) { + // TODO 24000: [Microsoft][ODBC Driver Manager] Invalid cursor state + GTEST_SKIP(); + this->connect(); + + validateGetStmtAttr(this->stmt, SQL_ATTR_ROW_NUMBER, static_cast(0)); + + this->disconnect(); +} + +TYPED_TEST(FlightSQLODBCTestBase, TestSQLGetStmtAttrRowOperationPtr) { + this->connect(); + + validateGetStmtAttr(this->stmt, SQL_ATTR_ROW_OPERATION_PTR, static_cast(0)); + + this->disconnect(); +} + +TYPED_TEST(FlightSQLODBCTestBase, TestSQLGetStmtAttrRowStatusPtr) { + this->connect(); + + validateGetStmtAttr(this->stmt, SQL_ATTR_ROW_STATUS_PTR, static_cast(0)); + + this->disconnect(); +} + +TYPED_TEST(FlightSQLODBCTestBase, TestSQLGetStmtAttrRowsFetchedPtr) { + this->connect(); + + validateGetStmtAttr(this->stmt, SQL_ATTR_ROWS_FETCHED_PTR, static_cast(0)); + + this->disconnect(); +} + +TYPED_TEST(FlightSQLODBCTestBase, TestSQLGetStmtAttrSimulateCursor) { + this->connect(); + + validateGetStmtAttr(this->stmt, SQL_ATTR_SIMULATE_CURSOR, + static_cast(SQL_SC_UNIQUE)); + + this->disconnect(); +} + +TYPED_TEST(FlightSQLODBCTestBase, TestSQLGetStmtAttrUseBookmarks) { + this->connect(); + + validateGetStmtAttr(this->stmt, SQL_ATTR_USE_BOOKMARKS, + static_cast(SQL_UB_OFF)); + + this->disconnect(); +} + +// TODO This is not a standard SQL_ATTR type parameter +TYPED_TEST(FlightSQLODBCTestBase, TestSQLGetStmtAttrRowsetSize) { + this->connect(); + + validateGetStmtAttr(this->stmt, SQL_ROWSET_SIZE, static_cast(1)); + + this->disconnect(); +} + +// The handle to the APD for subsequent calls to SQLExecute and SQLExecDirect on the +// statement handle. The initial value of this attribute is the descriptor implicitly +// allocated when the statement was initially allocated. If the value of this attribute is +// set to SQL_NULL_DESC or the handle originally allocated for the descriptor, an +// explicitly allocated APD handle that was previously associated with the statement +// handle is dissociated from it and the statement handle reverts to the implicitly +// allocated APD handle. +// +// This attribute cannot be set to a descriptor handle that was implicitly allocated for +// another statement or to another descriptor handle that was implicitly set on the same +// statement; implicitly allocated descriptor handles cannot be associated with more than +// one +// statement or +// descriptor handle. +// +TYPED_TEST(FlightSQLODBCTestBase, TestSQLSetStmtAttrAppParamDesc) { + // GTEST_SKIP(); + SQLULEN app_param_desc = 0; + SQLINTEGER stringLengthPtr; + this->connect(); + + SQLRETURN ret = SQLGetStmtAttr(this->stmt, SQL_ATTR_APP_PARAM_DESC, &app_param_desc, 0, + &stringLengthPtr); + + EXPECT_EQ(ret, SQL_SUCCESS); + + // TODO SQL_NULL_DESC not found + // setStmtAttr(this->stmt, SQL_ATTR_APP_PARAM_DESC, + // static_cast(SQL_NULL_DESC)); setStmtAttr(this->stmt, + // SQL_ATTR_APP_PARAM_DESC, static_cast(0)); + + // validateSetStmtAttr(this->stmt, SQL_ATTR_APP_PARAM_DESC, + // static_cast(app_param_desc)); + + this->disconnect(); +} + +// The handle to the ARD for subsequent fetches on the statement handle. The initial value +// of this attribute is the descriptor implicitly allocated when the statement was +// initially allocated. If the value of this attribute is set to SQL_NULL_DESC or the +// handle originally allocated for the descriptor, an explicitly allocated ARD handle that +// was previously associated with the statement handle is dissociated from it and the +// statement handle reverts to the implicitly allocated ARD handle. +// +// This attribute cannot be set to a descriptor handle that was implicitly allocated for +// another statement or to another descriptor handle that was implicitly set on the same +// statement; implicitly allocated descriptor handles cannot be associated with more than +// one +// statement or descriptor handle. +// +TYPED_TEST(FlightSQLODBCTestBase, TestSQLSetStmtAttrAppRowDesc) { + // GTEST_SKIP(); + SQLULEN app_row_desc = 0; + SQLINTEGER stringLengthPtr; + this->connect(); + + SQLRETURN ret = SQLGetStmtAttr(this->stmt, SQL_ATTR_APP_ROW_DESC, &app_row_desc, 0, + &stringLengthPtr); + + EXPECT_EQ(ret, SQL_SUCCESS); + + // TODO SQL_NULL_DESC not found + // setStmtAttr(this->stmt, SQL_ATTR_APP_ROW_DESC, static_cast(SQL_NULL_DESC)); + // setStmtAttr(this->stmt, SQL_ATTR_APP_ROW_DESC, static_cast(0)); + + // validateSetStmtAttr(this->stmt, SQL_ATTR_APP_ROW_DESC, + // static_cast(app_row_desc)); + + this->disconnect(); +} + +// case SQL_ATTR_ASYNC_ENABLE: +// throw DriverException("Unsupported attribute", "HYC00"); +// +#ifdef SQL_ATTR_ASYNC_ENABLE +TYPED_TEST(FlightSQLODBCTestBase, TestSQLSetStmtAttrAsyncEnableUnsupported) { + // TODO HY024: [Microsoft][ODBC Driver Manager] Invalid argument value + GTEST_SKIP(); + this->connect(); + + // Optional feature not implemented + validateSetStmtAttrErrorCode(this->stmt, SQL_ATTR_ASYNC_ENABLE, SQL_ASYNC_ENABLE_OFF, + error_state_HYC00); + + this->disconnect(); +} +#endif + +// #ifdef SQL_ATTR_ASYNC_STMT_EVENT +// case SQL_ATTR_ASYNC_STMT_EVENT: +// throw DriverException("Unsupported attribute", "HYC00"); +// #endif +// +#ifdef SQL_ATTR_ASYNC_STMT_EVENT +TYPED_TEST(FlightSQLODBCTestBase, TestSQLSetStmtAttrAsyncStmtEventUnsupported) { + // TODO HY118: [Microsoft][ODBC Driver Manager] Driver does not support asynchronous + // notification + GTEST_SKIP(); + this->connect(); + + // Optional feature not implemented + validateSetStmtAttrErrorCode(this->stmt, SQL_ATTR_ASYNC_STMT_EVENT, 0, + error_state_HYC00); + + this->disconnect(); +} +#endif + +// TODO +// +// Only the Driver Manager can call a driver's SQLSetStmtAttr function with this +// attribute. +// +// #ifdef SQL_ATTR_ASYNC_STMT_PCALLBACK +// case SQL_ATTR_ASYNC_STMT_PCALLBACK: +// throw DriverException("Unsupported attribute", "HYC00"); +// #endif +// +#ifdef SQL_ATTR_ASYNC_STMT_PCALLBACK +TYPED_TEST(FlightSQLODBCTestBase, TestSQLSetStmtAttrAsyncStmtPCCallbackUnsupported) { + this->connect(); + + validateSetStmtAttrErrorCode(this->stmt, SQL_ATTR_ASYNC_STMT_PCALLBACK, 0, + error_state_HYC00); + + this->disconnect(); +} +#endif + +// TODO +// Only the Driver Manager can call a driver's SQLSetStmtAttr function with this +// attribute. +// +// #ifdef SQL_ATTR_ASYNC_STMT_PCONTEXT +// case SQL_ATTR_ASYNC_STMT_PCONTEXT: +// throw DriverException("Unsupported attribute", "HYC00"); +// #endif +// +#ifdef SQL_ATTR_ASYNC_STMT_PCONTEXT +TYPED_TEST(FlightSQLODBCTestBase, TestSQLSetStmtAttrAsyncStmtPCContextUnsupported) { + this->connect(); + + // Optional feature not implemented + validateSetStmtAttrErrorCode(this->stmt, SQL_ATTR_ASYNC_STMT_PCONTEXT, 0, + error_state_HYC00); + + this->disconnect(); +} +#endif + +// SQL_CONCUR_READ_ONLY = Cursor is read-only. No updates are allowed. +// +// SQL_CONCUR_LOCK = Cursor uses the lowest level of locking sufficient to ensure that the +// row can be updated. +// +// SQL_CONCUR_ROWVER = Cursor uses optimistic concurrency control, comparing row versions +// such as SQLBase ROWID or Sybase TIMESTAMP. +// +// SQL_CONCUR_VALUES = Cursor uses optimistic concurrency control, comparing values. +// +// The default value for SQL_ATTR_CONCURRENCY is SQL_CONCUR_READ_ONLY. +// +// case SQL_ATTR_CONCURRENCY: +// CheckIfAttributeIsSetToOnlyValidValue(value, +// static_cast(SQL_CONCUR_READ_ONLY)); +// +TYPED_TEST(FlightSQLODBCTestBase, TestSQLSetStmtAttrConcurrency) { + // TODO HY024: [Microsoft][ODBC Driver Manager] Invalid argument value + GTEST_SKIP(); + this->connect(); + + validateSetStmtAttr(this->stmt, SQL_ATTR_CONCURRENCY, + static_cast(SQL_CONCUR_READ_ONLY)); + + this->disconnect(); +} + +// SQL_NONSCROLLABLE = Scrollable cursors are not required on the statement handle.If the +// application calls SQLFetchScroll on this handle, +// the only valid value of FetchOrientation is SQL_FETCH_NEXT.This is the default. +// +// SQL_SCROLLABLE = Scrollable cursors are required on the statement handle.When +// calling +// SQLFetchScroll, +// the application may specify any valid value of FetchOrientation, +// achieving cursor positioning in modes other than the sequential mode. +// +// case SQL_ATTR_CURSOR_SCROLLABLE: +// CheckIfAttributeIsSetToOnlyValidValue(value, static_cast(SQL_NONSCROLLABLE)); +// +TYPED_TEST(FlightSQLODBCTestBase, TestSQLSetStmtAttrCursorScrollable) { + // TODO HY092: [Apache Arrow][Flight SQL] (100) Invalid attribute: 0 + GTEST_SKIP(); + this->connect(); + + // Both 0 or 1 are + validateSetStmtAttr(this->stmt, SQL_ATTR_CURSOR_SCROLLABLE, + static_cast(SQL_NONSCROLLABLE)); + // validateSetStmtAttr(this->stmt, SQL_ATTR_CURSOR_SCROLLABLE, + // static_cast(SQL_SCROLLABLE)); + + this->disconnect(); +} + +// SQL_UNSPECIFIED = +// It is unspecified what the cursor type is and +// whether cursors on the statement handle make visible the changes made to a result +// set +// by another cursor.Cursors on the statement handle may make visible none, +// some, +// or all such changes.This is the default. +// +// SQL_INSENSITIVE = +// All cursors on the statement handle show the result set without reflecting any +// changes made to it by any other cursor.Insensitive cursors are read - +// only.This corresponds to a static cursor, +// which has a concurrency that is read - only. +// +// SQL_SENSITIVE = +// All cursors on the statement handle make visible all changes made to a result +// set +// by another cursor. +// +// case SQL_ATTR_CURSOR_SENSITIVITY: +// CheckIfAttributeIsSetToOnlyValidValue(value, static_cast(SQL_UNSPECIFIED)); +// +TYPED_TEST(FlightSQLODBCTestBase, TestSQLSetStmtAttrCursorSensitivity) { + // TODO HY092: [Apache Arrow][Flight SQL] (100) Invalid attribute: 0 + GTEST_SKIP(); + this->connect(); + + validateSetStmtAttr(this->stmt, SQL_ATTR_CURSOR_SENSITIVITY, + static_cast(SQL_UNSPECIFIED)); + + this->disconnect(); +} + +// SQL_CURSOR_FORWARD_ONLY = The cursor only scrolls forward. +// +// SQL_CURSOR_STATIC = The data in the result set is static. +// +// SQL_CURSOR_KEYSET_DRIVEN = The driver saves and uses the keys for the number of rows +// specified in the SQL_ATTR_KEYSET_SIZE statement attribute. +// +// SQL_CURSOR_DYNAMIC = The driver saves and uses only the keys for the rows in the +// rowset. +// +// The default value is SQL_CURSOR_FORWARD_ONLY. This attribute cannot be specified after +// the SQL statement has been prepared. +// +// +// If the specified cursor type is not supported by the data source, +// the driver substitutes a different cursor type and returns +// SQLSTATE 01S02(Option value changed) +// .For a mixed +// or dynamic cursor, +// the driver substitutes, in order, +// a keyset - driven or static cursor.For a keyset - driven cursor, +// the driver substitutes a static cursor. +// +// case SQL_ATTR_CURSOR_SENSITIVITY: +// CheckIfAttributeIsSetToOnlyValidValue(value, static_cast(SQL_UNSPECIFIED)); +// +TYPED_TEST(FlightSQLODBCTestBase, TestSQLSetStmtAttrCursorType) { + // TODO HY024: [Microsoft][ODBC Driver Manager] Invalid argument value + GTEST_SKIP(); + this->connect(); + + validateSetStmtAttr(this->stmt, SQL_ATTR_CURSOR_TYPE, + static_cast(SQL_CURSOR_FORWARD_ONLY)); + + this->disconnect(); +} + +// SQL_TRUE = Turns on automatic population of the IPD after a call to +// SQLPrepare.SQL_FALSE = +// Turns off automatic population of the IPD after a call to SQLPrepare +// .(An application can still obtain IPD field information by calling +// SQLDescribeParam, +// if supported.)The default value of the statement attribute +// SQL_ATTR_ENABLE_AUTO_IPD is SQL_FALSE. +// +// case SQL_ATTR_ENABLE_AUTO_IPD: +// CheckIfAttributeIsSetToOnlyValidValue(value, static_cast(SQL_FALSE)); +// +TYPED_TEST(FlightSQLODBCTestBase, TestSQLSetStmtAttrEnableAutoIPD) { + // TODO HY024: [Microsoft][ODBC Driver Manager] Invalid argument value + GTEST_SKIP(); + this->connect(); + + validateSetStmtAttr(this->stmt, SQL_ATTR_ENABLE_AUTO_IPD, + static_cast(SQL_FALSE)); + + this->disconnect(); +} + +// A SQLLEN *that points to a binary bookmark value.When SQLFetchScroll is called with +// fFetchOrientation equal to SQL_FETCH_BOOKMARK, +// the driver picks up the bookmark value +// from this field.This field defaults to a null pointer.For more information, +// see Scrolling by Bookmark. +// +// case SQL_ATTR_FETCH_BOOKMARK_PTR: +// if (value != NULL) { +// throw DriverException("Optional feature not implemented", "HYC00"); +// } +// +TYPED_TEST(FlightSQLODBCTestBase, TestSQLSetStmtAttrFetchBookmarkPointer) { + // TODO HYC00: [Apache Arrow][Flight SQL] (100) Optional feature not implemented + GTEST_SKIP(); + this->connect(); + + // TODO Passing NULL in does not get recognized as null + validateSetStmtAttr(this->stmt, SQL_ATTR_FETCH_BOOKMARK_PTR, static_cast(NULL)); + + this->disconnect(); +} + +// The handle to the +// IPD.The value of this attribute is the descriptor allocated when the statement was +// initially allocated.The application cannot set this attribute. +// +// This attribute can be retrieved by a call to SQLGetStmtAttr but not set by a call to +// SQLSetStmtAttr. +// +// case SQL_ATTR_IMP_PARAM_DESC: +// throw DriverException("Cannot assign implementation descriptor.", "HY017"); +// +TYPED_TEST(FlightSQLODBCTestBase, TestSQLSetStmtAttrIMPParamDesc) { + // TODO HY017: [Microsoft][ODBC Driver Manager] Invalid use of an + // automatically-allocated descriptor handle + GTEST_SKIP(); + this->connect(); + + validateSetStmtAttr(this->stmt, SQL_ATTR_IMP_PARAM_DESC, static_cast(0)); + + this->disconnect(); +} + +// The handle to the +// IRD.The value of this attribute is the descriptor allocated when the statement was +// initially allocated.The application cannot set this attribute. +// +// This attribute can be retrieved by a call to SQLGetStmtAttr but not set by a call to +// SQLSetStmtAttr. +// +// case SQL_ATTR_IMP_ROW_DESC: +// throw DriverException("Cannot assign implementation descriptor.", "HY017"); +TYPED_TEST(FlightSQLODBCTestBase, TestSQLSetStmtAttrIMPRowDesc) { + // TODO HY017: [Microsoft][ODBC Driver Manager] Invalid use of an + // automatically-allocated descriptor handle + GTEST_SKIP(); + this->connect(); + + validateSetStmtAttr(this->stmt, SQL_ATTR_IMP_ROW_DESC, static_cast(0)); + + this->disconnect(); +} + +TYPED_TEST(FlightSQLODBCTestBase, TestSQLSetStmtAttrKeysetSizeUnsupported) { + this->connect(); + + // Optional feature not implemented + validateSetStmtAttrErrorCode(this->stmt, SQL_ATTR_KEYSET_SIZE, 0, error_state_HYC00); + + this->disconnect(); +} + +// An SQLULEN value that specifies the maximum amount of data that the driver returns from +// a +// character or +// binary column.If ValuePtr is less than the length of the available data, +// SQLFetch or SQLGetData truncates the data and returns SQL_SUCCESS.If ValuePtr +// is 0(the default), +// the driver attempts to return all available data. +// +// If the specified length is less than the minimum amount of data that the data +// source can return or +// greater than the maximum amount of data that the data source can return, +// the driver substitutes that value and returns SQLSTATE 01S02(Option value changed) +// . +// +// The value of this attribute can be set on an open cursor; however, the setting might +// not take effect immediately, in which case the driver will return SQLSTATE 01S02 +// (Option value changed) and reset the attribute to its original value. +// +// This attribute is intended to reduce network traffic and should be supported only when +// the data source (as opposed to the driver) in a multiple-tier driver can implement it. +// This mechanism should not be used by applications to truncate data; to truncate data +// received, an application should specify the maximum buffer length in the BufferLength +// argument in SQLBindCol or SQLGetData. +// +// case SQL_ATTR_MAX_LENGTH: +// SetAttribute(value, attributeToWrite); +// successfully_written = +// m_spiStatement->SetAttribute(Statement::MAX_LENGTH, attributeToWrite); +// +TYPED_TEST(FlightSQLODBCTestBase, TestSQLSetStmtAttrMaxLength) { + // TODO 01S02: [Apache Arrow][Flight SQL] (1000000) Optional value changed. + GTEST_SKIP(); + this->connect(); + + validateSetStmtAttr(this->stmt, SQL_ATTR_MAX_LENGTH, static_cast(1)); + + this->disconnect(); +} + +// An SQLULEN value corresponding to the maximum number of rows to return to the +// application for a SELECT statement. If *ValuePtr equals 0 (the default), the driver +// returns all rows. +// +// This attribute is intended to reduce network traffic. Conceptually, it is applied when +// the result set is created and limits the result set to the first ValuePtr rows. If the +// number of rows in the result set is greater than ValuePtr, the result set is truncated. +// +// SQL_ATTR_MAX_ROWS applies to all result sets on the Statement, including those returned +// by catalog functions. SQL_ATTR_MAX_ROWS establishes a maximum for the value of the +// cursor row count. +// +// A driver should not emulate SQL_ATTR_MAX_ROWS behavior for SQLFetch or SQLFetchScroll +// (if result set size limitations cannot be implemented at the data source) if it cannot +// guarantee that SQL_ATTR_MAX_ROWS will be implemented properly. +// +// It is driver-defined whether SQL_ATTR_MAX_ROWS applies to statements other than SELECT +// statements (such as catalog functions). +// +// The value of this attribute can be set on an open cursor; however, the setting might +// not take effect immediately, in which case the driver will return SQLSTATE 01S02 +// (Option value changed) and reset the attribute to its original value. +// +// case SQL_ATTR_MAX_ROWS: +// throw DriverException("Cannot set read-only attribute", "HY092"); +// +TYPED_TEST(FlightSQLODBCTestBase, TestSQLSetStmtAttrMaxRows) { + this->connect(); + + // TODO HY092: [Apache Arrow][Flight SQL] (100) Cannot set read-only attribute + // validateSetStmtAttr(this->stmt, SQL_ATTR_MAX_ROWS, static_cast(0)); + // + // Cannot set read-only attribute + validateSetStmtAttrErrorCode(this->stmt, SQL_ATTR_MAX_ROWS, static_cast(0), + error_state_HY092); + + this->disconnect(); +} + +// An SQLULEN value that determines how the string arguments of catalog functions are +// treated. +// +// If SQL_TRUE, the string argument of catalog functions are treated as identifiers. The +// case is not significant. For nondelimited strings, the driver removes any trailing +// spaces and the string is folded to uppercase. For delimited strings, the driver removes +// any leading or trailing spaces and takes whatever is between the delimiters literally. +// If one of these arguments is set to a null pointer, the function returns SQL_ERROR and +// SQLSTATE HY009 (Invalid use of null pointer). +// +// If SQL_FALSE, the string arguments of catalog functions are not treated as identifiers. +// The case is significant. They can either contain a string search pattern or not, +// depending on the argument. +// +// The default value is SQL_FALSE. +// +// The TableType argument of SQLTables, which takes a list of values, is not affected by +// this attribute. +// +// SQL_ATTR_METADATA_ID can also be set on the connection level. (It and +// SQL_ATTR_ASYNC_ENABLE are the only statement attributes that are also connection +// attributes.) +// +TYPED_TEST(FlightSQLODBCTestBase, TestSQLSetStmtAttrMetadataID) { + // TODO HY024: [Microsoft][ODBC Driver Manager] Invalid argument value + GTEST_SKIP(); + this->connect(); + + // validateSetStmtAttr(this->stmt, SQL_ATTR_METADATA_ID, + // static_cast(SQL_TRUE)); + validateSetStmtAttr(this->stmt, SQL_ATTR_METADATA_ID, static_cast(SQL_FALSE)); + + this->disconnect(); +} + +// An SQLULEN value that indicates whether the driver should scan SQL strings for escape +// sequences: +// +// SQL_NOSCAN_OFF = The driver scans SQL strings for escape sequences (the default). +// +// SQL_NOSCAN_ON = The driver does not scan SQL strings for escape sequences. Instead, the +// driver sends the statement directly to the data source. +// +TYPED_TEST(FlightSQLODBCTestBase, TestSQLSetStmtAttrNoscan) { + // TODO HY024: [Microsoft][ODBC Driver Manager] Invalid argument value + GTEST_SKIP(); + this->connect(); + + // validateSetStmtAttr(this->stmt, SQL_ATTR_NOSCAN, + // static_cast(SQL_NOSCAN_ON)); + validateSetStmtAttr(this->stmt, SQL_ATTR_NOSCAN, static_cast(SQL_NOSCAN_OFF)); + + this->disconnect(); +} + +TYPED_TEST(FlightSQLODBCTestBase, TestSQLSetStmtAttrParamBindOffsetPtr) { + this->connect(); + + validateSetStmtAttr(this->stmt, SQL_ATTR_PARAM_BIND_OFFSET_PTR, + static_cast(1)); + + this->disconnect(); +} + +TYPED_TEST(FlightSQLODBCTestBase, TestSQLSetStmtAttrParamBindType) { + this->connect(); + + validateSetStmtAttr(this->stmt, SQL_ATTR_PARAM_BIND_TYPE, + static_cast(SQL_PARAM_BIND_BY_COLUMN)); + + this->disconnect(); +} + +TYPED_TEST(FlightSQLODBCTestBase, TestSQLSetStmtAttrParamOperationPtr) { + this->connect(); + + validateSetStmtAttr(this->stmt, SQL_ATTR_PARAM_OPERATION_PTR, static_cast(0)); + + this->disconnect(); +} + +TYPED_TEST(FlightSQLODBCTestBase, TestSQLSetStmtAttrParamStatusPtr) { + this->connect(); + + validateSetStmtAttr(this->stmt, SQL_ATTR_PARAM_STATUS_PTR, static_cast(0)); + + this->disconnect(); +} + +TYPED_TEST(FlightSQLODBCTestBase, TestSQLSetStmtAttrParamsProcessedPtr) { + this->connect(); + + validateSetStmtAttr(this->stmt, SQL_ATTR_PARAMS_PROCESSED_PTR, static_cast(0)); + + this->disconnect(); +} + +TYPED_TEST(FlightSQLODBCTestBase, TestSQLSetStmtAttrParamsetSize) { + // TODO HY090: [Microsoft][ODBC Driver Manager] Invalid string or buffer length + GTEST_SKIP(); + this->connect(); + + validateSetStmtAttr(this->stmt, SQL_ATTR_PARAMSET_SIZE, static_cast(1)); + + this->disconnect(); +} + +TYPED_TEST(FlightSQLODBCTestBase, TestSQLSetStmtAttrQueryTimeout) { + this->connect(); + + validateSetStmtAttr(this->stmt, SQL_ATTR_QUERY_TIMEOUT, static_cast(1)); + + this->disconnect(); +} + +// An SQLULEN value: +// +// SQL_RD_ON = SQLFetchScroll and, in ODBC 3.x, SQLFetch retrieve data after it positions +// the cursor to the specified location. This is the default. +// +// SQL_RD_OFF = SQLFetchScroll and, in ODBC 3.x, SQLFetch do not retrieve data after it +// positions the cursor. +// +// By setting SQL_RETRIEVE_DATA to SQL_RD_OFF, an application can verify that a row exists +// or retrieve a bookmark for the row without incurring the overhead of retrieving rows. +// For more information, see Scrolling and Fetching Rows. +// +// The value of this attribute can be set on an open cursor; however, the setting might +// not take effect immediately, in which case the driver will return SQLSTATE 01S02 +// (Option value changed) and reset the attribute to its original value. +// +// case SQL_ATTR_RETRIEVE_DATA: +// CheckIfAttributeIsSetToOnlyValidValue(value, static_cast(SQL_TRUE)); +// +TYPED_TEST(FlightSQLODBCTestBase, TestSQLSetStmtAttrRetrieveData) { + // TODO HY024: [Microsoft][ODBC Driver Manager] Invalid argument value + GTEST_SKIP(); + this->connect(); + + // TODO + // validateSetStmtAttr(this->stmt, SQL_ATTR_RETRIEVE_DATA, + // static_cast(SQL_RD_OFF)); + // validateSetStmtAttr(this->stmt, SQL_ATTR_RETRIEVE_DATA, + // static_cast(SQL_RD_OFF)); + validateSetStmtAttr(this->stmt, SQL_ATTR_RETRIEVE_DATA, + static_cast(SQL_RD_ON)); + + this->disconnect(); +} + +TYPED_TEST(FlightSQLODBCTestBase, TestSQLSetStmtAttrRowArraySize) { + this->connect(); + + validateSetStmtAttr(this->stmt, SQL_ATTR_ROW_ARRAY_SIZE, static_cast(1)); + + this->disconnect(); +} + +TYPED_TEST(FlightSQLODBCTestBase, TestSQLSetStmtAttrRowBindOffsetPtr) { + this->connect(); + + validateSetStmtAttr(this->stmt, SQL_ATTR_ROW_BIND_OFFSET_PTR, static_cast(0)); + + this->disconnect(); +} + +TYPED_TEST(FlightSQLODBCTestBase, TestSQLSetStmtAttrRowBindType) { + this->connect(); + + validateSetStmtAttr(this->stmt, SQL_ATTR_ROW_BIND_TYPE, static_cast(0)); + + this->disconnect(); +} + +TYPED_TEST(FlightSQLODBCTestBase, TestSQLSetStmtAttrRowNumber) { + this->connect(); + + // Cannot set read-only attribute + validateSetStmtAttrErrorCode(this->stmt, SQL_ATTR_ROW_NUMBER, static_cast(0), + error_state_HY092); + + this->disconnect(); +} + +// An SQLUSMALLINT * value that points to an array of SQLUSMALLINT values used to ignore a +// row during a bulk operation using SQLSetPos. Each value is set to either +// SQL_ROW_PROCEED (for the row to be included in the bulk operation) or SQL_ROW_IGNORE +// (for the row to be excluded from the bulk operation). (Rows cannot be ignored by using +// this array during calls to SQLBulkOperations.) +// +// This statement attribute can be set to a null pointer, in which case the driver does +// not return row status values. This attribute can be set at any time, but the new value +// is not used until the next time SQLSetPos is called. +// +TYPED_TEST(FlightSQLODBCTestBase, TestSQLSetStmtAttrRowOperationPtr) { + this->connect(); + + // TODO Can be set to null pointer + validateSetStmtAttr(this->stmt, SQL_ATTR_ROW_OPERATION_PTR, static_cast(0)); + + this->disconnect(); +} + +// An SQLUSMALLINT * value that points to an array of SQLUSMALLINT values containing row +// status values after a call to SQLFetch or SQLFetchScroll. The array has as many +// elements as there are rows in the rowset. +// +// This statement attribute can be set to a null pointer, in which case the driver does +// not return row status values. This attribute can be set at any time, but the new value +// is not used until the next time SQLBulkOperations, SQLFetch, SQLFetchScroll, or +// SQLSetPos is called. +// +// For more information, see Number of Rows Fetched and Status. +// +// Setting this statement attribute sets the SQL_DESC_ARRAY_STATUS_PTR field in the IRD +// header. +// +TYPED_TEST(FlightSQLODBCTestBase, TestSQLSetStmtAttrRowStatusPtr) { + this->connect(); + + // TODO Can be set to null pointer + validateSetStmtAttr(this->stmt, SQL_ATTR_ROW_STATUS_PTR, static_cast(0)); + + this->disconnect(); +} + +TYPED_TEST(FlightSQLODBCTestBase, TestSQLSetStmtAttrRowsFetchedPtr) { + this->connect(); + + validateSetStmtAttr(this->stmt, SQL_ATTR_ROWS_FETCHED_PTR, static_cast(0)); + + this->disconnect(); +} + +// An SQLULEN value that specifies whether drivers that simulate positioned update +// and delete statements guarantee that such statements affect only one single row. +// +// To simulate positioned update and delete statements, +// most drivers construct a searched UPDATE or +// DELETE statement containing a WHERE clause that specifies the value of each +// column +// in the current row.Unless these columns make up a unique key, +// such a statement can affect more than one row. +// +// To guarantee that such statements affect only one row, +// the driver determines the columns in a unique key and adds these columns to the +// result +// set.If an application guarantees that the columns in the result set make up a +// unique key, +// the driver is not required to do so.This may reduce execution time. +// +// SQL_SC_NON_UNIQUE = The driver does not guarantee that simulated positioned update +// or +// delete statements will affect only one row; it is the +// application's responsibility to do so. If a statement affects +// more than one row, SQLExecute, SQLExecDirect, or SQLSetPos +// returns SQLSTATE 01001 (Cursor operation conflict). +// +// SQL_SC_TRY_UNIQUE = The driver attempts to guarantee that simulated positioned update +// or delete statements affect only one row. The driver always executes such statements, +// even if they might affect more than one row, such as when there is no unique key. If a +// statement affects more than one row, SQLExecute, SQLExecDirect, or SQLSetPos returns +// SQLSTATE 01001 (Cursor operation conflict). +// +// SQL_SC_UNIQUE = The driver guarantees that simulated positioned update or delete +// statements affect only one row. If the driver cannot guarantee this for a given +// statement, SQLExecDirect or SQLPrepare returns an error. +// +// If the data source provides native SQL support for positioned update and delete +// statements and the driver does not simulate cursors, SQL_SUCCESS is returned when +// SQL_SC_UNIQUE is requested for SQL_SIMULATE_CURSOR. SQL_SUCCESS_WITH_INFO is returned +// if SQL_SC_TRY_UNIQUE or SQL_SC_NON_UNIQUE is requested. If the data source provides the +// SQL_SC_TRY_UNIQUE level of support and the driver does not, SQL_SUCCESS is returned for +// SQL_SC_TRY_UNIQUE and SQL_SUCCESS_WITH_INFO is returned for SQL_SC_NON_UNIQUE. +// +// If the specified cursor simulation type is not supported by the data source, the driver +// substitutes a different simulation type and returns SQLSTATE 01S02 (Option value +// changed). For SQL_SC_UNIQUE, the driver substitutes, in order, SQL_SC_TRY_UNIQUE or +// SQL_SC_NON_UNIQUE. For SQL_SC_TRY_UNIQUE, the driver substitutes SQL_SC_NON_UNIQUE. +// +// The default is SQL_SC_UNIQUE. +// +TYPED_TEST(FlightSQLODBCTestBase, TestSQLSetStmtAttrSimulateCursor) { + // TODO HY024: [Microsoft][ODBC Driver Manager] Invalid argument value + GTEST_SKIP(); + this->connect(); + + validateSetStmtAttr(this->stmt, SQL_ATTR_SIMULATE_CURSOR, + static_cast(SQL_SC_UNIQUE)); + + this->disconnect(); +} + +// An SQLULEN value that specifies whether an application will use bookmarks with a +// cursor: +// +// SQL_UB_OFF = Off (the default) +// +// SQL_UB_VARIABLE = An application will use bookmarks with a cursor, and the driver will +// provide variable-length bookmarks if they are supported. SQL_UB_FIXED is deprecated in +// ODBC 3.x. ODBC 3.x applications should always use variable-length bookmarks, even when +// working with ODBC 2.x drivers (which supported only 4-byte, fixed-length bookmarks). +// This is because a fixed-length bookmark is just a special case of a variable-length +// bookmark. When working with an ODBC 2.x driver, the Driver Manager maps SQL_UB_VARIABLE +// to SQL_UB_FIXED. +// +// To use bookmarks with a cursor, the application must specify this attribute with the +// SQL_UB_VARIABLE value before opening the cursor. +// +// case SQL_ATTR_USE_BOOKMARKS: +// CheckIfAttributeIsSetToOnlyValidValue(value, static_cast(SQL_UB_OFF)); +// +TYPED_TEST(FlightSQLODBCTestBase, TestSQLSetStmtAttrUseBookmarks) { + // TODO HY024: [Microsoft][ODBC Driver Manager] Invalid argument value + GTEST_SKIP(); + this->connect(); + + validateSetStmtAttr(this->stmt, SQL_ATTR_USE_BOOKMARKS, + static_cast(SQL_UB_OFF)); + + this->disconnect(); +} + +// TODO This is not a standard SQL_ATTR type parameter +TYPED_TEST(FlightSQLODBCTestBase, TestSQLSetStmtAttrRowsetSize) { + // TODO HY090: [Microsoft][ODBC Driver Manager] Invalid string or buffer length + GTEST_SKIP(); + this->connect(); + + validateSetStmtAttr(this->stmt, SQL_ROWSET_SIZE, static_cast(1)); + + this->disconnect(); +} + +} // namespace arrow::flight::sql::odbc From a5735bf72ec7de4f16e115f30da3befbf2cc3471 Mon Sep 17 00:00:00 2001 From: rscales Date: Tue, 8 Jul 2025 17:36:06 +0100 Subject: [PATCH 04/18] Change order to have log as first line in method --- cpp/src/arrow/flight/sql/odbc/odbc_api.cc | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/cpp/src/arrow/flight/sql/odbc/odbc_api.cc b/cpp/src/arrow/flight/sql/odbc/odbc_api.cc index 884c0888c610..5a7fa727f924 100644 --- a/cpp/src/arrow/flight/sql/odbc/odbc_api.cc +++ b/cpp/src/arrow/flight/sql/odbc/odbc_api.cc @@ -873,12 +873,11 @@ SQLRETURN SQLGetInfo(SQLHDBC conn, SQLUSMALLINT infoType, SQLPOINTER infoValuePt SQLRETURN SQLGetStmtAttr(SQLHSTMT stmt, SQLINTEGER attribute, SQLPOINTER valuePtr, SQLINTEGER bufferLength, SQLINTEGER* stringLengthPtr) { - using ODBC::ODBCStatement; - LOG_DEBUG( "SQLGetStmtAttrW called with stmt: {}, attribute: {}, valuePtr: {}, " "bufferLength: {}, stringLengthPtr: {}", stmt, attribute, valuePtr, bufferLength, fmt::ptr(stringLengthPtr)); + using ODBC::ODBCStatement; return ODBCStatement::ExecuteWithDiagnostics(stmt, SQL_ERROR, [=]() { ODBCStatement* statement = reinterpret_cast(stmt); @@ -892,12 +891,11 @@ SQLRETURN SQLGetStmtAttr(SQLHSTMT stmt, SQLINTEGER attribute, SQLPOINTER valuePt SQLRETURN SQLSetStmtAttr(SQLHSTMT stmt, SQLINTEGER attribute, SQLPOINTER valuePtr, SQLINTEGER stringLength) { - using ODBC::ODBCStatement; - LOG_DEBUG( "SQLSetStmtAttrW called with stmt: {}, attribute: {}, valuePtr: {}, " "stringLength: {}", stmt, attribute, valuePtr, stringLength); + using ODBC::ODBCStatement; return ODBCStatement::ExecuteWithDiagnostics(stmt, SQL_ERROR, [=]() { ODBCStatement* statement = reinterpret_cast(stmt); From 6f3e58aef9af193fa2c9dcf10dd3564f6b6ea939 Mon Sep 17 00:00:00 2001 From: rscales Date: Tue, 8 Jul 2025 22:58:34 +0100 Subject: [PATCH 05/18] Fix outstanding issues from test cases --- .../odbc_impl/odbc_statement.cc | 2 +- .../flight/sql/odbc/tests/odbc_test_suite.h | 1 + .../sql/odbc/tests/statement_attr_test.cc | 564 ++---------------- 3 files changed, 48 insertions(+), 519 deletions(-) diff --git a/cpp/src/arrow/flight/sql/odbc/odbcabstraction/odbc_impl/odbc_statement.cc b/cpp/src/arrow/flight/sql/odbc/odbcabstraction/odbc_impl/odbc_statement.cc index 8b507fdb64e8..bb73f9ac1069 100644 --- a/cpp/src/arrow/flight/sql/odbc/odbcabstraction/odbc_impl/odbc_statement.cc +++ b/cpp/src/arrow/flight/sql/odbc/odbcabstraction/odbc_impl/odbc_statement.cc @@ -458,7 +458,7 @@ SQLRETURN ODBCStatement::GetStmtAttr(SQLINTEGER statementAttribute, SQLPOINTER o return SQL_SUCCESS; case SQL_ATTR_FETCH_BOOKMARK_PTR: - GetAttribute(static_cast(NULL), output, bufferSize, strLenPtr); + GetAttribute(static_cast(NULL), output, bufferSize, strLenPtr); return SQL_SUCCESS; case SQL_ATTR_KEYSET_SIZE: diff --git a/cpp/src/arrow/flight/sql/odbc/tests/odbc_test_suite.h b/cpp/src/arrow/flight/sql/odbc/tests/odbc_test_suite.h index 30017b019373..1dd71e12f737 100644 --- a/cpp/src/arrow/flight/sql/odbc/tests/odbc_test_suite.h +++ b/cpp/src/arrow/flight/sql/odbc/tests/odbc_test_suite.h @@ -168,6 +168,7 @@ static constexpr std::string_view error_state_HY024 = "HY024"; static constexpr std::string_view error_state_HY092 = "HY092"; static constexpr std::string_view error_state_HYC00 = "HYC00"; static constexpr std::string_view error_state_HY114 = "HY114"; +static constexpr std::string_view error_state_HY017 = "HY017"; static constexpr std::string_view error_state_HY118 = "HY118"; /// Verify ODBC Error State diff --git a/cpp/src/arrow/flight/sql/odbc/tests/statement_attr_test.cc b/cpp/src/arrow/flight/sql/odbc/tests/statement_attr_test.cc index f4bc05d0e2f3..dfe29e970d47 100644 --- a/cpp/src/arrow/flight/sql/odbc/tests/statement_attr_test.cc +++ b/cpp/src/arrow/flight/sql/odbc/tests/statement_attr_test.cc @@ -33,43 +33,33 @@ namespace arrow::flight::sql::odbc { // Helper Functions // Validate unsigned length SQLULEN return value -void validateGetStmtAttr(SQLHSTMT statement, SQLUSMALLINT attribute, +void validateGetStmtAttr(SQLHSTMT statement, SQLINTEGER attribute, SQLULEN expected_value) { SQLULEN value = 0; SQLINTEGER stringLengthPtr; SQLRETURN ret = SQLGetStmtAttr(statement, attribute, &value, 0, &stringLengthPtr); - if (ret != SQL_SUCCESS) { - // TODO remove later - std::cerr << GetOdbcErrorMessage(SQL_HANDLE_STMT, statement) << std::endl; - } - EXPECT_EQ(ret, SQL_SUCCESS); EXPECT_EQ(value, expected_value); } // Validate unsigned length SQLULEN return value is greater than -void validateGetStmtAttrGreaterThan(SQLHSTMT statement, SQLUSMALLINT attribute, +void validateGetStmtAttrGreaterThan(SQLHSTMT statement, SQLINTEGER attribute, SQLULEN compared_value) { SQLULEN value = 0; SQLINTEGER stringLengthPtr; SQLRETURN ret = SQLGetStmtAttr(statement, attribute, &value, 0, &stringLengthPtr); - if (ret != SQL_SUCCESS) { - // TODO remove later - std::cerr << GetOdbcErrorMessage(SQL_HANDLE_STMT, statement) << std::endl; - } - EXPECT_EQ(ret, SQL_SUCCESS); EXPECT_GT(value, compared_value); } // Validate error return value and code -void validateGetStmtAttrErrorCode(SQLHSTMT statement, SQLUSMALLINT attribute, +void validateGetStmtAttrErrorCode(SQLHSTMT statement, SQLINTEGER attribute, std::string_view error_code) { SQLULEN value = 0; SQLINTEGER stringLengthPtr; @@ -78,45 +68,35 @@ void validateGetStmtAttrErrorCode(SQLHSTMT statement, SQLUSMALLINT attribute, EXPECT_EQ(ret, SQL_ERROR); - if (ret != SQL_SUCCESS) { - // TODO remove later - std::cerr << GetOdbcErrorMessage(SQL_HANDLE_STMT, statement) << std::endl; - } - VerifyOdbcErrorState(SQL_HANDLE_STMT, statement, error_code); } // Validate unsigned length SQLULEN return value -void validateSetStmtAttr(SQLHSTMT statement, SQLUSMALLINT attribute, SQLULEN new_value) { +void validateSetStmtAttr(SQLHSTMT statement, SQLINTEGER attribute, SQLULEN new_value) { SQLINTEGER stringLengthPtr = sizeof(SQLULEN); - SQLRETURN ret = SQLSetStmtAttr(statement, attribute, &new_value, stringLengthPtr); - - if (ret != SQL_SUCCESS) { - // TODO remove later - std::cerr << GetOdbcErrorMessage(SQL_HANDLE_STMT, statement) << std::endl; - } + SQLRETURN ret = SQLSetStmtAttr( + statement, attribute, reinterpret_cast(new_value), + stringLengthPtr); EXPECT_EQ(ret, SQL_SUCCESS); } // Validate error return value and code -void validateSetStmtAttrErrorCode(SQLHSTMT statement, SQLUSMALLINT attribute, +void validateSetStmtAttrErrorCode(SQLHSTMT statement, SQLINTEGER attribute, SQLULEN new_value, std::string_view error_code) { - // SQLINTEGER stringLengthPtr; - - SQLRETURN ret = SQLSetStmtAttr(statement, attribute, &new_value, 0); + //SQLINTEGER stringLengthPtr = sizeof(SQLULEN); - if (ret != SQL_SUCCESS) { - // TODO remove later - std::cerr << GetOdbcErrorMessage(SQL_HANDLE_STMT, statement) << std::endl; - } + SQLRETURN ret = SQLSetStmtAttr( + statement, attribute, reinterpret_cast(new_value), 0); EXPECT_EQ(ret, SQL_ERROR); VerifyOdbcErrorState(SQL_HANDLE_STMT, statement, error_code); } +// Test Cases + TYPED_TEST(FlightSQLODBCTestBase, TestSQLGetStmtAttrAppParamDesc) { this->connect(); @@ -144,11 +124,6 @@ TYPED_TEST(FlightSQLODBCTestBase, TestSQLGetStmtAttrAsyncEnable) { this->disconnect(); } -// #ifdef SQL_ATTR_ASYNC_STMT_EVENT -// case SQL_ATTR_ASYNC_STMT_EVENT: -// throw DriverException("Unsupported attribute", "HYC00"); -// #endif -// #ifdef SQL_ATTR_ASYNC_STMT_EVENT TYPED_TEST(FlightSQLODBCTestBase, TestSQLGetStmtAttrAsyncStmtEventUnsupported) { this->connect(); @@ -160,11 +135,6 @@ TYPED_TEST(FlightSQLODBCTestBase, TestSQLGetStmtAttrAsyncStmtEventUnsupported) { } #endif -// #ifdef SQL_ATTR_ASYNC_STMT_PCALLBACK -// case SQL_ATTR_ASYNC_STMT_PCALLBACK: -// throw DriverException("Unsupported attribute", "HYC00"); -// #endif -// #ifdef SQL_ATTR_ASYNC_STMT_PCALLBACK TYPED_TEST(FlightSQLODBCTestBase, TestSQLGetStmtAttrAsyncStmtPCCallbackUnsupported) { this->connect(); @@ -177,11 +147,6 @@ TYPED_TEST(FlightSQLODBCTestBase, TestSQLGetStmtAttrAsyncStmtPCCallbackUnsupport } #endif -// #ifdef SQL_ATTR_ASYNC_STMT_PCONTEXT -// case SQL_ATTR_ASYNC_STMT_PCONTEXT: -// throw DriverException("Unsupported attribute", "HYC00"); -// #endif -// #ifdef SQL_ATTR_ASYNC_STMT_PCONTEXT TYPED_TEST(FlightSQLODBCTestBase, TestSQLGetStmtAttrAsyncStmtPCContextUnsupported) { this->connect(); @@ -203,12 +168,7 @@ TYPED_TEST(FlightSQLODBCTestBase, TestSQLGetStmtAttrConcurrency) { this->disconnect(); } -// case SQL_ATTR_CURSOR_SCROLLABLE: -// GetAttribute(static_cast(SQL_NONSCROLLABLE), output, bufferSize, strLenPtr); -// TYPED_TEST(FlightSQLODBCTestBase, TestSQLGetStmtAttrCursorScrollable) { - // TODO HY092: [Apache Arrow][Flight SQL] (100) Invalid statement attribute: 65535 - GTEST_SKIP(); this->connect(); validateGetStmtAttr(this->stmt, SQL_ATTR_CURSOR_SCROLLABLE, @@ -217,12 +177,7 @@ TYPED_TEST(FlightSQLODBCTestBase, TestSQLGetStmtAttrCursorScrollable) { this->disconnect(); } -// case SQL_ATTR_CURSOR_SENSITIVITY: -// GetAttribute(static_cast(SQL_UNSPECIFIED), output, bufferSize, strLenPtr); -// TYPED_TEST(FlightSQLODBCTestBase, TestSQLGetStmtAttrCursorSensitivity) { - // TODO HY092: [Apache Arrow][Flight SQL] (100) Invalid statement attribute: 65534 - GTEST_SKIP(); this->connect(); validateGetStmtAttr(this->stmt, SQL_ATTR_CURSOR_SENSITIVITY, @@ -252,10 +207,6 @@ TYPED_TEST(FlightSQLODBCTestBase, TestSQLGetStmtAttrEnableAutoIPD) { TYPED_TEST(FlightSQLODBCTestBase, TestSQLGetStmtAttrFetchBookmarkPointer) { this->connect(); - // TODO static_cast(NULL) does not compile, but was used as default in - // odbc_statement.cc - // validateGetStmtAttr(this->stmt, SQL_ATTR_FETCH_BOOKMARK_PTR, - // static_cast(NULL)); validateGetStmtAttr(this->stmt, SQL_ATTR_FETCH_BOOKMARK_PTR, static_cast(NULL)); this->disconnect(); @@ -410,20 +361,19 @@ TYPED_TEST(FlightSQLODBCTestBase, TestSQLGetStmtAttrRowBindType) { this->disconnect(); } -// An SQLULEN value that is the number of the current row in the entire result set.If the -// number of the current row cannot be determined or -// there is no current row, -// the driver returns 0. -// -// This attribute can be retrieved by a call to SQLGetStmtAttr but not set by a call to -// SQLSetStmtAttr. -// TYPED_TEST(FlightSQLODBCTestBase, TestSQLGetStmtAttrRowNumber) { - // TODO 24000: [Microsoft][ODBC Driver Manager] Invalid cursor state GTEST_SKIP(); this->connect(); - validateGetStmtAttr(this->stmt, SQL_ATTR_ROW_NUMBER, static_cast(0)); + std::wstring wsql = L"SELECT 1;"; + std::vector sql0(wsql.begin(), wsql.end()); + + SQLRETURN ret = SQLExecDirect(this->stmt, &sql0[0], static_cast(sql0.size())); + + EXPECT_EQ(ret, SQL_SUCCESS); + + // TODO 24000: [Microsoft][ODBC Driver Manager] Invalid cursor state + //validateGetStmtAttr(this->stmt, SQL_ATTR_ROW_NUMBER, static_cast(0)); this->disconnect(); } @@ -470,7 +420,7 @@ TYPED_TEST(FlightSQLODBCTestBase, TestSQLGetStmtAttrUseBookmarks) { this->disconnect(); } -// TODO This is not a standard SQL_ATTR type parameter +// This is a pre ODBC 3 attribute TYPED_TEST(FlightSQLODBCTestBase, TestSQLGetStmtAttrRowsetSize) { this->connect(); @@ -479,23 +429,7 @@ TYPED_TEST(FlightSQLODBCTestBase, TestSQLGetStmtAttrRowsetSize) { this->disconnect(); } -// The handle to the APD for subsequent calls to SQLExecute and SQLExecDirect on the -// statement handle. The initial value of this attribute is the descriptor implicitly -// allocated when the statement was initially allocated. If the value of this attribute is -// set to SQL_NULL_DESC or the handle originally allocated for the descriptor, an -// explicitly allocated APD handle that was previously associated with the statement -// handle is dissociated from it and the statement handle reverts to the implicitly -// allocated APD handle. -// -// This attribute cannot be set to a descriptor handle that was implicitly allocated for -// another statement or to another descriptor handle that was implicitly set on the same -// statement; implicitly allocated descriptor handles cannot be associated with more than -// one -// statement or -// descriptor handle. -// TYPED_TEST(FlightSQLODBCTestBase, TestSQLSetStmtAttrAppParamDesc) { - // GTEST_SKIP(); SQLULEN app_param_desc = 0; SQLINTEGER stringLengthPtr; this->connect(); @@ -505,32 +439,15 @@ TYPED_TEST(FlightSQLODBCTestBase, TestSQLSetStmtAttrAppParamDesc) { EXPECT_EQ(ret, SQL_SUCCESS); - // TODO SQL_NULL_DESC not found - // setStmtAttr(this->stmt, SQL_ATTR_APP_PARAM_DESC, - // static_cast(SQL_NULL_DESC)); setStmtAttr(this->stmt, - // SQL_ATTR_APP_PARAM_DESC, static_cast(0)); + validateSetStmtAttr(this->stmt, SQL_ATTR_APP_PARAM_DESC, static_cast(0)); - // validateSetStmtAttr(this->stmt, SQL_ATTR_APP_PARAM_DESC, - // static_cast(app_param_desc)); + validateSetStmtAttr(this->stmt, SQL_ATTR_APP_PARAM_DESC, + static_cast(app_param_desc)); this->disconnect(); } -// The handle to the ARD for subsequent fetches on the statement handle. The initial value -// of this attribute is the descriptor implicitly allocated when the statement was -// initially allocated. If the value of this attribute is set to SQL_NULL_DESC or the -// handle originally allocated for the descriptor, an explicitly allocated ARD handle that -// was previously associated with the statement handle is dissociated from it and the -// statement handle reverts to the implicitly allocated ARD handle. -// -// This attribute cannot be set to a descriptor handle that was implicitly allocated for -// another statement or to another descriptor handle that was implicitly set on the same -// statement; implicitly allocated descriptor handles cannot be associated with more than -// one -// statement or descriptor handle. -// TYPED_TEST(FlightSQLODBCTestBase, TestSQLSetStmtAttrAppRowDesc) { - // GTEST_SKIP(); SQLULEN app_row_desc = 0; SQLINTEGER stringLengthPtr; this->connect(); @@ -540,23 +457,16 @@ TYPED_TEST(FlightSQLODBCTestBase, TestSQLSetStmtAttrAppRowDesc) { EXPECT_EQ(ret, SQL_SUCCESS); - // TODO SQL_NULL_DESC not found - // setStmtAttr(this->stmt, SQL_ATTR_APP_ROW_DESC, static_cast(SQL_NULL_DESC)); - // setStmtAttr(this->stmt, SQL_ATTR_APP_ROW_DESC, static_cast(0)); + validateSetStmtAttr(this->stmt, SQL_ATTR_APP_ROW_DESC, static_cast(0)); - // validateSetStmtAttr(this->stmt, SQL_ATTR_APP_ROW_DESC, - // static_cast(app_row_desc)); + validateSetStmtAttr(this->stmt, SQL_ATTR_APP_ROW_DESC, + static_cast(app_row_desc)); this->disconnect(); } -// case SQL_ATTR_ASYNC_ENABLE: -// throw DriverException("Unsupported attribute", "HYC00"); -// #ifdef SQL_ATTR_ASYNC_ENABLE TYPED_TEST(FlightSQLODBCTestBase, TestSQLSetStmtAttrAsyncEnableUnsupported) { - // TODO HY024: [Microsoft][ODBC Driver Manager] Invalid argument value - GTEST_SKIP(); this->connect(); // Optional feature not implemented @@ -567,36 +477,21 @@ TYPED_TEST(FlightSQLODBCTestBase, TestSQLSetStmtAttrAsyncEnableUnsupported) { } #endif -// #ifdef SQL_ATTR_ASYNC_STMT_EVENT -// case SQL_ATTR_ASYNC_STMT_EVENT: -// throw DriverException("Unsupported attribute", "HYC00"); -// #endif -// + #ifdef SQL_ATTR_ASYNC_STMT_EVENT TYPED_TEST(FlightSQLODBCTestBase, TestSQLSetStmtAttrAsyncStmtEventUnsupported) { - // TODO HY118: [Microsoft][ODBC Driver Manager] Driver does not support asynchronous - // notification - GTEST_SKIP(); this->connect(); - // Optional feature not implemented + // Driver does not support asynchronous notification validateSetStmtAttrErrorCode(this->stmt, SQL_ATTR_ASYNC_STMT_EVENT, 0, - error_state_HYC00); + error_state_HY118); this->disconnect(); } #endif -// TODO -// -// Only the Driver Manager can call a driver's SQLSetStmtAttr function with this -// attribute. -// -// #ifdef SQL_ATTR_ASYNC_STMT_PCALLBACK -// case SQL_ATTR_ASYNC_STMT_PCALLBACK: -// throw DriverException("Unsupported attribute", "HYC00"); -// #endif -// +// TODO Check for windows is not necessary +//#ifndef _WIN32 #ifdef SQL_ATTR_ASYNC_STMT_PCALLBACK TYPED_TEST(FlightSQLODBCTestBase, TestSQLSetStmtAttrAsyncStmtPCCallbackUnsupported) { this->connect(); @@ -607,16 +502,10 @@ TYPED_TEST(FlightSQLODBCTestBase, TestSQLSetStmtAttrAsyncStmtPCCallbackUnsupport this->disconnect(); } #endif +//#endif -// TODO -// Only the Driver Manager can call a driver's SQLSetStmtAttr function with this -// attribute. -// -// #ifdef SQL_ATTR_ASYNC_STMT_PCONTEXT -// case SQL_ATTR_ASYNC_STMT_PCONTEXT: -// throw DriverException("Unsupported attribute", "HYC00"); -// #endif -// +// TODO Check for windows is not necessary +// #ifndef _WIN32 #ifdef SQL_ATTR_ASYNC_STMT_PCONTEXT TYPED_TEST(FlightSQLODBCTestBase, TestSQLSetStmtAttrAsyncStmtPCContextUnsupported) { this->connect(); @@ -628,26 +517,9 @@ TYPED_TEST(FlightSQLODBCTestBase, TestSQLSetStmtAttrAsyncStmtPCContextUnsupporte this->disconnect(); } #endif +//#endif -// SQL_CONCUR_READ_ONLY = Cursor is read-only. No updates are allowed. -// -// SQL_CONCUR_LOCK = Cursor uses the lowest level of locking sufficient to ensure that the -// row can be updated. -// -// SQL_CONCUR_ROWVER = Cursor uses optimistic concurrency control, comparing row versions -// such as SQLBase ROWID or Sybase TIMESTAMP. -// -// SQL_CONCUR_VALUES = Cursor uses optimistic concurrency control, comparing values. -// -// The default value for SQL_ATTR_CONCURRENCY is SQL_CONCUR_READ_ONLY. -// -// case SQL_ATTR_CONCURRENCY: -// CheckIfAttributeIsSetToOnlyValidValue(value, -// static_cast(SQL_CONCUR_READ_ONLY)); -// TYPED_TEST(FlightSQLODBCTestBase, TestSQLSetStmtAttrConcurrency) { - // TODO HY024: [Microsoft][ODBC Driver Manager] Invalid argument value - GTEST_SKIP(); this->connect(); validateSetStmtAttr(this->stmt, SQL_ATTR_CONCURRENCY, @@ -656,58 +528,16 @@ TYPED_TEST(FlightSQLODBCTestBase, TestSQLSetStmtAttrConcurrency) { this->disconnect(); } -// SQL_NONSCROLLABLE = Scrollable cursors are not required on the statement handle.If the -// application calls SQLFetchScroll on this handle, -// the only valid value of FetchOrientation is SQL_FETCH_NEXT.This is the default. -// -// SQL_SCROLLABLE = Scrollable cursors are required on the statement handle.When -// calling -// SQLFetchScroll, -// the application may specify any valid value of FetchOrientation, -// achieving cursor positioning in modes other than the sequential mode. -// -// case SQL_ATTR_CURSOR_SCROLLABLE: -// CheckIfAttributeIsSetToOnlyValidValue(value, static_cast(SQL_NONSCROLLABLE)); -// TYPED_TEST(FlightSQLODBCTestBase, TestSQLSetStmtAttrCursorScrollable) { - // TODO HY092: [Apache Arrow][Flight SQL] (100) Invalid attribute: 0 - GTEST_SKIP(); this->connect(); - // Both 0 or 1 are validateSetStmtAttr(this->stmt, SQL_ATTR_CURSOR_SCROLLABLE, static_cast(SQL_NONSCROLLABLE)); - // validateSetStmtAttr(this->stmt, SQL_ATTR_CURSOR_SCROLLABLE, - // static_cast(SQL_SCROLLABLE)); this->disconnect(); } -// SQL_UNSPECIFIED = -// It is unspecified what the cursor type is and -// whether cursors on the statement handle make visible the changes made to a result -// set -// by another cursor.Cursors on the statement handle may make visible none, -// some, -// or all such changes.This is the default. -// -// SQL_INSENSITIVE = -// All cursors on the statement handle show the result set without reflecting any -// changes made to it by any other cursor.Insensitive cursors are read - -// only.This corresponds to a static cursor, -// which has a concurrency that is read - only. -// -// SQL_SENSITIVE = -// All cursors on the statement handle make visible all changes made to a result -// set -// by another cursor. -// -// case SQL_ATTR_CURSOR_SENSITIVITY: -// CheckIfAttributeIsSetToOnlyValidValue(value, static_cast(SQL_UNSPECIFIED)); -// TYPED_TEST(FlightSQLODBCTestBase, TestSQLSetStmtAttrCursorSensitivity) { - // TODO HY092: [Apache Arrow][Flight SQL] (100) Invalid attribute: 0 - GTEST_SKIP(); this->connect(); validateSetStmtAttr(this->stmt, SQL_ATTR_CURSOR_SENSITIVITY, @@ -716,35 +546,7 @@ TYPED_TEST(FlightSQLODBCTestBase, TestSQLSetStmtAttrCursorSensitivity) { this->disconnect(); } -// SQL_CURSOR_FORWARD_ONLY = The cursor only scrolls forward. -// -// SQL_CURSOR_STATIC = The data in the result set is static. -// -// SQL_CURSOR_KEYSET_DRIVEN = The driver saves and uses the keys for the number of rows -// specified in the SQL_ATTR_KEYSET_SIZE statement attribute. -// -// SQL_CURSOR_DYNAMIC = The driver saves and uses only the keys for the rows in the -// rowset. -// -// The default value is SQL_CURSOR_FORWARD_ONLY. This attribute cannot be specified after -// the SQL statement has been prepared. -// -// -// If the specified cursor type is not supported by the data source, -// the driver substitutes a different cursor type and returns -// SQLSTATE 01S02(Option value changed) -// .For a mixed -// or dynamic cursor, -// the driver substitutes, in order, -// a keyset - driven or static cursor.For a keyset - driven cursor, -// the driver substitutes a static cursor. -// -// case SQL_ATTR_CURSOR_SENSITIVITY: -// CheckIfAttributeIsSetToOnlyValidValue(value, static_cast(SQL_UNSPECIFIED)); -// TYPED_TEST(FlightSQLODBCTestBase, TestSQLSetStmtAttrCursorType) { - // TODO HY024: [Microsoft][ODBC Driver Manager] Invalid argument value - GTEST_SKIP(); this->connect(); validateSetStmtAttr(this->stmt, SQL_ATTR_CURSOR_TYPE, @@ -753,20 +555,7 @@ TYPED_TEST(FlightSQLODBCTestBase, TestSQLSetStmtAttrCursorType) { this->disconnect(); } -// SQL_TRUE = Turns on automatic population of the IPD after a call to -// SQLPrepare.SQL_FALSE = -// Turns off automatic population of the IPD after a call to SQLPrepare -// .(An application can still obtain IPD field information by calling -// SQLDescribeParam, -// if supported.)The default value of the statement attribute -// SQL_ATTR_ENABLE_AUTO_IPD is SQL_FALSE. -// -// case SQL_ATTR_ENABLE_AUTO_IPD: -// CheckIfAttributeIsSetToOnlyValidValue(value, static_cast(SQL_FALSE)); -// TYPED_TEST(FlightSQLODBCTestBase, TestSQLSetStmtAttrEnableAutoIPD) { - // TODO HY024: [Microsoft][ODBC Driver Manager] Invalid argument value - GTEST_SKIP(); this->connect(); validateSetStmtAttr(this->stmt, SQL_ATTR_ENABLE_AUTO_IPD, @@ -775,70 +564,35 @@ TYPED_TEST(FlightSQLODBCTestBase, TestSQLSetStmtAttrEnableAutoIPD) { this->disconnect(); } -// A SQLLEN *that points to a binary bookmark value.When SQLFetchScroll is called with -// fFetchOrientation equal to SQL_FETCH_BOOKMARK, -// the driver picks up the bookmark value -// from this field.This field defaults to a null pointer.For more information, -// see Scrolling by Bookmark. -// -// case SQL_ATTR_FETCH_BOOKMARK_PTR: -// if (value != NULL) { -// throw DriverException("Optional feature not implemented", "HYC00"); -// } -// TYPED_TEST(FlightSQLODBCTestBase, TestSQLSetStmtAttrFetchBookmarkPointer) { - // TODO HYC00: [Apache Arrow][Flight SQL] (100) Optional feature not implemented - GTEST_SKIP(); this->connect(); - // TODO Passing NULL in does not get recognized as null validateSetStmtAttr(this->stmt, SQL_ATTR_FETCH_BOOKMARK_PTR, static_cast(NULL)); this->disconnect(); } -// The handle to the -// IPD.The value of this attribute is the descriptor allocated when the statement was -// initially allocated.The application cannot set this attribute. -// -// This attribute can be retrieved by a call to SQLGetStmtAttr but not set by a call to -// SQLSetStmtAttr. -// -// case SQL_ATTR_IMP_PARAM_DESC: -// throw DriverException("Cannot assign implementation descriptor.", "HY017"); -// TYPED_TEST(FlightSQLODBCTestBase, TestSQLSetStmtAttrIMPParamDesc) { - // TODO HY017: [Microsoft][ODBC Driver Manager] Invalid use of an - // automatically-allocated descriptor handle - GTEST_SKIP(); this->connect(); - validateSetStmtAttr(this->stmt, SQL_ATTR_IMP_PARAM_DESC, static_cast(0)); + // Invalid use of an automatically allocated descriptor handle + validateSetStmtAttrErrorCode(this->stmt, SQL_ATTR_IMP_PARAM_DESC, static_cast(0), error_state_HY017); this->disconnect(); } -// The handle to the -// IRD.The value of this attribute is the descriptor allocated when the statement was -// initially allocated.The application cannot set this attribute. -// -// This attribute can be retrieved by a call to SQLGetStmtAttr but not set by a call to -// SQLSetStmtAttr. -// -// case SQL_ATTR_IMP_ROW_DESC: -// throw DriverException("Cannot assign implementation descriptor.", "HY017"); TYPED_TEST(FlightSQLODBCTestBase, TestSQLSetStmtAttrIMPRowDesc) { - // TODO HY017: [Microsoft][ODBC Driver Manager] Invalid use of an - // automatically-allocated descriptor handle - GTEST_SKIP(); this->connect(); - validateSetStmtAttr(this->stmt, SQL_ATTR_IMP_ROW_DESC, static_cast(0)); + // Invalid use of an automatically allocated descriptor handle + validateSetStmtAttrErrorCode(this->stmt, SQL_ATTR_IMP_ROW_DESC, static_cast(0), + error_state_HY017); this->disconnect(); } TYPED_TEST(FlightSQLODBCTestBase, TestSQLSetStmtAttrKeysetSizeUnsupported) { + GTEST_SKIP(); this->connect(); // Optional feature not implemented @@ -847,77 +601,17 @@ TYPED_TEST(FlightSQLODBCTestBase, TestSQLSetStmtAttrKeysetSizeUnsupported) { this->disconnect(); } -// An SQLULEN value that specifies the maximum amount of data that the driver returns from -// a -// character or -// binary column.If ValuePtr is less than the length of the available data, -// SQLFetch or SQLGetData truncates the data and returns SQL_SUCCESS.If ValuePtr -// is 0(the default), -// the driver attempts to return all available data. -// -// If the specified length is less than the minimum amount of data that the data -// source can return or -// greater than the maximum amount of data that the data source can return, -// the driver substitutes that value and returns SQLSTATE 01S02(Option value changed) -// . -// -// The value of this attribute can be set on an open cursor; however, the setting might -// not take effect immediately, in which case the driver will return SQLSTATE 01S02 -// (Option value changed) and reset the attribute to its original value. -// -// This attribute is intended to reduce network traffic and should be supported only when -// the data source (as opposed to the driver) in a multiple-tier driver can implement it. -// This mechanism should not be used by applications to truncate data; to truncate data -// received, an application should specify the maximum buffer length in the BufferLength -// argument in SQLBindCol or SQLGetData. -// -// case SQL_ATTR_MAX_LENGTH: -// SetAttribute(value, attributeToWrite); -// successfully_written = -// m_spiStatement->SetAttribute(Statement::MAX_LENGTH, attributeToWrite); -// TYPED_TEST(FlightSQLODBCTestBase, TestSQLSetStmtAttrMaxLength) { - // TODO 01S02: [Apache Arrow][Flight SQL] (1000000) Optional value changed. - GTEST_SKIP(); this->connect(); - validateSetStmtAttr(this->stmt, SQL_ATTR_MAX_LENGTH, static_cast(1)); + validateSetStmtAttr(this->stmt, SQL_ATTR_MAX_LENGTH, static_cast(0)); this->disconnect(); } -// An SQLULEN value corresponding to the maximum number of rows to return to the -// application for a SELECT statement. If *ValuePtr equals 0 (the default), the driver -// returns all rows. -// -// This attribute is intended to reduce network traffic. Conceptually, it is applied when -// the result set is created and limits the result set to the first ValuePtr rows. If the -// number of rows in the result set is greater than ValuePtr, the result set is truncated. -// -// SQL_ATTR_MAX_ROWS applies to all result sets on the Statement, including those returned -// by catalog functions. SQL_ATTR_MAX_ROWS establishes a maximum for the value of the -// cursor row count. -// -// A driver should not emulate SQL_ATTR_MAX_ROWS behavior for SQLFetch or SQLFetchScroll -// (if result set size limitations cannot be implemented at the data source) if it cannot -// guarantee that SQL_ATTR_MAX_ROWS will be implemented properly. -// -// It is driver-defined whether SQL_ATTR_MAX_ROWS applies to statements other than SELECT -// statements (such as catalog functions). -// -// The value of this attribute can be set on an open cursor; however, the setting might -// not take effect immediately, in which case the driver will return SQLSTATE 01S02 -// (Option value changed) and reset the attribute to its original value. -// -// case SQL_ATTR_MAX_ROWS: -// throw DriverException("Cannot set read-only attribute", "HY092"); -// TYPED_TEST(FlightSQLODBCTestBase, TestSQLSetStmtAttrMaxRows) { this->connect(); - // TODO HY092: [Apache Arrow][Flight SQL] (100) Cannot set read-only attribute - // validateSetStmtAttr(this->stmt, SQL_ATTR_MAX_ROWS, static_cast(0)); - // // Cannot set read-only attribute validateSetStmtAttrErrorCode(this->stmt, SQL_ATTR_MAX_ROWS, static_cast(0), error_state_HY092); @@ -925,56 +619,17 @@ TYPED_TEST(FlightSQLODBCTestBase, TestSQLSetStmtAttrMaxRows) { this->disconnect(); } -// An SQLULEN value that determines how the string arguments of catalog functions are -// treated. -// -// If SQL_TRUE, the string argument of catalog functions are treated as identifiers. The -// case is not significant. For nondelimited strings, the driver removes any trailing -// spaces and the string is folded to uppercase. For delimited strings, the driver removes -// any leading or trailing spaces and takes whatever is between the delimiters literally. -// If one of these arguments is set to a null pointer, the function returns SQL_ERROR and -// SQLSTATE HY009 (Invalid use of null pointer). -// -// If SQL_FALSE, the string arguments of catalog functions are not treated as identifiers. -// The case is significant. They can either contain a string search pattern or not, -// depending on the argument. -// -// The default value is SQL_FALSE. -// -// The TableType argument of SQLTables, which takes a list of values, is not affected by -// this attribute. -// -// SQL_ATTR_METADATA_ID can also be set on the connection level. (It and -// SQL_ATTR_ASYNC_ENABLE are the only statement attributes that are also connection -// attributes.) -// TYPED_TEST(FlightSQLODBCTestBase, TestSQLSetStmtAttrMetadataID) { - // TODO HY024: [Microsoft][ODBC Driver Manager] Invalid argument value - GTEST_SKIP(); this->connect(); - // validateSetStmtAttr(this->stmt, SQL_ATTR_METADATA_ID, - // static_cast(SQL_TRUE)); validateSetStmtAttr(this->stmt, SQL_ATTR_METADATA_ID, static_cast(SQL_FALSE)); this->disconnect(); } -// An SQLULEN value that indicates whether the driver should scan SQL strings for escape -// sequences: -// -// SQL_NOSCAN_OFF = The driver scans SQL strings for escape sequences (the default). -// -// SQL_NOSCAN_ON = The driver does not scan SQL strings for escape sequences. Instead, the -// driver sends the statement directly to the data source. -// TYPED_TEST(FlightSQLODBCTestBase, TestSQLSetStmtAttrNoscan) { - // TODO HY024: [Microsoft][ODBC Driver Manager] Invalid argument value - GTEST_SKIP(); this->connect(); - // validateSetStmtAttr(this->stmt, SQL_ATTR_NOSCAN, - // static_cast(SQL_NOSCAN_ON)); validateSetStmtAttr(this->stmt, SQL_ATTR_NOSCAN, static_cast(SQL_NOSCAN_OFF)); this->disconnect(); @@ -1023,8 +678,6 @@ TYPED_TEST(FlightSQLODBCTestBase, TestSQLSetStmtAttrParamsProcessedPtr) { } TYPED_TEST(FlightSQLODBCTestBase, TestSQLSetStmtAttrParamsetSize) { - // TODO HY090: [Microsoft][ODBC Driver Manager] Invalid string or buffer length - GTEST_SKIP(); this->connect(); validateSetStmtAttr(this->stmt, SQL_ATTR_PARAMSET_SIZE, static_cast(1)); @@ -1040,35 +693,9 @@ TYPED_TEST(FlightSQLODBCTestBase, TestSQLSetStmtAttrQueryTimeout) { this->disconnect(); } -// An SQLULEN value: -// -// SQL_RD_ON = SQLFetchScroll and, in ODBC 3.x, SQLFetch retrieve data after it positions -// the cursor to the specified location. This is the default. -// -// SQL_RD_OFF = SQLFetchScroll and, in ODBC 3.x, SQLFetch do not retrieve data after it -// positions the cursor. -// -// By setting SQL_RETRIEVE_DATA to SQL_RD_OFF, an application can verify that a row exists -// or retrieve a bookmark for the row without incurring the overhead of retrieving rows. -// For more information, see Scrolling and Fetching Rows. -// -// The value of this attribute can be set on an open cursor; however, the setting might -// not take effect immediately, in which case the driver will return SQLSTATE 01S02 -// (Option value changed) and reset the attribute to its original value. -// -// case SQL_ATTR_RETRIEVE_DATA: -// CheckIfAttributeIsSetToOnlyValidValue(value, static_cast(SQL_TRUE)); -// TYPED_TEST(FlightSQLODBCTestBase, TestSQLSetStmtAttrRetrieveData) { - // TODO HY024: [Microsoft][ODBC Driver Manager] Invalid argument value - GTEST_SKIP(); this->connect(); - // TODO - // validateSetStmtAttr(this->stmt, SQL_ATTR_RETRIEVE_DATA, - // static_cast(SQL_RD_OFF)); - // validateSetStmtAttr(this->stmt, SQL_ATTR_RETRIEVE_DATA, - // static_cast(SQL_RD_OFF)); validateSetStmtAttr(this->stmt, SQL_ATTR_RETRIEVE_DATA, static_cast(SQL_RD_ON)); @@ -1109,43 +736,17 @@ TYPED_TEST(FlightSQLODBCTestBase, TestSQLSetStmtAttrRowNumber) { this->disconnect(); } -// An SQLUSMALLINT * value that points to an array of SQLUSMALLINT values used to ignore a -// row during a bulk operation using SQLSetPos. Each value is set to either -// SQL_ROW_PROCEED (for the row to be included in the bulk operation) or SQL_ROW_IGNORE -// (for the row to be excluded from the bulk operation). (Rows cannot be ignored by using -// this array during calls to SQLBulkOperations.) -// -// This statement attribute can be set to a null pointer, in which case the driver does -// not return row status values. This attribute can be set at any time, but the new value -// is not used until the next time SQLSetPos is called. -// TYPED_TEST(FlightSQLODBCTestBase, TestSQLSetStmtAttrRowOperationPtr) { this->connect(); - // TODO Can be set to null pointer validateSetStmtAttr(this->stmt, SQL_ATTR_ROW_OPERATION_PTR, static_cast(0)); this->disconnect(); } -// An SQLUSMALLINT * value that points to an array of SQLUSMALLINT values containing row -// status values after a call to SQLFetch or SQLFetchScroll. The array has as many -// elements as there are rows in the rowset. -// -// This statement attribute can be set to a null pointer, in which case the driver does -// not return row status values. This attribute can be set at any time, but the new value -// is not used until the next time SQLBulkOperations, SQLFetch, SQLFetchScroll, or -// SQLSetPos is called. -// -// For more information, see Number of Rows Fetched and Status. -// -// Setting this statement attribute sets the SQL_DESC_ARRAY_STATUS_PTR field in the IRD -// header. -// TYPED_TEST(FlightSQLODBCTestBase, TestSQLSetStmtAttrRowStatusPtr) { this->connect(); - // TODO Can be set to null pointer validateSetStmtAttr(this->stmt, SQL_ATTR_ROW_STATUS_PTR, static_cast(0)); this->disconnect(); @@ -1159,57 +760,7 @@ TYPED_TEST(FlightSQLODBCTestBase, TestSQLSetStmtAttrRowsFetchedPtr) { this->disconnect(); } -// An SQLULEN value that specifies whether drivers that simulate positioned update -// and delete statements guarantee that such statements affect only one single row. -// -// To simulate positioned update and delete statements, -// most drivers construct a searched UPDATE or -// DELETE statement containing a WHERE clause that specifies the value of each -// column -// in the current row.Unless these columns make up a unique key, -// such a statement can affect more than one row. -// -// To guarantee that such statements affect only one row, -// the driver determines the columns in a unique key and adds these columns to the -// result -// set.If an application guarantees that the columns in the result set make up a -// unique key, -// the driver is not required to do so.This may reduce execution time. -// -// SQL_SC_NON_UNIQUE = The driver does not guarantee that simulated positioned update -// or -// delete statements will affect only one row; it is the -// application's responsibility to do so. If a statement affects -// more than one row, SQLExecute, SQLExecDirect, or SQLSetPos -// returns SQLSTATE 01001 (Cursor operation conflict). -// -// SQL_SC_TRY_UNIQUE = The driver attempts to guarantee that simulated positioned update -// or delete statements affect only one row. The driver always executes such statements, -// even if they might affect more than one row, such as when there is no unique key. If a -// statement affects more than one row, SQLExecute, SQLExecDirect, or SQLSetPos returns -// SQLSTATE 01001 (Cursor operation conflict). -// -// SQL_SC_UNIQUE = The driver guarantees that simulated positioned update or delete -// statements affect only one row. If the driver cannot guarantee this for a given -// statement, SQLExecDirect or SQLPrepare returns an error. -// -// If the data source provides native SQL support for positioned update and delete -// statements and the driver does not simulate cursors, SQL_SUCCESS is returned when -// SQL_SC_UNIQUE is requested for SQL_SIMULATE_CURSOR. SQL_SUCCESS_WITH_INFO is returned -// if SQL_SC_TRY_UNIQUE or SQL_SC_NON_UNIQUE is requested. If the data source provides the -// SQL_SC_TRY_UNIQUE level of support and the driver does not, SQL_SUCCESS is returned for -// SQL_SC_TRY_UNIQUE and SQL_SUCCESS_WITH_INFO is returned for SQL_SC_NON_UNIQUE. -// -// If the specified cursor simulation type is not supported by the data source, the driver -// substitutes a different simulation type and returns SQLSTATE 01S02 (Option value -// changed). For SQL_SC_UNIQUE, the driver substitutes, in order, SQL_SC_TRY_UNIQUE or -// SQL_SC_NON_UNIQUE. For SQL_SC_TRY_UNIQUE, the driver substitutes SQL_SC_NON_UNIQUE. -// -// The default is SQL_SC_UNIQUE. -// TYPED_TEST(FlightSQLODBCTestBase, TestSQLSetStmtAttrSimulateCursor) { - // TODO HY024: [Microsoft][ODBC Driver Manager] Invalid argument value - GTEST_SKIP(); this->connect(); validateSetStmtAttr(this->stmt, SQL_ATTR_SIMULATE_CURSOR, @@ -1218,28 +769,7 @@ TYPED_TEST(FlightSQLODBCTestBase, TestSQLSetStmtAttrSimulateCursor) { this->disconnect(); } -// An SQLULEN value that specifies whether an application will use bookmarks with a -// cursor: -// -// SQL_UB_OFF = Off (the default) -// -// SQL_UB_VARIABLE = An application will use bookmarks with a cursor, and the driver will -// provide variable-length bookmarks if they are supported. SQL_UB_FIXED is deprecated in -// ODBC 3.x. ODBC 3.x applications should always use variable-length bookmarks, even when -// working with ODBC 2.x drivers (which supported only 4-byte, fixed-length bookmarks). -// This is because a fixed-length bookmark is just a special case of a variable-length -// bookmark. When working with an ODBC 2.x driver, the Driver Manager maps SQL_UB_VARIABLE -// to SQL_UB_FIXED. -// -// To use bookmarks with a cursor, the application must specify this attribute with the -// SQL_UB_VARIABLE value before opening the cursor. -// -// case SQL_ATTR_USE_BOOKMARKS: -// CheckIfAttributeIsSetToOnlyValidValue(value, static_cast(SQL_UB_OFF)); -// TYPED_TEST(FlightSQLODBCTestBase, TestSQLSetStmtAttrUseBookmarks) { - // TODO HY024: [Microsoft][ODBC Driver Manager] Invalid argument value - GTEST_SKIP(); this->connect(); validateSetStmtAttr(this->stmt, SQL_ATTR_USE_BOOKMARKS, @@ -1248,10 +778,8 @@ TYPED_TEST(FlightSQLODBCTestBase, TestSQLSetStmtAttrUseBookmarks) { this->disconnect(); } -// TODO This is not a standard SQL_ATTR type parameter +// This is a pre ODBC 3 attribute TYPED_TEST(FlightSQLODBCTestBase, TestSQLSetStmtAttrRowsetSize) { - // TODO HY090: [Microsoft][ODBC Driver Manager] Invalid string or buffer length - GTEST_SKIP(); this->connect(); validateSetStmtAttr(this->stmt, SQL_ROWSET_SIZE, static_cast(1)); From 22469a077c8e001101379e2ad7886eb24e132a94 Mon Sep 17 00:00:00 2001 From: rscales Date: Wed, 9 Jul 2025 17:58:10 +0100 Subject: [PATCH 06/18] Revert back to use void return for ODBCStatement GetStmtAttr and SetStmtAttr --- cpp/src/arrow/flight/sql/odbc/odbc_api.cc | 9 +- .../odbc_impl/odbc_statement.h | 8 +- .../odbc_impl/odbc_statement.cc | 127 +++++++++--------- 3 files changed, 72 insertions(+), 72 deletions(-) diff --git a/cpp/src/arrow/flight/sql/odbc/odbc_api.cc b/cpp/src/arrow/flight/sql/odbc/odbc_api.cc index 5a7fa727f924..574650604c2c 100644 --- a/cpp/src/arrow/flight/sql/odbc/odbc_api.cc +++ b/cpp/src/arrow/flight/sql/odbc/odbc_api.cc @@ -884,8 +884,9 @@ SQLRETURN SQLGetStmtAttr(SQLHSTMT stmt, SQLINTEGER attribute, SQLPOINTER valuePt bool isUnicode = true; - return statement->GetStmtAttr(attribute, valuePtr, bufferLength, stringLengthPtr, - isUnicode); + statement->GetStmtAttr(attribute, valuePtr, bufferLength, stringLengthPtr, isUnicode); + + return SQL_SUCCESS; }); } @@ -902,7 +903,9 @@ SQLRETURN SQLSetStmtAttr(SQLHSTMT stmt, SQLINTEGER attribute, SQLPOINTER valuePt bool isUnicode = true; - return statement->SetStmtAttr(attribute, valuePtr, stringLength, isUnicode); + statement->SetStmtAttr(attribute, valuePtr, stringLength, isUnicode); + + return SQL_SUCCESS; }); } diff --git a/cpp/src/arrow/flight/sql/odbc/odbcabstraction/include/odbcabstraction/odbc_impl/odbc_statement.h b/cpp/src/arrow/flight/sql/odbc/odbcabstraction/include/odbcabstraction/odbc_impl/odbc_statement.h index dba2134666b1..e0ca9f7ba2e4 100644 --- a/cpp/src/arrow/flight/sql/odbc/odbcabstraction/include/odbcabstraction/odbc_impl/odbc_statement.h +++ b/cpp/src/arrow/flight/sql/odbc/odbcabstraction/include/odbcabstraction/odbc_impl/odbc_statement.h @@ -70,10 +70,10 @@ class ODBCStatement : public ODBCHandle { bool Fetch(size_t rows); bool isPrepared() const; - SQLRETURN GetStmtAttr(SQLINTEGER statementAttribute, SQLPOINTER output, - SQLINTEGER bufferSize, SQLINTEGER* strLenPtr, bool isUnicode); - SQLRETURN SetStmtAttr(SQLINTEGER statementAttribute, SQLPOINTER value, - SQLINTEGER bufferSize, bool isUnicode); + void GetStmtAttr(SQLINTEGER statementAttribute, SQLPOINTER output, + SQLINTEGER bufferSize, SQLINTEGER* strLenPtr, bool isUnicode); + void SetStmtAttr(SQLINTEGER statementAttribute, SQLPOINTER value, + SQLINTEGER bufferSize, bool isUnicode); void RevertAppDescriptor(bool isApd); diff --git a/cpp/src/arrow/flight/sql/odbc/odbcabstraction/odbc_impl/odbc_statement.cc b/cpp/src/arrow/flight/sql/odbc/odbcabstraction/odbc_impl/odbc_statement.cc index bb73f9ac1069..85c90807eedb 100644 --- a/cpp/src/arrow/flight/sql/odbc/odbcabstraction/odbc_impl/odbc_statement.cc +++ b/cpp/src/arrow/flight/sql/odbc/odbcabstraction/odbc_impl/odbc_statement.cc @@ -360,72 +360,72 @@ bool ODBCStatement::Fetch(size_t rows) { return rowsFetched != 0; } -SQLRETURN ODBCStatement::GetStmtAttr(SQLINTEGER statementAttribute, SQLPOINTER output, - SQLINTEGER bufferSize, SQLINTEGER* strLenPtr, - bool isUnicode) { +void ODBCStatement::GetStmtAttr(SQLINTEGER statementAttribute, SQLPOINTER output, + SQLINTEGER bufferSize, SQLINTEGER* strLenPtr, + bool isUnicode) { using driver::odbcabstraction::Statement; boost::optional spiAttribute; switch (statementAttribute) { // Descriptor accessor attributes case SQL_ATTR_APP_PARAM_DESC: DescriptorToHandle(output, m_currentApd, strLenPtr); - return SQL_SUCCESS; + return; case SQL_ATTR_APP_ROW_DESC: DescriptorToHandle(output, m_currentArd, strLenPtr); - return SQL_SUCCESS; + return; case SQL_ATTR_IMP_PARAM_DESC: DescriptorToHandle(output, m_ipd.get(), strLenPtr); - return SQL_SUCCESS; + return; case SQL_ATTR_IMP_ROW_DESC: DescriptorToHandle(output, m_ird.get(), strLenPtr); - return SQL_SUCCESS; + return; // Attributes that are descriptor fields case SQL_ATTR_PARAM_BIND_OFFSET_PTR: m_currentApd->GetHeaderField(SQL_DESC_BIND_OFFSET_PTR, output, bufferSize, strLenPtr); - return SQL_SUCCESS; + return; case SQL_ATTR_PARAM_BIND_TYPE: m_currentApd->GetHeaderField(SQL_DESC_BIND_TYPE, output, bufferSize, strLenPtr); - return SQL_SUCCESS; + return; case SQL_ATTR_PARAM_OPERATION_PTR: m_currentApd->GetHeaderField(SQL_DESC_ARRAY_STATUS_PTR, output, bufferSize, strLenPtr); - return SQL_SUCCESS; + return; case SQL_ATTR_PARAM_STATUS_PTR: m_ipd->GetHeaderField(SQL_DESC_ARRAY_STATUS_PTR, output, bufferSize, strLenPtr); - return SQL_SUCCESS; + return; case SQL_ATTR_PARAMS_PROCESSED_PTR: m_ipd->GetHeaderField(SQL_DESC_ROWS_PROCESSED_PTR, output, bufferSize, strLenPtr); - return SQL_SUCCESS; + return; case SQL_ATTR_PARAMSET_SIZE: m_currentApd->GetHeaderField(SQL_DESC_ARRAY_SIZE, output, bufferSize, strLenPtr); - return SQL_SUCCESS; + return; case SQL_ATTR_ROW_ARRAY_SIZE: m_currentArd->GetHeaderField(SQL_DESC_ARRAY_SIZE, output, bufferSize, strLenPtr); - return SQL_SUCCESS; + return; case SQL_ATTR_ROW_BIND_OFFSET_PTR: m_currentArd->GetHeaderField(SQL_DESC_BIND_OFFSET_PTR, output, bufferSize, strLenPtr); - return SQL_SUCCESS; + return; case SQL_ATTR_ROW_BIND_TYPE: m_currentArd->GetHeaderField(SQL_DESC_BIND_TYPE, output, bufferSize, strLenPtr); - return SQL_SUCCESS; + return; case SQL_ATTR_ROW_OPERATION_PTR: m_currentArd->GetHeaderField(SQL_DESC_ARRAY_STATUS_PTR, output, bufferSize, strLenPtr); - return SQL_SUCCESS; + return; case SQL_ATTR_ROW_STATUS_PTR: m_ird->GetHeaderField(SQL_DESC_ARRAY_STATUS_PTR, output, bufferSize, strLenPtr); - return SQL_SUCCESS; + return; case SQL_ATTR_ROWS_FETCHED_PTR: m_ird->GetHeaderField(SQL_DESC_ROWS_PROCESSED_PTR, output, bufferSize, strLenPtr); - return SQL_SUCCESS; + return; case SQL_ATTR_ASYNC_ENABLE: GetAttribute(static_cast(SQL_ASYNC_ENABLE_OFF), output, bufferSize, strLenPtr); - return SQL_SUCCESS; + return; #ifdef SQL_ATTR_ASYNC_STMT_EVENT case SQL_ATTR_ASYNC_STMT_EVENT: @@ -442,51 +442,51 @@ SQLRETURN ODBCStatement::GetStmtAttr(SQLINTEGER statementAttribute, SQLPOINTER o case SQL_ATTR_CURSOR_SCROLLABLE: GetAttribute(static_cast(SQL_NONSCROLLABLE), output, bufferSize, strLenPtr); - return SQL_SUCCESS; + return; case SQL_ATTR_CURSOR_SENSITIVITY: GetAttribute(static_cast(SQL_UNSPECIFIED), output, bufferSize, strLenPtr); - return SQL_SUCCESS; + return; case SQL_ATTR_CURSOR_TYPE: GetAttribute(static_cast(SQL_CURSOR_FORWARD_ONLY), output, bufferSize, strLenPtr); - return SQL_SUCCESS; + return; case SQL_ATTR_ENABLE_AUTO_IPD: GetAttribute(static_cast(SQL_FALSE), output, bufferSize, strLenPtr); - return SQL_SUCCESS; + return; case SQL_ATTR_FETCH_BOOKMARK_PTR: GetAttribute(static_cast(NULL), output, bufferSize, strLenPtr); - return SQL_SUCCESS; + return; case SQL_ATTR_KEYSET_SIZE: GetAttribute(static_cast(0), output, bufferSize, strLenPtr); - return SQL_SUCCESS; + return; case SQL_ATTR_ROW_NUMBER: GetAttribute(static_cast(m_rowNumber), output, bufferSize, strLenPtr); - return SQL_SUCCESS; + return; case SQL_ATTR_SIMULATE_CURSOR: GetAttribute(static_cast(SQL_SC_UNIQUE), output, bufferSize, strLenPtr); - return SQL_SUCCESS; + return; case SQL_ATTR_USE_BOOKMARKS: GetAttribute(static_cast(SQL_UB_OFF), output, bufferSize, strLenPtr); - return SQL_SUCCESS; + return; case SQL_ATTR_CONCURRENCY: GetAttribute(static_cast(SQL_CONCUR_READ_ONLY), output, bufferSize, strLenPtr); - return SQL_SUCCESS; + return; case SQL_ATTR_MAX_ROWS: GetAttribute(static_cast(m_maxRows), output, bufferSize, strLenPtr); - return SQL_SUCCESS; + return; case SQL_ATTR_RETRIEVE_DATA: GetAttribute(static_cast(SQL_RD_ON), output, bufferSize, strLenPtr); - return SQL_SUCCESS; + return; case SQL_ROWSET_SIZE: GetAttribute(static_cast(m_rowsetSize), output, bufferSize, strLenPtr); - return SQL_SUCCESS; + return; // Driver-level statement attributes. These are all SQLULEN attributes. case SQL_ATTR_MAX_LENGTH: @@ -509,15 +509,15 @@ SQLRETURN ODBCStatement::GetStmtAttr(SQLINTEGER statementAttribute, SQLPOINTER o if (spiAttribute) { GetAttribute(static_cast(boost::get(*spiAttribute)), output, bufferSize, strLenPtr); - return SQL_SUCCESS; + return; } throw DriverException( "Invalid statement attribute: " + std::to_string(statementAttribute), "HY092"); } -SQLRETURN ODBCStatement::SetStmtAttr(SQLINTEGER statementAttribute, SQLPOINTER value, - SQLINTEGER bufferSize, bool isUnicode) { +void ODBCStatement::SetStmtAttr(SQLINTEGER statementAttribute, SQLPOINTER value, + SQLINTEGER bufferSize, bool isUnicode) { size_t attributeToWrite = 0; bool successfully_written = false; @@ -533,7 +533,7 @@ SQLRETURN ODBCStatement::SetStmtAttr(SQLINTEGER statementAttribute, SQLPOINTER v desc->RegisterToStatement(this, true); } } - return SQL_SUCCESS; + return; } case SQL_ATTR_APP_ROW_DESC: { ODBCDescriptor* desc = static_cast(value); @@ -546,7 +546,7 @@ SQLRETURN ODBCStatement::SetStmtAttr(SQLINTEGER statementAttribute, SQLPOINTER v desc->RegisterToStatement(this, false); } } - return SQL_SUCCESS; + return; } case SQL_ATTR_IMP_PARAM_DESC: throw DriverException("Cannot assign implementation descriptor.", "HY017"); @@ -555,40 +555,40 @@ SQLRETURN ODBCStatement::SetStmtAttr(SQLINTEGER statementAttribute, SQLPOINTER v // Attributes that are descriptor fields case SQL_ATTR_PARAM_BIND_OFFSET_PTR: m_currentApd->SetHeaderField(SQL_DESC_BIND_OFFSET_PTR, value, bufferSize); - return SQL_SUCCESS; + return; case SQL_ATTR_PARAM_BIND_TYPE: m_currentApd->SetHeaderField(SQL_DESC_BIND_TYPE, value, bufferSize); - return SQL_SUCCESS; + return; case SQL_ATTR_PARAM_OPERATION_PTR: m_currentApd->SetHeaderField(SQL_DESC_ARRAY_STATUS_PTR, value, bufferSize); - return SQL_SUCCESS; + return; case SQL_ATTR_PARAM_STATUS_PTR: m_ipd->SetHeaderField(SQL_DESC_ARRAY_STATUS_PTR, value, bufferSize); - return SQL_SUCCESS; + return; case SQL_ATTR_PARAMS_PROCESSED_PTR: m_ipd->SetHeaderField(SQL_DESC_ROWS_PROCESSED_PTR, value, bufferSize); - return SQL_SUCCESS; + return; case SQL_ATTR_PARAMSET_SIZE: m_currentApd->SetHeaderField(SQL_DESC_ARRAY_SIZE, value, bufferSize); - return SQL_SUCCESS; + return; case SQL_ATTR_ROW_ARRAY_SIZE: m_currentArd->SetHeaderField(SQL_DESC_ARRAY_SIZE, value, bufferSize); - return SQL_SUCCESS; + return; case SQL_ATTR_ROW_BIND_OFFSET_PTR: m_currentArd->SetHeaderField(SQL_DESC_BIND_OFFSET_PTR, value, bufferSize); - return SQL_SUCCESS; + return; case SQL_ATTR_ROW_BIND_TYPE: m_currentArd->SetHeaderField(SQL_DESC_BIND_TYPE, value, bufferSize); - return SQL_SUCCESS; + return; case SQL_ATTR_ROW_OPERATION_PTR: m_currentArd->SetHeaderField(SQL_DESC_ARRAY_STATUS_PTR, value, bufferSize); - return SQL_SUCCESS; + return; case SQL_ATTR_ROW_STATUS_PTR: m_ird->SetHeaderField(SQL_DESC_ARRAY_STATUS_PTR, value, bufferSize); - return SQL_SUCCESS; + return; case SQL_ATTR_ROWS_FETCHED_PTR: m_ird->SetHeaderField(SQL_DESC_ROWS_PROCESSED_PTR, value, bufferSize); - return SQL_SUCCESS; + return; case SQL_ATTR_ASYNC_ENABLE: throw DriverException("Unsupported attribute", "HYC00"); @@ -607,43 +607,43 @@ SQLRETURN ODBCStatement::SetStmtAttr(SQLINTEGER statementAttribute, SQLPOINTER v case SQL_ATTR_CONCURRENCY: CheckIfAttributeIsSetToOnlyValidValue(value, static_cast(SQL_CONCUR_READ_ONLY)); - return SQL_SUCCESS; + return; case SQL_ATTR_CURSOR_SCROLLABLE: CheckIfAttributeIsSetToOnlyValidValue(value, static_cast(SQL_NONSCROLLABLE)); - return SQL_SUCCESS; + return; case SQL_ATTR_CURSOR_SENSITIVITY: CheckIfAttributeIsSetToOnlyValidValue(value, static_cast(SQL_UNSPECIFIED)); - return SQL_SUCCESS; + return; case SQL_ATTR_CURSOR_TYPE: CheckIfAttributeIsSetToOnlyValidValue( value, static_cast(SQL_CURSOR_FORWARD_ONLY)); - return SQL_SUCCESS; + return; case SQL_ATTR_ENABLE_AUTO_IPD: CheckIfAttributeIsSetToOnlyValidValue(value, static_cast(SQL_FALSE)); - return SQL_SUCCESS; + return; case SQL_ATTR_FETCH_BOOKMARK_PTR: if (value != NULL) { throw DriverException("Optional feature not implemented", "HYC00"); } - return SQL_SUCCESS; + return; case SQL_ATTR_KEYSET_SIZE: CheckIfAttributeIsSetToOnlyValidValue(value, static_cast(0)); - return SQL_SUCCESS; + return; case SQL_ATTR_ROW_NUMBER: throw DriverException("Cannot set read-only attribute", "HY092"); case SQL_ATTR_SIMULATE_CURSOR: CheckIfAttributeIsSetToOnlyValidValue(value, static_cast(SQL_SC_UNIQUE)); - return SQL_SUCCESS; + return; case SQL_ATTR_USE_BOOKMARKS: CheckIfAttributeIsSetToOnlyValidValue(value, static_cast(SQL_UB_OFF)); - return SQL_SUCCESS; + return; case SQL_ATTR_RETRIEVE_DATA: CheckIfAttributeIsSetToOnlyValidValue(value, static_cast(SQL_RD_ON)); - return SQL_SUCCESS; + return; case SQL_ROWSET_SIZE: SetAttribute(value, m_rowsetSize); - return SQL_SUCCESS; + return; case SQL_ATTR_MAX_ROWS: throw DriverException("Cannot set read-only attribute", "HY092"); @@ -673,12 +673,9 @@ SQLRETURN ODBCStatement::SetStmtAttr(SQLINTEGER statementAttribute, SQLPOINTER v throw DriverException("Invalid attribute: " + std::to_string(attributeToWrite), "HY092"); } - if (successfully_written) { - return SQL_SUCCESS; - } else { + if (!successfully_written) { GetDiagnostics().AddWarning("Optional value changed.", "01S02", driver::odbcabstraction::ODBCErrorCodes_GENERAL_WARNING); - return SQL_SUCCESS_WITH_INFO; } } From e5cdd2b1e9bcb0c9209d6007bd6f476fdb2f4929 Mon Sep 17 00:00:00 2001 From: rscales Date: Wed, 9 Jul 2025 18:06:19 +0100 Subject: [PATCH 07/18] Fix formatting issues --- .../odbc_impl/odbc_statement.h | 4 ++-- .../sql/odbc/tests/statement_attr_test.cc | 20 +++++++++---------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/cpp/src/arrow/flight/sql/odbc/odbcabstraction/include/odbcabstraction/odbc_impl/odbc_statement.h b/cpp/src/arrow/flight/sql/odbc/odbcabstraction/include/odbcabstraction/odbc_impl/odbc_statement.h index e0ca9f7ba2e4..df5ca5e34abe 100644 --- a/cpp/src/arrow/flight/sql/odbc/odbcabstraction/include/odbcabstraction/odbc_impl/odbc_statement.h +++ b/cpp/src/arrow/flight/sql/odbc/odbcabstraction/include/odbcabstraction/odbc_impl/odbc_statement.h @@ -72,8 +72,8 @@ class ODBCStatement : public ODBCHandle { void GetStmtAttr(SQLINTEGER statementAttribute, SQLPOINTER output, SQLINTEGER bufferSize, SQLINTEGER* strLenPtr, bool isUnicode); - void SetStmtAttr(SQLINTEGER statementAttribute, SQLPOINTER value, - SQLINTEGER bufferSize, bool isUnicode); + void SetStmtAttr(SQLINTEGER statementAttribute, SQLPOINTER value, SQLINTEGER bufferSize, + bool isUnicode); void RevertAppDescriptor(bool isApd); diff --git a/cpp/src/arrow/flight/sql/odbc/tests/statement_attr_test.cc b/cpp/src/arrow/flight/sql/odbc/tests/statement_attr_test.cc index dfe29e970d47..302422344617 100644 --- a/cpp/src/arrow/flight/sql/odbc/tests/statement_attr_test.cc +++ b/cpp/src/arrow/flight/sql/odbc/tests/statement_attr_test.cc @@ -14,9 +14,9 @@ // KIND, either express or implied. See the License for the // specific language governing permissions and limitations // under the License. -#include "arrow/flight/sql/odbc/tests/odbc_test_suite.h" #include "arrow/flight/sql/odbc/odbcabstraction/include/odbcabstraction/odbc_impl/odbc_statement.h" #include "arrow/flight/sql/odbc/odbcabstraction/include/odbcabstraction/spi/statement.h" +#include "arrow/flight/sql/odbc/tests/odbc_test_suite.h" #ifdef _WIN32 # include @@ -76,8 +76,7 @@ void validateSetStmtAttr(SQLHSTMT statement, SQLINTEGER attribute, SQLULEN new_v SQLINTEGER stringLengthPtr = sizeof(SQLULEN); SQLRETURN ret = SQLSetStmtAttr( - statement, attribute, reinterpret_cast(new_value), - stringLengthPtr); + statement, attribute, reinterpret_cast(new_value), stringLengthPtr); EXPECT_EQ(ret, SQL_SUCCESS); } @@ -85,10 +84,10 @@ void validateSetStmtAttr(SQLHSTMT statement, SQLINTEGER attribute, SQLULEN new_v // Validate error return value and code void validateSetStmtAttrErrorCode(SQLHSTMT statement, SQLINTEGER attribute, SQLULEN new_value, std::string_view error_code) { - //SQLINTEGER stringLengthPtr = sizeof(SQLULEN); + // SQLINTEGER stringLengthPtr = sizeof(SQLULEN); - SQLRETURN ret = SQLSetStmtAttr( - statement, attribute, reinterpret_cast(new_value), 0); + SQLRETURN ret = + SQLSetStmtAttr(statement, attribute, reinterpret_cast(new_value), 0); EXPECT_EQ(ret, SQL_ERROR); @@ -368,12 +367,13 @@ TYPED_TEST(FlightSQLODBCTestBase, TestSQLGetStmtAttrRowNumber) { std::wstring wsql = L"SELECT 1;"; std::vector sql0(wsql.begin(), wsql.end()); - SQLRETURN ret = SQLExecDirect(this->stmt, &sql0[0], static_cast(sql0.size())); + SQLRETURN ret = + SQLExecDirect(this->stmt, &sql0[0], static_cast(sql0.size())); EXPECT_EQ(ret, SQL_SUCCESS); // TODO 24000: [Microsoft][ODBC Driver Manager] Invalid cursor state - //validateGetStmtAttr(this->stmt, SQL_ATTR_ROW_NUMBER, static_cast(0)); + // validateGetStmtAttr(this->stmt, SQL_ATTR_ROW_NUMBER, static_cast(0)); this->disconnect(); } @@ -477,7 +477,6 @@ TYPED_TEST(FlightSQLODBCTestBase, TestSQLSetStmtAttrAsyncEnableUnsupported) { } #endif - #ifdef SQL_ATTR_ASYNC_STMT_EVENT TYPED_TEST(FlightSQLODBCTestBase, TestSQLSetStmtAttrAsyncStmtEventUnsupported) { this->connect(); @@ -576,7 +575,8 @@ TYPED_TEST(FlightSQLODBCTestBase, TestSQLSetStmtAttrIMPParamDesc) { this->connect(); // Invalid use of an automatically allocated descriptor handle - validateSetStmtAttrErrorCode(this->stmt, SQL_ATTR_IMP_PARAM_DESC, static_cast(0), error_state_HY017); + validateSetStmtAttrErrorCode(this->stmt, SQL_ATTR_IMP_PARAM_DESC, + static_cast(0), error_state_HY017); this->disconnect(); } From bbbdedf909668b56d74012eb95d0306eeb8629fb Mon Sep 17 00:00:00 2001 From: rscales Date: Wed, 9 Jul 2025 21:46:15 +0100 Subject: [PATCH 08/18] Fix additional test case issues --- .../sql/odbc/tests/statement_attr_test.cc | 24 +++++++++---------- 1 file changed, 11 insertions(+), 13 deletions(-) diff --git a/cpp/src/arrow/flight/sql/odbc/tests/statement_attr_test.cc b/cpp/src/arrow/flight/sql/odbc/tests/statement_attr_test.cc index 302422344617..5eae273137f1 100644 --- a/cpp/src/arrow/flight/sql/odbc/tests/statement_attr_test.cc +++ b/cpp/src/arrow/flight/sql/odbc/tests/statement_attr_test.cc @@ -14,9 +14,10 @@ // KIND, either express or implied. See the License for the // specific language governing permissions and limitations // under the License. +#include "arrow/flight/sql/odbc/tests/odbc_test_suite.h" + #include "arrow/flight/sql/odbc/odbcabstraction/include/odbcabstraction/odbc_impl/odbc_statement.h" #include "arrow/flight/sql/odbc/odbcabstraction/include/odbcabstraction/spi/statement.h" -#include "arrow/flight/sql/odbc/tests/odbc_test_suite.h" #ifdef _WIN32 # include @@ -84,10 +85,10 @@ void validateSetStmtAttr(SQLHSTMT statement, SQLINTEGER attribute, SQLULEN new_v // Validate error return value and code void validateSetStmtAttrErrorCode(SQLHSTMT statement, SQLINTEGER attribute, SQLULEN new_value, std::string_view error_code) { - // SQLINTEGER stringLengthPtr = sizeof(SQLULEN); + SQLINTEGER stringLengthPtr = sizeof(SQLULEN); - SQLRETURN ret = - SQLSetStmtAttr(statement, attribute, reinterpret_cast(new_value), 0); + SQLRETURN ret = SQLSetStmtAttr( + statement, attribute, reinterpret_cast(new_value), stringLengthPtr); EXPECT_EQ(ret, SQL_ERROR); @@ -361,7 +362,6 @@ TYPED_TEST(FlightSQLODBCTestBase, TestSQLGetStmtAttrRowBindType) { } TYPED_TEST(FlightSQLODBCTestBase, TestSQLGetStmtAttrRowNumber) { - GTEST_SKIP(); this->connect(); std::wstring wsql = L"SELECT 1;"; @@ -372,8 +372,12 @@ TYPED_TEST(FlightSQLODBCTestBase, TestSQLGetStmtAttrRowNumber) { EXPECT_EQ(ret, SQL_SUCCESS); - // TODO 24000: [Microsoft][ODBC Driver Manager] Invalid cursor state - // validateGetStmtAttr(this->stmt, SQL_ATTR_ROW_NUMBER, static_cast(0)); + ret = SQLFetch(this->stmt); + + EXPECT_EQ(ret, SQL_SUCCESS); + + // TODO Returns SQL_ERROR but no ODBC Code + //validateGetStmtAttr(this->stmt, SQL_ATTR_ROW_NUMBER, static_cast(0)); this->disconnect(); } @@ -489,8 +493,6 @@ TYPED_TEST(FlightSQLODBCTestBase, TestSQLSetStmtAttrAsyncStmtEventUnsupported) { } #endif -// TODO Check for windows is not necessary -//#ifndef _WIN32 #ifdef SQL_ATTR_ASYNC_STMT_PCALLBACK TYPED_TEST(FlightSQLODBCTestBase, TestSQLSetStmtAttrAsyncStmtPCCallbackUnsupported) { this->connect(); @@ -501,10 +503,7 @@ TYPED_TEST(FlightSQLODBCTestBase, TestSQLSetStmtAttrAsyncStmtPCCallbackUnsupport this->disconnect(); } #endif -//#endif -// TODO Check for windows is not necessary -// #ifndef _WIN32 #ifdef SQL_ATTR_ASYNC_STMT_PCONTEXT TYPED_TEST(FlightSQLODBCTestBase, TestSQLSetStmtAttrAsyncStmtPCContextUnsupported) { this->connect(); @@ -516,7 +515,6 @@ TYPED_TEST(FlightSQLODBCTestBase, TestSQLSetStmtAttrAsyncStmtPCContextUnsupporte this->disconnect(); } #endif -//#endif TYPED_TEST(FlightSQLODBCTestBase, TestSQLSetStmtAttrConcurrency) { this->connect(); From 570fa289b81748a3fa4ccbc0222d24151c1cf6c5 Mon Sep 17 00:00:00 2001 From: rscales Date: Wed, 9 Jul 2025 22:08:40 +0100 Subject: [PATCH 09/18] Fix issue with expected returned value for SQL_ATTR_ROW_NUMBER test --- cpp/src/arrow/flight/sql/odbc/tests/statement_attr_test.cc | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/cpp/src/arrow/flight/sql/odbc/tests/statement_attr_test.cc b/cpp/src/arrow/flight/sql/odbc/tests/statement_attr_test.cc index 5eae273137f1..bc742eeee297 100644 --- a/cpp/src/arrow/flight/sql/odbc/tests/statement_attr_test.cc +++ b/cpp/src/arrow/flight/sql/odbc/tests/statement_attr_test.cc @@ -376,8 +376,7 @@ TYPED_TEST(FlightSQLODBCTestBase, TestSQLGetStmtAttrRowNumber) { EXPECT_EQ(ret, SQL_SUCCESS); - // TODO Returns SQL_ERROR but no ODBC Code - //validateGetStmtAttr(this->stmt, SQL_ATTR_ROW_NUMBER, static_cast(0)); + validateGetStmtAttr(this->stmt, SQL_ATTR_ROW_NUMBER, static_cast(1)); this->disconnect(); } From 87800d59f21d0d7b256fcf959849b1d6a0869f4f Mon Sep 17 00:00:00 2001 From: rscales Date: Wed, 9 Jul 2025 22:58:44 +0100 Subject: [PATCH 10/18] Revert to use null SQLPOINTER for SQL_ATTR_FETCH_BOOKMARK_PTR default in GetStmtAttr --- .../flight/sql/odbc/odbcabstraction/odbc_impl/odbc_statement.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cpp/src/arrow/flight/sql/odbc/odbcabstraction/odbc_impl/odbc_statement.cc b/cpp/src/arrow/flight/sql/odbc/odbcabstraction/odbc_impl/odbc_statement.cc index 85c90807eedb..1714850323a7 100644 --- a/cpp/src/arrow/flight/sql/odbc/odbcabstraction/odbc_impl/odbc_statement.cc +++ b/cpp/src/arrow/flight/sql/odbc/odbcabstraction/odbc_impl/odbc_statement.cc @@ -458,7 +458,7 @@ void ODBCStatement::GetStmtAttr(SQLINTEGER statementAttribute, SQLPOINTER output return; case SQL_ATTR_FETCH_BOOKMARK_PTR: - GetAttribute(static_cast(NULL), output, bufferSize, strLenPtr); + GetAttribute(static_cast(NULL), output, bufferSize, strLenPtr); return; case SQL_ATTR_KEYSET_SIZE: From 403a53b2e6e72a2865705a0a7053a708a4e1ae8a Mon Sep 17 00:00:00 2001 From: rscales Date: Fri, 11 Jul 2025 23:04:41 +0100 Subject: [PATCH 11/18] Update tests that require use of SQLPOINTER data --- .../sql/odbc/tests/statement_attr_test.cc | 147 +++++++++++++++--- 1 file changed, 124 insertions(+), 23 deletions(-) diff --git a/cpp/src/arrow/flight/sql/odbc/tests/statement_attr_test.cc b/cpp/src/arrow/flight/sql/odbc/tests/statement_attr_test.cc index bc742eeee297..b9bb491d96a7 100644 --- a/cpp/src/arrow/flight/sql/odbc/tests/statement_attr_test.cc +++ b/cpp/src/arrow/flight/sql/odbc/tests/statement_attr_test.cc @@ -33,13 +33,42 @@ namespace arrow::flight::sql::odbc { // Helper Functions -// Validate unsigned length SQLULEN return value +// Validate SQLULEN return value void validateGetStmtAttr(SQLHSTMT statement, SQLINTEGER attribute, SQLULEN expected_value) { SQLULEN value = 0; - SQLINTEGER stringLengthPtr; + SQLINTEGER stringLength = 0; - SQLRETURN ret = SQLGetStmtAttr(statement, attribute, &value, 0, &stringLengthPtr); + SQLRETURN ret = + SQLGetStmtAttr(statement, attribute, &value, sizeof(value), &stringLength); + + EXPECT_EQ(ret, SQL_SUCCESS); + + EXPECT_EQ(value, expected_value); +} + +// Validate SQLLEN return value +void validateGetStmtAttr(SQLHSTMT statement, SQLINTEGER attribute, + SQLLEN expected_value) { + SQLLEN value = 0; + SQLINTEGER stringLength = 0; + + SQLRETURN ret = + SQLGetStmtAttr(statement, attribute, &value, sizeof(value), &stringLength); + + EXPECT_EQ(ret, SQL_SUCCESS); + + EXPECT_EQ(value, expected_value); +} + +// Validate SQLPOINTER return value +void validateGetStmtAttr(SQLHSTMT statement, SQLINTEGER attribute, + SQLPOINTER expected_value) { + SQLPOINTER value = nullptr; + SQLINTEGER stringLength = 0; + + SQLRETURN ret = + SQLGetStmtAttr(statement, attribute, &value, sizeof(value), &stringLength); EXPECT_EQ(ret, SQL_SUCCESS); @@ -72,7 +101,7 @@ void validateGetStmtAttrErrorCode(SQLHSTMT statement, SQLINTEGER attribute, VerifyOdbcErrorState(SQL_HANDLE_STMT, statement, error_code); } -// Validate unsigned length SQLULEN return value +// Validate return value for call to SQLSetStmtAttr with SQLULEN void validateSetStmtAttr(SQLHSTMT statement, SQLINTEGER attribute, SQLULEN new_value) { SQLINTEGER stringLengthPtr = sizeof(SQLULEN); @@ -82,6 +111,23 @@ void validateSetStmtAttr(SQLHSTMT statement, SQLINTEGER attribute, SQLULEN new_v EXPECT_EQ(ret, SQL_SUCCESS); } +// Validate return value for call to SQLSetStmtAttr with SQLLEN +void validateSetStmtAttr(SQLHSTMT statement, SQLINTEGER attribute, SQLLEN new_value) { + SQLINTEGER stringLengthPtr = sizeof(SQLLEN); + + SQLRETURN ret = SQLSetStmtAttr( + statement, attribute, reinterpret_cast(new_value), stringLengthPtr); + + EXPECT_EQ(ret, SQL_SUCCESS); +} + +// Validate return value for call to SQLSetStmtAttr with SQLPOINTER +void validateSetStmtAttr(SQLHSTMT statement, SQLINTEGER attribute, SQLPOINTER value) { + SQLRETURN ret = SQLSetStmtAttr(statement, attribute, value, 0); + + EXPECT_EQ(ret, SQL_SUCCESS); +} + // Validate error return value and code void validateSetStmtAttrErrorCode(SQLHSTMT statement, SQLINTEGER attribute, SQLULEN new_value, std::string_view error_code) { @@ -274,7 +320,7 @@ TYPED_TEST(FlightSQLODBCTestBase, TestSQLGetStmtAttrParamBindOffsetPtr) { this->connect(); validateGetStmtAttr(this->stmt, SQL_ATTR_PARAM_BIND_OFFSET_PTR, - static_cast(0)); + static_cast(nullptr)); this->disconnect(); } @@ -291,7 +337,8 @@ TYPED_TEST(FlightSQLODBCTestBase, TestSQLGetStmtAttrParamBindType) { TYPED_TEST(FlightSQLODBCTestBase, TestSQLGetStmtAttrParamOperationPtr) { this->connect(); - validateGetStmtAttr(this->stmt, SQL_ATTR_PARAM_OPERATION_PTR, static_cast(0)); + validateGetStmtAttr(this->stmt, SQL_ATTR_PARAM_OPERATION_PTR, + static_cast(nullptr)); this->disconnect(); } @@ -299,7 +346,8 @@ TYPED_TEST(FlightSQLODBCTestBase, TestSQLGetStmtAttrParamOperationPtr) { TYPED_TEST(FlightSQLODBCTestBase, TestSQLGetStmtAttrParamStatusPtr) { this->connect(); - validateGetStmtAttr(this->stmt, SQL_ATTR_PARAM_STATUS_PTR, static_cast(0)); + validateGetStmtAttr(this->stmt, SQL_ATTR_PARAM_STATUS_PTR, + static_cast(nullptr)); this->disconnect(); } @@ -307,7 +355,8 @@ TYPED_TEST(FlightSQLODBCTestBase, TestSQLGetStmtAttrParamStatusPtr) { TYPED_TEST(FlightSQLODBCTestBase, TestSQLGetStmtAttrParamsProcessedPtr) { this->connect(); - validateGetStmtAttr(this->stmt, SQL_ATTR_PARAMS_PROCESSED_PTR, static_cast(0)); + validateGetStmtAttr(this->stmt, SQL_ATTR_PARAMS_PROCESSED_PTR, + static_cast(nullptr)); this->disconnect(); } @@ -348,7 +397,8 @@ TYPED_TEST(FlightSQLODBCTestBase, TestSQLGetStmtAttrRowArraySize) { TYPED_TEST(FlightSQLODBCTestBase, TestSQLGetStmtAttrRowBindOffsetPtr) { this->connect(); - validateGetStmtAttr(this->stmt, SQL_ATTR_ROW_BIND_OFFSET_PTR, static_cast(0)); + validateGetStmtAttr(this->stmt, SQL_ATTR_ROW_BIND_OFFSET_PTR, + static_cast(nullptr)); this->disconnect(); } @@ -384,7 +434,8 @@ TYPED_TEST(FlightSQLODBCTestBase, TestSQLGetStmtAttrRowNumber) { TYPED_TEST(FlightSQLODBCTestBase, TestSQLGetStmtAttrRowOperationPtr) { this->connect(); - validateGetStmtAttr(this->stmt, SQL_ATTR_ROW_OPERATION_PTR, static_cast(0)); + validateGetStmtAttr(this->stmt, SQL_ATTR_ROW_OPERATION_PTR, + static_cast(nullptr)); this->disconnect(); } @@ -392,7 +443,8 @@ TYPED_TEST(FlightSQLODBCTestBase, TestSQLGetStmtAttrRowOperationPtr) { TYPED_TEST(FlightSQLODBCTestBase, TestSQLGetStmtAttrRowStatusPtr) { this->connect(); - validateGetStmtAttr(this->stmt, SQL_ATTR_ROW_STATUS_PTR, static_cast(0)); + validateGetStmtAttr(this->stmt, SQL_ATTR_ROW_STATUS_PTR, + static_cast(nullptr)); this->disconnect(); } @@ -400,7 +452,8 @@ TYPED_TEST(FlightSQLODBCTestBase, TestSQLGetStmtAttrRowStatusPtr) { TYPED_TEST(FlightSQLODBCTestBase, TestSQLGetStmtAttrRowsFetchedPtr) { this->connect(); - validateGetStmtAttr(this->stmt, SQL_ATTR_ROWS_FETCHED_PTR, static_cast(0)); + validateGetStmtAttr(this->stmt, SQL_ATTR_ROWS_FETCHED_PTR, + static_cast(nullptr)); this->disconnect(); } @@ -589,11 +642,9 @@ TYPED_TEST(FlightSQLODBCTestBase, TestSQLSetStmtAttrIMPRowDesc) { } TYPED_TEST(FlightSQLODBCTestBase, TestSQLSetStmtAttrKeysetSizeUnsupported) { - GTEST_SKIP(); this->connect(); - // Optional feature not implemented - validateSetStmtAttrErrorCode(this->stmt, SQL_ATTR_KEYSET_SIZE, 0, error_state_HYC00); + validateSetStmtAttr(this->stmt, SQL_ATTR_KEYSET_SIZE, static_cast(0)); this->disconnect(); } @@ -635,8 +686,13 @@ TYPED_TEST(FlightSQLODBCTestBase, TestSQLSetStmtAttrNoscan) { TYPED_TEST(FlightSQLODBCTestBase, TestSQLSetStmtAttrParamBindOffsetPtr) { this->connect(); + SQLULEN offset = 1000; + validateSetStmtAttr(this->stmt, SQL_ATTR_PARAM_BIND_OFFSET_PTR, - static_cast(1)); + static_cast(&offset)); + + validateGetStmtAttr(this->stmt, SQL_ATTR_PARAM_BIND_OFFSET_PTR, + static_cast(&offset)); this->disconnect(); } @@ -653,7 +709,13 @@ TYPED_TEST(FlightSQLODBCTestBase, TestSQLSetStmtAttrParamBindType) { TYPED_TEST(FlightSQLODBCTestBase, TestSQLSetStmtAttrParamOperationPtr) { this->connect(); - validateSetStmtAttr(this->stmt, SQL_ATTR_PARAM_OPERATION_PTR, static_cast(0)); + constexpr SQLULEN param_set_size = 4; + SQLUSMALLINT param_operations[param_set_size] = {SQL_PARAM_PROCEED, SQL_PARAM_IGNORE, + SQL_PARAM_PROCEED, SQL_PARAM_IGNORE}; + + validateSetStmtAttr(this->stmt, SQL_ATTR_PARAM_OPERATION_PTR, param_operations); + + validateGetStmtAttr(this->stmt, SQL_ATTR_PARAM_OPERATION_PTR, param_operations); this->disconnect(); } @@ -661,7 +723,17 @@ TYPED_TEST(FlightSQLODBCTestBase, TestSQLSetStmtAttrParamOperationPtr) { TYPED_TEST(FlightSQLODBCTestBase, TestSQLSetStmtAttrParamStatusPtr) { this->connect(); - validateSetStmtAttr(this->stmt, SQL_ATTR_PARAM_STATUS_PTR, static_cast(0)); + // Driver does not support parameters, so just check array can be saved/retrieved + constexpr SQLULEN param_status_size = 4; + SQLUSMALLINT param_status[param_status_size] = {SQL_PARAM_PROCEED, SQL_PARAM_IGNORE, + SQL_PARAM_PROCEED, SQL_PARAM_IGNORE}; + + validateSetStmtAttr(this->stmt, SQL_ATTR_PARAM_OPERATION_PTR, param_status); + + validateGetStmtAttr(this->stmt, SQL_ATTR_PARAM_OPERATION_PTR, param_status); + + // Driver does not support parameters, so just check that array can be saved and + // retrieved this->disconnect(); } @@ -669,7 +741,11 @@ TYPED_TEST(FlightSQLODBCTestBase, TestSQLSetStmtAttrParamStatusPtr) { TYPED_TEST(FlightSQLODBCTestBase, TestSQLSetStmtAttrParamsProcessedPtr) { this->connect(); - validateSetStmtAttr(this->stmt, SQL_ATTR_PARAMS_PROCESSED_PTR, static_cast(0)); + SQLULEN processed_count = 0; + + validateSetStmtAttr(stmt, SQL_ATTR_PARAMS_PROCESSED_PTR, &processed_count); + + validateGetStmtAttr(stmt, SQL_ATTR_PARAMS_PROCESSED_PTR, &processed_count); this->disconnect(); } @@ -710,7 +786,13 @@ TYPED_TEST(FlightSQLODBCTestBase, TestSQLSetStmtAttrRowArraySize) { TYPED_TEST(FlightSQLODBCTestBase, TestSQLSetStmtAttrRowBindOffsetPtr) { this->connect(); - validateSetStmtAttr(this->stmt, SQL_ATTR_ROW_BIND_OFFSET_PTR, static_cast(0)); + SQLULEN offset = 1000; + + validateSetStmtAttr(this->stmt, SQL_ATTR_ROW_BIND_OFFSET_PTR, + static_cast(&offset)); + + validateGetStmtAttr(this->stmt, SQL_ATTR_ROW_BIND_OFFSET_PTR, + static_cast(&offset)); this->disconnect(); } @@ -736,7 +818,13 @@ TYPED_TEST(FlightSQLODBCTestBase, TestSQLSetStmtAttrRowNumber) { TYPED_TEST(FlightSQLODBCTestBase, TestSQLSetStmtAttrRowOperationPtr) { this->connect(); - validateSetStmtAttr(this->stmt, SQL_ATTR_ROW_OPERATION_PTR, static_cast(0)); + constexpr SQLULEN param_set_size = 4; + SQLUSMALLINT row_operations[param_set_size] = {SQL_ROW_PROCEED, SQL_ROW_IGNORE, + SQL_ROW_PROCEED, SQL_ROW_IGNORE}; + + validateSetStmtAttr(this->stmt, SQL_ATTR_ROW_OPERATION_PTR, row_operations); + + validateGetStmtAttr(this->stmt, SQL_ATTR_ROW_OPERATION_PTR, row_operations); this->disconnect(); } @@ -744,7 +832,14 @@ TYPED_TEST(FlightSQLODBCTestBase, TestSQLSetStmtAttrRowOperationPtr) { TYPED_TEST(FlightSQLODBCTestBase, TestSQLSetStmtAttrRowStatusPtr) { this->connect(); - validateSetStmtAttr(this->stmt, SQL_ATTR_ROW_STATUS_PTR, static_cast(0)); + constexpr SQLULEN row_status_size = 4; + SQLUSMALLINT values[4] = {0, 0, 0, 0}; + + validateSetStmtAttr(this->stmt, SQL_ATTR_ROW_STATUS_PTR, + static_cast(&values)); + + validateGetStmtAttr(this->stmt, SQL_ATTR_ROW_STATUS_PTR, + static_cast(&values)); this->disconnect(); } @@ -752,7 +847,13 @@ TYPED_TEST(FlightSQLODBCTestBase, TestSQLSetStmtAttrRowStatusPtr) { TYPED_TEST(FlightSQLODBCTestBase, TestSQLSetStmtAttrRowsFetchedPtr) { this->connect(); - validateSetStmtAttr(this->stmt, SQL_ATTR_ROWS_FETCHED_PTR, static_cast(0)); + SQLULEN rows_fetched = 1; + + validateSetStmtAttr(this->stmt, SQL_ATTR_ROWS_FETCHED_PTR, + static_cast(&rows_fetched)); + + validateGetStmtAttr(this->stmt, SQL_ATTR_ROWS_FETCHED_PTR, + static_cast(&rows_fetched)); this->disconnect(); } From 44c331ccfbaeb3ef8e0879be9ef41f1f8c0df650 Mon Sep 17 00:00:00 2001 From: rscales Date: Fri, 11 Jul 2025 23:14:06 +0100 Subject: [PATCH 12/18] Fix copy and paste error for SQL_ATTR_PARAM_STATUS_PTR test case --- cpp/src/arrow/flight/sql/odbc/tests/statement_attr_test.cc | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/cpp/src/arrow/flight/sql/odbc/tests/statement_attr_test.cc b/cpp/src/arrow/flight/sql/odbc/tests/statement_attr_test.cc index b9bb491d96a7..56375feec55f 100644 --- a/cpp/src/arrow/flight/sql/odbc/tests/statement_attr_test.cc +++ b/cpp/src/arrow/flight/sql/odbc/tests/statement_attr_test.cc @@ -728,12 +728,9 @@ TYPED_TEST(FlightSQLODBCTestBase, TestSQLSetStmtAttrParamStatusPtr) { SQLUSMALLINT param_status[param_status_size] = {SQL_PARAM_PROCEED, SQL_PARAM_IGNORE, SQL_PARAM_PROCEED, SQL_PARAM_IGNORE}; - validateSetStmtAttr(this->stmt, SQL_ATTR_PARAM_OPERATION_PTR, param_status); + validateSetStmtAttr(this->stmt, SQL_ATTR_PARAM_STATUS_PTR, param_status); - validateGetStmtAttr(this->stmt, SQL_ATTR_PARAM_OPERATION_PTR, param_status); - - // Driver does not support parameters, so just check that array can be saved and - // retrieved + validateGetStmtAttr(this->stmt, SQL_ATTR_PARAM_STATUS_PTR, param_status); this->disconnect(); } From 6cdff259d7ca467ba13cf932cba3a90500e0eebc Mon Sep 17 00:00:00 2001 From: rscales Date: Fri, 11 Jul 2025 23:47:50 +0100 Subject: [PATCH 13/18] Update to address comments from review --- .../flight/sql/odbc/tests/statement_attr_test.cc | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/cpp/src/arrow/flight/sql/odbc/tests/statement_attr_test.cc b/cpp/src/arrow/flight/sql/odbc/tests/statement_attr_test.cc index 56375feec55f..212262f64131 100644 --- a/cpp/src/arrow/flight/sql/odbc/tests/statement_attr_test.cc +++ b/cpp/src/arrow/flight/sql/odbc/tests/statement_attr_test.cc @@ -740,9 +740,11 @@ TYPED_TEST(FlightSQLODBCTestBase, TestSQLSetStmtAttrParamsProcessedPtr) { SQLULEN processed_count = 0; - validateSetStmtAttr(stmt, SQL_ATTR_PARAMS_PROCESSED_PTR, &processed_count); + validateSetStmtAttr(stmt, SQL_ATTR_PARAMS_PROCESSED_PTR, + static_cast(&processed_count)); - validateGetStmtAttr(stmt, SQL_ATTR_PARAMS_PROCESSED_PTR, &processed_count); + validateGetStmtAttr(stmt, SQL_ATTR_PARAMS_PROCESSED_PTR, + static_cast(&processed_count)); this->disconnect(); } @@ -819,9 +821,11 @@ TYPED_TEST(FlightSQLODBCTestBase, TestSQLSetStmtAttrRowOperationPtr) { SQLUSMALLINT row_operations[param_set_size] = {SQL_ROW_PROCEED, SQL_ROW_IGNORE, SQL_ROW_PROCEED, SQL_ROW_IGNORE}; - validateSetStmtAttr(this->stmt, SQL_ATTR_ROW_OPERATION_PTR, row_operations); + validateSetStmtAttr(this->stmt, SQL_ATTR_ROW_OPERATION_PTR, + static_cast(row_operations)); - validateGetStmtAttr(this->stmt, SQL_ATTR_ROW_OPERATION_PTR, row_operations); + validateGetStmtAttr(this->stmt, SQL_ATTR_ROW_OPERATION_PTR, + static_cast(row_operations)); this->disconnect(); } @@ -833,10 +837,10 @@ TYPED_TEST(FlightSQLODBCTestBase, TestSQLSetStmtAttrRowStatusPtr) { SQLUSMALLINT values[4] = {0, 0, 0, 0}; validateSetStmtAttr(this->stmt, SQL_ATTR_ROW_STATUS_PTR, - static_cast(&values)); + static_cast(values)); validateGetStmtAttr(this->stmt, SQL_ATTR_ROW_STATUS_PTR, - static_cast(&values)); + static_cast(values)); this->disconnect(); } From 17f2e16b887307967470b1b00a1b8a64798b6f1b Mon Sep 17 00:00:00 2001 From: rscales Date: Fri, 11 Jul 2025 23:55:40 +0100 Subject: [PATCH 14/18] Add SQLPOINTER static casts where missing --- .../flight/sql/odbc/tests/statement_attr_test.cc | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/cpp/src/arrow/flight/sql/odbc/tests/statement_attr_test.cc b/cpp/src/arrow/flight/sql/odbc/tests/statement_attr_test.cc index 212262f64131..d1da49a423db 100644 --- a/cpp/src/arrow/flight/sql/odbc/tests/statement_attr_test.cc +++ b/cpp/src/arrow/flight/sql/odbc/tests/statement_attr_test.cc @@ -713,9 +713,11 @@ TYPED_TEST(FlightSQLODBCTestBase, TestSQLSetStmtAttrParamOperationPtr) { SQLUSMALLINT param_operations[param_set_size] = {SQL_PARAM_PROCEED, SQL_PARAM_IGNORE, SQL_PARAM_PROCEED, SQL_PARAM_IGNORE}; - validateSetStmtAttr(this->stmt, SQL_ATTR_PARAM_OPERATION_PTR, param_operations); + validateSetStmtAttr(this->stmt, SQL_ATTR_PARAM_OPERATION_PTR, + static_cast(param_operations)); - validateGetStmtAttr(this->stmt, SQL_ATTR_PARAM_OPERATION_PTR, param_operations); + validateGetStmtAttr(this->stmt, SQL_ATTR_PARAM_OPERATION_PTR, + static_cast(param_operations)); this->disconnect(); } @@ -728,9 +730,11 @@ TYPED_TEST(FlightSQLODBCTestBase, TestSQLSetStmtAttrParamStatusPtr) { SQLUSMALLINT param_status[param_status_size] = {SQL_PARAM_PROCEED, SQL_PARAM_IGNORE, SQL_PARAM_PROCEED, SQL_PARAM_IGNORE}; - validateSetStmtAttr(this->stmt, SQL_ATTR_PARAM_STATUS_PTR, param_status); + validateSetStmtAttr(this->stmt, SQL_ATTR_PARAM_STATUS_PTR, + static_cast(param_status)); - validateGetStmtAttr(this->stmt, SQL_ATTR_PARAM_STATUS_PTR, param_status); + validateGetStmtAttr(this->stmt, SQL_ATTR_PARAM_STATUS_PTR, + static_cast(param_status)); this->disconnect(); } From 599ffec631859fc6e35b9d283198a0198cd66522 Mon Sep 17 00:00:00 2001 From: rscales Date: Fri, 11 Jul 2025 23:59:00 +0100 Subject: [PATCH 15/18] Empty commit to force running workflows From 61af11df2e4e786f20fe8cb739ad5288da29cbe4 Mon Sep 17 00:00:00 2001 From: rscales Date: Sat, 12 Jul 2025 00:03:51 +0100 Subject: [PATCH 16/18] Empty commit to force running workflows From db63cdb383babfb51e4b2f4fd186881d9affb63d Mon Sep 17 00:00:00 2001 From: rscales Date: Sat, 12 Jul 2025 00:27:04 +0100 Subject: [PATCH 17/18] Reformat entry points file due to line added by merge --- cpp/src/arrow/flight/sql/odbc/entry_points.cc | 1 - 1 file changed, 1 deletion(-) diff --git a/cpp/src/arrow/flight/sql/odbc/entry_points.cc b/cpp/src/arrow/flight/sql/odbc/entry_points.cc index 308dcfc17880..e7c55d5a73a1 100644 --- a/cpp/src/arrow/flight/sql/odbc/entry_points.cc +++ b/cpp/src/arrow/flight/sql/odbc/entry_points.cc @@ -279,7 +279,6 @@ SQLRETURN SQL_API SQLPrimaryKeys(SQLHSTMT stmt, SQLWCHAR* catalogName, SQLRETURN SQL_API SQLSetStmtAttr(SQLHSTMT stmt, SQLINTEGER attribute, SQLPOINTER valuePtr, SQLINTEGER stringLength) { - return arrow::SQLSetStmtAttr(stmt, attribute, valuePtr, stringLength); } From dfbf53b2778e4b87b4fb91c3e46bcd4f32e36979 Mon Sep 17 00:00:00 2001 From: rscales Date: Mon, 14 Jul 2025 17:58:47 +0100 Subject: [PATCH 18/18] Add missing this reference for statement instance --- cpp/src/arrow/flight/sql/odbc/tests/statement_attr_test.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cpp/src/arrow/flight/sql/odbc/tests/statement_attr_test.cc b/cpp/src/arrow/flight/sql/odbc/tests/statement_attr_test.cc index d1da49a423db..aff661dec00b 100644 --- a/cpp/src/arrow/flight/sql/odbc/tests/statement_attr_test.cc +++ b/cpp/src/arrow/flight/sql/odbc/tests/statement_attr_test.cc @@ -744,10 +744,10 @@ TYPED_TEST(FlightSQLODBCTestBase, TestSQLSetStmtAttrParamsProcessedPtr) { SQLULEN processed_count = 0; - validateSetStmtAttr(stmt, SQL_ATTR_PARAMS_PROCESSED_PTR, + validateSetStmtAttr(this->stmt, SQL_ATTR_PARAMS_PROCESSED_PTR, static_cast(&processed_count)); - validateGetStmtAttr(stmt, SQL_ATTR_PARAMS_PROCESSED_PTR, + validateGetStmtAttr(this->stmt, SQL_ATTR_PARAMS_PROCESSED_PTR, static_cast(&processed_count)); this->disconnect();