diff --git a/cpp/src/arrow/flight/sql/odbc/odbc_api.cc b/cpp/src/arrow/flight/sql/odbc/odbc_api.cc index b4408d26565..2a2290f9323 100644 --- a/cpp/src/arrow/flight/sql/odbc/odbc_api.cc +++ b/cpp/src/arrow/flight/sql/odbc/odbc_api.cc @@ -195,8 +195,19 @@ SQLRETURN SQLGetStmtAttr(SQLHSTMT stmt, SQLINTEGER attribute, SQLPOINTER value_p << ", attribute: " << attribute << ", value_ptr: " << value_ptr << ", buffer_length: " << buffer_length << ", string_length_ptr: " << static_cast(string_length_ptr); - // GH-47710 TODO: Implement SQLGetStmtAttr - return SQL_INVALID_HANDLE; + + using ODBC::ODBCStatement; + + return ODBCStatement::ExecuteWithDiagnostics(stmt, SQL_ERROR, [=]() { + ODBCStatement* statement = reinterpret_cast(stmt); + + bool is_unicode = true; + + statement->GetStmtAttr(attribute, value_ptr, buffer_length, string_length_ptr, + is_unicode); + + return SQL_SUCCESS; + }); } SQLRETURN SQLSetStmtAttr(SQLHSTMT stmt, SQLINTEGER attribute, SQLPOINTER value_ptr, @@ -204,8 +215,18 @@ SQLRETURN SQLSetStmtAttr(SQLHSTMT stmt, SQLINTEGER attribute, SQLPOINTER value_p ARROW_LOG(DEBUG) << "SQLSetStmtAttrW called with stmt: " << stmt << ", attribute: " << attribute << ", value_ptr: " << value_ptr << ", string_length: " << string_length; - // GH-47710 TODO: Implement SQLSetStmtAttr - return SQL_INVALID_HANDLE; + + using ODBC::ODBCStatement; + + return ODBCStatement::ExecuteWithDiagnostics(stmt, SQL_ERROR, [=]() { + ODBCStatement* statement = reinterpret_cast(stmt); + + bool is_unicode = true; + + statement->SetStmtAttr(attribute, value_ptr, string_length, is_unicode); + + return SQL_SUCCESS; + }); } SQLRETURN SQLExecDirect(SQLHSTMT stmt, SQLWCHAR* query_text, SQLINTEGER text_length) { diff --git a/cpp/src/arrow/flight/sql/odbc/odbc_impl/odbc_statement.cc b/cpp/src/arrow/flight/sql/odbc/odbc_impl/odbc_statement.cc index d452e77db1d..66af015680f 100644 --- a/cpp/src/arrow/flight/sql/odbc/odbc_impl/odbc_statement.cc +++ b/cpp/src/arrow/flight/sql/odbc/odbc_impl/odbc_statement.cc @@ -627,7 +627,7 @@ void ODBCStatement::SetStmtAttr(SQLINTEGER statement_attribute, SQLPOINTER value CheckIfAttributeIsSetToOnlyValidValue(value, static_cast(SQL_UB_OFF)); return; case SQL_ATTR_RETRIEVE_DATA: - CheckIfAttributeIsSetToOnlyValidValue(value, static_cast(SQL_TRUE)); + CheckIfAttributeIsSetToOnlyValidValue(value, static_cast(SQL_RD_ON)); return; case SQL_ROWSET_SIZE: SetAttribute(value, rowset_size_); diff --git a/cpp/src/arrow/flight/sql/odbc/tests/CMakeLists.txt b/cpp/src/arrow/flight/sql/odbc/tests/CMakeLists.txt index 4bc240637e7..7be318039db 100644 --- a/cpp/src/arrow/flight/sql/odbc/tests/CMakeLists.txt +++ b/cpp/src/arrow/flight/sql/odbc/tests/CMakeLists.txt @@ -34,6 +34,7 @@ add_arrow_test(flight_sql_odbc_test SOURCES odbc_test_suite.cc odbc_test_suite.h + statement_attr_test.cc connection_test.cc # Enable Protobuf cleanup after test execution # GH-46889: move protobuf_test_util to a more common location 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 00000000000..85cb2e96b59 --- /dev/null +++ b/cpp/src/arrow/flight/sql/odbc/tests/statement_attr_test.cc @@ -0,0 +1,585 @@ +// 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/odbc_impl/odbc_statement.h" +#include "arrow/flight/sql/odbc/odbc_impl/spi/statement.h" + +#include "arrow/flight/sql/odbc/odbc_impl/platform.h" + +#include +#include +#include + +#include + +namespace arrow::flight::sql::odbc { + +template +class StatementAttributeTest : public T {}; + +using TestTypes = + ::testing::Types; +TYPED_TEST_SUITE(StatementAttributeTest, TestTypes); + +namespace { +// Helper Functions + +// Validate SQLULEN return value +void ValidateGetStmtAttr(SQLHSTMT statement, SQLINTEGER attribute, + SQLULEN expected_value) { + SQLULEN value = 0; + SQLINTEGER string_length = 0; + + ASSERT_EQ(SQL_SUCCESS, + SQLGetStmtAttr(statement, attribute, &value, sizeof(value), &string_length)); + + EXPECT_EQ(expected_value, value); +} + +// Validate SQLLEN return value +void ValidateGetStmtAttr(SQLHSTMT statement, SQLINTEGER attribute, + SQLLEN expected_value) { + SQLLEN value = 0; + SQLINTEGER string_length = 0; + + ASSERT_EQ(SQL_SUCCESS, + SQLGetStmtAttr(statement, attribute, &value, sizeof(value), &string_length)); + + EXPECT_EQ(expected_value, value); +} + +// Validate SQLPOINTER return value +void ValidateGetStmtAttr(SQLHSTMT statement, SQLINTEGER attribute, + SQLPOINTER expected_value) { + SQLPOINTER value = nullptr; + SQLINTEGER string_length = 0; + + ASSERT_EQ(SQL_SUCCESS, + SQLGetStmtAttr(statement, attribute, &value, sizeof(value), &string_length)); + + EXPECT_EQ(expected_value, value); +} + +// Validate unsigned length SQLULEN return value is greater than +void ValidateGetStmtAttrGreaterThan(SQLHSTMT statement, SQLINTEGER attribute, + SQLULEN compared_value) { + SQLULEN value = 0; + SQLINTEGER string_length_ptr; + + ASSERT_EQ(SQL_SUCCESS, + SQLGetStmtAttr(statement, attribute, &value, 0, &string_length_ptr)); + + EXPECT_GT(value, compared_value); +} + +// Validate error return value and code +void ValidateGetStmtAttrErrorCode(SQLHSTMT statement, SQLINTEGER attribute, + std::string_view error_code) { + SQLULEN value = 0; + SQLINTEGER string_length_ptr; + + ASSERT_EQ(SQL_ERROR, + SQLGetStmtAttr(statement, attribute, &value, 0, &string_length_ptr)); + + VerifyOdbcErrorState(SQL_HANDLE_STMT, statement, error_code); +} + +// Validate return value for call to SQLSetStmtAttr with SQLULEN +void ValidateSetStmtAttr(SQLHSTMT statement, SQLINTEGER attribute, SQLULEN new_value) { + SQLINTEGER string_length_ptr = sizeof(SQLULEN); + + EXPECT_EQ(SQL_SUCCESS, + SQLSetStmtAttr(statement, attribute, reinterpret_cast(new_value), + string_length_ptr)); +} + +// Validate return value for call to SQLSetStmtAttr with SQLLEN +void ValidateSetStmtAttr(SQLHSTMT statement, SQLINTEGER attribute, SQLLEN new_value) { + SQLINTEGER string_length_ptr = sizeof(SQLLEN); + + EXPECT_EQ(SQL_SUCCESS, + SQLSetStmtAttr(statement, attribute, reinterpret_cast(new_value), + string_length_ptr)); +} + +// Validate return value for call to SQLSetStmtAttr with SQLPOINTER +void ValidateSetStmtAttr(SQLHSTMT statement, SQLINTEGER attribute, SQLPOINTER value) { + EXPECT_EQ(SQL_SUCCESS, SQLSetStmtAttr(statement, attribute, value, 0)); +} + +// Validate error return value and code +void ValidateSetStmtAttrErrorCode(SQLHSTMT statement, SQLINTEGER attribute, + SQLULEN new_value, std::string_view error_code) { + SQLINTEGER string_length_ptr = sizeof(SQLULEN); + + ASSERT_EQ(SQL_ERROR, + SQLSetStmtAttr(statement, attribute, reinterpret_cast(new_value), + string_length_ptr)); + + VerifyOdbcErrorState(SQL_HANDLE_STMT, statement, error_code); +} +} // namespace + +// Test Cases + +TYPED_TEST(StatementAttributeTest, TestSQLGetStmtAttrAppParamDesc) { + ValidateGetStmtAttrGreaterThan(this->stmt, SQL_ATTR_APP_PARAM_DESC, + static_cast(0)); +} + +TYPED_TEST(StatementAttributeTest, TestSQLGetStmtAttrAppRowDesc) { + ValidateGetStmtAttrGreaterThan(this->stmt, SQL_ATTR_APP_ROW_DESC, + static_cast(0)); +} + +TYPED_TEST(StatementAttributeTest, TestSQLGetStmtAttrAsyncEnable) { + ValidateGetStmtAttr(this->stmt, SQL_ATTR_ASYNC_ENABLE, + static_cast(SQL_ASYNC_ENABLE_OFF)); +} + +#ifdef SQL_ATTR_ASYNC_STMT_EVENT +TYPED_TEST(StatementAttributeTest, TestSQLGetStmtAttrAsyncStmtEventUnsupported) { + // Optional feature not implemented + ValidateGetStmtAttrErrorCode(this->stmt, SQL_ATTR_ASYNC_STMT_EVENT, kErrorStateHYC00); +} +#endif + +#ifdef SQL_ATTR_ASYNC_STMT_PCALLBACK +TYPED_TEST(StatementAttributeTest, TestSQLGetStmtAttrAsyncStmtPCCallbackUnsupported) { + // Optional feature not implemented + ValidateGetStmtAttrErrorCode(this->stmt, SQL_ATTR_ASYNC_STMT_PCALLBACK, + kErrorStateHYC00); +} +#endif + +#ifdef SQL_ATTR_ASYNC_STMT_PCONTEXT +TYPED_TEST(StatementAttributeTest, TestSQLGetStmtAttrAsyncStmtPCContextUnsupported) { + // Optional feature not implemented + ValidateGetStmtAttrErrorCode(this->stmt, SQL_ATTR_ASYNC_STMT_PCONTEXT, + kErrorStateHYC00); +} +#endif + +TYPED_TEST(StatementAttributeTest, TestSQLGetStmtAttrConcurrency) { + ValidateGetStmtAttr(this->stmt, SQL_ATTR_CONCURRENCY, + static_cast(SQL_CONCUR_READ_ONLY)); +} + +TYPED_TEST(StatementAttributeTest, TestSQLGetStmtAttrCursorScrollable) { + ValidateGetStmtAttr(this->stmt, SQL_ATTR_CURSOR_SCROLLABLE, + static_cast(SQL_NONSCROLLABLE)); +} + +TYPED_TEST(StatementAttributeTest, TestSQLGetStmtAttrCursorSensitivity) { + ValidateGetStmtAttr(this->stmt, SQL_ATTR_CURSOR_SENSITIVITY, + static_cast(SQL_UNSPECIFIED)); +} + +TYPED_TEST(StatementAttributeTest, TestSQLGetStmtAttrCursorType) { + ValidateGetStmtAttr(this->stmt, SQL_ATTR_CURSOR_TYPE, + static_cast(SQL_CURSOR_FORWARD_ONLY)); +} + +TYPED_TEST(StatementAttributeTest, TestSQLGetStmtAttrEnableAutoIPD) { + ValidateGetStmtAttr(this->stmt, SQL_ATTR_ENABLE_AUTO_IPD, + static_cast(SQL_FALSE)); +} + +TYPED_TEST(StatementAttributeTest, TestSQLGetStmtAttrFetchBookmarkPointer) { + ValidateGetStmtAttr(this->stmt, SQL_ATTR_FETCH_BOOKMARK_PTR, static_cast(NULL)); +} + +TYPED_TEST(StatementAttributeTest, TestSQLGetStmtAttrIMPParamDesc) { + ValidateGetStmtAttrGreaterThan(this->stmt, SQL_ATTR_IMP_PARAM_DESC, + static_cast(0)); +} + +TYPED_TEST(StatementAttributeTest, TestSQLGetStmtAttrIMPRowDesc) { + ValidateGetStmtAttrGreaterThan(this->stmt, SQL_ATTR_IMP_ROW_DESC, + static_cast(0)); +} + +TYPED_TEST(StatementAttributeTest, TestSQLGetStmtAttrKeysetSize) { + ValidateGetStmtAttr(this->stmt, SQL_ATTR_KEYSET_SIZE, static_cast(0)); +} + +TYPED_TEST(StatementAttributeTest, TestSQLGetStmtAttrMaxLength) { + ValidateGetStmtAttr(this->stmt, SQL_ATTR_MAX_LENGTH, static_cast(0)); +} + +TYPED_TEST(StatementAttributeTest, TestSQLGetStmtAttrMaxRows) { + ValidateGetStmtAttr(this->stmt, SQL_ATTR_MAX_ROWS, static_cast(0)); +} + +TYPED_TEST(StatementAttributeTest, TestSQLGetStmtAttrMetadataID) { + ValidateGetStmtAttr(this->stmt, SQL_ATTR_METADATA_ID, static_cast(SQL_FALSE)); +} + +TYPED_TEST(StatementAttributeTest, TestSQLGetStmtAttrNoscan) { + ValidateGetStmtAttr(this->stmt, SQL_ATTR_NOSCAN, static_cast(SQL_NOSCAN_OFF)); +} + +TYPED_TEST(StatementAttributeTest, TestSQLGetStmtAttrParamBindOffsetPtr) { + ValidateGetStmtAttr(this->stmt, SQL_ATTR_PARAM_BIND_OFFSET_PTR, + static_cast(nullptr)); +} + +TYPED_TEST(StatementAttributeTest, TestSQLGetStmtAttrParamBindType) { + ValidateGetStmtAttr(this->stmt, SQL_ATTR_PARAM_BIND_TYPE, + static_cast(SQL_PARAM_BIND_BY_COLUMN)); +} + +TYPED_TEST(StatementAttributeTest, TestSQLGetStmtAttrParamOperationPtr) { + ValidateGetStmtAttr(this->stmt, SQL_ATTR_PARAM_OPERATION_PTR, + static_cast(nullptr)); +} + +TYPED_TEST(StatementAttributeTest, TestSQLGetStmtAttrParamStatusPtr) { + ValidateGetStmtAttr(this->stmt, SQL_ATTR_PARAM_STATUS_PTR, + static_cast(nullptr)); +} + +TYPED_TEST(StatementAttributeTest, TestSQLGetStmtAttrParamsProcessedPtr) { + ValidateGetStmtAttr(this->stmt, SQL_ATTR_PARAMS_PROCESSED_PTR, + static_cast(nullptr)); +} + +TYPED_TEST(StatementAttributeTest, TestSQLGetStmtAttrParamsetSize) { + ValidateGetStmtAttr(this->stmt, SQL_ATTR_PARAMSET_SIZE, static_cast(1)); +} + +TYPED_TEST(StatementAttributeTest, TestSQLGetStmtAttrQueryTimeout) { + ValidateGetStmtAttr(this->stmt, SQL_ATTR_QUERY_TIMEOUT, static_cast(0)); +} + +TYPED_TEST(StatementAttributeTest, TestSQLGetStmtAttrRetrieveData) { + ValidateGetStmtAttr(this->stmt, SQL_ATTR_RETRIEVE_DATA, + static_cast(SQL_RD_ON)); +} + +TYPED_TEST(StatementAttributeTest, TestSQLGetStmtAttrRowArraySize) { + ValidateGetStmtAttr(this->stmt, SQL_ATTR_ROW_ARRAY_SIZE, static_cast(1)); +} + +TYPED_TEST(StatementAttributeTest, TestSQLGetStmtAttrRowBindOffsetPtr) { + ValidateGetStmtAttr(this->stmt, SQL_ATTR_ROW_BIND_OFFSET_PTR, + static_cast(nullptr)); +} + +TYPED_TEST(StatementAttributeTest, TestSQLGetStmtAttrRowBindType) { + ValidateGetStmtAttr(this->stmt, SQL_ATTR_ROW_BIND_TYPE, static_cast(0)); +} + +TYPED_TEST(StatementAttributeTest, TestSQLGetStmtAttrRowNumber) { + std::wstring wsql = L"SELECT 1;"; + std::vector sql0(wsql.begin(), wsql.end()); + + ASSERT_EQ(SQL_SUCCESS, + SQLExecDirect(this->stmt, &sql0[0], static_cast(sql0.size()))); + + ASSERT_EQ(SQL_SUCCESS, SQLFetch(this->stmt)); + + ValidateGetStmtAttr(this->stmt, SQL_ATTR_ROW_NUMBER, static_cast(1)); +} + +TYPED_TEST(StatementAttributeTest, TestSQLGetStmtAttrRowOperationPtr) { + ValidateGetStmtAttr(this->stmt, SQL_ATTR_ROW_OPERATION_PTR, + static_cast(nullptr)); +} + +TYPED_TEST(StatementAttributeTest, TestSQLGetStmtAttrRowStatusPtr) { + ValidateGetStmtAttr(this->stmt, SQL_ATTR_ROW_STATUS_PTR, + static_cast(nullptr)); +} + +TYPED_TEST(StatementAttributeTest, TestSQLGetStmtAttrRowsFetchedPtr) { + ValidateGetStmtAttr(this->stmt, SQL_ATTR_ROWS_FETCHED_PTR, + static_cast(nullptr)); +} + +TYPED_TEST(StatementAttributeTest, TestSQLGetStmtAttrSimulateCursor) { + ValidateGetStmtAttr(this->stmt, SQL_ATTR_SIMULATE_CURSOR, + static_cast(SQL_SC_UNIQUE)); +} + +TYPED_TEST(StatementAttributeTest, TestSQLGetStmtAttrUseBookmarks) { + ValidateGetStmtAttr(this->stmt, SQL_ATTR_USE_BOOKMARKS, + static_cast(SQL_UB_OFF)); +} + +// This is a pre ODBC 3 attribute +TYPED_TEST(StatementAttributeTest, TestSQLGetStmtAttrRowsetSize) { + ValidateGetStmtAttr(this->stmt, SQL_ROWSET_SIZE, static_cast(1)); +} + +TYPED_TEST(StatementAttributeTest, TestSQLSetStmtAttrAppParamDesc) { + SQLULEN app_param_desc = 0; + SQLINTEGER string_length_ptr; + + ASSERT_EQ(SQL_SUCCESS, SQLGetStmtAttr(this->stmt, SQL_ATTR_APP_PARAM_DESC, + &app_param_desc, 0, &string_length_ptr)); + + ValidateSetStmtAttr(this->stmt, SQL_ATTR_APP_PARAM_DESC, static_cast(0)); + + ValidateSetStmtAttr(this->stmt, SQL_ATTR_APP_PARAM_DESC, + static_cast(app_param_desc)); +} + +TYPED_TEST(StatementAttributeTest, TestSQLSetStmtAttrAppRowDesc) { + SQLULEN app_row_desc = 0; + SQLINTEGER string_length_ptr; + + ASSERT_EQ(SQL_SUCCESS, SQLGetStmtAttr(this->stmt, SQL_ATTR_APP_ROW_DESC, &app_row_desc, + 0, &string_length_ptr)); + + ValidateSetStmtAttr(this->stmt, SQL_ATTR_APP_ROW_DESC, static_cast(0)); + + ValidateSetStmtAttr(this->stmt, SQL_ATTR_APP_ROW_DESC, + static_cast(app_row_desc)); +} + +#ifdef SQL_ATTR_ASYNC_ENABLE +TYPED_TEST(StatementAttributeTest, TestSQLSetStmtAttrAsyncEnableUnsupported) { + // Optional feature not implemented + ValidateSetStmtAttrErrorCode(this->stmt, SQL_ATTR_ASYNC_ENABLE, SQL_ASYNC_ENABLE_OFF, + kErrorStateHYC00); +} +#endif + +#ifdef SQL_ATTR_ASYNC_STMT_EVENT +TYPED_TEST(StatementAttributeTest, TestSQLSetStmtAttrAsyncStmtEventUnsupported) { + // Driver does not support asynchronous notification + ValidateSetStmtAttrErrorCode(this->stmt, SQL_ATTR_ASYNC_STMT_EVENT, 0, + kErrorStateHY118); +} +#endif + +#ifdef SQL_ATTR_ASYNC_STMT_PCALLBACK +TYPED_TEST(StatementAttributeTest, TestSQLSetStmtAttrAsyncStmtPCCallbackUnsupported) { + ValidateSetStmtAttrErrorCode(this->stmt, SQL_ATTR_ASYNC_STMT_PCALLBACK, 0, + kErrorStateHYC00); +} +#endif + +#ifdef SQL_ATTR_ASYNC_STMT_PCONTEXT +TYPED_TEST(StatementAttributeTest, TestSQLSetStmtAttrAsyncStmtPCContextUnsupported) { + // Optional feature not implemented + ValidateSetStmtAttrErrorCode(this->stmt, SQL_ATTR_ASYNC_STMT_PCONTEXT, 0, + kErrorStateHYC00); +} +#endif + +TYPED_TEST(StatementAttributeTest, TestSQLSetStmtAttrConcurrency) { + ValidateSetStmtAttr(this->stmt, SQL_ATTR_CONCURRENCY, + static_cast(SQL_CONCUR_READ_ONLY)); +} + +TYPED_TEST(StatementAttributeTest, TestSQLSetStmtAttrCursorScrollable) { + ValidateSetStmtAttr(this->stmt, SQL_ATTR_CURSOR_SCROLLABLE, + static_cast(SQL_NONSCROLLABLE)); +} + +TYPED_TEST(StatementAttributeTest, TestSQLSetStmtAttrCursorSensitivity) { + ValidateSetStmtAttr(this->stmt, SQL_ATTR_CURSOR_SENSITIVITY, + static_cast(SQL_UNSPECIFIED)); +} + +TYPED_TEST(StatementAttributeTest, TestSQLSetStmtAttrCursorType) { + ValidateSetStmtAttr(this->stmt, SQL_ATTR_CURSOR_TYPE, + static_cast(SQL_CURSOR_FORWARD_ONLY)); +} + +TYPED_TEST(StatementAttributeTest, TestSQLSetStmtAttrEnableAutoIPD) { + ValidateSetStmtAttr(this->stmt, SQL_ATTR_ENABLE_AUTO_IPD, + static_cast(SQL_FALSE)); +} + +TYPED_TEST(StatementAttributeTest, TestSQLSetStmtAttrFetchBookmarkPointer) { + ValidateSetStmtAttr(this->stmt, SQL_ATTR_FETCH_BOOKMARK_PTR, static_cast(NULL)); +} + +TYPED_TEST(StatementAttributeTest, TestSQLSetStmtAttrIMPParamDesc) { + // Invalid use of an automatically allocated descriptor handle + ValidateSetStmtAttrErrorCode(this->stmt, SQL_ATTR_IMP_PARAM_DESC, + static_cast(0), kErrorStateHY017); +} + +TYPED_TEST(StatementAttributeTest, TestSQLSetStmtAttrIMPRowDesc) { + // Invalid use of an automatically allocated descriptor handle + ValidateSetStmtAttrErrorCode(this->stmt, SQL_ATTR_IMP_ROW_DESC, static_cast(0), + kErrorStateHY017); +} + +TYPED_TEST(StatementAttributeTest, TestSQLSetStmtAttrKeysetSizeUnsupported) { + ValidateSetStmtAttr(this->stmt, SQL_ATTR_KEYSET_SIZE, static_cast(0)); +} + +TYPED_TEST(StatementAttributeTest, TestSQLSetStmtAttrMaxLength) { + ValidateSetStmtAttr(this->stmt, SQL_ATTR_MAX_LENGTH, static_cast(0)); +} + +TYPED_TEST(StatementAttributeTest, TestSQLSetStmtAttrMaxRows) { + // Cannot set read-only attribute + ValidateSetStmtAttrErrorCode(this->stmt, SQL_ATTR_MAX_ROWS, static_cast(0), + kErrorStateHY092); +} + +TYPED_TEST(StatementAttributeTest, TestSQLSetStmtAttrMetadataID) { + ValidateSetStmtAttr(this->stmt, SQL_ATTR_METADATA_ID, static_cast(SQL_FALSE)); +} + +TYPED_TEST(StatementAttributeTest, TestSQLSetStmtAttrNoscan) { + ValidateSetStmtAttr(this->stmt, SQL_ATTR_NOSCAN, static_cast(SQL_NOSCAN_OFF)); +} + +TYPED_TEST(StatementAttributeTest, TestSQLSetStmtAttrParamBindOffsetPtr) { + SQLULEN offset = 1000; + + ValidateSetStmtAttr(this->stmt, SQL_ATTR_PARAM_BIND_OFFSET_PTR, + static_cast(&offset)); + + ValidateGetStmtAttr(this->stmt, SQL_ATTR_PARAM_BIND_OFFSET_PTR, + static_cast(&offset)); +} + +TYPED_TEST(StatementAttributeTest, TestSQLSetStmtAttrParamBindType) { + ValidateSetStmtAttr(this->stmt, SQL_ATTR_PARAM_BIND_TYPE, + static_cast(SQL_PARAM_BIND_BY_COLUMN)); +} + +TYPED_TEST(StatementAttributeTest, TestSQLSetStmtAttrParamOperationPtr) { + 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, + static_cast(param_operations)); + + ValidateGetStmtAttr(this->stmt, SQL_ATTR_PARAM_OPERATION_PTR, + static_cast(param_operations)); +} + +TYPED_TEST(StatementAttributeTest, TestSQLSetStmtAttrParamStatusPtr) { + // 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_STATUS_PTR, + static_cast(param_status)); + + ValidateGetStmtAttr(this->stmt, SQL_ATTR_PARAM_STATUS_PTR, + static_cast(param_status)); +} + +TYPED_TEST(StatementAttributeTest, TestSQLSetStmtAttrParamsProcessedPtr) { + SQLULEN processed_count = 0; + + ValidateSetStmtAttr(this->stmt, SQL_ATTR_PARAMS_PROCESSED_PTR, + static_cast(&processed_count)); + + ValidateGetStmtAttr(this->stmt, SQL_ATTR_PARAMS_PROCESSED_PTR, + static_cast(&processed_count)); +} + +TYPED_TEST(StatementAttributeTest, TestSQLSetStmtAttrParamsetSize) { + ValidateSetStmtAttr(this->stmt, SQL_ATTR_PARAMSET_SIZE, static_cast(1)); +} + +TYPED_TEST(StatementAttributeTest, TestSQLSetStmtAttrQueryTimeout) { + ValidateSetStmtAttr(this->stmt, SQL_ATTR_QUERY_TIMEOUT, static_cast(1)); +} + +TYPED_TEST(StatementAttributeTest, TestSQLSetStmtAttrRetrieveData) { + ValidateSetStmtAttr(this->stmt, SQL_ATTR_RETRIEVE_DATA, + static_cast(SQL_RD_ON)); +} + +TYPED_TEST(StatementAttributeTest, TestSQLSetStmtAttrRowArraySize) { + ValidateSetStmtAttr(this->stmt, SQL_ATTR_ROW_ARRAY_SIZE, static_cast(1)); +} + +TYPED_TEST(StatementAttributeTest, TestSQLSetStmtAttrRowBindOffsetPtr) { + 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)); +} + +TYPED_TEST(StatementAttributeTest, TestSQLSetStmtAttrRowBindType) { + ValidateSetStmtAttr(this->stmt, SQL_ATTR_ROW_BIND_TYPE, static_cast(0)); +} + +TYPED_TEST(StatementAttributeTest, TestSQLSetStmtAttrRowNumber) { + // Cannot set read-only attribute + ValidateSetStmtAttrErrorCode(this->stmt, SQL_ATTR_ROW_NUMBER, static_cast(0), + kErrorStateHY092); +} + +TYPED_TEST(StatementAttributeTest, TestSQLSetStmtAttrRowOperationPtr) { + 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, + static_cast(row_operations)); + + ValidateGetStmtAttr(this->stmt, SQL_ATTR_ROW_OPERATION_PTR, + static_cast(row_operations)); +} + +TYPED_TEST(StatementAttributeTest, TestSQLSetStmtAttrRowStatusPtr) { + 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)); +} + +TYPED_TEST(StatementAttributeTest, TestSQLSetStmtAttrRowsFetchedPtr) { + 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)); +} + +TYPED_TEST(StatementAttributeTest, TestSQLSetStmtAttrSimulateCursor) { + ValidateSetStmtAttr(this->stmt, SQL_ATTR_SIMULATE_CURSOR, + static_cast(SQL_SC_UNIQUE)); +} + +TYPED_TEST(StatementAttributeTest, TestSQLSetStmtAttrUseBookmarks) { + ValidateSetStmtAttr(this->stmt, SQL_ATTR_USE_BOOKMARKS, + static_cast(SQL_UB_OFF)); +} + +// This is a pre ODBC 3 attribute +TYPED_TEST(StatementAttributeTest, TestSQLSetStmtAttrRowsetSize) { + ValidateSetStmtAttr(this->stmt, SQL_ROWSET_SIZE, static_cast(1)); +} + +} // namespace arrow::flight::sql::odbc