From 404d074c5f270852f5b4c1a22e7fa57a33fd07b1 Mon Sep 17 00:00:00 2001 From: rscales Date: Thu, 19 Jun 2025 04:02:11 +0100 Subject: [PATCH 01/29] Implement SQLGetInfo --- cpp/src/arrow/flight/sql/odbc/odbc_api.cc | 28 +- .../odbc_impl/odbc_connection.h | 4 +- .../odbc_impl/odbc_connection.cc | 122 +++-- .../flight/sql/odbc/tests/CMakeLists.txt | 1 + .../sql/odbc/tests/connection_info_test.cc | 464 ++++++++++++++++++ 5 files changed, 541 insertions(+), 78 deletions(-) create mode 100644 cpp/src/arrow/flight/sql/odbc/tests/connection_info_test.cc diff --git a/cpp/src/arrow/flight/sql/odbc/odbc_api.cc b/cpp/src/arrow/flight/sql/odbc/odbc_api.cc index 1e59791f2e25..68f506327a5e 100644 --- a/cpp/src/arrow/flight/sql/odbc/odbc_api.cc +++ b/cpp/src/arrow/flight/sql/odbc/odbc_api.cc @@ -846,27 +846,33 @@ SQLRETURN SQLDisconnect(SQLHDBC conn) { } SQLRETURN SQLGetInfo(SQLHDBC conn, SQLUSMALLINT infoType, SQLPOINTER infoValuePtr, - SQLSMALLINT bufLen, SQLSMALLINT* length) { + SQLSMALLINT bufLen, SQLSMALLINT* stringLengthPtr) { // TODO: complete implementation of SQLGetInfoW and write tests using ODBC::ODBCConnection; + // Set character type to be Unicode by default + const bool isUnicode = true; + LOG_DEBUG( "SQLGetInfoW called with conn: {}, infoType: {}, infoValuePtr: {}, bufLen: {}, " - "length: {}", - conn, infoType, infoValuePtr, bufLen, fmt::ptr(length)); + "stringLengthPtr: {}", + conn, infoType, infoValuePtr, bufLen, fmt::ptr(stringLengthPtr)); + + if (!conn) { + return SQL_INVALID_HANDLE; + } + + if (!infoValuePtr && !stringLengthPtr) { + return SQL_ERROR; + } return ODBCConnection::ExecuteWithDiagnostics(conn, SQL_ERROR, [=]() { ODBCConnection* connection = reinterpret_cast(conn); - // Partially stubbed implementation of SQLGetInfoW - if (infoType == SQL_DRIVER_ODBC_VER) { - std::string_view ver("03.80"); - - return ODBC::GetStringAttribute(true, ver, true, infoValuePtr, bufLen, length, - connection->GetDiagnostics()); - } + // TODO Validate buffer - return static_cast(SQL_ERROR); + return connection->GetInfo(infoType, infoValuePtr, bufLen, stringLengthPtr, + isUnicode); }); } diff --git a/cpp/src/arrow/flight/sql/odbc/odbcabstraction/include/odbcabstraction/odbc_impl/odbc_connection.h b/cpp/src/arrow/flight/sql/odbc/odbcabstraction/include/odbcabstraction/odbc_impl/odbc_connection.h index 92946e5a0a59..0e9498bcb8a0 100644 --- a/cpp/src/arrow/flight/sql/odbc/odbcabstraction/include/odbcabstraction/odbc_impl/odbc_connection.h +++ b/cpp/src/arrow/flight/sql/odbc/odbcabstraction/include/odbcabstraction/odbc_impl/odbc_connection.h @@ -60,8 +60,8 @@ class ODBCConnection : public ODBCHandle { const driver::odbcabstraction::Connection::ConnPropertyMap& properties, std::vector& missing_properties); - void GetInfo(SQLUSMALLINT infoType, SQLPOINTER value, SQLSMALLINT bufferLength, - SQLSMALLINT* outputLength, bool isUnicode); + SQLRETURN GetInfo(SQLUSMALLINT infoType, SQLPOINTER value, SQLSMALLINT bufferLength, + SQLSMALLINT* outputLength, bool isUnicode); void SetConnectAttr(SQLINTEGER attribute, SQLPOINTER value, SQLINTEGER stringLength, bool isUnicode); SQLRETURN GetConnectAttr(SQLINTEGER attribute, SQLPOINTER value, diff --git a/cpp/src/arrow/flight/sql/odbc/odbcabstraction/odbc_impl/odbc_connection.cc b/cpp/src/arrow/flight/sql/odbc/odbcabstraction/odbc_impl/odbc_connection.cc index c98674094070..cc5dab9b8b49 100644 --- a/cpp/src/arrow/flight/sql/odbc/odbcabstraction/odbc_impl/odbc_connection.cc +++ b/cpp/src/arrow/flight/sql/odbc/odbcabstraction/odbc_impl/odbc_connection.cc @@ -136,152 +136,144 @@ void ODBCConnection::connect(std::string dsn, m_attributeTrackingStatement = std::make_shared(*this, spiStatement); } -void ODBCConnection::GetInfo(SQLUSMALLINT infoType, SQLPOINTER value, - SQLSMALLINT bufferLength, SQLSMALLINT* outputLength, - bool isUnicode) { +SQLRETURN ODBCConnection::GetInfo(SQLUSMALLINT infoType, SQLPOINTER value, + SQLSMALLINT bufferLength, SQLSMALLINT* outputLength, + bool isUnicode) { switch (infoType) { case SQL_ACTIVE_ENVIRONMENTS: GetAttribute(static_cast(0), value, bufferLength, outputLength); - break; + return SQL_SUCCESS; #ifdef SQL_ASYNC_DBC_FUNCTIONS case SQL_ASYNC_DBC_FUNCTIONS: GetAttribute(static_cast(SQL_ASYNC_DBC_NOT_CAPABLE), value, bufferLength, outputLength); - break; + return SQL_SUCCESS; #endif case SQL_ASYNC_MODE: GetAttribute(static_cast(SQL_AM_NONE), value, bufferLength, outputLength); - break; + return SQL_SUCCESS; #ifdef SQL_ASYNC_NOTIFICATION case SQL_ASYNC_NOTIFICATION: GetAttribute(static_cast(SQL_ASYNC_NOTIFICATION_NOT_CAPABLE), value, bufferLength, outputLength); - break; + return SQL_SUCCESS; #endif case SQL_BATCH_ROW_COUNT: GetAttribute(static_cast(0), value, bufferLength, outputLength); - break; + return SQL_SUCCESS; case SQL_BATCH_SUPPORT: GetAttribute(static_cast(0), value, bufferLength, outputLength); - break; + return SQL_SUCCESS; case SQL_DATA_SOURCE_NAME: - GetStringAttribute(isUnicode, m_dsn, true, value, bufferLength, outputLength, - GetDiagnostics()); - break; + return GetStringAttribute(isUnicode, m_dsn, true, value, bufferLength, outputLength, + GetDiagnostics()); case SQL_DRIVER_ODBC_VER: - GetStringAttribute(isUnicode, "03.80", true, value, bufferLength, outputLength, - GetDiagnostics()); - break; + return GetStringAttribute(isUnicode, "03.80", true, value, bufferLength, outputLength, + GetDiagnostics()); case SQL_DYNAMIC_CURSOR_ATTRIBUTES1: GetAttribute(static_cast(0), value, bufferLength, outputLength); - break; + return SQL_SUCCESS; case SQL_DYNAMIC_CURSOR_ATTRIBUTES2: GetAttribute(static_cast(0), value, bufferLength, outputLength); - break; + return SQL_SUCCESS; case SQL_FORWARD_ONLY_CURSOR_ATTRIBUTES1: GetAttribute(static_cast(SQL_CA1_NEXT), value, bufferLength, outputLength); - break; + return SQL_SUCCESS; case SQL_FORWARD_ONLY_CURSOR_ATTRIBUTES2: GetAttribute(static_cast(SQL_CA2_READ_ONLY_CONCURRENCY), value, bufferLength, outputLength); - break; + return SQL_SUCCESS; case SQL_FILE_USAGE: GetAttribute(static_cast(SQL_FILE_NOT_SUPPORTED), value, bufferLength, outputLength); - break; + return SQL_SUCCESS; case SQL_KEYSET_CURSOR_ATTRIBUTES1: GetAttribute(static_cast(0), value, bufferLength, outputLength); - break; + return SQL_SUCCESS; case SQL_KEYSET_CURSOR_ATTRIBUTES2: GetAttribute(static_cast(0), value, bufferLength, outputLength); - break; + return SQL_SUCCESS; case SQL_MAX_ASYNC_CONCURRENT_STATEMENTS: GetAttribute(static_cast(0), value, bufferLength, outputLength); - break; + return SQL_SUCCESS; case SQL_ODBC_INTERFACE_CONFORMANCE: GetAttribute(static_cast(SQL_OIC_CORE), value, bufferLength, outputLength); - break; + return SQL_SUCCESS; // case SQL_ODBC_STANDARD_CLI_CONFORMANCE: - mentioned in SQLGetInfo spec with no // description and there is no constant for this. case SQL_PARAM_ARRAY_ROW_COUNTS: GetAttribute(static_cast(SQL_PARC_NO_BATCH), value, bufferLength, outputLength); - break; + return SQL_SUCCESS; case SQL_PARAM_ARRAY_SELECTS: GetAttribute(static_cast(SQL_PAS_NO_SELECT), value, bufferLength, outputLength); - break; + return SQL_SUCCESS; case SQL_ROW_UPDATES: - GetStringAttribute(isUnicode, "N", true, value, bufferLength, outputLength, - GetDiagnostics()); - break; + return GetStringAttribute(isUnicode, "N", true, value, bufferLength, outputLength, + GetDiagnostics()); case SQL_SCROLL_OPTIONS: GetAttribute(static_cast(SQL_SO_FORWARD_ONLY), value, bufferLength, outputLength); - break; + return SQL_SUCCESS; case SQL_STATIC_CURSOR_ATTRIBUTES1: GetAttribute(static_cast(0), value, bufferLength, outputLength); - break; + return SQL_SUCCESS; case SQL_STATIC_CURSOR_ATTRIBUTES2: GetAttribute(static_cast(0), value, bufferLength, outputLength); - break; + return SQL_SUCCESS; case SQL_BOOKMARK_PERSISTENCE: GetAttribute(static_cast(0), value, bufferLength, outputLength); - break; + return SQL_SUCCESS; case SQL_DESCRIBE_PARAMETER: - GetStringAttribute(isUnicode, "N", true, value, bufferLength, outputLength, - GetDiagnostics()); - break; + return GetStringAttribute(isUnicode, "N", true, value, bufferLength, outputLength, + GetDiagnostics()); case SQL_MULT_RESULT_SETS: - GetStringAttribute(isUnicode, "N", true, value, bufferLength, outputLength, - GetDiagnostics()); - break; + return GetStringAttribute(isUnicode, "N", true, value, bufferLength, outputLength, + GetDiagnostics()); case SQL_MULTIPLE_ACTIVE_TXN: - GetStringAttribute(isUnicode, "N", true, value, bufferLength, outputLength, - GetDiagnostics()); - break; + return GetStringAttribute(isUnicode, "N", true, value, bufferLength, outputLength, + GetDiagnostics()); case SQL_NEED_LONG_DATA_LEN: - GetStringAttribute(isUnicode, "N", true, value, bufferLength, outputLength, - GetDiagnostics()); - break; + return GetStringAttribute(isUnicode, "N", true, value, bufferLength, outputLength, + GetDiagnostics()); case SQL_TXN_CAPABLE: GetAttribute(static_cast(SQL_TC_NONE), value, bufferLength, outputLength); - break; + return SQL_SUCCESS; case SQL_TXN_ISOLATION_OPTION: GetAttribute(static_cast(0), value, bufferLength, outputLength); - break; + return SQL_SUCCESS; case SQL_TABLE_TERM: - GetStringAttribute(isUnicode, "table", true, value, bufferLength, outputLength, - GetDiagnostics()); - break; + return GetStringAttribute(isUnicode, "table", true, value, bufferLength, outputLength, + GetDiagnostics()); // Deprecated ODBC 2.x fields required for backwards compatibility. case SQL_ODBC_API_CONFORMANCE: GetAttribute(static_cast(SQL_OAC_LEVEL1), value, bufferLength, outputLength); - break; + return SQL_SUCCESS; case SQL_FETCH_DIRECTION: GetAttribute(static_cast(SQL_FETCH_NEXT), value, bufferLength, outputLength); - break; + return SQL_SUCCESS; case SQL_LOCK_TYPES: GetAttribute(static_cast(0), value, bufferLength, outputLength); - break; + return SQL_SUCCESS; case SQL_POS_OPERATIONS: GetAttribute(static_cast(0), value, bufferLength, outputLength); - break; + return SQL_SUCCESS; case SQL_POSITIONED_STATEMENTS: GetAttribute(static_cast(0), value, bufferLength, outputLength); - break; + return SQL_SUCCESS; case SQL_SCROLL_CONCURRENCY: GetAttribute(static_cast(0), value, bufferLength, outputLength); - break; + return SQL_SUCCESS; case SQL_STATIC_SENSITIVITY: GetAttribute(static_cast(0), value, bufferLength, outputLength); - break; + return SQL_SUCCESS; // Driver-level string properties. case SQL_USER_NAME: @@ -316,9 +308,8 @@ void ODBCConnection::GetInfo(SQLUSMALLINT infoType, SQLPOINTER value, case SQL_XOPEN_CLI_YEAR: { const auto& info = m_spiConnection->GetInfo(infoType); const std::string& infoValue = boost::get(info); - GetStringAttribute(isUnicode, infoValue, true, value, bufferLength, outputLength, - GetDiagnostics()); - break; + return GetStringAttribute(isUnicode, infoValue, true, value, bufferLength, outputLength, + GetDiagnostics()); } // Driver-level 32-bit integer properties. @@ -408,7 +399,7 @@ void ODBCConnection::GetInfo(SQLUSMALLINT infoType, SQLPOINTER value, const auto& info = m_spiConnection->GetInfo(infoType); uint32_t infoValue = boost::get(info); GetAttribute(infoValue, value, bufferLength, outputLength); - break; + return SQL_SUCCESS; } // Driver-level 16-bit integer properties. @@ -443,7 +434,7 @@ void ODBCConnection::GetInfo(SQLUSMALLINT infoType, SQLPOINTER value, const auto& info = m_spiConnection->GetInfo(infoType); uint16_t infoValue = boost::get(info); GetAttribute(infoValue, value, bufferLength, outputLength); - break; + return SQL_SUCCESS; } // Special case - SQL_DATABASE_NAME is an alias for SQL_ATTR_CURRENT_CATALOG. @@ -453,13 +444,14 @@ void ODBCConnection::GetInfo(SQLUSMALLINT infoType, SQLPOINTER value, throw DriverException("Optional feature not supported.", "HYC00"); } const std::string& infoValue = boost::get(*attr); - GetStringAttribute(isUnicode, infoValue, true, value, bufferLength, outputLength, - GetDiagnostics()); - break; + return GetStringAttribute(isUnicode, infoValue, true, value, bufferLength, outputLength, + GetDiagnostics()); } default: throw DriverException("Unknown SQLGetInfo type: " + std::to_string(infoType)); } + + return SQL_ERROR; } void ODBCConnection::SetConnectAttr(SQLINTEGER attribute, SQLPOINTER value, diff --git a/cpp/src/arrow/flight/sql/odbc/tests/CMakeLists.txt b/cpp/src/arrow/flight/sql/odbc/tests/CMakeLists.txt index 9f30502ac36f..23c0f47a1ce9 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(connection_test SOURCES connection_test.cc connection_attr_test.cc + connection_info_test.cc odbc_test_suite.cc odbc_test_suite.h ${ARROW_FLIGHT_SQL_MOCK_SERVER_SRCS} diff --git a/cpp/src/arrow/flight/sql/odbc/tests/connection_info_test.cc b/cpp/src/arrow/flight/sql/odbc/tests/connection_info_test.cc new file mode 100644 index 000000000000..0e641c9bbf05 --- /dev/null +++ b/cpp/src/arrow/flight/sql/odbc/tests/connection_info_test.cc @@ -0,0 +1,464 @@ +// 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" + +#ifdef _WIN32 +# include +#endif + +#include +#include +#include + +#include "gtest/gtest.h" + +namespace arrow { +namespace flight { +namespace odbc { +namespace integration_tests { + +TYPED_TEST(FlightSQLODBCTestBase, Test_SQL_ACTIVE_ENVIRONMENTS) { + this->connect(); + + validate(conn, SQL_ACTIVE_ENVIRONMENTS, static_cast(0)); + + this->disconnect(); +} + +TYPED_TEST(FlightSQLODBCTestBase, Test_SQL_ASYNC_DBC_FUNCTIONS) { + this->connect(); + +#ifdef SQL_ASYNC_DBC_FUNCTIONS + validate(conn, SQL_ASYNC_DBC_FUNCTIONS, + static_cast(SQL_ASYNC_DBC_NOT_CAPABLE)); +#endif + + this->disconnect(); +} + +TYPED_TEST(FlightSQLODBCTestBase, Test_SQL_ASYNC_MODE) { + this->connect(); + + validate(conn, SQL_ASYNC_MODE, static_cast(SQL_AM_NONE)); + + this->disconnect(); +} + +TYPED_TEST(FlightSQLODBCTestBase, Test_SQL_ASYNC_NOTIFICATION) { + this->connect(); + +#ifdef SQL_ASYNC_NOTIFICATION + validate(conn, SQL_ASYNC_NOTIFICATION, + static_cast(SQL_ASYNC_NOTIFICATION_NOT_CAPABLE)); +#endif + + this->disconnect(); +} + +TYPED_TEST(FlightSQLODBCTestBase, Test_SQL_BATCH_ROW_COUNT) { + this->connect(); + + validate(conn, SQL_BATCH_ROW_COUNT, static_cast(0)); + + this->disconnect(); +} + +TYPED_TEST(FlightSQLODBCTestBase, Test_SQL_BATCH_SUPPORT) { + this->connect(); + + validate(conn, SQL_BATCH_SUPPORT, static_cast(0)); + + this->disconnect(); +} + +TYPED_TEST(FlightSQLODBCTestBase, Test_SQL_DATA_SOURCE_NAME) { + this->connect(); + + validate(conn, SQL_DATA_SOURCE_NAME, L""); + + this->disconnect(); +} + +TYPED_TEST(FlightSQLODBCTestBase, Test_SQL_DRIVER_AWARE_POOLING_SUPPORTED) { + // TODO A driver does not need to implement SQL_DRIVER_AWARE_POOLING_SUPPORTED and the + // Driver Manager will not honor to the driver's return value. + this->connect(); + + validate(conn, SQL_DRIVER_AWARE_POOLING_SUPPORTED, + static_cast(SQL_DRIVER_AWARE_POOLING_NOT_CAPABLE)); + + this->disconnect(); +} + +// These information types are implemented by the Driver Manager alone. +TYPED_TEST(FlightSQLODBCTestBase, Test_SQL_DRIVER_HDBC) { + this->connect(); + + validateGreaterThan(conn, SQL_DRIVER_HDBC, static_cast(0)); + + this->disconnect(); +} + +// These information types are implemented by the Driver Manager alone. +TYPED_TEST(FlightSQLODBCTestBase, Test_SQL_DRIVER_HDESC) { + // TODO Exception thrown at 0x00007FFCB628E6DA (odbc32.dll) in + // arrow-connection-test.exe: 0xC0000005: Access violation reading location + // 0xFFFFFFFFFFFFFFFF. + GTEST_SKIP(); + this->connect(); + + validateGreaterThan(conn, SQL_DRIVER_HDESC, static_cast(0)); + + this->disconnect(); +} + +// These information types are implemented by the Driver Manager alone. +TYPED_TEST(FlightSQLODBCTestBase, Test_SQL_DRIVER_HENV) { + this->connect(); + + validateGreaterThan(conn, SQL_DRIVER_HENV, static_cast(0)); + + this->disconnect(); +} + +// These information types are implemented by the Driver Manager alone. +TYPED_TEST(FlightSQLODBCTestBase, Test_SQL_DRIVER_HLIB) { + this->connect(); + + validateGreaterThan(conn, SQL_DRIVER_HLIB, static_cast(0)); + + this->disconnect(); +} + +// These information types are implemented by the Driver Manager alone. +TYPED_TEST(FlightSQLODBCTestBase, Test_SQL_DRIVER_HSTMT) { + // TODO unknown file: error: SEH exception with code 0xc0000005 thrown in the test body. + GTEST_SKIP(); + this->connect(); + + validateGreaterThan(conn, SQL_DRIVER_HSTMT, static_cast(0)); + + this->disconnect(); +} + +TYPED_TEST(FlightSQLODBCTestBase, Test_SQL_DRIVER_NAME) { + this->connect(); + + validate(conn, SQL_DRIVER_NAME, L"Arrow Flight ODBC Driver"); + + this->disconnect(); +} + +TYPED_TEST(FlightSQLODBCTestBase, Test_SQL_DRIVER_ODBC_VER) { + this->connect(); + + validate(conn, SQL_DRIVER_ODBC_VER, L"03.80"); + + this->disconnect(); +} + +TYPED_TEST(FlightSQLODBCTestBase, Test_SQL_DRIVER_VER) { + this->connect(); + + validate(conn, SQL_DRIVER_VER, L"00.09.0000.0"); + + this->disconnect(); +} + +TYPED_TEST(FlightSQLODBCTestBase, Test_SQL_DYNAMIC_CURSOR_ATTRIBUTES1) { + this->connect(); + + validate(conn, SQL_DYNAMIC_CURSOR_ATTRIBUTES1, static_cast(0)); + + this->disconnect(); +} + +TYPED_TEST(FlightSQLODBCTestBase, Test_SQL_DYNAMIC_CURSOR_ATTRIBUTES2) { + this->connect(); + + validate(conn, SQL_DYNAMIC_CURSOR_ATTRIBUTES2, static_cast(0)); + + this->disconnect(); +} + +TYPED_TEST(FlightSQLODBCTestBase, Test_SQL_FORWARD_ONLY_CURSOR_ATTRIBUTES1) { + this->connect(); + + validate(conn, SQL_FORWARD_ONLY_CURSOR_ATTRIBUTES1, + static_cast(SQL_CA1_NEXT)); + + this->disconnect(); +} + +TYPED_TEST(FlightSQLODBCTestBase, Test_SQL_FORWARD_ONLY_CURSOR_ATTRIBUTES2) { + this->connect(); + + validate(conn, SQL_FORWARD_ONLY_CURSOR_ATTRIBUTES2, + static_cast(SQL_CA2_READ_ONLY_CONCURRENCY)); + + this->disconnect(); +} + +TYPED_TEST(FlightSQLODBCTestBase, Test_SQL_FILE_USAGE) { + this->connect(); + + validate(conn, SQL_FILE_USAGE, static_cast(SQL_FILE_NOT_SUPPORTED)); + + this->disconnect(); +} + +TYPED_TEST(FlightSQLODBCTestBase, Test_SQL_GETDATA_EXTENSIONS) { + // TODO Run-Time Check Failure #2 - Stack around the variable 'info_value' was + // corrupted. + GTEST_SKIP(); + this->connect(); + + validate(conn, SQL_GETDATA_EXTENSIONS, static_cast(3)); + + this->disconnect(); +} + +TYPED_TEST(FlightSQLODBCTestBase, Test_SQL_INFO_SCHEMA_VIEWS) { + // TODO Run-Time Check Failure #2 - Stack around the variable 'info_value' was + // corrupted. + GTEST_SKIP(); + this->connect(); + + validate(conn, SQL_INFO_SCHEMA_VIEWS, static_cast(64)); + + this->disconnect(); +} + +TYPED_TEST(FlightSQLODBCTestBase, Test_SQL_KEYSET_CURSOR_ATTRIBUTES1) { + this->connect(); + + validate(conn, SQL_KEYSET_CURSOR_ATTRIBUTES1, static_cast(0)); + + this->disconnect(); +} + +TYPED_TEST(FlightSQLODBCTestBase, Test_SQL_KEYSET_CURSOR_ATTRIBUTES2) { + this->connect(); + + validate(conn, SQL_KEYSET_CURSOR_ATTRIBUTES2, static_cast(0)); + + this->disconnect(); +} + +TYPED_TEST(FlightSQLODBCTestBase, Test_SQL_MAX_ASYNC_CONCURRENT_STATEMENTS) { + this->connect(); + + validate(conn, SQL_MAX_ASYNC_CONCURRENT_STATEMENTS, static_cast(0)); + + this->disconnect(); +} + +TYPED_TEST(FlightSQLODBCTestBase, Test_SQL_MAX_CONCURRENT_ACTIVITIES) { + // TODO Driver manager returns failure code + GTEST_SKIP(); + this->connect(); + + validateGreaterThan(conn, SQL_MAX_CONCURRENT_ACTIVITIES, static_cast(0)); + + this->disconnect(); +} + +TYPED_TEST(FlightSQLODBCTestBase, Test_SQL_MAX_DRIVER_CONNECTIONS) { + this->connect(); + + validate(conn, SQL_MAX_DRIVER_CONNECTIONS, static_cast(0)); + + this->disconnect(); +} + +TYPED_TEST(FlightSQLODBCTestBase, Test_SQL_ODBC_INTERFACE_CONFORMANCE) { + this->connect(); + + validate(conn, SQL_ODBC_INTERFACE_CONFORMANCE, static_cast(SQL_OIC_CORE)); + + this->disconnect(); +} + +// case SQL_ODBC_STANDARD_CLI_CONFORMANCE: - mentioned in SQLGetInfo spec with no +// description and there is no constant for this. +TYPED_TEST(FlightSQLODBCTestBase, Test_SQL_ODBC_STANDARD_CLI_CONFORMANCE) { + // Type not supported in odbc_connection.cc GetInfo + GTEST_SKIP(); + this->connect(); + + // validate(conn, SQL_ODBC_STANDARD_CLI_CONFORMANCE, ); + + this->disconnect(); +} + +TYPED_TEST(FlightSQLODBCTestBase, Test_SQL_ODBC_VER) { + // TODO This is implemented only in the Driver Manager. + this->connect(); + + validate(conn, SQL_ODBC_VER, L"03.80.0000"); + + this->disconnect(); +} + +TYPED_TEST(FlightSQLODBCTestBase, Test_SQL_PARAM_ARRAY_ROW_COUNTS) { + this->connect(); + + validate(conn, SQL_PARAM_ARRAY_ROW_COUNTS, static_cast(SQL_PARC_NO_BATCH)); + + this->disconnect(); +} + +TYPED_TEST(FlightSQLODBCTestBase, Test_SQL_PARAM_ARRAY_SELECTS) { + this->connect(); + + validate(conn, SQL_PARAM_ARRAY_SELECTS, static_cast(SQL_PAS_NO_SELECT)); + + this->disconnect(); +} + +TYPED_TEST(FlightSQLODBCTestBase, Test_SQL_ROW_UPDATES) { + this->connect(); + + validate(conn, SQL_ROW_UPDATES, L"N"); + + this->disconnect(); +} + +TYPED_TEST(FlightSQLODBCTestBase, Test_SQL_SEARCH_PATTERN_ESCAPE) { + this->connect(); + + validate(conn, SQL_SEARCH_PATTERN_ESCAPE, L"\\"); + + this->disconnect(); +} + +TYPED_TEST(FlightSQLODBCTestBase, Test_SQL_SERVER_NAME) { + this->connect(); + + validateNotEmptySQLWCHAR(conn, SQL_SERVER_NAME); + + this->disconnect(); +} + +TYPED_TEST(FlightSQLODBCTestBase, Test_SQL_STATIC_CURSOR_ATTRIBUTES1) { + this->connect(); + + validate(conn, SQL_STATIC_CURSOR_ATTRIBUTES1, static_cast(0)); + + this->disconnect(); +} + +TYPED_TEST(FlightSQLODBCTestBase, Test_SQL_STATIC_CURSOR_ATTRIBUTES2) { + this->connect(); + + validate(conn, SQL_STATIC_CURSOR_ATTRIBUTES2, static_cast(0)); + + this->disconnect(); +} + +// wchar +void validateNotEmptySQLWCHAR(SQLHDBC connection, SQLUSMALLINT infoType) { + SQLWCHAR info_value[ODBC_BUFFER_SIZE] = L""; + SQLSMALLINT message_length; + + SQLRETURN ret = + SQLGetInfoW(connection, infoType, info_value, ODBC_BUFFER_SIZE, &message_length); + + EXPECT_EQ(ret, SQL_SUCCESS); + + EXPECT_GT(wcslen(info_value), 0); +} + +// wchar +void validate(SQLHDBC connection, SQLUSMALLINT infoType, SQLWCHAR* expected_value) { + SQLWCHAR info_value[ODBC_BUFFER_SIZE] = L""; + SQLSMALLINT message_length; + + SQLRETURN ret = + SQLGetInfoW(connection, infoType, info_value, ODBC_BUFFER_SIZE, &message_length); + + EXPECT_EQ(ret, SQL_SUCCESS); + + EXPECT_EQ(*info_value, *expected_value); +} + +// long +void validate(SQLHDBC connection, SQLUSMALLINT infoType, SQLUSMALLINT expected_value) { + SQLUSMALLINT info_value; + SQLSMALLINT message_length; + + SQLRETURN ret = SQLGetInfoW(connection, infoType, &info_value, 0, &message_length); + + EXPECT_EQ(ret, SQL_SUCCESS); + + EXPECT_EQ(info_value, expected_value); +} + +// long +void validateGreaterThan(SQLHDBC connection, SQLUSMALLINT infoType, + SQLUSMALLINT compared_value) { + SQLUSMALLINT info_value; + SQLSMALLINT message_length; + + SQLRETURN ret = SQLGetInfoW(connection, infoType, &info_value, 0, &message_length); + + EXPECT_EQ(ret, SQL_SUCCESS); + + EXPECT_GT(info_value, compared_value); +} + +// unsigned long +void validate(SQLHDBC connection, SQLUSMALLINT infoType, SQLUINTEGER expected_value) { + SQLUINTEGER info_value; + SQLSMALLINT message_length; + + SQLRETURN ret = SQLGetInfoW(connection, infoType, &info_value, 0, &message_length); + + EXPECT_EQ(ret, SQL_SUCCESS); + + EXPECT_EQ(info_value, expected_value); +} + +// sql unsigned length +void validate(SQLHDBC connection, SQLUSMALLINT infoType, SQLULEN expected_value) { + SQLULEN info_value; + SQLSMALLINT message_length; + + SQLRETURN ret = SQLGetInfoW(connection, infoType, &info_value, 0, &message_length); + + EXPECT_EQ(ret, SQL_SUCCESS); + + EXPECT_EQ(info_value, expected_value); +} + +// sql unsigned length +void validateGreaterThan(SQLHDBC connection, SQLUSMALLINT infoType, + SQLULEN compared_value) { + SQLULEN info_value; + SQLSMALLINT message_length; + + SQLRETURN ret = SQLGetInfoW(connection, infoType, &info_value, 0, &message_length); + + EXPECT_EQ(ret, SQL_SUCCESS); + + EXPECT_GT(info_value, compared_value); +} + +} // namespace integration_tests +} // namespace odbc +} // namespace flight +} // namespace arrow From 17417e16ced31f3c9db899a5e3fc67cea54f916d Mon Sep 17 00:00:00 2001 From: rscales Date: Fri, 20 Jun 2025 19:35:12 +0100 Subject: [PATCH 02/29] Add build related directories to git ignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 52ffa6c6124c..984a97b87900 100644 --- a/.gitignore +++ b/.gitignore @@ -33,6 +33,7 @@ dependency-reduced-pom.xml MANIFEST compile_commands.json build.ninja +*/build*/ # Generated Visual Studio files *.vcxproj From ddef36c51f7af705c0f5cfd5b029a6d0ee2dd7e3 Mon Sep 17 00:00:00 2001 From: rscales Date: Fri, 20 Jun 2025 23:28:46 +0100 Subject: [PATCH 03/29] Update based on comments from draft review --- cpp/src/arrow/flight/sql/odbc/odbc_api.cc | 13 +--- .../sql/odbc/tests/connection_info_test.cc | 73 ++++++++++--------- 2 files changed, 41 insertions(+), 45 deletions(-) diff --git a/cpp/src/arrow/flight/sql/odbc/odbc_api.cc b/cpp/src/arrow/flight/sql/odbc/odbc_api.cc index 68f506327a5e..ed79ab72b7d4 100644 --- a/cpp/src/arrow/flight/sql/odbc/odbc_api.cc +++ b/cpp/src/arrow/flight/sql/odbc/odbc_api.cc @@ -847,21 +847,13 @@ SQLRETURN SQLDisconnect(SQLHDBC conn) { SQLRETURN SQLGetInfo(SQLHDBC conn, SQLUSMALLINT infoType, SQLPOINTER infoValuePtr, SQLSMALLINT bufLen, SQLSMALLINT* stringLengthPtr) { - // TODO: complete implementation of SQLGetInfoW and write tests using ODBC::ODBCConnection; - // Set character type to be Unicode by default - const bool isUnicode = true; - LOG_DEBUG( - "SQLGetInfoW called with conn: {}, infoType: {}, infoValuePtr: {}, bufLen: {}, " + "SQLGetInfo called with conn: {}, infoType: {}, infoValuePtr: {}, bufLen: {}, " "stringLengthPtr: {}", conn, infoType, infoValuePtr, bufLen, fmt::ptr(stringLengthPtr)); - if (!conn) { - return SQL_INVALID_HANDLE; - } - if (!infoValuePtr && !stringLengthPtr) { return SQL_ERROR; } @@ -869,7 +861,8 @@ SQLRETURN SQLGetInfo(SQLHDBC conn, SQLUSMALLINT infoType, SQLPOINTER infoValuePt return ODBCConnection::ExecuteWithDiagnostics(conn, SQL_ERROR, [=]() { ODBCConnection* connection = reinterpret_cast(conn); - // TODO Validate buffer + // Set character type to be Unicode by default + const bool isUnicode = true; return connection->GetInfo(infoType, infoValuePtr, bufLen, stringLengthPtr, isUnicode); diff --git a/cpp/src/arrow/flight/sql/odbc/tests/connection_info_test.cc b/cpp/src/arrow/flight/sql/odbc/tests/connection_info_test.cc index 0e641c9bbf05..ba087aab297a 100644 --- a/cpp/src/arrow/flight/sql/odbc/tests/connection_info_test.cc +++ b/cpp/src/arrow/flight/sql/odbc/tests/connection_info_test.cc @@ -31,6 +31,8 @@ namespace flight { namespace odbc { namespace integration_tests { +// Driver Information + TYPED_TEST(FlightSQLODBCTestBase, Test_SQL_ACTIVE_ENVIRONMENTS) { this->connect(); @@ -94,7 +96,7 @@ TYPED_TEST(FlightSQLODBCTestBase, Test_SQL_DATA_SOURCE_NAME) { } TYPED_TEST(FlightSQLODBCTestBase, Test_SQL_DRIVER_AWARE_POOLING_SUPPORTED) { - // TODO A driver does not need to implement SQL_DRIVER_AWARE_POOLING_SUPPORTED and the + // A driver does not need to implement SQL_DRIVER_AWARE_POOLING_SUPPORTED and the // Driver Manager will not honor to the driver's return value. this->connect(); @@ -108,6 +110,7 @@ TYPED_TEST(FlightSQLODBCTestBase, Test_SQL_DRIVER_AWARE_POOLING_SUPPORTED) { TYPED_TEST(FlightSQLODBCTestBase, Test_SQL_DRIVER_HDBC) { this->connect(); + // Value returned from driver manager is the connection address validateGreaterThan(conn, SQL_DRIVER_HDBC, static_cast(0)); this->disconnect(); @@ -115,13 +118,11 @@ TYPED_TEST(FlightSQLODBCTestBase, Test_SQL_DRIVER_HDBC) { // These information types are implemented by the Driver Manager alone. TYPED_TEST(FlightSQLODBCTestBase, Test_SQL_DRIVER_HDESC) { - // TODO Exception thrown at 0x00007FFCB628E6DA (odbc32.dll) in - // arrow-connection-test.exe: 0xC0000005: Access violation reading location - // 0xFFFFFFFFFFFFFFFF. + // TODO This is failing due to no descriptor being created GTEST_SKIP(); this->connect(); - validateGreaterThan(conn, SQL_DRIVER_HDESC, static_cast(0)); + validate(conn, SQL_DRIVER_HDESC, static_cast(0)); this->disconnect(); } @@ -130,6 +131,7 @@ TYPED_TEST(FlightSQLODBCTestBase, Test_SQL_DRIVER_HDESC) { TYPED_TEST(FlightSQLODBCTestBase, Test_SQL_DRIVER_HENV) { this->connect(); + // Value returned from driver manager is the env address validateGreaterThan(conn, SQL_DRIVER_HENV, static_cast(0)); this->disconnect(); @@ -139,6 +141,9 @@ TYPED_TEST(FlightSQLODBCTestBase, Test_SQL_DRIVER_HENV) { TYPED_TEST(FlightSQLODBCTestBase, Test_SQL_DRIVER_HLIB) { this->connect(); + // An SQLULEN value, the hinst from the load library returned to the Driver Manager when + // it loaded the driver DLL on a Microsoft Windows operating system, or its equivalent + // on another operating system. validateGreaterThan(conn, SQL_DRIVER_HLIB, static_cast(0)); this->disconnect(); @@ -146,11 +151,12 @@ TYPED_TEST(FlightSQLODBCTestBase, Test_SQL_DRIVER_HLIB) { // These information types are implemented by the Driver Manager alone. TYPED_TEST(FlightSQLODBCTestBase, Test_SQL_DRIVER_HSTMT) { - // TODO unknown file: error: SEH exception with code 0xc0000005 thrown in the test body. + // TODO This is failing due to no statement being created + // This should run after SQLGetStmtAttr is implemented GTEST_SKIP(); this->connect(); - validateGreaterThan(conn, SQL_DRIVER_HSTMT, static_cast(0)); + validate(conn, SQL_DRIVER_HSTMT, static_cast(0)); this->disconnect(); } @@ -222,23 +228,19 @@ TYPED_TEST(FlightSQLODBCTestBase, Test_SQL_FILE_USAGE) { } TYPED_TEST(FlightSQLODBCTestBase, Test_SQL_GETDATA_EXTENSIONS) { - // TODO Run-Time Check Failure #2 - Stack around the variable 'info_value' was - // corrupted. - GTEST_SKIP(); this->connect(); - validate(conn, SQL_GETDATA_EXTENSIONS, static_cast(3)); + validate(conn, SQL_GETDATA_EXTENSIONS, static_cast(3)); this->disconnect(); } TYPED_TEST(FlightSQLODBCTestBase, Test_SQL_INFO_SCHEMA_VIEWS) { - // TODO Run-Time Check Failure #2 - Stack around the variable 'info_value' was - // corrupted. - GTEST_SKIP(); this->connect(); - validate(conn, SQL_INFO_SCHEMA_VIEWS, static_cast(64)); + // An SQLUINTEGER bitmask enumerating the views in the INFORMATION_SCHEMA that are + // supported by the driver. + validateGreaterThan(conn, SQL_INFO_SCHEMA_VIEWS, static_cast(0)); this->disconnect(); } @@ -268,11 +270,11 @@ TYPED_TEST(FlightSQLODBCTestBase, Test_SQL_MAX_ASYNC_CONCURRENT_STATEMENTS) { } TYPED_TEST(FlightSQLODBCTestBase, Test_SQL_MAX_CONCURRENT_ACTIVITIES) { - // TODO Driver manager returns failure code + // Driver manager returns failure code GTEST_SKIP(); this->connect(); - validateGreaterThan(conn, SQL_MAX_CONCURRENT_ACTIVITIES, static_cast(0)); + validate(conn, SQL_MAX_CONCURRENT_ACTIVITIES, static_cast(0)); this->disconnect(); } @@ -296,17 +298,18 @@ TYPED_TEST(FlightSQLODBCTestBase, Test_SQL_ODBC_INTERFACE_CONFORMANCE) { // case SQL_ODBC_STANDARD_CLI_CONFORMANCE: - mentioned in SQLGetInfo spec with no // description and there is no constant for this. TYPED_TEST(FlightSQLODBCTestBase, Test_SQL_ODBC_STANDARD_CLI_CONFORMANCE) { - // Type not supported in odbc_connection.cc GetInfo + // Type not supported in odbc_connection.cc GTEST_SKIP(); this->connect(); - // validate(conn, SQL_ODBC_STANDARD_CLI_CONFORMANCE, ); + // Type does not exist in sql.h + // validate(conn, SQL_ODBC_STANDARD_CLI_CONFORMANCE, static_cast(0)); this->disconnect(); } TYPED_TEST(FlightSQLODBCTestBase, Test_SQL_ODBC_VER) { - // TODO This is implemented only in the Driver Manager. + // This is implemented only in the Driver Manager. this->connect(); validate(conn, SQL_ODBC_VER, L"03.80.0000"); @@ -376,7 +379,7 @@ void validateNotEmptySQLWCHAR(SQLHDBC connection, SQLUSMALLINT infoType) { SQLSMALLINT message_length; SQLRETURN ret = - SQLGetInfoW(connection, infoType, info_value, ODBC_BUFFER_SIZE, &message_length); + SQLGetInfo(connection, infoType, info_value, ODBC_BUFFER_SIZE, &message_length); EXPECT_EQ(ret, SQL_SUCCESS); @@ -389,48 +392,48 @@ void validate(SQLHDBC connection, SQLUSMALLINT infoType, SQLWCHAR* expected_valu SQLSMALLINT message_length; SQLRETURN ret = - SQLGetInfoW(connection, infoType, info_value, ODBC_BUFFER_SIZE, &message_length); + SQLGetInfo(connection, infoType, info_value, ODBC_BUFFER_SIZE, &message_length); EXPECT_EQ(ret, SQL_SUCCESS); EXPECT_EQ(*info_value, *expected_value); } -// long +// unsigned short void validate(SQLHDBC connection, SQLUSMALLINT infoType, SQLUSMALLINT expected_value) { SQLUSMALLINT info_value; SQLSMALLINT message_length; - SQLRETURN ret = SQLGetInfoW(connection, infoType, &info_value, 0, &message_length); + SQLRETURN ret = SQLGetInfo(connection, infoType, &info_value, 0, &message_length); EXPECT_EQ(ret, SQL_SUCCESS); EXPECT_EQ(info_value, expected_value); } -// long -void validateGreaterThan(SQLHDBC connection, SQLUSMALLINT infoType, - SQLUSMALLINT compared_value) { - SQLUSMALLINT info_value; +// unsigned long +void validate(SQLHDBC connection, SQLUSMALLINT infoType, SQLUINTEGER expected_value) { + SQLUINTEGER info_value; SQLSMALLINT message_length; - SQLRETURN ret = SQLGetInfoW(connection, infoType, &info_value, 0, &message_length); + SQLRETURN ret = SQLGetInfo(connection, infoType, &info_value, 0, &message_length); EXPECT_EQ(ret, SQL_SUCCESS); - EXPECT_GT(info_value, compared_value); + EXPECT_EQ(info_value, expected_value); } // unsigned long -void validate(SQLHDBC connection, SQLUSMALLINT infoType, SQLUINTEGER expected_value) { +void validateGreaterThan(SQLHDBC connection, SQLUSMALLINT infoType, + SQLUINTEGER compared_value) { SQLUINTEGER info_value; SQLSMALLINT message_length; - SQLRETURN ret = SQLGetInfoW(connection, infoType, &info_value, 0, &message_length); + SQLRETURN ret = SQLGetInfo(connection, infoType, &info_value, 0, &message_length); EXPECT_EQ(ret, SQL_SUCCESS); - EXPECT_EQ(info_value, expected_value); + EXPECT_GT(info_value, compared_value); } // sql unsigned length @@ -438,7 +441,7 @@ void validate(SQLHDBC connection, SQLUSMALLINT infoType, SQLULEN expected_value) SQLULEN info_value; SQLSMALLINT message_length; - SQLRETURN ret = SQLGetInfoW(connection, infoType, &info_value, 0, &message_length); + SQLRETURN ret = SQLGetInfo(connection, infoType, &info_value, 0, &message_length); EXPECT_EQ(ret, SQL_SUCCESS); @@ -451,7 +454,7 @@ void validateGreaterThan(SQLHDBC connection, SQLUSMALLINT infoType, SQLULEN info_value; SQLSMALLINT message_length; - SQLRETURN ret = SQLGetInfoW(connection, infoType, &info_value, 0, &message_length); + SQLRETURN ret = SQLGetInfo(connection, infoType, &info_value, 0, &message_length); EXPECT_EQ(ret, SQL_SUCCESS); From 9b86d587c3aa330982b6e3de5c0a7d5817982bf4 Mon Sep 17 00:00:00 2001 From: rscales Date: Fri, 20 Jun 2025 23:34:09 +0100 Subject: [PATCH 04/29] Add tests for DBMS Product Information --- .../sql/odbc/tests/connection_info_test.cc | 26 +++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/cpp/src/arrow/flight/sql/odbc/tests/connection_info_test.cc b/cpp/src/arrow/flight/sql/odbc/tests/connection_info_test.cc index ba087aab297a..b97d930b8dc2 100644 --- a/cpp/src/arrow/flight/sql/odbc/tests/connection_info_test.cc +++ b/cpp/src/arrow/flight/sql/odbc/tests/connection_info_test.cc @@ -373,6 +373,32 @@ TYPED_TEST(FlightSQLODBCTestBase, Test_SQL_STATIC_CURSOR_ATTRIBUTES2) { this->disconnect(); } +// DBMS Product Information + +TYPED_TEST(FlightSQLODBCTestBase, Test_SQL_DATABASE_NAME) { + this->connect(); + + validate(conn, SQL_DATABASE_NAME, L""); + + this->disconnect(); +} + +TYPED_TEST(FlightSQLODBCTestBase, Test_SQL_DBMS_NAME) { + this->connect(); + + validateNotEmptySQLWCHAR(conn, SQL_DBMS_NAME); + + this->disconnect(); +} + +TYPED_TEST(FlightSQLODBCTestBase, Test_SQL_DBMS_VER) { + this->connect(); + + validateNotEmptySQLWCHAR(conn, SQL_DBMS_VER); + + this->disconnect(); +} + // wchar void validateNotEmptySQLWCHAR(SQLHDBC connection, SQLUSMALLINT infoType) { SQLWCHAR info_value[ODBC_BUFFER_SIZE] = L""; From 14948b5bbe9372048070c50b6a9fd07305831e2b Mon Sep 17 00:00:00 2001 From: rscales Date: Sat, 21 Jun 2025 00:29:04 +0100 Subject: [PATCH 05/29] Move validation check to within ExecuteWithDiagnostics lambda --- cpp/src/arrow/flight/sql/odbc/odbc_api.cc | 8 ++++---- 1 file changed, 4 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 ed79ab72b7d4..d35d470e3061 100644 --- a/cpp/src/arrow/flight/sql/odbc/odbc_api.cc +++ b/cpp/src/arrow/flight/sql/odbc/odbc_api.cc @@ -854,16 +854,16 @@ SQLRETURN SQLGetInfo(SQLHDBC conn, SQLUSMALLINT infoType, SQLPOINTER infoValuePt "stringLengthPtr: {}", conn, infoType, infoValuePtr, bufLen, fmt::ptr(stringLengthPtr)); - if (!infoValuePtr && !stringLengthPtr) { - return SQL_ERROR; - } - return ODBCConnection::ExecuteWithDiagnostics(conn, SQL_ERROR, [=]() { ODBCConnection* connection = reinterpret_cast(conn); // Set character type to be Unicode by default const bool isUnicode = true; + if (!infoValuePtr && !stringLengthPtr) { + return static_cast SQL_ERROR; + } + return connection->GetInfo(infoType, infoValuePtr, bufLen, stringLengthPtr, isUnicode); }); From 50c6ae0c4f3feb59ee80ac5847ab10ccf750c9a3 Mon Sep 17 00:00:00 2001 From: rscales Date: Sat, 21 Jun 2025 01:49:16 +0100 Subject: [PATCH 06/29] Move helper functions to top of file --- .../sql/odbc/tests/connection_info_test.cc | 264 +++++++++--------- 1 file changed, 135 insertions(+), 129 deletions(-) diff --git a/cpp/src/arrow/flight/sql/odbc/tests/connection_info_test.cc b/cpp/src/arrow/flight/sql/odbc/tests/connection_info_test.cc index b97d930b8dc2..bce22b7e3028 100644 --- a/cpp/src/arrow/flight/sql/odbc/tests/connection_info_test.cc +++ b/cpp/src/arrow/flight/sql/odbc/tests/connection_info_test.cc @@ -31,12 +31,102 @@ namespace flight { namespace odbc { namespace integration_tests { +// Helper Functions + +// Validate unsigned short SQLUSMALLINT return value +void validate(SQLHDBC connection, SQLUSMALLINT infoType, SQLUSMALLINT expected_value) { + SQLUSMALLINT info_value; + SQLSMALLINT message_length; + + SQLRETURN ret = SQLGetInfo(connection, infoType, &info_value, 0, &message_length); + + EXPECT_EQ(ret, SQL_SUCCESS); + + EXPECT_EQ(info_value, expected_value); +} + +// Validate unsigned long SQLUINTEGER return value +void validate(SQLHDBC connection, SQLUSMALLINT infoType, SQLUINTEGER expected_value) { + SQLUINTEGER info_value; + SQLSMALLINT message_length; + + SQLRETURN ret = SQLGetInfo(connection, infoType, &info_value, 0, &message_length); + + EXPECT_EQ(ret, SQL_SUCCESS); + + EXPECT_EQ(info_value, expected_value); +} + +// Validate unsigned length SQLULEN return value +void validate(SQLHDBC connection, SQLUSMALLINT infoType, SQLULEN expected_value) { + SQLULEN info_value; + SQLSMALLINT message_length; + + SQLRETURN ret = SQLGetInfo(connection, infoType, &info_value, 0, &message_length); + + EXPECT_EQ(ret, SQL_SUCCESS); + + EXPECT_EQ(info_value, expected_value); +} + +// Validate wchar string SQLWCHAR return value +void validate(SQLHDBC connection, SQLUSMALLINT infoType, SQLWCHAR* expected_value) { + SQLWCHAR info_value[ODBC_BUFFER_SIZE] = L""; + SQLSMALLINT message_length; + + SQLRETURN ret = + SQLGetInfo(connection, infoType, info_value, ODBC_BUFFER_SIZE, &message_length); + + EXPECT_EQ(ret, SQL_SUCCESS); + + EXPECT_EQ(*info_value, *expected_value); +} + +// Validate unsigned long SQLUINTEGER return value is greater than +void validateGreaterThan(SQLHDBC connection, SQLUSMALLINT infoType, + SQLUINTEGER compared_value) { + SQLUINTEGER info_value; + SQLSMALLINT message_length; + + SQLRETURN ret = SQLGetInfo(connection, infoType, &info_value, 0, &message_length); + + EXPECT_EQ(ret, SQL_SUCCESS); + + EXPECT_GT(info_value, compared_value); +} + +// Validate unsigned length SQLULEN return value is greater than +void validateGreaterThan(SQLHDBC connection, SQLUSMALLINT infoType, + SQLULEN compared_value) { + SQLULEN info_value; + SQLSMALLINT message_length; + + SQLRETURN ret = SQLGetInfo(connection, infoType, &info_value, 0, &message_length); + + EXPECT_EQ(ret, SQL_SUCCESS); + + EXPECT_GT(info_value, compared_value); +} + +// Validate wchar string SQLWCHAR return value is not empty +void validateNotEmptySQLWCHAR(SQLHDBC connection, SQLUSMALLINT infoType) { + SQLWCHAR info_value[ODBC_BUFFER_SIZE] = L""; + SQLSMALLINT message_length; + + SQLRETURN ret = + SQLGetInfo(connection, infoType, info_value, ODBC_BUFFER_SIZE, &message_length); + + EXPECT_EQ(ret, SQL_SUCCESS); + + EXPECT_GT(wcslen(info_value), 0); +} + // Driver Information TYPED_TEST(FlightSQLODBCTestBase, Test_SQL_ACTIVE_ENVIRONMENTS) { this->connect(); - validate(conn, SQL_ACTIVE_ENVIRONMENTS, static_cast(0)); + validate(this->conn, SQL_ACTIVE_ENVIRONMENTS, static_cast(0)); this->disconnect(); } @@ -45,7 +135,7 @@ TYPED_TEST(FlightSQLODBCTestBase, Test_SQL_ASYNC_DBC_FUNCTIONS) { this->connect(); #ifdef SQL_ASYNC_DBC_FUNCTIONS - validate(conn, SQL_ASYNC_DBC_FUNCTIONS, + validate(this->conn, SQL_ASYNC_DBC_FUNCTIONS, static_cast(SQL_ASYNC_DBC_NOT_CAPABLE)); #endif @@ -55,7 +145,7 @@ TYPED_TEST(FlightSQLODBCTestBase, Test_SQL_ASYNC_DBC_FUNCTIONS) { TYPED_TEST(FlightSQLODBCTestBase, Test_SQL_ASYNC_MODE) { this->connect(); - validate(conn, SQL_ASYNC_MODE, static_cast(SQL_AM_NONE)); + validate(this->conn, SQL_ASYNC_MODE, static_cast(SQL_AM_NONE)); this->disconnect(); } @@ -64,7 +154,7 @@ TYPED_TEST(FlightSQLODBCTestBase, Test_SQL_ASYNC_NOTIFICATION) { this->connect(); #ifdef SQL_ASYNC_NOTIFICATION - validate(conn, SQL_ASYNC_NOTIFICATION, + validate(this->conn, SQL_ASYNC_NOTIFICATION, static_cast(SQL_ASYNC_NOTIFICATION_NOT_CAPABLE)); #endif @@ -74,7 +164,7 @@ TYPED_TEST(FlightSQLODBCTestBase, Test_SQL_ASYNC_NOTIFICATION) { TYPED_TEST(FlightSQLODBCTestBase, Test_SQL_BATCH_ROW_COUNT) { this->connect(); - validate(conn, SQL_BATCH_ROW_COUNT, static_cast(0)); + validate(this->conn, SQL_BATCH_ROW_COUNT, static_cast(0)); this->disconnect(); } @@ -82,7 +172,7 @@ TYPED_TEST(FlightSQLODBCTestBase, Test_SQL_BATCH_ROW_COUNT) { TYPED_TEST(FlightSQLODBCTestBase, Test_SQL_BATCH_SUPPORT) { this->connect(); - validate(conn, SQL_BATCH_SUPPORT, static_cast(0)); + validate(this->conn, SQL_BATCH_SUPPORT, static_cast(0)); this->disconnect(); } @@ -90,7 +180,7 @@ TYPED_TEST(FlightSQLODBCTestBase, Test_SQL_BATCH_SUPPORT) { TYPED_TEST(FlightSQLODBCTestBase, Test_SQL_DATA_SOURCE_NAME) { this->connect(); - validate(conn, SQL_DATA_SOURCE_NAME, L""); + validate(this->conn, SQL_DATA_SOURCE_NAME, L""); this->disconnect(); } @@ -100,7 +190,7 @@ TYPED_TEST(FlightSQLODBCTestBase, Test_SQL_DRIVER_AWARE_POOLING_SUPPORTED) { // Driver Manager will not honor to the driver's return value. this->connect(); - validate(conn, SQL_DRIVER_AWARE_POOLING_SUPPORTED, + validate(this->conn, SQL_DRIVER_AWARE_POOLING_SUPPORTED, static_cast(SQL_DRIVER_AWARE_POOLING_NOT_CAPABLE)); this->disconnect(); @@ -111,7 +201,7 @@ TYPED_TEST(FlightSQLODBCTestBase, Test_SQL_DRIVER_HDBC) { this->connect(); // Value returned from driver manager is the connection address - validateGreaterThan(conn, SQL_DRIVER_HDBC, static_cast(0)); + validateGreaterThan(this->conn, SQL_DRIVER_HDBC, static_cast(0)); this->disconnect(); } @@ -122,7 +212,7 @@ TYPED_TEST(FlightSQLODBCTestBase, Test_SQL_DRIVER_HDESC) { GTEST_SKIP(); this->connect(); - validate(conn, SQL_DRIVER_HDESC, static_cast(0)); + validate(this->conn, SQL_DRIVER_HDESC, static_cast(0)); this->disconnect(); } @@ -132,7 +222,7 @@ TYPED_TEST(FlightSQLODBCTestBase, Test_SQL_DRIVER_HENV) { this->connect(); // Value returned from driver manager is the env address - validateGreaterThan(conn, SQL_DRIVER_HENV, static_cast(0)); + validateGreaterThan(this->conn, SQL_DRIVER_HENV, static_cast(0)); this->disconnect(); } @@ -144,7 +234,7 @@ TYPED_TEST(FlightSQLODBCTestBase, Test_SQL_DRIVER_HLIB) { // An SQLULEN value, the hinst from the load library returned to the Driver Manager when // it loaded the driver DLL on a Microsoft Windows operating system, or its equivalent // on another operating system. - validateGreaterThan(conn, SQL_DRIVER_HLIB, static_cast(0)); + validateGreaterThan(this->conn, SQL_DRIVER_HLIB, static_cast(0)); this->disconnect(); } @@ -156,7 +246,7 @@ TYPED_TEST(FlightSQLODBCTestBase, Test_SQL_DRIVER_HSTMT) { GTEST_SKIP(); this->connect(); - validate(conn, SQL_DRIVER_HSTMT, static_cast(0)); + validate(this->conn, SQL_DRIVER_HSTMT, static_cast(0)); this->disconnect(); } @@ -164,7 +254,7 @@ TYPED_TEST(FlightSQLODBCTestBase, Test_SQL_DRIVER_HSTMT) { TYPED_TEST(FlightSQLODBCTestBase, Test_SQL_DRIVER_NAME) { this->connect(); - validate(conn, SQL_DRIVER_NAME, L"Arrow Flight ODBC Driver"); + validate(this->conn, SQL_DRIVER_NAME, L"Arrow Flight ODBC Driver"); this->disconnect(); } @@ -172,7 +262,7 @@ TYPED_TEST(FlightSQLODBCTestBase, Test_SQL_DRIVER_NAME) { TYPED_TEST(FlightSQLODBCTestBase, Test_SQL_DRIVER_ODBC_VER) { this->connect(); - validate(conn, SQL_DRIVER_ODBC_VER, L"03.80"); + validate(this->conn, SQL_DRIVER_ODBC_VER, L"03.80"); this->disconnect(); } @@ -180,7 +270,7 @@ TYPED_TEST(FlightSQLODBCTestBase, Test_SQL_DRIVER_ODBC_VER) { TYPED_TEST(FlightSQLODBCTestBase, Test_SQL_DRIVER_VER) { this->connect(); - validate(conn, SQL_DRIVER_VER, L"00.09.0000.0"); + validate(this->conn, SQL_DRIVER_VER, L"00.09.0000.0"); this->disconnect(); } @@ -188,7 +278,7 @@ TYPED_TEST(FlightSQLODBCTestBase, Test_SQL_DRIVER_VER) { TYPED_TEST(FlightSQLODBCTestBase, Test_SQL_DYNAMIC_CURSOR_ATTRIBUTES1) { this->connect(); - validate(conn, SQL_DYNAMIC_CURSOR_ATTRIBUTES1, static_cast(0)); + validate(this->conn, SQL_DYNAMIC_CURSOR_ATTRIBUTES1, static_cast(0)); this->disconnect(); } @@ -196,7 +286,7 @@ TYPED_TEST(FlightSQLODBCTestBase, Test_SQL_DYNAMIC_CURSOR_ATTRIBUTES1) { TYPED_TEST(FlightSQLODBCTestBase, Test_SQL_DYNAMIC_CURSOR_ATTRIBUTES2) { this->connect(); - validate(conn, SQL_DYNAMIC_CURSOR_ATTRIBUTES2, static_cast(0)); + validate(this->conn, SQL_DYNAMIC_CURSOR_ATTRIBUTES2, static_cast(0)); this->disconnect(); } @@ -204,7 +294,7 @@ TYPED_TEST(FlightSQLODBCTestBase, Test_SQL_DYNAMIC_CURSOR_ATTRIBUTES2) { TYPED_TEST(FlightSQLODBCTestBase, Test_SQL_FORWARD_ONLY_CURSOR_ATTRIBUTES1) { this->connect(); - validate(conn, SQL_FORWARD_ONLY_CURSOR_ATTRIBUTES1, + validate(this->conn, SQL_FORWARD_ONLY_CURSOR_ATTRIBUTES1, static_cast(SQL_CA1_NEXT)); this->disconnect(); @@ -213,7 +303,7 @@ TYPED_TEST(FlightSQLODBCTestBase, Test_SQL_FORWARD_ONLY_CURSOR_ATTRIBUTES1) { TYPED_TEST(FlightSQLODBCTestBase, Test_SQL_FORWARD_ONLY_CURSOR_ATTRIBUTES2) { this->connect(); - validate(conn, SQL_FORWARD_ONLY_CURSOR_ATTRIBUTES2, + validate(this->conn, SQL_FORWARD_ONLY_CURSOR_ATTRIBUTES2, static_cast(SQL_CA2_READ_ONLY_CONCURRENCY)); this->disconnect(); @@ -222,7 +312,7 @@ TYPED_TEST(FlightSQLODBCTestBase, Test_SQL_FORWARD_ONLY_CURSOR_ATTRIBUTES2) { TYPED_TEST(FlightSQLODBCTestBase, Test_SQL_FILE_USAGE) { this->connect(); - validate(conn, SQL_FILE_USAGE, static_cast(SQL_FILE_NOT_SUPPORTED)); + validate(this->conn, SQL_FILE_USAGE, static_cast(SQL_FILE_NOT_SUPPORTED)); this->disconnect(); } @@ -230,7 +320,7 @@ TYPED_TEST(FlightSQLODBCTestBase, Test_SQL_FILE_USAGE) { TYPED_TEST(FlightSQLODBCTestBase, Test_SQL_GETDATA_EXTENSIONS) { this->connect(); - validate(conn, SQL_GETDATA_EXTENSIONS, static_cast(3)); + validate(this->conn, SQL_GETDATA_EXTENSIONS, static_cast(3)); this->disconnect(); } @@ -240,7 +330,7 @@ TYPED_TEST(FlightSQLODBCTestBase, Test_SQL_INFO_SCHEMA_VIEWS) { // An SQLUINTEGER bitmask enumerating the views in the INFORMATION_SCHEMA that are // supported by the driver. - validateGreaterThan(conn, SQL_INFO_SCHEMA_VIEWS, static_cast(0)); + validateGreaterThan(this->conn, SQL_INFO_SCHEMA_VIEWS, static_cast(0)); this->disconnect(); } @@ -248,7 +338,7 @@ TYPED_TEST(FlightSQLODBCTestBase, Test_SQL_INFO_SCHEMA_VIEWS) { TYPED_TEST(FlightSQLODBCTestBase, Test_SQL_KEYSET_CURSOR_ATTRIBUTES1) { this->connect(); - validate(conn, SQL_KEYSET_CURSOR_ATTRIBUTES1, static_cast(0)); + validate(this->conn, SQL_KEYSET_CURSOR_ATTRIBUTES1, static_cast(0)); this->disconnect(); } @@ -256,7 +346,7 @@ TYPED_TEST(FlightSQLODBCTestBase, Test_SQL_KEYSET_CURSOR_ATTRIBUTES1) { TYPED_TEST(FlightSQLODBCTestBase, Test_SQL_KEYSET_CURSOR_ATTRIBUTES2) { this->connect(); - validate(conn, SQL_KEYSET_CURSOR_ATTRIBUTES2, static_cast(0)); + validate(this->conn, SQL_KEYSET_CURSOR_ATTRIBUTES2, static_cast(0)); this->disconnect(); } @@ -264,7 +354,7 @@ TYPED_TEST(FlightSQLODBCTestBase, Test_SQL_KEYSET_CURSOR_ATTRIBUTES2) { TYPED_TEST(FlightSQLODBCTestBase, Test_SQL_MAX_ASYNC_CONCURRENT_STATEMENTS) { this->connect(); - validate(conn, SQL_MAX_ASYNC_CONCURRENT_STATEMENTS, static_cast(0)); + validate(this->conn, SQL_MAX_ASYNC_CONCURRENT_STATEMENTS, static_cast(0)); this->disconnect(); } @@ -274,7 +364,7 @@ TYPED_TEST(FlightSQLODBCTestBase, Test_SQL_MAX_CONCURRENT_ACTIVITIES) { GTEST_SKIP(); this->connect(); - validate(conn, SQL_MAX_CONCURRENT_ACTIVITIES, static_cast(0)); + validate(this->conn, SQL_MAX_CONCURRENT_ACTIVITIES, static_cast(0)); this->disconnect(); } @@ -282,7 +372,7 @@ TYPED_TEST(FlightSQLODBCTestBase, Test_SQL_MAX_CONCURRENT_ACTIVITIES) { TYPED_TEST(FlightSQLODBCTestBase, Test_SQL_MAX_DRIVER_CONNECTIONS) { this->connect(); - validate(conn, SQL_MAX_DRIVER_CONNECTIONS, static_cast(0)); + validate(this->conn, SQL_MAX_DRIVER_CONNECTIONS, static_cast(0)); this->disconnect(); } @@ -290,7 +380,8 @@ TYPED_TEST(FlightSQLODBCTestBase, Test_SQL_MAX_DRIVER_CONNECTIONS) { TYPED_TEST(FlightSQLODBCTestBase, Test_SQL_ODBC_INTERFACE_CONFORMANCE) { this->connect(); - validate(conn, SQL_ODBC_INTERFACE_CONFORMANCE, static_cast(SQL_OIC_CORE)); + validate(this->conn, SQL_ODBC_INTERFACE_CONFORMANCE, + static_cast(SQL_OIC_CORE)); this->disconnect(); } @@ -303,7 +394,8 @@ TYPED_TEST(FlightSQLODBCTestBase, Test_SQL_ODBC_STANDARD_CLI_CONFORMANCE) { this->connect(); // Type does not exist in sql.h - // validate(conn, SQL_ODBC_STANDARD_CLI_CONFORMANCE, static_cast(0)); + // validate(this->conn, SQL_ODBC_STANDARD_CLI_CONFORMANCE, + // static_cast(0)); this->disconnect(); } @@ -312,7 +404,7 @@ TYPED_TEST(FlightSQLODBCTestBase, Test_SQL_ODBC_VER) { // This is implemented only in the Driver Manager. this->connect(); - validate(conn, SQL_ODBC_VER, L"03.80.0000"); + validate(this->conn, SQL_ODBC_VER, L"03.80.0000"); this->disconnect(); } @@ -320,7 +412,8 @@ TYPED_TEST(FlightSQLODBCTestBase, Test_SQL_ODBC_VER) { TYPED_TEST(FlightSQLODBCTestBase, Test_SQL_PARAM_ARRAY_ROW_COUNTS) { this->connect(); - validate(conn, SQL_PARAM_ARRAY_ROW_COUNTS, static_cast(SQL_PARC_NO_BATCH)); + validate(this->conn, SQL_PARAM_ARRAY_ROW_COUNTS, + static_cast(SQL_PARC_NO_BATCH)); this->disconnect(); } @@ -328,7 +421,8 @@ TYPED_TEST(FlightSQLODBCTestBase, Test_SQL_PARAM_ARRAY_ROW_COUNTS) { TYPED_TEST(FlightSQLODBCTestBase, Test_SQL_PARAM_ARRAY_SELECTS) { this->connect(); - validate(conn, SQL_PARAM_ARRAY_SELECTS, static_cast(SQL_PAS_NO_SELECT)); + validate(this->conn, SQL_PARAM_ARRAY_SELECTS, + static_cast(SQL_PAS_NO_SELECT)); this->disconnect(); } @@ -336,7 +430,7 @@ TYPED_TEST(FlightSQLODBCTestBase, Test_SQL_PARAM_ARRAY_SELECTS) { TYPED_TEST(FlightSQLODBCTestBase, Test_SQL_ROW_UPDATES) { this->connect(); - validate(conn, SQL_ROW_UPDATES, L"N"); + validate(this->conn, SQL_ROW_UPDATES, L"N"); this->disconnect(); } @@ -344,7 +438,7 @@ TYPED_TEST(FlightSQLODBCTestBase, Test_SQL_ROW_UPDATES) { TYPED_TEST(FlightSQLODBCTestBase, Test_SQL_SEARCH_PATTERN_ESCAPE) { this->connect(); - validate(conn, SQL_SEARCH_PATTERN_ESCAPE, L"\\"); + validate(this->conn, SQL_SEARCH_PATTERN_ESCAPE, L"\\"); this->disconnect(); } @@ -352,7 +446,7 @@ TYPED_TEST(FlightSQLODBCTestBase, Test_SQL_SEARCH_PATTERN_ESCAPE) { TYPED_TEST(FlightSQLODBCTestBase, Test_SQL_SERVER_NAME) { this->connect(); - validateNotEmptySQLWCHAR(conn, SQL_SERVER_NAME); + validateNotEmptySQLWCHAR(this->conn, SQL_SERVER_NAME); this->disconnect(); } @@ -360,7 +454,7 @@ TYPED_TEST(FlightSQLODBCTestBase, Test_SQL_SERVER_NAME) { TYPED_TEST(FlightSQLODBCTestBase, Test_SQL_STATIC_CURSOR_ATTRIBUTES1) { this->connect(); - validate(conn, SQL_STATIC_CURSOR_ATTRIBUTES1, static_cast(0)); + validate(this->conn, SQL_STATIC_CURSOR_ATTRIBUTES1, static_cast(0)); this->disconnect(); } @@ -368,7 +462,7 @@ TYPED_TEST(FlightSQLODBCTestBase, Test_SQL_STATIC_CURSOR_ATTRIBUTES1) { TYPED_TEST(FlightSQLODBCTestBase, Test_SQL_STATIC_CURSOR_ATTRIBUTES2) { this->connect(); - validate(conn, SQL_STATIC_CURSOR_ATTRIBUTES2, static_cast(0)); + validate(this->conn, SQL_STATIC_CURSOR_ATTRIBUTES2, static_cast(0)); this->disconnect(); } @@ -378,7 +472,7 @@ TYPED_TEST(FlightSQLODBCTestBase, Test_SQL_STATIC_CURSOR_ATTRIBUTES2) { TYPED_TEST(FlightSQLODBCTestBase, Test_SQL_DATABASE_NAME) { this->connect(); - validate(conn, SQL_DATABASE_NAME, L""); + validate(this->conn, SQL_DATABASE_NAME, L""); this->disconnect(); } @@ -386,7 +480,7 @@ TYPED_TEST(FlightSQLODBCTestBase, Test_SQL_DATABASE_NAME) { TYPED_TEST(FlightSQLODBCTestBase, Test_SQL_DBMS_NAME) { this->connect(); - validateNotEmptySQLWCHAR(conn, SQL_DBMS_NAME); + validateNotEmptySQLWCHAR(this->conn, SQL_DBMS_NAME); this->disconnect(); } @@ -394,99 +488,11 @@ TYPED_TEST(FlightSQLODBCTestBase, Test_SQL_DBMS_NAME) { TYPED_TEST(FlightSQLODBCTestBase, Test_SQL_DBMS_VER) { this->connect(); - validateNotEmptySQLWCHAR(conn, SQL_DBMS_VER); + validateNotEmptySQLWCHAR(this->conn, SQL_DBMS_VER); this->disconnect(); } -// wchar -void validateNotEmptySQLWCHAR(SQLHDBC connection, SQLUSMALLINT infoType) { - SQLWCHAR info_value[ODBC_BUFFER_SIZE] = L""; - SQLSMALLINT message_length; - - SQLRETURN ret = - SQLGetInfo(connection, infoType, info_value, ODBC_BUFFER_SIZE, &message_length); - - EXPECT_EQ(ret, SQL_SUCCESS); - - EXPECT_GT(wcslen(info_value), 0); -} - -// wchar -void validate(SQLHDBC connection, SQLUSMALLINT infoType, SQLWCHAR* expected_value) { - SQLWCHAR info_value[ODBC_BUFFER_SIZE] = L""; - SQLSMALLINT message_length; - - SQLRETURN ret = - SQLGetInfo(connection, infoType, info_value, ODBC_BUFFER_SIZE, &message_length); - - EXPECT_EQ(ret, SQL_SUCCESS); - - EXPECT_EQ(*info_value, *expected_value); -} - -// unsigned short -void validate(SQLHDBC connection, SQLUSMALLINT infoType, SQLUSMALLINT expected_value) { - SQLUSMALLINT info_value; - SQLSMALLINT message_length; - - SQLRETURN ret = SQLGetInfo(connection, infoType, &info_value, 0, &message_length); - - EXPECT_EQ(ret, SQL_SUCCESS); - - EXPECT_EQ(info_value, expected_value); -} - -// unsigned long -void validate(SQLHDBC connection, SQLUSMALLINT infoType, SQLUINTEGER expected_value) { - SQLUINTEGER info_value; - SQLSMALLINT message_length; - - SQLRETURN ret = SQLGetInfo(connection, infoType, &info_value, 0, &message_length); - - EXPECT_EQ(ret, SQL_SUCCESS); - - EXPECT_EQ(info_value, expected_value); -} - -// unsigned long -void validateGreaterThan(SQLHDBC connection, SQLUSMALLINT infoType, - SQLUINTEGER compared_value) { - SQLUINTEGER info_value; - SQLSMALLINT message_length; - - SQLRETURN ret = SQLGetInfo(connection, infoType, &info_value, 0, &message_length); - - EXPECT_EQ(ret, SQL_SUCCESS); - - EXPECT_GT(info_value, compared_value); -} - -// sql unsigned length -void validate(SQLHDBC connection, SQLUSMALLINT infoType, SQLULEN expected_value) { - SQLULEN info_value; - SQLSMALLINT message_length; - - SQLRETURN ret = SQLGetInfo(connection, infoType, &info_value, 0, &message_length); - - EXPECT_EQ(ret, SQL_SUCCESS); - - EXPECT_EQ(info_value, expected_value); -} - -// sql unsigned length -void validateGreaterThan(SQLHDBC connection, SQLUSMALLINT infoType, - SQLULEN compared_value) { - SQLULEN info_value; - SQLSMALLINT message_length; - - SQLRETURN ret = SQLGetInfo(connection, infoType, &info_value, 0, &message_length); - - EXPECT_EQ(ret, SQL_SUCCESS); - - EXPECT_GT(info_value, compared_value); -} - } // namespace integration_tests } // namespace odbc } // namespace flight From 01aca6fe85425c59c8eee1030fa9cfbcdbb9a25f Mon Sep 17 00:00:00 2001 From: rscales Date: Sat, 21 Jun 2025 02:17:12 +0100 Subject: [PATCH 07/29] Add tests for Data Source Information --- .../sql/odbc/tests/connection_info_test.cc | 186 ++++++++++++++++++ 1 file changed, 186 insertions(+) diff --git a/cpp/src/arrow/flight/sql/odbc/tests/connection_info_test.cc b/cpp/src/arrow/flight/sql/odbc/tests/connection_info_test.cc index bce22b7e3028..b2ab65f32694 100644 --- a/cpp/src/arrow/flight/sql/odbc/tests/connection_info_test.cc +++ b/cpp/src/arrow/flight/sql/odbc/tests/connection_info_test.cc @@ -493,6 +493,192 @@ TYPED_TEST(FlightSQLODBCTestBase, Test_SQL_DBMS_VER) { this->disconnect(); } +// Data Source Information + +TYPED_TEST(FlightSQLODBCTestBase, Test_SQL_ACCESSIBLE_PROCEDURES) { + this->connect(); + + validate(conn, SQL_ACCESSIBLE_PROCEDURES, L"N"); + + this->disconnect(); +} + +TYPED_TEST(FlightSQLODBCTestBase, Test_SQL_ACCESSIBLE_TABLES) { + this->connect(); + + validate(conn, SQL_ACCESSIBLE_TABLES, L"Y"); + + this->disconnect(); +} + +TYPED_TEST(FlightSQLODBCTestBase, Test_SQL_BOOKMARK_PERSISTENCE) { + this->connect(); + + validate(conn, SQL_BOOKMARK_PERSISTENCE, static_cast(0)); + + this->disconnect(); +} + +TYPED_TEST(FlightSQLODBCTestBase, Test_SQL_CATALOG_TERM) { + this->connect(); + + validate(conn, SQL_CATALOG_TERM, L""); + + this->disconnect(); +} + +TYPED_TEST(FlightSQLODBCTestBase, Test_SQL_COLLATION_SEQ) { + this->connect(); + + validate(conn, SQL_COLLATION_SEQ, L""); + + this->disconnect(); +} + +TYPED_TEST(FlightSQLODBCTestBase, Test_SQL_CONCAT_NULL_BEHAVIOR) { + this->connect(); + + validate(conn, SQL_CONCAT_NULL_BEHAVIOR, static_cast(0)); + + this->disconnect(); +} + +TYPED_TEST(FlightSQLODBCTestBase, Test_SQL_CURSOR_COMMIT_BEHAVIOR) { + this->connect(); + + validate(conn, SQL_CURSOR_COMMIT_BEHAVIOR, static_cast(1)); + + this->disconnect(); +} + +TYPED_TEST(FlightSQLODBCTestBase, Test_SQL_CURSOR_ROLLBACK_BEHAVIOR) { + this->connect(); + + validate(conn, SQL_CURSOR_ROLLBACK_BEHAVIOR, static_cast(1)); + + this->disconnect(); +} + +TYPED_TEST(FlightSQLODBCTestBase, Test_SQL_CURSOR_SENSITIVITY) { + this->connect(); + + validate(conn, SQL_CURSOR_SENSITIVITY, static_cast(0)); + + this->disconnect(); +} + +TYPED_TEST(FlightSQLODBCTestBase, Test_SQL_DATA_SOURCE_READ_ONLY) { + this->connect(); + + validate(conn, SQL_DATA_SOURCE_READ_ONLY, L"N"); + + this->disconnect(); +} + +TYPED_TEST(FlightSQLODBCTestBase, Test_SQL_DEFAULT_TXN_ISOLATION) { + this->connect(); + + validate(conn, SQL_DEFAULT_TXN_ISOLATION, static_cast(0)); + + this->disconnect(); +} + +TYPED_TEST(FlightSQLODBCTestBase, Test_SQL_DESCRIBE_PARAMETER) { + this->connect(); + + validate(conn, SQL_DESCRIBE_PARAMETER, L"N"); + + this->disconnect(); +} + +TYPED_TEST(FlightSQLODBCTestBase, Test_SQL_MULT_RESULT_SETS) { + this->connect(); + + validate(conn, SQL_MULT_RESULT_SETS, L"N"); + + this->disconnect(); +} + +TYPED_TEST(FlightSQLODBCTestBase, Test_SQL_MULTIPLE_ACTIVE_TXN) { + this->connect(); + + validate(conn, SQL_MULTIPLE_ACTIVE_TXN, L"N"); + + this->disconnect(); +} + +TYPED_TEST(FlightSQLODBCTestBase, Test_SQL_NEED_LONG_DATA_LEN) { + this->connect(); + + validate(conn, SQL_NEED_LONG_DATA_LEN, L"N"); + + this->disconnect(); +} + +TEST_F(FlightSQLODBCMockTestBase, Test_SQL_NULL_COLLATION) { + this->connect(); + + validate(conn, SQL_NULL_COLLATION, static_cast(2)); + + this->disconnect(); +} + +TEST_F(FlightSQLODBCMockTestBase, Test_SQL_PROCEDURE_TERM) { + this->connect(); + + validate(conn, SQL_PROCEDURE_TERM, L""); + + this->disconnect(); +} + +TYPED_TEST(FlightSQLODBCTestBase, Test_SQL_SCHEMA_TERM) { + this->connect(); + + validate(conn, SQL_SCHEMA_TERM, L"schema"); + + this->disconnect(); +} + +TYPED_TEST(FlightSQLODBCTestBase, Test_SQL_SCROLL_OPTIONS) { + this->connect(); + + validate(conn, SQL_SCROLL_OPTIONS, static_cast(SQL_SO_FORWARD_ONLY)); + + this->disconnect(); +} + +TYPED_TEST(FlightSQLODBCTestBase, Test_SQL_TABLE_TERM) { + this->connect(); + + validate(conn, SQL_TABLE_TERM, L"table"); + + this->disconnect(); +} + +TYPED_TEST(FlightSQLODBCTestBase, Test_SQL_TXN_CAPABLE) { + this->connect(); + + validate(conn, SQL_TXN_CAPABLE, static_cast(SQL_TC_NONE)); + + this->disconnect(); +} + +TYPED_TEST(FlightSQLODBCTestBase, Test_SQL_TXN_ISOLATION_OPTION) { + this->connect(); + + validate(conn, SQL_TXN_ISOLATION_OPTION, static_cast(0)); + + this->disconnect(); +} + +TEST_F(FlightSQLODBCMockTestBase, Test_SQL_USER_NAME) { + this->connect(); + + validate(conn, SQL_USER_NAME, L""); + + this->disconnect(); +} + } // namespace integration_tests } // namespace odbc } // namespace flight From 6ee5e08fb2d3819798077f16749042d52247b63b Mon Sep 17 00:00:00 2001 From: rscales Date: Mon, 23 Jun 2025 05:19:33 +0100 Subject: [PATCH 08/29] Add tests for Supported SQL --- .../sql/odbc/tests/connection_info_test.cc | 396 ++++++++++++++++++ 1 file changed, 396 insertions(+) diff --git a/cpp/src/arrow/flight/sql/odbc/tests/connection_info_test.cc b/cpp/src/arrow/flight/sql/odbc/tests/connection_info_test.cc index b2ab65f32694..b72fc593175d 100644 --- a/cpp/src/arrow/flight/sql/odbc/tests/connection_info_test.cc +++ b/cpp/src/arrow/flight/sql/odbc/tests/connection_info_test.cc @@ -679,6 +679,402 @@ TEST_F(FlightSQLODBCMockTestBase, Test_SQL_USER_NAME) { this->disconnect(); } +// Supported SQL + +TYPED_TEST(FlightSQLODBCTestBase, Test_SQL_AGGREGATE_FUNCTIONS) { + this->connect(); + + validate(conn, SQL_AGGREGATE_FUNCTIONS, static_cast(127)); + + this->disconnect(); +} + +TYPED_TEST(FlightSQLODBCTestBase, Test_SQL_ALTER_DOMAIN) { + this->connect(); + + validate(conn, SQL_ALTER_DOMAIN, static_cast(0)); + + this->disconnect(); +} + +TYPED_TEST(FlightSQLODBCTestBase, Test_SQL_ALTER_SCHEMA) { + // TODO Value commented out in odbc_connection.cc and does not exist in sql.h + GTEST_SKIP(); + this->connect(); + + // validate(conn, SQL_ALTER_SCHEMA, static_cast(0)); + + this->disconnect(); +} + +TYPED_TEST(FlightSQLODBCTestBase, Test_SQL_ALTER_TABLE) { + this->connect(); + + validate(conn, SQL_ALTER_TABLE, static_cast(0)); + + this->disconnect(); +} + +TYPED_TEST(FlightSQLODBCTestBase, Test_SQL_ANSI_SQL_DATETIME_LITERALS) { + // TODO Value does not exist in odbc_connection.cc or in sql.h + GTEST_SKIP(); + this->connect(); + + // validate(conn, SQL_ANSI_SQL_DATETIME_LITERALS, L""); + + this->disconnect(); +} + +TYPED_TEST(FlightSQLODBCTestBase, Test_SQL_CATALOG_LOCATION) { + this->connect(); + + validate(conn, SQL_CATALOG_LOCATION, static_cast(0)); + + this->disconnect(); +} + +TYPED_TEST(FlightSQLODBCTestBase, Test_SQL_CATALOG_NAME) { + this->connect(); + + validate(conn, SQL_CATALOG_NAME, L"N"); + + this->disconnect(); +} + +TYPED_TEST(FlightSQLODBCTestBase, Test_SQL_CATALOG_NAME_SEPARATOR) { + this->connect(); + + validate(conn, SQL_CATALOG_NAME_SEPARATOR, L""); + + this->disconnect(); +} + +TEST_F(FlightSQLODBCMockTestBase, Test_SQL_CATALOG_USAGE) { + this->connect(); + + validate(conn, SQL_CATALOG_USAGE, static_cast(0)); + + this->disconnect(); +} + +TYPED_TEST(FlightSQLODBCTestBase, Test_SQL_COLUMN_ALIAS) { + this->connect(); + + validate(conn, SQL_COLUMN_ALIAS, L"Y"); + + this->disconnect(); +} + +TEST_F(FlightSQLODBCMockTestBase, Test_SQL_CORRELATION_NAME) { + this->connect(); + + validate(conn, SQL_CORRELATION_NAME, static_cast(0)); + + this->disconnect(); +} + +TYPED_TEST(FlightSQLODBCTestBase, Test_SQL_CREATE_ASSERTION) { + this->connect(); + + validate(conn, SQL_CREATE_ASSERTION, static_cast(0)); + + this->disconnect(); +} + +TYPED_TEST(FlightSQLODBCTestBase, Test_SQL_CREATE_CHARACTER_SET) { + this->connect(); + + validate(conn, SQL_CREATE_CHARACTER_SET, static_cast(0)); + + this->disconnect(); +} + +TYPED_TEST(FlightSQLODBCTestBase, Test_SQL_CREATE_COLLATION) { + this->connect(); + + validate(conn, SQL_CREATE_COLLATION, static_cast(0)); + + this->disconnect(); +} + +TYPED_TEST(FlightSQLODBCTestBase, Test_SQL_CREATE_DOMAIN) { + this->connect(); + + validate(conn, SQL_CREATE_DOMAIN, static_cast(0)); + + this->disconnect(); +} + +TEST_F(FlightSQLODBCMockTestBase, Test_SQL_CREATE_SCHEMA) { + this->connect(); + + validate(conn, SQL_CREATE_SCHEMA, static_cast(1)); + + this->disconnect(); +} + +TEST_F(FlightSQLODBCMockTestBase, Test_SQL_CREATE_TABLE) { + this->connect(); + + validate(conn, SQL_CREATE_TABLE, static_cast(1)); + + this->disconnect(); +} + +TYPED_TEST(FlightSQLODBCTestBase, Test_SQL_CREATE_TRANSLATION) { + this->connect(); + + validate(conn, SQL_CREATE_TRANSLATION, static_cast(0)); + + this->disconnect(); +} + +TYPED_TEST(FlightSQLODBCTestBase, Test_SQL_DDL_INDEX) { + this->connect(); + + validate(conn, SQL_DDL_INDEX, static_cast(0)); + + this->disconnect(); +} + +TYPED_TEST(FlightSQLODBCTestBase, Test_SQL_DROP_ASSERTION) { + this->connect(); + + validate(conn, SQL_DROP_ASSERTION, static_cast(0)); + + this->disconnect(); +} + +TYPED_TEST(FlightSQLODBCTestBase, Test_SQL_DROP_CHARACTER_SET) { + this->connect(); + + validate(conn, SQL_DROP_CHARACTER_SET, static_cast(0)); + + this->disconnect(); +} + +TYPED_TEST(FlightSQLODBCTestBase, Test_SQL_DROP_COLLATION) { + this->connect(); + + validate(conn, SQL_DROP_COLLATION, static_cast(0)); + + this->disconnect(); +} + +TYPED_TEST(FlightSQLODBCTestBase, Test_SQL_DROP_DOMAIN) { + this->connect(); + + validate(conn, SQL_DROP_DOMAIN, static_cast(0)); + + this->disconnect(); +} + +TYPED_TEST(FlightSQLODBCTestBase, Test_SQL_DROP_SCHEMA) { + this->connect(); + + validate(conn, SQL_DROP_SCHEMA, static_cast(0)); + + this->disconnect(); +} + +TYPED_TEST(FlightSQLODBCTestBase, Test_SQL_DROP_TABLE) { + this->connect(); + + validate(conn, SQL_DROP_TABLE, static_cast(0)); + + this->disconnect(); +} + +TYPED_TEST(FlightSQLODBCTestBase, Test_SQL_DROP_TRANSLATION) { + this->connect(); + + validate(conn, SQL_DROP_TRANSLATION, static_cast(0)); + + this->disconnect(); +} + +TYPED_TEST(FlightSQLODBCTestBase, Test_SQL_DROP_VIEW) { + this->connect(); + + validate(conn, SQL_DROP_VIEW, static_cast(0)); + + this->disconnect(); +} + +TEST_F(FlightSQLODBCMockTestBase, Test_SQL_EXPRESSIONS_IN_ORDERBY) { + this->connect(); + + validate(conn, SQL_EXPRESSIONS_IN_ORDERBY, L"N"); + + this->disconnect(); +} + +TEST_F(FlightSQLODBCMockTestBase, Test_SQL_GROUP_BY) { + this->connect(); + + validate(conn, SQL_GROUP_BY, static_cast(2)); + + this->disconnect(); +} + +TYPED_TEST(FlightSQLODBCTestBase, Test_SQL_IDENTIFIER_CASE) { + this->connect(); + + validate(conn, SQL_IDENTIFIER_CASE, static_cast(4)); + + this->disconnect(); +} + +TYPED_TEST(FlightSQLODBCTestBase, Test_SQL_IDENTIFIER_QUOTE_CHAR) { + this->connect(); + + validate(conn, SQL_IDENTIFIER_QUOTE_CHAR, L"\""); + + this->disconnect(); +} + +TYPED_TEST(FlightSQLODBCTestBase, Test_SQL_INDEX_KEYWORDS) { + this->connect(); + + validate(conn, SQL_INDEX_KEYWORDS, static_cast(0)); + + this->disconnect(); +} + +TYPED_TEST(FlightSQLODBCTestBase, Test_SQL_INSERT_STATEMENT) { + this->connect(); + + validate(conn, SQL_INSERT_STATEMENT, static_cast(7)); + + this->disconnect(); +} + +TYPED_TEST(FlightSQLODBCTestBase, Test_SQL_INTEGRITY) { + this->connect(); + + validate(conn, SQL_INTEGRITY, L"N"); + + this->disconnect(); +} + +TYPED_TEST(FlightSQLODBCTestBase, Test_SQL_KEYWORDS) { + GTEST_SKIP(); + this->connect(); + + validate( + conn, SQL_KEYWORDS, + L"ABORT, ACTION, ADD, AFTER, ALL, ALTER, ALWAYS, ANALYZE, AND, AS, ASC, ATTACH, " + L"AUTOINCREMENT, BEFORE, BEGIN, BETWEEN, BY, CASCADE, CASE, CAST, CHECK, COLLATE, " + L"COLUMN, COMMIT, CONFLICT, CONSTRAINT, CREATE, CROSS, CURRENT, CURRENT_DATE, " + L"CURRENT_TIME, CURRENT_TIMESTAMP, DATABASE, DEFAULT, DEFERRABLE, DEFERRED, " + L"DELETE, DESC, DETACH, DISTINCT, DO, DROP, EACH, ELSE, END, ESCAPE, EXCEPT, " + L"EXCLUDE, EXCLUSIVE, EXISTS, EXPLAIN, FAIL, FILTER, FIRST, FOLLOWING, FOR, " + L"FOREIGN, FROM, FULL, GENERATED, GLOB, GROUP, GROUPS, H"); + + this->disconnect(); +} + +TYPED_TEST(FlightSQLODBCTestBase, Test_SQL_LIKE_ESCAPE_CLAUSE) { + this->connect(); + + validate(conn, SQL_LIKE_ESCAPE_CLAUSE, L"Y"); + + this->disconnect(); +} + +TEST_F(FlightSQLODBCMockTestBase, Test_SQL_NON_NULLABLE_COLUMNS) { + this->connect(); + + validate(conn, SQL_NON_NULLABLE_COLUMNS, static_cast(0)); + + this->disconnect(); +} + +TEST_F(FlightSQLODBCMockTestBase, Test_SQL_OJ_CAPABILITIES) { + this->connect(); + + validate(this->conn, SQL_OJ_CAPABILITIES, static_cast(7)); + + this->disconnect(); +} + +TEST_F(FlightSQLODBCMockTestBase, Test_SQL_ORDER_BY_COLUMNS_IN_SELECT) { + this->connect(); + + validate(conn, SQL_ORDER_BY_COLUMNS_IN_SELECT, L"Y"); + + this->disconnect(); +} + +TYPED_TEST(FlightSQLODBCTestBase, Test_SQL_OUTER_JOINS) { + // Not documented in SQLGetInfo, but other drivers return Y/N + // TODO The call returns value SQL_ERROR + GTEST_SKIP(); + this->connect(); + + validate(conn, SQL_OUTER_JOINS, L"Y"); + + this->disconnect(); +} + +TYPED_TEST(FlightSQLODBCTestBase, Test_SQL_PROCEDURES) { + this->connect(); + + validate(conn, SQL_PROCEDURES, L"N"); + + this->disconnect(); +} + +TEST_F(FlightSQLODBCMockTestBase, Test_SQL_QUOTED_IDENTIFIER_CASE) { + this->connect(); + + validate(conn, SQL_QUOTED_IDENTIFIER_CASE, static_cast(4)); + + this->disconnect(); +} + +TEST_F(FlightSQLODBCMockTestBase, Test_SQL_SCHEMA_USAGE) { + this->connect(); + + validate(conn, SQL_SCHEMA_USAGE, static_cast(1)); + + this->disconnect(); +} + +TYPED_TEST(FlightSQLODBCTestBase, Test_SQL_SPECIAL_CHARACTERS) { + // TODO The call returns value SQL_ERROR + GTEST_SKIP(); + this->connect(); + + validate(conn, SQL_SPECIAL_CHARACTERS, L""); + + this->disconnect(); +} + +TYPED_TEST(FlightSQLODBCTestBase, Test_SQL_SQL_CONFORMANCE) { + this->connect(); + + validate(conn, SQL_SQL_CONFORMANCE, static_cast(1)); + + this->disconnect(); +} + +TEST_F(FlightSQLODBCMockTestBase, Test_SQL_SUBQUERIES) { + this->connect(); + + validate(conn, SQL_SUBQUERIES, static_cast(31)); + + this->disconnect(); +} + +TYPED_TEST(FlightSQLODBCTestBase, Test_SQL_UNION) { + this->connect(); + + validate(conn, SQL_UNION, static_cast(3)); + + this->disconnect(); +} + } // namespace integration_tests } // namespace odbc } // namespace flight From 0856fc4673b19eb45a6af0e0673eb2ae18bf9090 Mon Sep 17 00:00:00 2001 From: rscales Date: Mon, 23 Jun 2025 07:26:01 +0100 Subject: [PATCH 09/29] Add tests for SQL Limits --- .../sql/odbc/tests/connection_info_test.cc | 162 ++++++++++++++++++ 1 file changed, 162 insertions(+) diff --git a/cpp/src/arrow/flight/sql/odbc/tests/connection_info_test.cc b/cpp/src/arrow/flight/sql/odbc/tests/connection_info_test.cc index b72fc593175d..fcfa1799d013 100644 --- a/cpp/src/arrow/flight/sql/odbc/tests/connection_info_test.cc +++ b/cpp/src/arrow/flight/sql/odbc/tests/connection_info_test.cc @@ -1075,6 +1075,168 @@ TYPED_TEST(FlightSQLODBCTestBase, Test_SQL_UNION) { this->disconnect(); } +// SQL Limits + +TYPED_TEST(FlightSQLODBCTestBase, Test_SQL_MAX_BINARY_LITERAL_LEN) { + this->connect(); + + validate(conn, SQL_MAX_BINARY_LITERAL_LEN, static_cast(0)); + + this->disconnect(); +} + +TEST_F(FlightSQLODBCMockTestBase, Test_SQL_MAX_CATALOG_NAME_LEN) { + this->connect(); + + validate(conn, SQL_MAX_CATALOG_NAME_LEN, static_cast(0)); + + this->disconnect(); +} + +TYPED_TEST(FlightSQLODBCTestBase, Test_SQL_MAX_CHAR_LITERAL_LEN) { + this->connect(); + + validate(conn, SQL_MAX_CHAR_LITERAL_LEN, static_cast(0)); + + this->disconnect(); +} + +TEST_F(FlightSQLODBCMockTestBase, Test_SQL_MAX_COLUMN_NAME_LEN) { + this->connect(); + + validate(conn, SQL_MAX_COLUMN_NAME_LEN, static_cast(0)); + + this->disconnect(); +} + +TYPED_TEST(FlightSQLODBCTestBase, Test_SQL_MAX_COLUMNS_IN_GROUP_BY) { + this->connect(); + + validate(conn, SQL_MAX_COLUMNS_IN_GROUP_BY, static_cast(0)); + + this->disconnect(); +} + +TYPED_TEST(FlightSQLODBCTestBase, Test_SQL_MAX_COLUMNS_IN_INDEX) { + this->connect(); + + validate(conn, SQL_MAX_COLUMNS_IN_INDEX, static_cast(0)); + + this->disconnect(); +} + +TYPED_TEST(FlightSQLODBCTestBase, Test_SQL_MAX_COLUMNS_IN_ORDER_BY) { + this->connect(); + + validate(conn, SQL_MAX_COLUMNS_IN_ORDER_BY, static_cast(0)); + + this->disconnect(); +} + +TYPED_TEST(FlightSQLODBCTestBase, Test_SQL_MAX_COLUMNS_IN_SELECT) { + this->connect(); + + validate(conn, SQL_MAX_COLUMNS_IN_SELECT, static_cast(0)); + + this->disconnect(); +} + +TYPED_TEST(FlightSQLODBCTestBase, Test_SQL_MAX_COLUMNS_IN_TABLE) { + this->connect(); + + validate(conn, SQL_MAX_COLUMNS_IN_TABLE, static_cast(0)); + + this->disconnect(); +} + +TEST_F(FlightSQLODBCMockTestBase, Test_SQL_MAX_CURSOR_NAME_LEN) { + this->connect(); + + validate(conn, SQL_MAX_CURSOR_NAME_LEN, static_cast(0)); + + this->disconnect(); +} + +TYPED_TEST(FlightSQLODBCTestBase, Test_SQL_MAX_IDENTIFIER_LEN) { + this->connect(); + + validate(conn, SQL_MAX_IDENTIFIER_LEN, static_cast(65535)); + + this->disconnect(); +} + +TYPED_TEST(FlightSQLODBCTestBase, Test_SQL_MAX_INDEX_SIZE) { + this->connect(); + + validate(conn, SQL_MAX_INDEX_SIZE, static_cast(0)); + + this->disconnect(); +} + +TYPED_TEST(FlightSQLODBCTestBase, Test_SQL_MAX_PROCEDURE_NAME_LEN) { + this->connect(); + + validate(conn, SQL_MAX_PROCEDURE_NAME_LEN, static_cast(0)); + + this->disconnect(); +} + +TYPED_TEST(FlightSQLODBCTestBase, Test_SQL_MAX_ROW_SIZE) { + this->connect(); + + validate(conn, SQL_MAX_ROW_SIZE, L""); + + this->disconnect(); +} + +TEST_F(FlightSQLODBCMockTestBase, Test_SQL_MAX_ROW_SIZE_INCLUDES_LONG) { + this->connect(); + + validate(conn, SQL_MAX_ROW_SIZE_INCLUDES_LONG, L"N"); + + this->disconnect(); +} + +TEST_F(FlightSQLODBCMockTestBase, Test_SQL_MAX_SCHEMA_NAME_LEN) { + this->connect(); + + validate(conn, SQL_MAX_SCHEMA_NAME_LEN, static_cast(0)); + + this->disconnect(); +} + +TYPED_TEST(FlightSQLODBCTestBase, Test_SQL_MAX_STATEMENT_LEN) { + this->connect(); + + validate(conn, SQL_MAX_STATEMENT_LEN, static_cast(0)); + + this->disconnect(); +} + +TEST_F(FlightSQLODBCMockTestBase, Test_SQL_MAX_TABLE_NAME_LEN) { + this->connect(); + + validate(conn, SQL_MAX_TABLE_NAME_LEN, static_cast(0)); + + this->disconnect(); +} + +TYPED_TEST(FlightSQLODBCTestBase, Test_SQL_MAX_TABLES_IN_SELECT) { + this->connect(); + + validate(conn, SQL_MAX_TABLES_IN_SELECT, static_cast(0)); + + this->disconnect(); +} + +TEST_F(FlightSQLODBCMockTestBase, Test_SQL_MAX_USER_NAME_LEN) { + this->connect(); + + validate(conn, SQL_MAX_USER_NAME_LEN, static_cast(0)); + + this->disconnect(); +} + } // namespace integration_tests } // namespace odbc } // namespace flight From f532c12d4316329a46b5e12b721d04cc0a059bcc Mon Sep 17 00:00:00 2001 From: rscales Date: Mon, 23 Jun 2025 08:14:57 +0100 Subject: [PATCH 10/29] Add tests for Scalar Function Information --- .../sql/odbc/tests/connection_info_test.cc | 62 +++++++++++++++++++ 1 file changed, 62 insertions(+) diff --git a/cpp/src/arrow/flight/sql/odbc/tests/connection_info_test.cc b/cpp/src/arrow/flight/sql/odbc/tests/connection_info_test.cc index fcfa1799d013..ced192be7855 100644 --- a/cpp/src/arrow/flight/sql/odbc/tests/connection_info_test.cc +++ b/cpp/src/arrow/flight/sql/odbc/tests/connection_info_test.cc @@ -1237,6 +1237,68 @@ TEST_F(FlightSQLODBCMockTestBase, Test_SQL_MAX_USER_NAME_LEN) { this->disconnect(); } +// Scalar Function Information + +TYPED_TEST(FlightSQLODBCTestBase, Test_SQL_CONVERT_FUNCTIONS) { + // TODO Driver manager returns -1 + GTEST_SKIP(); + this->connect(); + + validate(conn, SQL_CONVERT_FUNCTIONS, static_cast(3435973836)); + + this->disconnect(); +} + +TEST_F(FlightSQLODBCMockTestBase, Test_SQL_NUMERIC_FUNCTIONS) { + this->connect(); + + validate(conn, SQL_NUMERIC_FUNCTIONS, static_cast(4058942)); + + this->disconnect(); +} + +TEST_F(FlightSQLODBCMockTestBase, Test_SQL_STRING_FUNCTIONS) { + this->connect(); + + validate(conn, SQL_STRING_FUNCTIONS, static_cast(1304)); + + this->disconnect(); +} + +TEST_F(FlightSQLODBCMockTestBase, Test_SQL_SYSTEM_FUNCTIONS) { + this->connect(); + + validate(conn, SQL_SYSTEM_FUNCTIONS, static_cast(5)); + + this->disconnect(); +} + +TYPED_TEST(FlightSQLODBCTestBase, Test_SQL_TIMEDATE_ADD_INTERVALS) { + this->connect(); + + validate(conn, SQL_TIMEDATE_ADD_INTERVALS, static_cast(511)); + + this->disconnect(); +} + +TYPED_TEST(FlightSQLODBCTestBase, Test_SQL_TIMEDATE_DIFF_INTERVALS) { + this->connect(); + + validate(conn, SQL_TIMEDATE_DIFF_INTERVALS, static_cast(511)); + + this->disconnect(); +} + +TYPED_TEST(FlightSQLODBCTestBase, Test_SQL_TIMEDATE_FUNCTIONS) { + // TODO Driver manager returns -1 + GTEST_SKIP(); + this->connect(); + + validate(conn, SQL_TIMEDATE_FUNCTIONS, static_cast(3435973836)); + + this->disconnect(); +} + } // namespace integration_tests } // namespace odbc } // namespace flight From 562057b18206498371d8be18fed88075a31be30e Mon Sep 17 00:00:00 2001 From: rscales Date: Mon, 23 Jun 2025 17:47:47 +0100 Subject: [PATCH 11/29] Add tests for Conversion Information --- .../sql/odbc/tests/connection_info_test.cc | 170 ++++++++++++++++++ 1 file changed, 170 insertions(+) diff --git a/cpp/src/arrow/flight/sql/odbc/tests/connection_info_test.cc b/cpp/src/arrow/flight/sql/odbc/tests/connection_info_test.cc index ced192be7855..0d92b2ba750b 100644 --- a/cpp/src/arrow/flight/sql/odbc/tests/connection_info_test.cc +++ b/cpp/src/arrow/flight/sql/odbc/tests/connection_info_test.cc @@ -1299,6 +1299,176 @@ TYPED_TEST(FlightSQLODBCTestBase, Test_SQL_TIMEDATE_FUNCTIONS) { this->disconnect(); } +// Conversion Information + +TEST_F(FlightSQLODBCMockTestBase, Test_SQL_CONVERT_BIGINT) { + this->connect(); + + validate(conn, SQL_CONVERT_BIGINT, static_cast(8)); + + this->disconnect(); +} + +TEST_F(FlightSQLODBCMockTestBase, Test_SQL_CONVERT_BINARY) { + this->connect(); + + validate(conn, SQL_CONVERT_BINARY, static_cast(0)); + + this->disconnect(); +} + +TYPED_TEST(FlightSQLODBCTestBase, Test_SQL_CONVERT_BIT) { + this->connect(); + + validate(conn, SQL_CONVERT_BIT, static_cast(0)); + + this->disconnect(); +} + +TEST_F(FlightSQLODBCMockTestBase, Test_SQL_CONVERT_CHAR) { + this->connect(); + + validate(conn, SQL_CONVERT_CHAR, static_cast(0)); + + this->disconnect(); +} + +TEST_F(FlightSQLODBCMockTestBase, Test_SQL_CONVERT_DATE) { + this->connect(); + + validate(conn, SQL_CONVERT_DATE, static_cast(0)); + + this->disconnect(); +} + +TEST_F(FlightSQLODBCMockTestBase, Test_SQL_CONVERT_DECIMAL) { + this->connect(); + + validate(conn, SQL_CONVERT_DECIMAL, static_cast(0)); + + this->disconnect(); +} + +TYPED_TEST(FlightSQLODBCTestBase, Test_SQL_CONVERT_DOUBLE) { + this->connect(); + + validate(conn, SQL_CONVERT_DOUBLE, static_cast(0)); + + this->disconnect(); +} + +TEST_F(FlightSQLODBCMockTestBase, Test_SQL_CONVERT_FLOAT) { + this->connect(); + + validate(conn, SQL_CONVERT_FLOAT, static_cast(0)); + + this->disconnect(); +} + +TYPED_TEST(FlightSQLODBCTestBase, Test_SQL_CONVERT_INTEGER) { + this->connect(); + + validate(conn, SQL_CONVERT_INTEGER, static_cast(0)); + + this->disconnect(); +} + +TEST_F(FlightSQLODBCMockTestBase, Test_SQL_CONVERT_INTERVAL_DAY_TIME) { + this->connect(); + + validate(conn, SQL_CONVERT_INTERVAL_DAY_TIME, static_cast(0)); + + this->disconnect(); +} + +TYPED_TEST(FlightSQLODBCTestBase, Test_SQL_CONVERT_INTERVAL_YEAR_MONTH) { + this->connect(); + + validate(conn, SQL_CONVERT_INTERVAL_YEAR_MONTH, static_cast(0)); + + this->disconnect(); +} + +TYPED_TEST(FlightSQLODBCTestBase, Test_SQL_CONVERT_LONGVARBINARY) { + this->connect(); + + validate(conn, SQL_CONVERT_LONGVARBINARY, static_cast(0)); + + this->disconnect(); +} + +TYPED_TEST(FlightSQLODBCTestBase, Test_SQL_CONVERT_LONGVARCHAR) { + this->connect(); + + validate(conn, SQL_CONVERT_LONGVARCHAR, static_cast(0)); + + this->disconnect(); +} + +TEST_F(FlightSQLODBCMockTestBase, Test_SQL_CONVERT_NUMERIC) { + this->connect(); + + validate(conn, SQL_CONVERT_NUMERIC, static_cast(0)); + + this->disconnect(); +} + +TYPED_TEST(FlightSQLODBCTestBase, Test_SQL_CONVERT_REAL) { + this->connect(); + + validate(conn, SQL_CONVERT_REAL, static_cast(0)); + + this->disconnect(); +} + +TYPED_TEST(FlightSQLODBCTestBase, Test_SQL_CONVERT_SMALLINT) { + this->connect(); + + validate(conn, SQL_CONVERT_SMALLINT, static_cast(0)); + + this->disconnect(); +} + +TYPED_TEST(FlightSQLODBCTestBase, Test_SQL_CONVERT_TIME) { + this->connect(); + + validate(conn, SQL_CONVERT_TIME, static_cast(0)); + + this->disconnect(); +} + +TYPED_TEST(FlightSQLODBCTestBase, Test_SQL_CONVERT_TIMESTAMP) { + this->connect(); + + validate(conn, SQL_CONVERT_TIMESTAMP, static_cast(0)); + + this->disconnect(); +} + +TYPED_TEST(FlightSQLODBCTestBase, Test_SQL_CONVERT_TINYINT) { + this->connect(); + + validate(conn, SQL_CONVERT_TINYINT, static_cast(0)); + + this->disconnect(); +} + +TYPED_TEST(FlightSQLODBCTestBase, Test_SQL_CONVERT_VARBINARY) { + this->connect(); + + validate(conn, SQL_CONVERT_VARBINARY, static_cast(0)); + + this->disconnect(); +} + +TYPED_TEST(FlightSQLODBCTestBase, Test_SQL_CONVERT_VARCHAR) { + this->connect(); + + validate(conn, SQL_CONVERT_VARCHAR, static_cast(0)); + + this->disconnect(); +} + } // namespace integration_tests } // namespace odbc } // namespace flight From f9e6f54be1ed4498f83665f4a05e37e0d15a34d2 Mon Sep 17 00:00:00 2001 From: rscales Date: Mon, 23 Jun 2025 20:01:14 +0100 Subject: [PATCH 12/29] Move connection info test to be above connection test file --- cpp/src/arrow/flight/sql/odbc/tests/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cpp/src/arrow/flight/sql/odbc/tests/CMakeLists.txt b/cpp/src/arrow/flight/sql/odbc/tests/CMakeLists.txt index 23c0f47a1ce9..264457b9afc4 100644 --- a/cpp/src/arrow/flight/sql/odbc/tests/CMakeLists.txt +++ b/cpp/src/arrow/flight/sql/odbc/tests/CMakeLists.txt @@ -31,9 +31,9 @@ set(ARROW_FLIGHT_SQL_MOCK_SERVER_SRCS add_arrow_test(connection_test SOURCES - connection_test.cc connection_attr_test.cc connection_info_test.cc + connection_test.cc odbc_test_suite.cc odbc_test_suite.h ${ARROW_FLIGHT_SQL_MOCK_SERVER_SRCS} From 27f5b5f95c22bf0d17b089578ed11bf8f6e5b2fe Mon Sep 17 00:00:00 2001 From: rscales Date: Mon, 23 Jun 2025 20:21:19 +0100 Subject: [PATCH 13/29] Change to use compact namespace inclusion --- .../flight/sql/odbc/tests/connection_info_test.cc | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/cpp/src/arrow/flight/sql/odbc/tests/connection_info_test.cc b/cpp/src/arrow/flight/sql/odbc/tests/connection_info_test.cc index 0d92b2ba750b..21c1f1c19729 100644 --- a/cpp/src/arrow/flight/sql/odbc/tests/connection_info_test.cc +++ b/cpp/src/arrow/flight/sql/odbc/tests/connection_info_test.cc @@ -26,10 +26,7 @@ #include "gtest/gtest.h" -namespace arrow { -namespace flight { -namespace odbc { -namespace integration_tests { +namespace arrow::flight::odbc::integration_tests { // Helper Functions @@ -1469,7 +1466,4 @@ TYPED_TEST(FlightSQLODBCTestBase, Test_SQL_CONVERT_VARCHAR) { this->disconnect(); } -} // namespace integration_tests -} // namespace odbc -} // namespace flight -} // namespace arrow +} // namespace arrow::flight::odbc::integration_tests From e8747d33916b78b81832dea10f7f3ef0fc95104e Mon Sep 17 00:00:00 2001 From: rscales Date: Mon, 23 Jun 2025 21:23:36 +0100 Subject: [PATCH 14/29] Clean up comments --- .../sql/odbc/tests/connection_info_test.cc | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/cpp/src/arrow/flight/sql/odbc/tests/connection_info_test.cc b/cpp/src/arrow/flight/sql/odbc/tests/connection_info_test.cc index 21c1f1c19729..e263e421bd37 100644 --- a/cpp/src/arrow/flight/sql/odbc/tests/connection_info_test.cc +++ b/cpp/src/arrow/flight/sql/odbc/tests/connection_info_test.cc @@ -228,9 +228,6 @@ TYPED_TEST(FlightSQLODBCTestBase, Test_SQL_DRIVER_HENV) { TYPED_TEST(FlightSQLODBCTestBase, Test_SQL_DRIVER_HLIB) { this->connect(); - // An SQLULEN value, the hinst from the load library returned to the Driver Manager when - // it loaded the driver DLL on a Microsoft Windows operating system, or its equivalent - // on another operating system. validateGreaterThan(this->conn, SQL_DRIVER_HLIB, static_cast(0)); this->disconnect(); @@ -386,7 +383,7 @@ TYPED_TEST(FlightSQLODBCTestBase, Test_SQL_ODBC_INTERFACE_CONFORMANCE) { // case SQL_ODBC_STANDARD_CLI_CONFORMANCE: - mentioned in SQLGetInfo spec with no // description and there is no constant for this. TYPED_TEST(FlightSQLODBCTestBase, Test_SQL_ODBC_STANDARD_CLI_CONFORMANCE) { - // Type not supported in odbc_connection.cc + // Type commented out in odbc_connection.cc GTEST_SKIP(); this->connect(); @@ -695,10 +692,11 @@ TYPED_TEST(FlightSQLODBCTestBase, Test_SQL_ALTER_DOMAIN) { } TYPED_TEST(FlightSQLODBCTestBase, Test_SQL_ALTER_SCHEMA) { - // TODO Value commented out in odbc_connection.cc and does not exist in sql.h + // Type commented out in odbc_connection.cc GTEST_SKIP(); this->connect(); + // Type does not exist in sql.h // validate(conn, SQL_ALTER_SCHEMA, static_cast(0)); this->disconnect(); @@ -713,10 +711,11 @@ TYPED_TEST(FlightSQLODBCTestBase, Test_SQL_ALTER_TABLE) { } TYPED_TEST(FlightSQLODBCTestBase, Test_SQL_ANSI_SQL_DATETIME_LITERALS) { - // TODO Value does not exist in odbc_connection.cc or in sql.h + // Type commented out in odbc_connection.cc GTEST_SKIP(); this->connect(); + // Type does not exist in sql.h // validate(conn, SQL_ANSI_SQL_DATETIME_LITERALS, L""); this->disconnect(); @@ -1005,7 +1004,7 @@ TEST_F(FlightSQLODBCMockTestBase, Test_SQL_ORDER_BY_COLUMNS_IN_SELECT) { TYPED_TEST(FlightSQLODBCTestBase, Test_SQL_OUTER_JOINS) { // Not documented in SQLGetInfo, but other drivers return Y/N - // TODO The call returns value SQL_ERROR + // TODO Call to GetInfo returns SQL_ERROR GTEST_SKIP(); this->connect(); @@ -1039,7 +1038,7 @@ TEST_F(FlightSQLODBCMockTestBase, Test_SQL_SCHEMA_USAGE) { } TYPED_TEST(FlightSQLODBCTestBase, Test_SQL_SPECIAL_CHARACTERS) { - // TODO The call returns value SQL_ERROR + // TODO Call to GetInfo returns SQL_ERROR GTEST_SKIP(); this->connect(); @@ -1237,7 +1236,7 @@ TEST_F(FlightSQLODBCMockTestBase, Test_SQL_MAX_USER_NAME_LEN) { // Scalar Function Information TYPED_TEST(FlightSQLODBCTestBase, Test_SQL_CONVERT_FUNCTIONS) { - // TODO Driver manager returns -1 + // TODO Call to GetInfo returns SQL_ERROR GTEST_SKIP(); this->connect(); @@ -1287,7 +1286,7 @@ TYPED_TEST(FlightSQLODBCTestBase, Test_SQL_TIMEDATE_DIFF_INTERVALS) { } TYPED_TEST(FlightSQLODBCTestBase, Test_SQL_TIMEDATE_FUNCTIONS) { - // TODO Driver manager returns -1 + // TODO Call to GetInfo returns SQL_ERROR GTEST_SKIP(); this->connect(); From 7c5c27de8c39d68f4e7afb183f784340f442e7c1 Mon Sep 17 00:00:00 2001 From: rscales Date: Mon, 23 Jun 2025 21:54:22 +0100 Subject: [PATCH 15/29] Allow validation of wchar string when truncation occurs --- .../sql/odbc/tests/connection_info_test.cc | 25 ++++++++----------- 1 file changed, 10 insertions(+), 15 deletions(-) diff --git a/cpp/src/arrow/flight/sql/odbc/tests/connection_info_test.cc b/cpp/src/arrow/flight/sql/odbc/tests/connection_info_test.cc index e263e421bd37..e826093c1966 100644 --- a/cpp/src/arrow/flight/sql/odbc/tests/connection_info_test.cc +++ b/cpp/src/arrow/flight/sql/odbc/tests/connection_info_test.cc @@ -106,14 +106,18 @@ void validateGreaterThan(SQLHDBC connection, SQLUSMALLINT infoType, } // Validate wchar string SQLWCHAR return value is not empty -void validateNotEmptySQLWCHAR(SQLHDBC connection, SQLUSMALLINT infoType) { +void validateNotEmptySQLWCHAR(SQLHDBC connection, SQLUSMALLINT infoType, bool allowTruncation) { SQLWCHAR info_value[ODBC_BUFFER_SIZE] = L""; SQLSMALLINT message_length; SQLRETURN ret = SQLGetInfo(connection, infoType, info_value, ODBC_BUFFER_SIZE, &message_length); - EXPECT_EQ(ret, SQL_SUCCESS); + if (allowTruncation && ret == SQL_SUCCESS_WITH_INFO) { + EXPECT_EQ(ret, SQL_SUCCESS_WITH_INFO); + } else { + EXPECT_EQ(ret, SQL_SUCCESS); + } EXPECT_GT(wcslen(info_value), 0); } @@ -440,7 +444,7 @@ TYPED_TEST(FlightSQLODBCTestBase, Test_SQL_SEARCH_PATTERN_ESCAPE) { TYPED_TEST(FlightSQLODBCTestBase, Test_SQL_SERVER_NAME) { this->connect(); - validateNotEmptySQLWCHAR(this->conn, SQL_SERVER_NAME); + validateNotEmptySQLWCHAR(this->conn, SQL_SERVER_NAME, false); this->disconnect(); } @@ -474,7 +478,7 @@ TYPED_TEST(FlightSQLODBCTestBase, Test_SQL_DATABASE_NAME) { TYPED_TEST(FlightSQLODBCTestBase, Test_SQL_DBMS_NAME) { this->connect(); - validateNotEmptySQLWCHAR(this->conn, SQL_DBMS_NAME); + validateNotEmptySQLWCHAR(this->conn, SQL_DBMS_NAME, false); this->disconnect(); } @@ -482,7 +486,7 @@ TYPED_TEST(FlightSQLODBCTestBase, Test_SQL_DBMS_NAME) { TYPED_TEST(FlightSQLODBCTestBase, Test_SQL_DBMS_VER) { this->connect(); - validateNotEmptySQLWCHAR(this->conn, SQL_DBMS_VER); + validateNotEmptySQLWCHAR(this->conn, SQL_DBMS_VER, false); this->disconnect(); } @@ -954,18 +958,9 @@ TYPED_TEST(FlightSQLODBCTestBase, Test_SQL_INTEGRITY) { } TYPED_TEST(FlightSQLODBCTestBase, Test_SQL_KEYWORDS) { - GTEST_SKIP(); this->connect(); - validate( - conn, SQL_KEYWORDS, - L"ABORT, ACTION, ADD, AFTER, ALL, ALTER, ALWAYS, ANALYZE, AND, AS, ASC, ATTACH, " - L"AUTOINCREMENT, BEFORE, BEGIN, BETWEEN, BY, CASCADE, CASE, CAST, CHECK, COLLATE, " - L"COLUMN, COMMIT, CONFLICT, CONSTRAINT, CREATE, CROSS, CURRENT, CURRENT_DATE, " - L"CURRENT_TIME, CURRENT_TIMESTAMP, DATABASE, DEFAULT, DEFERRABLE, DEFERRED, " - L"DELETE, DESC, DETACH, DISTINCT, DO, DROP, EACH, ELSE, END, ESCAPE, EXCEPT, " - L"EXCLUDE, EXCLUSIVE, EXISTS, EXPLAIN, FAIL, FILTER, FIRST, FOLLOWING, FOR, " - L"FOREIGN, FROM, FULL, GENERATED, GLOB, GROUP, GROUPS, H"); + validateNotEmptySQLWCHAR(conn, SQL_KEYWORDS, true); this->disconnect(); } From 74a406b715add39751ce2beecd50c49b3eaee67d Mon Sep 17 00:00:00 2001 From: rscales Date: Mon, 23 Jun 2025 22:53:11 +0100 Subject: [PATCH 16/29] Fix comment for failing test case --- cpp/src/arrow/flight/sql/odbc/tests/connection_info_test.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cpp/src/arrow/flight/sql/odbc/tests/connection_info_test.cc b/cpp/src/arrow/flight/sql/odbc/tests/connection_info_test.cc index e826093c1966..ec00a1af27a6 100644 --- a/cpp/src/arrow/flight/sql/odbc/tests/connection_info_test.cc +++ b/cpp/src/arrow/flight/sql/odbc/tests/connection_info_test.cc @@ -358,7 +358,7 @@ TYPED_TEST(FlightSQLODBCTestBase, Test_SQL_MAX_ASYNC_CONCURRENT_STATEMENTS) { } TYPED_TEST(FlightSQLODBCTestBase, Test_SQL_MAX_CONCURRENT_ACTIVITIES) { - // Driver manager returns failure code + // TODO Call to GetInfo returns SQL_ERROR GTEST_SKIP(); this->connect(); From 902650140a0aeb7abd931562a28bbcee71054074 Mon Sep 17 00:00:00 2001 From: rscales Date: Wed, 25 Jun 2025 22:12:39 +0100 Subject: [PATCH 17/29] Add default values in get info cache for missing types --- .../sql/odbc/flight_sql/get_info_cache.cc | 5 ++++ .../sql/odbc/tests/connection_info_test.cc | 23 +++++-------------- 2 files changed, 11 insertions(+), 17 deletions(-) diff --git a/cpp/src/arrow/flight/sql/odbc/flight_sql/get_info_cache.cc b/cpp/src/arrow/flight/sql/odbc/flight_sql/get_info_cache.cc index d18322badbe3..a571e1c67393 100644 --- a/cpp/src/arrow/flight/sql/odbc/flight_sql/get_info_cache.cc +++ b/cpp/src/arrow/flight/sql/odbc/flight_sql/get_info_cache.cc @@ -1173,6 +1173,7 @@ void GetInfoCache::LoadDefaultsForMissingEntries() { SetDefaultIfMissing(info_, SQL_CONVERT_DECIMAL, static_cast(0)); SetDefaultIfMissing(info_, SQL_CONVERT_DOUBLE, static_cast(0)); SetDefaultIfMissing(info_, SQL_CONVERT_FLOAT, static_cast(0)); + SetDefaultIfMissing(info_, SQL_CONVERT_FUNCTIONS, static_cast(0)); SetDefaultIfMissing(info_, SQL_CONVERT_GUID, static_cast(0)); SetDefaultIfMissing(info_, SQL_CONVERT_INTEGER, static_cast(0)); SetDefaultIfMissing(info_, SQL_CONVERT_INTERVAL_YEAR_MONTH, static_cast(0)); @@ -1251,6 +1252,7 @@ void GetInfoCache::LoadDefaultsForMissingEntries() { SetDefaultIfMissing(info_, SQL_MAX_COLUMNS_IN_ORDER_BY, static_cast(0)); SetDefaultIfMissing(info_, SQL_MAX_COLUMNS_IN_SELECT, static_cast(0)); SetDefaultIfMissing(info_, SQL_MAX_COLUMNS_IN_TABLE, static_cast(0)); + SetDefaultIfMissing(info_, SQL_MAX_CONCURRENT_ACTIVITIES, static_cast(0)); SetDefaultIfMissing(info_, SQL_MAX_CURSOR_NAME_LEN, static_cast(0)); SetDefaultIfMissing(info_, SQL_MAX_DRIVER_CONNECTIONS, static_cast(0)); SetDefaultIfMissing(info_, SQL_MAX_IDENTIFIER_LEN, static_cast(65535)); @@ -1270,6 +1272,7 @@ void GetInfoCache::LoadDefaultsForMissingEntries() { SetDefaultIfMissing(info_, SQL_OJ_CAPABILITIES, static_cast(SQL_OJ_LEFT | SQL_OJ_RIGHT | SQL_OJ_FULL)); SetDefaultIfMissing(info_, SQL_ORDER_BY_COLUMNS_IN_SELECT, "Y"); + SetDefaultIfMissing(info_, SQL_OUTER_JOINS, "N"); SetDefaultIfMissing(info_, SQL_PROCEDURE_TERM, ""); SetDefaultIfMissing(info_, SQL_PROCEDURES, "N"); SetDefaultIfMissing(info_, SQL_QUOTED_IDENTIFIER_CASE, @@ -1278,6 +1281,7 @@ void GetInfoCache::LoadDefaultsForMissingEntries() { SetDefaultIfMissing(info_, SQL_SCHEMA_USAGE, static_cast(SQL_SU_DML_STATEMENTS)); SetDefaultIfMissing(info_, SQL_SEARCH_PATTERN_ESCAPE, "\\"); + SetDefaultIfMissing(info_, SQL_SPECIAL_CHARACTERS, ""); SetDefaultIfMissing( info_, SQL_SERVER_NAME, "Arrow Flight SQL Server"); // This might actually need to be the hostname. @@ -1332,6 +1336,7 @@ void GetInfoCache::LoadDefaultsForMissingEntries() { SQL_FN_TSI_FRAC_SECOND | SQL_FN_TSI_SECOND | SQL_FN_TSI_MINUTE | SQL_FN_TSI_HOUR | SQL_FN_TSI_DAY | SQL_FN_TSI_WEEK | SQL_FN_TSI_MONTH | SQL_FN_TSI_QUARTER | SQL_FN_TSI_YEAR)); + SetDefaultIfMissing(info_, SQL_TIMEDATE_FUNCTIONS, static_cast(0)); SetDefaultIfMissing(info_, SQL_UNION, static_cast(SQL_U_UNION | SQL_U_UNION_ALL)); SetDefaultIfMissing(info_, SQL_XOPEN_CLI_YEAR, "1995"); diff --git a/cpp/src/arrow/flight/sql/odbc/tests/connection_info_test.cc b/cpp/src/arrow/flight/sql/odbc/tests/connection_info_test.cc index ec00a1af27a6..666e55c82b6a 100644 --- a/cpp/src/arrow/flight/sql/odbc/tests/connection_info_test.cc +++ b/cpp/src/arrow/flight/sql/odbc/tests/connection_info_test.cc @@ -358,8 +358,6 @@ TYPED_TEST(FlightSQLODBCTestBase, Test_SQL_MAX_ASYNC_CONCURRENT_STATEMENTS) { } TYPED_TEST(FlightSQLODBCTestBase, Test_SQL_MAX_CONCURRENT_ACTIVITIES) { - // TODO Call to GetInfo returns SQL_ERROR - GTEST_SKIP(); this->connect(); validate(this->conn, SQL_MAX_CONCURRENT_ACTIVITIES, static_cast(0)); @@ -997,13 +995,10 @@ TEST_F(FlightSQLODBCMockTestBase, Test_SQL_ORDER_BY_COLUMNS_IN_SELECT) { this->disconnect(); } -TYPED_TEST(FlightSQLODBCTestBase, Test_SQL_OUTER_JOINS) { - // Not documented in SQLGetInfo, but other drivers return Y/N - // TODO Call to GetInfo returns SQL_ERROR - GTEST_SKIP(); +TEST_F(FlightSQLODBCMockTestBase, Test_SQL_OUTER_JOINS) { this->connect(); - validate(conn, SQL_OUTER_JOINS, L"Y"); + validate(conn, SQL_OUTER_JOINS, L"N"); this->disconnect(); } @@ -1033,8 +1028,6 @@ TEST_F(FlightSQLODBCMockTestBase, Test_SQL_SCHEMA_USAGE) { } TYPED_TEST(FlightSQLODBCTestBase, Test_SQL_SPECIAL_CHARACTERS) { - // TODO Call to GetInfo returns SQL_ERROR - GTEST_SKIP(); this->connect(); validate(conn, SQL_SPECIAL_CHARACTERS, L""); @@ -1230,12 +1223,10 @@ TEST_F(FlightSQLODBCMockTestBase, Test_SQL_MAX_USER_NAME_LEN) { // Scalar Function Information -TYPED_TEST(FlightSQLODBCTestBase, Test_SQL_CONVERT_FUNCTIONS) { - // TODO Call to GetInfo returns SQL_ERROR - GTEST_SKIP(); +TEST_F(FlightSQLODBCMockTestBase, Test_SQL_CONVERT_FUNCTIONS) { this->connect(); - validate(conn, SQL_CONVERT_FUNCTIONS, static_cast(3435973836)); + validate(conn, SQL_CONVERT_FUNCTIONS, static_cast(0)); this->disconnect(); } @@ -1280,12 +1271,10 @@ TYPED_TEST(FlightSQLODBCTestBase, Test_SQL_TIMEDATE_DIFF_INTERVALS) { this->disconnect(); } -TYPED_TEST(FlightSQLODBCTestBase, Test_SQL_TIMEDATE_FUNCTIONS) { - // TODO Call to GetInfo returns SQL_ERROR - GTEST_SKIP(); +TEST_F(FlightSQLODBCMockTestBase, Test_SQL_TIMEDATE_FUNCTIONS) { this->connect(); - validate(conn, SQL_TIMEDATE_FUNCTIONS, static_cast(3435973836)); + validate(conn, SQL_TIMEDATE_FUNCTIONS, static_cast(0)); this->disconnect(); } From 824ec82462724006b22fb97d02c8a4e7666bf39b Mon Sep 17 00:00:00 2001 From: rscales Date: Thu, 26 Jun 2025 00:21:22 +0100 Subject: [PATCH 18/29] Reformat connection info test cases --- cpp/src/arrow/flight/sql/odbc/tests/connection_info_test.cc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/cpp/src/arrow/flight/sql/odbc/tests/connection_info_test.cc b/cpp/src/arrow/flight/sql/odbc/tests/connection_info_test.cc index 666e55c82b6a..07ca466c133d 100644 --- a/cpp/src/arrow/flight/sql/odbc/tests/connection_info_test.cc +++ b/cpp/src/arrow/flight/sql/odbc/tests/connection_info_test.cc @@ -106,7 +106,8 @@ void validateGreaterThan(SQLHDBC connection, SQLUSMALLINT infoType, } // Validate wchar string SQLWCHAR return value is not empty -void validateNotEmptySQLWCHAR(SQLHDBC connection, SQLUSMALLINT infoType, bool allowTruncation) { +void validateNotEmptySQLWCHAR(SQLHDBC connection, SQLUSMALLINT infoType, + bool allowTruncation) { SQLWCHAR info_value[ODBC_BUFFER_SIZE] = L""; SQLSMALLINT message_length; From 5311299e0dcbb68daa62ef43090bdd76b0af9dc5 Mon Sep 17 00:00:00 2001 From: rscales Date: Thu, 26 Jun 2025 00:38:40 +0100 Subject: [PATCH 19/29] Update default value for SQL_TIMEDATE_FUNCTIONS to show support for all functions --- .../flight/sql/odbc/flight_sql/get_info_cache.cc | 11 ++++++++++- .../flight/sql/odbc/tests/connection_info_test.cc | 2 +- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/cpp/src/arrow/flight/sql/odbc/flight_sql/get_info_cache.cc b/cpp/src/arrow/flight/sql/odbc/flight_sql/get_info_cache.cc index a571e1c67393..cf10c658dbf4 100644 --- a/cpp/src/arrow/flight/sql/odbc/flight_sql/get_info_cache.cc +++ b/cpp/src/arrow/flight/sql/odbc/flight_sql/get_info_cache.cc @@ -1336,7 +1336,16 @@ void GetInfoCache::LoadDefaultsForMissingEntries() { SQL_FN_TSI_FRAC_SECOND | SQL_FN_TSI_SECOND | SQL_FN_TSI_MINUTE | SQL_FN_TSI_HOUR | SQL_FN_TSI_DAY | SQL_FN_TSI_WEEK | SQL_FN_TSI_MONTH | SQL_FN_TSI_QUARTER | SQL_FN_TSI_YEAR)); - SetDefaultIfMissing(info_, SQL_TIMEDATE_FUNCTIONS, static_cast(0)); + SetDefaultIfMissing( + info_, SQL_TIMEDATE_FUNCTIONS, + static_cast( + SQL_FN_TD_CURRENT_DATE | SQL_FN_TD_CURRENT_TIME | SQL_FN_TD_CURRENT_TIMESTAMP | + SQL_FN_TD_CURDATE | SQL_FN_TD_CURTIME | SQL_FN_TD_DAYNAME | + SQL_FN_TD_DAYOFMONTH | SQL_FN_TD_DAYOFWEEK | SQL_FN_TD_DAYOFYEAR | + SQL_FN_TD_EXTRACT | SQL_FN_TD_HOUR | SQL_FN_TD_MINUTE | SQL_FN_TD_MONTH | + SQL_FN_TD_MONTHNAME | SQL_FN_TD_NOW | SQL_FN_TD_QUARTER | SQL_FN_TD_SECOND | + SQL_FN_TD_TIMESTAMPADD | SQL_FN_TD_TIMESTAMPDIFF | SQL_FN_TD_WEEK | + SQL_FN_TD_YEAR)); SetDefaultIfMissing(info_, SQL_UNION, static_cast(SQL_U_UNION | SQL_U_UNION_ALL)); SetDefaultIfMissing(info_, SQL_XOPEN_CLI_YEAR, "1995"); diff --git a/cpp/src/arrow/flight/sql/odbc/tests/connection_info_test.cc b/cpp/src/arrow/flight/sql/odbc/tests/connection_info_test.cc index 07ca466c133d..33be89e357cc 100644 --- a/cpp/src/arrow/flight/sql/odbc/tests/connection_info_test.cc +++ b/cpp/src/arrow/flight/sql/odbc/tests/connection_info_test.cc @@ -1275,7 +1275,7 @@ TYPED_TEST(FlightSQLODBCTestBase, Test_SQL_TIMEDATE_DIFF_INTERVALS) { TEST_F(FlightSQLODBCMockTestBase, Test_SQL_TIMEDATE_FUNCTIONS) { this->connect(); - validate(conn, SQL_TIMEDATE_FUNCTIONS, static_cast(0)); + validate(conn, SQL_TIMEDATE_FUNCTIONS, static_cast(2097151)); this->disconnect(); } From 214f1a477351b5f7208a6b13d23e4a5ff2a9d441 Mon Sep 17 00:00:00 2001 From: rscales Date: Thu, 26 Jun 2025 00:54:38 +0100 Subject: [PATCH 20/29] Empty commit to force running workflows From ce4b7025b6277d20607e76656926b75675868be2 Mon Sep 17 00:00:00 2001 From: rscales Date: Thu, 26 Jun 2025 10:26:40 -0700 Subject: [PATCH 21/29] Use bitmasks to create value for SQL_AGGREGATE_FUNCTIONS Co-authored-by: Alina (Xi) Li <96995091+alinaliBQ@users.noreply.github.com> --- cpp/src/arrow/flight/sql/odbc/tests/connection_info_test.cc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/cpp/src/arrow/flight/sql/odbc/tests/connection_info_test.cc b/cpp/src/arrow/flight/sql/odbc/tests/connection_info_test.cc index 33be89e357cc..c03cae9e26e4 100644 --- a/cpp/src/arrow/flight/sql/odbc/tests/connection_info_test.cc +++ b/cpp/src/arrow/flight/sql/odbc/tests/connection_info_test.cc @@ -681,7 +681,8 @@ TEST_F(FlightSQLODBCMockTestBase, Test_SQL_USER_NAME) { TYPED_TEST(FlightSQLODBCTestBase, Test_SQL_AGGREGATE_FUNCTIONS) { this->connect(); - validate(conn, SQL_AGGREGATE_FUNCTIONS, static_cast(127)); + validate(conn, SQL_AGGREGATE_FUNCTIONS, static_cast(SQL_AF_ALL | SQL_AF_AVG | SQL_AF_COUNT | SQL_AF_DISTINCT | + SQL_AF_MAX | SQL_AF_MIN | SQL_AF_SUM)); this->disconnect(); } From e7f74932a7b41e16100d53582ca5bb4b78d051de Mon Sep 17 00:00:00 2001 From: rscales Date: Thu, 26 Jun 2025 10:27:16 -0700 Subject: [PATCH 22/29] Use bitmasks to create value for SQL_TIMEDATE_ADD_INTERVALS Co-authored-by: Alina (Xi) Li <96995091+alinaliBQ@users.noreply.github.com> --- cpp/src/arrow/flight/sql/odbc/tests/connection_info_test.cc | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/cpp/src/arrow/flight/sql/odbc/tests/connection_info_test.cc b/cpp/src/arrow/flight/sql/odbc/tests/connection_info_test.cc index c03cae9e26e4..359f01a6a6cb 100644 --- a/cpp/src/arrow/flight/sql/odbc/tests/connection_info_test.cc +++ b/cpp/src/arrow/flight/sql/odbc/tests/connection_info_test.cc @@ -1260,7 +1260,9 @@ TEST_F(FlightSQLODBCMockTestBase, Test_SQL_SYSTEM_FUNCTIONS) { TYPED_TEST(FlightSQLODBCTestBase, Test_SQL_TIMEDATE_ADD_INTERVALS) { this->connect(); - validate(conn, SQL_TIMEDATE_ADD_INTERVALS, static_cast(511)); + validate(conn, SQL_TIMEDATE_ADD_INTERVALS, static_cast(SQL_FN_TSI_FRAC_SECOND | SQL_FN_TSI_SECOND | SQL_FN_TSI_MINUTE | SQL_FN_TSI_HOUR | + SQL_FN_TSI_DAY | SQL_FN_TSI_WEEK | SQL_FN_TSI_MONTH | SQL_FN_TSI_QUARTER | + SQL_FN_TSI_YEAR)); this->disconnect(); } From ca3137b12ee7a94376156190f0bb4541a93b8e32 Mon Sep 17 00:00:00 2001 From: rscales Date: Thu, 26 Jun 2025 10:28:01 -0700 Subject: [PATCH 23/29] Use bitmask to create default value for SQL_TIMEDATE_DIFF_INTERVALS Co-authored-by: Alina (Xi) Li <96995091+alinaliBQ@users.noreply.github.com> --- cpp/src/arrow/flight/sql/odbc/tests/connection_info_test.cc | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/cpp/src/arrow/flight/sql/odbc/tests/connection_info_test.cc b/cpp/src/arrow/flight/sql/odbc/tests/connection_info_test.cc index 359f01a6a6cb..3d64a4aa4f20 100644 --- a/cpp/src/arrow/flight/sql/odbc/tests/connection_info_test.cc +++ b/cpp/src/arrow/flight/sql/odbc/tests/connection_info_test.cc @@ -1270,7 +1270,9 @@ TYPED_TEST(FlightSQLODBCTestBase, Test_SQL_TIMEDATE_ADD_INTERVALS) { TYPED_TEST(FlightSQLODBCTestBase, Test_SQL_TIMEDATE_DIFF_INTERVALS) { this->connect(); - validate(conn, SQL_TIMEDATE_DIFF_INTERVALS, static_cast(511)); + validate(conn, SQL_TIMEDATE_DIFF_INTERVALS, static_cast(SQL_FN_TSI_FRAC_SECOND | SQL_FN_TSI_SECOND | SQL_FN_TSI_MINUTE | SQL_FN_TSI_HOUR | + SQL_FN_TSI_DAY | SQL_FN_TSI_WEEK | SQL_FN_TSI_MONTH | SQL_FN_TSI_QUARTER | + SQL_FN_TSI_YEAR)); this->disconnect(); } From 443f4dcd8d17df8ed693443379145815f391082c Mon Sep 17 00:00:00 2001 From: rscales Date: Thu, 26 Jun 2025 10:28:34 -0700 Subject: [PATCH 24/29] Use bitmask to create default value for SQL_GETDATA_EXTENSIONS Co-authored-by: Alina (Xi) Li <96995091+alinaliBQ@users.noreply.github.com> --- cpp/src/arrow/flight/sql/odbc/tests/connection_info_test.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cpp/src/arrow/flight/sql/odbc/tests/connection_info_test.cc b/cpp/src/arrow/flight/sql/odbc/tests/connection_info_test.cc index 3d64a4aa4f20..eb3ca092f04a 100644 --- a/cpp/src/arrow/flight/sql/odbc/tests/connection_info_test.cc +++ b/cpp/src/arrow/flight/sql/odbc/tests/connection_info_test.cc @@ -319,7 +319,7 @@ TYPED_TEST(FlightSQLODBCTestBase, Test_SQL_FILE_USAGE) { TYPED_TEST(FlightSQLODBCTestBase, Test_SQL_GETDATA_EXTENSIONS) { this->connect(); - validate(this->conn, SQL_GETDATA_EXTENSIONS, static_cast(3)); + validate(this->conn, SQL_GETDATA_EXTENSIONS, static_cast(SQL_GD_ANY_COLUMN | SQL_GD_ANY_ORDER)); this->disconnect(); } From 070ec903fa236ca5ae880d1f233a715d62023091 Mon Sep 17 00:00:00 2001 From: rscales Date: Thu, 26 Jun 2025 10:30:14 -0700 Subject: [PATCH 25/29] Use default value for SQL_CURSOR_SENSITIVITY Co-authored-by: Alina (Xi) Li <96995091+alinaliBQ@users.noreply.github.com> --- cpp/src/arrow/flight/sql/odbc/tests/connection_info_test.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cpp/src/arrow/flight/sql/odbc/tests/connection_info_test.cc b/cpp/src/arrow/flight/sql/odbc/tests/connection_info_test.cc index eb3ca092f04a..22c9138f3e3d 100644 --- a/cpp/src/arrow/flight/sql/odbc/tests/connection_info_test.cc +++ b/cpp/src/arrow/flight/sql/odbc/tests/connection_info_test.cc @@ -559,7 +559,7 @@ TYPED_TEST(FlightSQLODBCTestBase, Test_SQL_CURSOR_ROLLBACK_BEHAVIOR) { TYPED_TEST(FlightSQLODBCTestBase, Test_SQL_CURSOR_SENSITIVITY) { this->connect(); - validate(conn, SQL_CURSOR_SENSITIVITY, static_cast(0)); + validate(conn, SQL_CURSOR_SENSITIVITY, static_cast(SQL_UNSPECIFIED)); this->disconnect(); } From bca77926cac6e82c29c95f661b568b4cc524206d Mon Sep 17 00:00:00 2001 From: rscales Date: Thu, 26 Jun 2025 18:55:03 +0100 Subject: [PATCH 26/29] Add error code to default return for odbc connection get info --- .../sql/odbc/odbcabstraction/odbc_impl/odbc_connection.cc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/cpp/src/arrow/flight/sql/odbc/odbcabstraction/odbc_impl/odbc_connection.cc b/cpp/src/arrow/flight/sql/odbc/odbcabstraction/odbc_impl/odbc_connection.cc index cc5dab9b8b49..81d4a52ec8be 100644 --- a/cpp/src/arrow/flight/sql/odbc/odbcabstraction/odbc_impl/odbc_connection.cc +++ b/cpp/src/arrow/flight/sql/odbc/odbcabstraction/odbc_impl/odbc_connection.cc @@ -448,7 +448,8 @@ SQLRETURN ODBCConnection::GetInfo(SQLUSMALLINT infoType, SQLPOINTER value, GetDiagnostics()); } default: - throw DriverException("Unknown SQLGetInfo type: " + std::to_string(infoType)); + throw DriverException("Unknown SQLGetInfo type: " + std::to_string(infoType), + "HYC00"); } return SQL_ERROR; From 8dbb6bd2b58b6ce48e4067aa0ec2b8d516551fda Mon Sep 17 00:00:00 2001 From: rscales Date: Fri, 27 Jun 2025 00:42:22 +0100 Subject: [PATCH 27/29] Update connection info test to use arrow::flight::sql::odbc namespace --- cpp/src/arrow/flight/sql/odbc/tests/connection_info_test.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cpp/src/arrow/flight/sql/odbc/tests/connection_info_test.cc b/cpp/src/arrow/flight/sql/odbc/tests/connection_info_test.cc index 22c9138f3e3d..4236115a0e4b 100644 --- a/cpp/src/arrow/flight/sql/odbc/tests/connection_info_test.cc +++ b/cpp/src/arrow/flight/sql/odbc/tests/connection_info_test.cc @@ -26,7 +26,7 @@ #include "gtest/gtest.h" -namespace arrow::flight::odbc::integration_tests { +namespace arrow::flight::sql::odbc { // Helper Functions @@ -1455,4 +1455,4 @@ TYPED_TEST(FlightSQLODBCTestBase, Test_SQL_CONVERT_VARCHAR) { this->disconnect(); } -} // namespace arrow::flight::odbc::integration_tests +} // namespace arrow::flight::sql::odbc From 5b3348e37bede098e29a5b72670e07a23ce2f561 Mon Sep 17 00:00:00 2001 From: rscales Date: Fri, 27 Jun 2025 04:15:09 +0100 Subject: [PATCH 28/29] Update to use defined values from sqlext.h --- .../sql/odbc/tests/connection_info_test.cc | 85 ++++++++++++------- 1 file changed, 54 insertions(+), 31 deletions(-) diff --git a/cpp/src/arrow/flight/sql/odbc/tests/connection_info_test.cc b/cpp/src/arrow/flight/sql/odbc/tests/connection_info_test.cc index 4236115a0e4b..b8734a8f8b26 100644 --- a/cpp/src/arrow/flight/sql/odbc/tests/connection_info_test.cc +++ b/cpp/src/arrow/flight/sql/odbc/tests/connection_info_test.cc @@ -319,7 +319,8 @@ TYPED_TEST(FlightSQLODBCTestBase, Test_SQL_FILE_USAGE) { TYPED_TEST(FlightSQLODBCTestBase, Test_SQL_GETDATA_EXTENSIONS) { this->connect(); - validate(this->conn, SQL_GETDATA_EXTENSIONS, static_cast(SQL_GD_ANY_COLUMN | SQL_GD_ANY_ORDER)); + validate(this->conn, SQL_GETDATA_EXTENSIONS, + static_cast(SQL_GD_ANY_COLUMN | SQL_GD_ANY_ORDER)); this->disconnect(); } @@ -327,9 +328,8 @@ TYPED_TEST(FlightSQLODBCTestBase, Test_SQL_GETDATA_EXTENSIONS) { TYPED_TEST(FlightSQLODBCTestBase, Test_SQL_INFO_SCHEMA_VIEWS) { this->connect(); - // An SQLUINTEGER bitmask enumerating the views in the INFORMATION_SCHEMA that are - // supported by the driver. - validateGreaterThan(this->conn, SQL_INFO_SCHEMA_VIEWS, static_cast(0)); + validate(this->conn, SQL_INFO_SCHEMA_VIEWS, + static_cast(SQL_ISV_TABLES | SQL_ISV_COLUMNS | SQL_ISV_VIEWS)); this->disconnect(); } @@ -535,7 +535,7 @@ TYPED_TEST(FlightSQLODBCTestBase, Test_SQL_COLLATION_SEQ) { TYPED_TEST(FlightSQLODBCTestBase, Test_SQL_CONCAT_NULL_BEHAVIOR) { this->connect(); - validate(conn, SQL_CONCAT_NULL_BEHAVIOR, static_cast(0)); + validate(conn, SQL_CONCAT_NULL_BEHAVIOR, static_cast(SQL_CB_NULL)); this->disconnect(); } @@ -543,7 +543,7 @@ TYPED_TEST(FlightSQLODBCTestBase, Test_SQL_CONCAT_NULL_BEHAVIOR) { TYPED_TEST(FlightSQLODBCTestBase, Test_SQL_CURSOR_COMMIT_BEHAVIOR) { this->connect(); - validate(conn, SQL_CURSOR_COMMIT_BEHAVIOR, static_cast(1)); + validate(conn, SQL_CURSOR_COMMIT_BEHAVIOR, static_cast(SQL_CB_CLOSE)); this->disconnect(); } @@ -551,7 +551,7 @@ TYPED_TEST(FlightSQLODBCTestBase, Test_SQL_CURSOR_COMMIT_BEHAVIOR) { TYPED_TEST(FlightSQLODBCTestBase, Test_SQL_CURSOR_ROLLBACK_BEHAVIOR) { this->connect(); - validate(conn, SQL_CURSOR_ROLLBACK_BEHAVIOR, static_cast(1)); + validate(conn, SQL_CURSOR_ROLLBACK_BEHAVIOR, static_cast(SQL_CB_CLOSE)); this->disconnect(); } @@ -615,7 +615,7 @@ TYPED_TEST(FlightSQLODBCTestBase, Test_SQL_NEED_LONG_DATA_LEN) { TEST_F(FlightSQLODBCMockTestBase, Test_SQL_NULL_COLLATION) { this->connect(); - validate(conn, SQL_NULL_COLLATION, static_cast(2)); + validate(conn, SQL_NULL_COLLATION, static_cast(SQL_NC_START)); this->disconnect(); } @@ -681,8 +681,10 @@ TEST_F(FlightSQLODBCMockTestBase, Test_SQL_USER_NAME) { TYPED_TEST(FlightSQLODBCTestBase, Test_SQL_AGGREGATE_FUNCTIONS) { this->connect(); - validate(conn, SQL_AGGREGATE_FUNCTIONS, static_cast(SQL_AF_ALL | SQL_AF_AVG | SQL_AF_COUNT | SQL_AF_DISTINCT | - SQL_AF_MAX | SQL_AF_MIN | SQL_AF_SUM)); + validate( + conn, SQL_AGGREGATE_FUNCTIONS, + static_cast(SQL_AF_ALL | SQL_AF_AVG | SQL_AF_COUNT | SQL_AF_DISTINCT | + SQL_AF_MAX | SQL_AF_MIN | SQL_AF_SUM)); this->disconnect(); } @@ -768,7 +770,7 @@ TYPED_TEST(FlightSQLODBCTestBase, Test_SQL_COLUMN_ALIAS) { TEST_F(FlightSQLODBCMockTestBase, Test_SQL_CORRELATION_NAME) { this->connect(); - validate(conn, SQL_CORRELATION_NAME, static_cast(0)); + validate(conn, SQL_CORRELATION_NAME, static_cast(SQL_CN_NONE)); this->disconnect(); } @@ -912,7 +914,8 @@ TEST_F(FlightSQLODBCMockTestBase, Test_SQL_EXPRESSIONS_IN_ORDERBY) { TEST_F(FlightSQLODBCMockTestBase, Test_SQL_GROUP_BY) { this->connect(); - validate(conn, SQL_GROUP_BY, static_cast(2)); + validate(conn, SQL_GROUP_BY, + static_cast(SQL_GB_GROUP_BY_CONTAINS_SELECT)); this->disconnect(); } @@ -920,7 +923,7 @@ TEST_F(FlightSQLODBCMockTestBase, Test_SQL_GROUP_BY) { TYPED_TEST(FlightSQLODBCTestBase, Test_SQL_IDENTIFIER_CASE) { this->connect(); - validate(conn, SQL_IDENTIFIER_CASE, static_cast(4)); + validate(conn, SQL_IDENTIFIER_CASE, static_cast(SQL_IC_MIXED)); this->disconnect(); } @@ -936,7 +939,7 @@ TYPED_TEST(FlightSQLODBCTestBase, Test_SQL_IDENTIFIER_QUOTE_CHAR) { TYPED_TEST(FlightSQLODBCTestBase, Test_SQL_INDEX_KEYWORDS) { this->connect(); - validate(conn, SQL_INDEX_KEYWORDS, static_cast(0)); + validate(conn, SQL_INDEX_KEYWORDS, static_cast(SQL_IK_NONE)); this->disconnect(); } @@ -944,7 +947,9 @@ TYPED_TEST(FlightSQLODBCTestBase, Test_SQL_INDEX_KEYWORDS) { TYPED_TEST(FlightSQLODBCTestBase, Test_SQL_INSERT_STATEMENT) { this->connect(); - validate(conn, SQL_INSERT_STATEMENT, static_cast(7)); + validate(conn, SQL_INSERT_STATEMENT, + static_cast(SQL_IS_INSERT_LITERALS | SQL_IS_INSERT_SEARCHED | + SQL_IS_SELECT_INTO)); this->disconnect(); } @@ -976,7 +981,7 @@ TYPED_TEST(FlightSQLODBCTestBase, Test_SQL_LIKE_ESCAPE_CLAUSE) { TEST_F(FlightSQLODBCMockTestBase, Test_SQL_NON_NULLABLE_COLUMNS) { this->connect(); - validate(conn, SQL_NON_NULLABLE_COLUMNS, static_cast(0)); + validate(conn, SQL_NON_NULLABLE_COLUMNS, static_cast(SQL_NNC_NULL)); this->disconnect(); } @@ -984,7 +989,8 @@ TEST_F(FlightSQLODBCMockTestBase, Test_SQL_NON_NULLABLE_COLUMNS) { TEST_F(FlightSQLODBCMockTestBase, Test_SQL_OJ_CAPABILITIES) { this->connect(); - validate(this->conn, SQL_OJ_CAPABILITIES, static_cast(7)); + validate(this->conn, SQL_OJ_CAPABILITIES, + static_cast(SQL_OJ_LEFT | SQL_OJ_RIGHT | SQL_OJ_FULL)); this->disconnect(); } @@ -1016,7 +1022,7 @@ TYPED_TEST(FlightSQLODBCTestBase, Test_SQL_PROCEDURES) { TEST_F(FlightSQLODBCMockTestBase, Test_SQL_QUOTED_IDENTIFIER_CASE) { this->connect(); - validate(conn, SQL_QUOTED_IDENTIFIER_CASE, static_cast(4)); + validate(conn, SQL_QUOTED_IDENTIFIER_CASE, static_cast(SQL_IC_MIXED)); this->disconnect(); } @@ -1024,7 +1030,7 @@ TEST_F(FlightSQLODBCMockTestBase, Test_SQL_QUOTED_IDENTIFIER_CASE) { TEST_F(FlightSQLODBCMockTestBase, Test_SQL_SCHEMA_USAGE) { this->connect(); - validate(conn, SQL_SCHEMA_USAGE, static_cast(1)); + validate(conn, SQL_SCHEMA_USAGE, static_cast(SQL_SU_DML_STATEMENTS)); this->disconnect(); } @@ -1040,7 +1046,7 @@ TYPED_TEST(FlightSQLODBCTestBase, Test_SQL_SPECIAL_CHARACTERS) { TYPED_TEST(FlightSQLODBCTestBase, Test_SQL_SQL_CONFORMANCE) { this->connect(); - validate(conn, SQL_SQL_CONFORMANCE, static_cast(1)); + validate(conn, SQL_SQL_CONFORMANCE, static_cast(SQL_SC_SQL92_ENTRY)); this->disconnect(); } @@ -1048,7 +1054,9 @@ TYPED_TEST(FlightSQLODBCTestBase, Test_SQL_SQL_CONFORMANCE) { TEST_F(FlightSQLODBCMockTestBase, Test_SQL_SUBQUERIES) { this->connect(); - validate(conn, SQL_SUBQUERIES, static_cast(31)); + validate(conn, SQL_SUBQUERIES, + static_cast(SQL_SQ_CORRELATED_SUBQUERIES | SQL_SQ_COMPARISON | + SQL_SQ_EXISTS | SQL_SQ_IN | SQL_SQ_QUANTIFIED)); this->disconnect(); } @@ -1056,7 +1064,7 @@ TEST_F(FlightSQLODBCMockTestBase, Test_SQL_SUBQUERIES) { TYPED_TEST(FlightSQLODBCTestBase, Test_SQL_UNION) { this->connect(); - validate(conn, SQL_UNION, static_cast(3)); + validate(conn, SQL_UNION, static_cast(SQL_U_UNION | SQL_U_UNION_ALL)); this->disconnect(); } @@ -1244,7 +1252,9 @@ TEST_F(FlightSQLODBCMockTestBase, Test_SQL_NUMERIC_FUNCTIONS) { TEST_F(FlightSQLODBCMockTestBase, Test_SQL_STRING_FUNCTIONS) { this->connect(); - validate(conn, SQL_STRING_FUNCTIONS, static_cast(1304)); + validate(conn, SQL_STRING_FUNCTIONS, + static_cast(SQL_FN_STR_LTRIM | SQL_FN_STR_LENGTH | + SQL_FN_STR_REPLACE | SQL_FN_STR_RTRIM)); this->disconnect(); } @@ -1252,7 +1262,8 @@ TEST_F(FlightSQLODBCMockTestBase, Test_SQL_STRING_FUNCTIONS) { TEST_F(FlightSQLODBCMockTestBase, Test_SQL_SYSTEM_FUNCTIONS) { this->connect(); - validate(conn, SQL_SYSTEM_FUNCTIONS, static_cast(5)); + validate(conn, SQL_SYSTEM_FUNCTIONS, + static_cast(SQL_FN_SYS_IFNULL | SQL_FN_SYS_USERNAME)); this->disconnect(); } @@ -1260,9 +1271,11 @@ TEST_F(FlightSQLODBCMockTestBase, Test_SQL_SYSTEM_FUNCTIONS) { TYPED_TEST(FlightSQLODBCTestBase, Test_SQL_TIMEDATE_ADD_INTERVALS) { this->connect(); - validate(conn, SQL_TIMEDATE_ADD_INTERVALS, static_cast(SQL_FN_TSI_FRAC_SECOND | SQL_FN_TSI_SECOND | SQL_FN_TSI_MINUTE | SQL_FN_TSI_HOUR | - SQL_FN_TSI_DAY | SQL_FN_TSI_WEEK | SQL_FN_TSI_MONTH | SQL_FN_TSI_QUARTER | - SQL_FN_TSI_YEAR)); + validate(conn, SQL_TIMEDATE_ADD_INTERVALS, + static_cast(SQL_FN_TSI_FRAC_SECOND | SQL_FN_TSI_SECOND | + SQL_FN_TSI_MINUTE | SQL_FN_TSI_HOUR | SQL_FN_TSI_DAY | + SQL_FN_TSI_WEEK | SQL_FN_TSI_MONTH | + SQL_FN_TSI_QUARTER | SQL_FN_TSI_YEAR)); this->disconnect(); } @@ -1270,9 +1283,11 @@ TYPED_TEST(FlightSQLODBCTestBase, Test_SQL_TIMEDATE_ADD_INTERVALS) { TYPED_TEST(FlightSQLODBCTestBase, Test_SQL_TIMEDATE_DIFF_INTERVALS) { this->connect(); - validate(conn, SQL_TIMEDATE_DIFF_INTERVALS, static_cast(SQL_FN_TSI_FRAC_SECOND | SQL_FN_TSI_SECOND | SQL_FN_TSI_MINUTE | SQL_FN_TSI_HOUR | - SQL_FN_TSI_DAY | SQL_FN_TSI_WEEK | SQL_FN_TSI_MONTH | SQL_FN_TSI_QUARTER | - SQL_FN_TSI_YEAR)); + validate(conn, SQL_TIMEDATE_DIFF_INTERVALS, + static_cast(SQL_FN_TSI_FRAC_SECOND | SQL_FN_TSI_SECOND | + SQL_FN_TSI_MINUTE | SQL_FN_TSI_HOUR | SQL_FN_TSI_DAY | + SQL_FN_TSI_WEEK | SQL_FN_TSI_MONTH | + SQL_FN_TSI_QUARTER | SQL_FN_TSI_YEAR)); this->disconnect(); } @@ -1280,7 +1295,15 @@ TYPED_TEST(FlightSQLODBCTestBase, Test_SQL_TIMEDATE_DIFF_INTERVALS) { TEST_F(FlightSQLODBCMockTestBase, Test_SQL_TIMEDATE_FUNCTIONS) { this->connect(); - validate(conn, SQL_TIMEDATE_FUNCTIONS, static_cast(2097151)); + validate(conn, SQL_TIMEDATE_FUNCTIONS, + static_cast( + SQL_FN_TD_CURRENT_DATE | SQL_FN_TD_CURRENT_TIME | + SQL_FN_TD_CURRENT_TIMESTAMP | SQL_FN_TD_CURDATE | SQL_FN_TD_CURTIME | + SQL_FN_TD_DAYNAME | SQL_FN_TD_DAYOFMONTH | SQL_FN_TD_DAYOFWEEK | + SQL_FN_TD_DAYOFYEAR | SQL_FN_TD_EXTRACT | SQL_FN_TD_HOUR | + SQL_FN_TD_MINUTE | SQL_FN_TD_MONTH | SQL_FN_TD_MONTHNAME | SQL_FN_TD_NOW | + SQL_FN_TD_QUARTER | SQL_FN_TD_SECOND | SQL_FN_TD_TIMESTAMPADD | + SQL_FN_TD_TIMESTAMPDIFF | SQL_FN_TD_WEEK | SQL_FN_TD_YEAR)); this->disconnect(); } From e385eedd1938d2e3092709c926c3977456722ca2 Mon Sep 17 00:00:00 2001 From: rscales Date: Thu, 3 Jul 2025 17:12:17 +0100 Subject: [PATCH 29/29] Return error code HY096 for invalid attributes in or out of expected range --- .../sql/odbc/odbcabstraction/odbc_impl/odbc_connection.cc | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/cpp/src/arrow/flight/sql/odbc/odbcabstraction/odbc_impl/odbc_connection.cc b/cpp/src/arrow/flight/sql/odbc/odbcabstraction/odbc_impl/odbc_connection.cc index 81d4a52ec8be..1f3d365397a7 100644 --- a/cpp/src/arrow/flight/sql/odbc/odbcabstraction/odbc_impl/odbc_connection.cc +++ b/cpp/src/arrow/flight/sql/odbc/odbcabstraction/odbc_impl/odbc_connection.cc @@ -448,8 +448,7 @@ SQLRETURN ODBCConnection::GetInfo(SQLUSMALLINT infoType, SQLPOINTER value, GetDiagnostics()); } default: - throw DriverException("Unknown SQLGetInfo type: " + std::to_string(infoType), - "HYC00"); + throw DriverException("Unknown SQLGetInfo type: " + std::to_string(infoType), "HY096"); } return SQL_ERROR;