diff --git a/ci/scripts/cpp_test.sh b/ci/scripts/cpp_test.sh index 2f88cdc819b2..241addbfebd2 100755 --- a/ci/scripts/cpp_test.sh +++ b/ci/scripts/cpp_test.sh @@ -55,7 +55,6 @@ if ! type minio >/dev/null 2>&1; then fi case "$(uname)" in Linux) - exclude_tests+=("arrow-flight-sql-odbc-test") n_jobs=$(nproc) ;; Darwin) diff --git a/cpp/src/arrow/flight/sql/odbc/tests/CMakeLists.txt b/cpp/src/arrow/flight/sql/odbc/tests/CMakeLists.txt index 0f45bfd0c811..79cd45ed0bc8 100644 --- a/cpp/src/arrow/flight/sql/odbc/tests/CMakeLists.txt +++ b/cpp/src/arrow/flight/sql/odbc/tests/CMakeLists.txt @@ -42,6 +42,11 @@ set(ARROW_FLIGHT_SQL_ODBC_TEST_SRCS # GH-46889: move protobuf_test_util to a more common location ../../../../engine/substrait/protobuf_test_util.cc) +# Resolve segmentation fault error on Windows platform due to Arrow Compute Library Initialization +if(WIN32) + list(APPEND ARROW_FLIGHT_SQL_ODBC_TEST_SRCS ../../../../compute/test_env.cc) +endif() + # GH-49651 Link ODBC tests statically on Linux and dynamically on macOS/Windows if(WIN32 OR APPLE) set(ARROW_FLIGHT_SQL_ODBC_TEST_LINK_LIBS arrow_flight_sql_odbc_shared diff --git a/cpp/src/arrow/flight/sql/odbc/tests/columns_test.cc b/cpp/src/arrow/flight/sql/odbc/tests/columns_test.cc index e4468d277ff4..571f52c76e13 100644 --- a/cpp/src/arrow/flight/sql/odbc/tests/columns_test.cc +++ b/cpp/src/arrow/flight/sql/odbc/tests/columns_test.cc @@ -46,6 +46,9 @@ TYPED_TEST_SUITE(ColumnsOdbcV2Test, TestTypesOdbcV2); namespace { // Helper functions + +// GH-49702: TODO Disabled on Linux due to BlockingQueue issue +#ifndef __linux__ void CheckSQLColumns( SQLHSTMT stmt, const std::wstring& expected_table, const std::wstring& expected_column, const SQLINTEGER& expected_data_type, @@ -125,6 +128,7 @@ void CheckRemoteSQLColumns( expected_octet_char_length, expected_ordinal_position, expected_is_nullable); } +#endif // __linux__ void CheckSQLColAttribute(SQLHSTMT stmt, SQLUSMALLINT idx, const std::string& expected_column_name, @@ -415,6 +419,8 @@ TYPED_TEST(ColumnsTest, SQLColumnsTestInputData) { ValidateFetch(stmt, SQL_SUCCESS); } +// GH-49702: TODO Disabled on Linux due to BlockingQueue issue +#ifndef __linux__ TEST_F(ColumnsMockTest, TestSQLColumnsAllColumns) { // Check table pattern and column pattern returns all columns @@ -1209,6 +1215,7 @@ TEST_F(ColumnsMockTest, TestSQLColumnsTableColumnPattern) { // There is no more column EXPECT_EQ(SQL_NO_DATA, SQLFetch(stmt)); } +#endif // __linux__ TEST_F(ColumnsMockTest, TestSQLColumnsInvalidTablePattern) { ASSIGN_SQLWCHAR_ARR(table_pattern, L"non-existent-table"); @@ -1224,8 +1231,7 @@ TEST_F(ColumnsMockTest, TestSQLColumnsInvalidTablePattern) { TYPED_TEST(ColumnsTest, SQLColAttributeTestInputData) { ASSIGN_SQLWCHAR_ARR_AND_LEN(wsql, L"SELECT 1 as col1;"); - ASSERT_EQ(SQL_SUCCESS, SQLExecDirect(stmt, wsql, wsql_len)) - << GetOdbcErrorMessage(SQL_HANDLE_DBC, conn); + ASSERT_EQ(SQL_SUCCESS, SQLExecDirect(stmt, wsql, wsql_len)); ASSERT_EQ(SQL_SUCCESS, SQLFetch(stmt)); @@ -2560,10 +2566,6 @@ TEST_F(ColumnsMockTest, SQLDescribeColUnicodeTableMetadata) { ASSIGN_SQLWCHAR_ARR_AND_LEN(sql_query, L"SELECT * from 数据 LIMIT 1;"); - ASSIGN_SQLWCHAR_ARR_AND_LEN(expected_column_name, L"资料"); - SQLSMALLINT expected_column_data_type = SQL_WVARCHAR; - SQLULEN expected_column_size = 0; - ASSERT_EQ(SQL_SUCCESS, SQLExecDirect(stmt, sql_query, sql_query_len)); ASSERT_EQ(SQL_SUCCESS, SQLFetch(stmt)); @@ -2572,13 +2574,14 @@ TEST_F(ColumnsMockTest, SQLDescribeColUnicodeTableMetadata) { SQLDescribeCol(stmt, column_index, column_name, buf_char_len, &name_length, &column_data_type, &column_size, &decimal_digits, &nullable)); - EXPECT_EQ(name_length, expected_column_name_len); + std::wstring expected_column_name_wstr = std::wstring(L"资料"); + size_t expected_column_name_len = expected_column_name_wstr.length(); std::wstring returned(column_name, column_name + name_length); - std::wstring expected_col_name_str = ConvertToWString(expected_column_name); - EXPECT_EQ(expected_col_name_str, returned); - EXPECT_EQ(expected_column_data_type, column_data_type); - EXPECT_EQ(expected_column_size, column_size); + EXPECT_EQ(expected_column_name_wstr, returned); + EXPECT_EQ(expected_column_name_len, name_length); + EXPECT_EQ(SQL_WVARCHAR, column_data_type); + EXPECT_EQ(0, column_size); EXPECT_EQ(0, decimal_digits); EXPECT_EQ(SQL_NULLABLE, nullable); diff --git a/cpp/src/arrow/flight/sql/odbc/tests/connection_attr_test.cc b/cpp/src/arrow/flight/sql/odbc/tests/connection_attr_test.cc index dbf2fbb74f8f..9db2ff74a109 100644 --- a/cpp/src/arrow/flight/sql/odbc/tests/connection_attr_test.cc +++ b/cpp/src/arrow/flight/sql/odbc/tests/connection_attr_test.cc @@ -33,6 +33,13 @@ using TestTypes = ::testing::Types; TYPED_TEST_SUITE(ConnectionAttributeTest, TestTypes); +template +class ConnectionAttributePreConnectTest : public T {}; + +using TestTypesHandle = ::testing::Types; +TYPED_TEST_SUITE(ConnectionAttributePreConnectTest, TestTypesHandle); + #ifdef SQL_ATTR_ASYNC_DBC_EVENT TYPED_TEST(ConnectionAttributeTest, TestSQLSetConnectAttrAsyncDbcEventUnsupported) { ASSERT_EQ(SQL_ERROR, SQLSetConnectAttr(conn, SQL_ATTR_ASYNC_DBC_EVENT, 0, 0)); @@ -87,15 +94,18 @@ TYPED_TEST(ConnectionAttributeTest, TestSQLSetConnectAttrEnlistInDtcUnsupported) } TYPED_TEST(ConnectionAttributeTest, TestSQLSetConnectAttrOdbcCursorsDMOnly) { - this->AllocEnvConnHandles(); + SQLHENV test_env = SQL_NULL_HENV; + SQLHDBC test_conn = SQL_NULL_HDBC; + this->AllocEnvConnHandles(test_env, test_conn); // Verify DM-only attribute is settable via Driver Manager ASSERT_EQ(SQL_SUCCESS, - SQLSetConnectAttr(conn, SQL_ATTR_ODBC_CURSORS, + SQLSetConnectAttr(test_conn, SQL_ATTR_ODBC_CURSORS, reinterpret_cast(SQL_CUR_USE_DRIVER), 0)); std::string connect_str = this->GetConnectionString(); - this->ConnectWithString(connect_str); + this->ConnectWithString(connect_str, test_conn); + this->Disconnect(test_env, test_conn); } TYPED_TEST(ConnectionAttributeTest, TestSQLSetConnectAttrQuietModeReadOnly) { @@ -114,31 +124,33 @@ TYPED_TEST(ConnectionAttributeTest, TestSQLSetConnectAttrTraceDMOnly) { } #endif // __APPLE__ -TYPED_TEST(ConnectionAttributeTest, TestSQLSetConnectAttrTracefileDMOnly) { +TYPED_TEST(ConnectionAttributePreConnectTest, TestSQLSetConnectAttrTracefileDMOnly) { // Verify DM-only attribute is handled by Driver Manager // Use placeholder value as we want the call to fail, or else // the driver manager will produce a trace file. std::wstring trace_file = L"invalid/file/path"; std::vector trace_file0(trace_file.begin(), trace_file.end()); + +#ifdef _WIN32 ASSERT_EQ(SQL_ERROR, SQLSetConnectAttr(conn, SQL_ATTR_TRACEFILE, &trace_file0[0], static_cast(trace_file0.size()))); -#ifdef __APPLE__ - VerifyOdbcErrorState(SQL_HANDLE_DBC, conn, kErrorStateHYC00); -#else VerifyOdbcErrorState(SQL_HANDLE_DBC, conn, kErrorStateHY000); -#endif // __APPLE__ +#else // Mac & Linux + ASSERT_EQ(SQL_SUCCESS, SQLSetConnectAttr(conn, SQL_ATTR_TRACEFILE, &trace_file0[0], + static_cast(trace_file0.size()))); +#endif } TYPED_TEST(ConnectionAttributeTest, TestSQLSetConnectAttrTranslateLabDMOnly) { // Verify DM-only attribute is handled by Driver Manager ASSERT_EQ(SQL_ERROR, SQLSetConnectAttr(conn, SQL_ATTR_TRANSLATE_LIB, 0, 0)); // Checks for invalid argument return error -#ifdef __APPLE__ - VerifyOdbcErrorState(SQL_HANDLE_DBC, conn, kErrorStateHYC00); -#else +#ifdef _WIN32 VerifyOdbcErrorState(SQL_HANDLE_DBC, conn, kErrorStateHY024); -#endif // __APPLE__ +#else // Mac & Linux + VerifyOdbcErrorState(SQL_HANDLE_DBC, conn, kErrorStateHYC00); +#endif } TYPED_TEST(ConnectionAttributeTest, TestSQLSetConnectAttrTranslateOptionUnsupported) { @@ -162,8 +174,8 @@ TYPED_TEST(ConnectionAttributeTest, TestSQLGetConnectAttrDbcInfoTokenSetOnly) { } #endif -// iODBC does not treat SQL_ATTR_ODBC_CURSORS as DM-only -#ifndef __APPLE__ +// Driver Manager behavior tests for Windows only. +#ifdef _WIN32 TYPED_TEST(ConnectionAttributeTest, TestSQLGetConnectAttrOdbcCursorsDMOnly) { // Verify that DM-only attribute is handled by driver manager SQLULEN cursor_attr; @@ -172,7 +184,6 @@ TYPED_TEST(ConnectionAttributeTest, TestSQLGetConnectAttrOdbcCursorsDMOnly) { EXPECT_EQ(SQL_CUR_USE_DRIVER, cursor_attr); } -// iODBC needs to be compiled with tracing enabled to handle SQL_ATTR_TRACE TYPED_TEST(ConnectionAttributeTest, TestSQLGetConnectAttrTraceDMOnly) { // Verify that DM-only attribute is handled by driver manager SQLUINTEGER trace; @@ -180,7 +191,6 @@ TYPED_TEST(ConnectionAttributeTest, TestSQLGetConnectAttrTraceDMOnly) { EXPECT_EQ(SQL_OPT_TRACE_OFF, trace); } -// iODBC needs to be compiled with tracing enabled to handle SQL_ATTR_TRACEFILE TYPED_TEST(ConnectionAttributeTest, TestSQLGetConnectAttrTraceFileDMOnly) { // Verify that DM-only attribute is handled by driver manager SQLWCHAR out_str[kOdbcBufferSize]; @@ -194,7 +204,7 @@ TYPED_TEST(ConnectionAttributeTest, TestSQLGetConnectAttrTraceFileDMOnly) { ODBC::SqlWcharToString(out_str, static_cast(out_str_len)); EXPECT_FALSE(out_connection_string.empty()); } -#endif // __APPLE__ +#endif // _WIN32 TYPED_TEST(ConnectionAttributeTest, TestSQLGetConnectAttrTranslateLibUnsupported) { SQLWCHAR out_str[kOdbcBufferSize]; @@ -221,11 +231,16 @@ TYPED_TEST(ConnectionAttributeTest, TestSQLGetConnectAttrTxnIsolationUnsupported #ifdef SQL_ATTR_ASYNC_DBC_FUNCTIONS_ENABLE TYPED_TEST(ConnectionAttributeTest, TestSQLGetConnectAttrAsyncDbcFunctionsEnableUnsupported) { - // Verifies that the Windows driver manager returns HY114 for unsupported functionality SQLUINTEGER enable; +# ifdef _WIN32 + // Verifies that the Windows driver manager returns HY114 for unsupported functionality ASSERT_EQ(SQL_ERROR, SQLGetConnectAttr(conn, SQL_ATTR_ASYNC_DBC_FUNCTIONS_ENABLE, &enable, 0, 0)); VerifyOdbcErrorState(SQL_HANDLE_DBC, conn, kErrorStateHY114); +# else // Mac & Linux + ASSERT_EQ(SQL_SUCCESS, + SQLGetConnectAttr(conn, SQL_ATTR_ASYNC_DBC_FUNCTIONS_ENABLE, &enable, 0, 0)); +# endif } #endif @@ -350,14 +365,23 @@ TYPED_TEST(ConnectionAttributeTest, TestSQLSetConnectAttrLoginTimeoutValid) { EXPECT_EQ(42, timeout); } +#ifdef __linux__ +// On Linux, SQL_ATTR_PACKET_SIZE can only be set before connection +// which is why use a different test fixture for Linux. +TYPED_TEST(ConnectionAttributePreConnectTest, TestSQLSetConnectAttrPacketSizeValid) { +#else // Windows & Mac TYPED_TEST(ConnectionAttributeTest, TestSQLSetConnectAttrPacketSizeValid) { - // The driver always returns 0. PACKET_SIZE value is unused by the driver. - +#endif // Check default value first SQLUINTEGER size = -1; +#ifdef __linux__ + ASSERT_EQ(SQL_ERROR, SQLGetConnectAttr(conn, SQL_ATTR_PACKET_SIZE, &size, 0, nullptr)); + VerifyOdbcErrorState(SQL_HANDLE_DBC, conn, kErrorState08003); +#else // Windows & Mac ASSERT_EQ(SQL_SUCCESS, SQLGetConnectAttr(conn, SQL_ATTR_PACKET_SIZE, &size, 0, nullptr)); EXPECT_EQ(0, size); +#endif ASSERT_EQ(SQL_SUCCESS, SQLSetConnectAttr(conn, SQL_ATTR_PACKET_SIZE, reinterpret_cast(0), 0)); @@ -367,12 +391,18 @@ TYPED_TEST(ConnectionAttributeTest, TestSQLSetConnectAttrPacketSizeValid) { SQLGetConnectAttr(conn, SQL_ATTR_PACKET_SIZE, &size, 0, nullptr)); EXPECT_EQ(0, size); - // Attempt to set to non-zero value, driver should return warning and not error + // Attempt to set to non-zero value, +#ifdef __linux__ + EXPECT_EQ(SQL_SUCCESS, SQLSetConnectAttr(conn, SQL_ATTR_PACKET_SIZE, + reinterpret_cast(2), 0)); +#else // Windows & Mac + // driver should return warning and not error EXPECT_EQ(SQL_SUCCESS_WITH_INFO, SQLSetConnectAttr(conn, SQL_ATTR_PACKET_SIZE, reinterpret_cast(2), 0)); // Verify warning status VerifyOdbcErrorState(SQL_HANDLE_DBC, conn, kErrorState01S02); +#endif } } // namespace arrow::flight::sql::odbc 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 cbd23e5647f8..737c5b0ce1d3 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 @@ -143,6 +143,9 @@ TYPED_TEST(ConnectionInfoTest, TestSQLGetInfoDataSourceName) { } #ifdef SQL_DRIVER_AWARE_POOLING_SUPPORTED +// GH-49782: TODO Disabled on Linux until SQL_DRIVER_AWARE_POOLING_SUPPORTED is +// implemented in the driver. +# ifndef __linux__ TYPED_TEST(ConnectionInfoTest, TestSQLGetInfoDriverAwarePoolingSupported) { // According to Microsoft documentation, ODBC driver does not need to implement // SQL_DRIVER_AWARE_POOLING_SUPPORTED and the Driver Manager will ignore the @@ -153,7 +156,8 @@ TYPED_TEST(ConnectionInfoTest, TestSQLGetInfoDriverAwarePoolingSupported) { EXPECT_EQ(static_cast(SQL_DRIVER_AWARE_POOLING_NOT_CAPABLE), value); } -#endif +# endif // __linux__ +#endif // SQL_DRIVER_AWARE_POOLING_SUPPORTED // These information types are implemented by the Driver Manager alone. TYPED_TEST(ConnectionInfoTest, TestSQLGetInfoDriverHdbc) { @@ -329,9 +333,11 @@ TYPED_TEST(ConnectionInfoTest, TestSQLGetInfoOdbcVer) { std::wstring result = ConvertToWString(value); -#ifdef __APPLE__ +#if defined(__APPLE__) EXPECT_EQ(std::wstring(L"03.52.0000"), result); -#else +#elif defined(__linux__) + EXPECT_EQ(std::wstring(L"03.52"), result); +#else // WINDOWS EXPECT_EQ(std::wstring(L"03.80.0000"), result); #endif // __APPLE__ } @@ -370,7 +376,8 @@ TYPED_TEST(ConnectionInfoTest, TestSQLGetInfoServerName) { SQLWCHAR value[kOdbcBufferSize] = {}; GetInfoSQLWCHAR(conn, SQL_SERVER_NAME, value); - EXPECT_GT(wcslen(reinterpret_cast(value)), 0); + std::wstring result = ConvertToWString(value); + EXPECT_GT(result.length(), 0); } TYPED_TEST(ConnectionInfoTest, TestSQLGetInfoStaticCursorAttributes1) { @@ -401,14 +408,16 @@ TYPED_TEST(ConnectionInfoTest, TestSQLGetInfoDbmsName) { SQLWCHAR value[kOdbcBufferSize] = {}; GetInfoSQLWCHAR(conn, SQL_DBMS_NAME, value); - EXPECT_GT(wcslen(reinterpret_cast(value)), 0); + std::wstring result = ConvertToWString(value); + EXPECT_GT(result.length(), 0); } TYPED_TEST(ConnectionInfoTest, TestSQLGetInfoDbmsVer) { SQLWCHAR value[kOdbcBufferSize] = {}; GetInfoSQLWCHAR(conn, SQL_DBMS_VER, value); - EXPECT_GT(wcslen(reinterpret_cast(value)), 0); + std::wstring result = ConvertToWString(value); + EXPECT_GT(result.length(), 0); } // Data Source Information @@ -614,7 +623,7 @@ TYPED_TEST(ConnectionInfoTest, TestSQLGetInfoAlterTable) { TYPED_TEST(ConnectionInfoHandleTest, TestSQLGetInfoCatalogLocation) { // GH-49482 TODO: resolve inconsitent return value for SQL_CATALOG_LOCATION and change // test type to `ConnectionInfoTest` - this->ConnectWithString(this->GetConnectionString()); + this->ConnectWithString(this->GetConnectionString(), conn); SQLUSMALLINT value; GetInfo(conn, SQL_CATALOG_LOCATION, &value); @@ -750,7 +759,7 @@ TYPED_TEST(ConnectionInfoTest, TestSQLGetInfoDropDomain) { TYPED_TEST(ConnectionInfoHandleTest, TestSQLGetInfoDropSchema) { // GH-49482 TODO: resolve inconsitent return value for SQL_DROP_SCHEMA and change test // type to `ConnectionInfoTest` - this->ConnectWithString(this->GetConnectionString()); + this->ConnectWithString(this->GetConnectionString(), conn); SQLUINTEGER value; GetInfo(conn, SQL_DROP_SCHEMA, &value); @@ -764,7 +773,7 @@ TYPED_TEST(ConnectionInfoHandleTest, TestSQLGetInfoDropSchema) { TYPED_TEST(ConnectionInfoHandleTest, TestSQLGetInfoDropTable) { // GH-49482 TODO: resolve inconsitent return value for SQL_DROP_TABLE and change test // type to `ConnectionInfoTest` - this->ConnectWithString(this->GetConnectionString()); + this->ConnectWithString(this->GetConnectionString(), conn); SQLUINTEGER value; GetInfo(conn, SQL_DROP_TABLE, &value); @@ -848,7 +857,8 @@ TYPED_TEST(ConnectionInfoTest, TestSQLGetInfoKeywords) { SQLWCHAR value[info_len] = {}; GetInfoSQLWCHAR(conn, SQL_KEYWORDS, value, info_len); - EXPECT_GT(wcslen(reinterpret_cast(value)), 0); + std::wstring result = ConvertToWString(value, -1, info_len); + EXPECT_GT(result.length(), 0); } TYPED_TEST(ConnectionInfoTest, TestSQLGetInfoLikeEscapeClause) { diff --git a/cpp/src/arrow/flight/sql/odbc/tests/errors_test.cc b/cpp/src/arrow/flight/sql/odbc/tests/errors_test.cc index 273d737e8193..d12d8669ee9e 100644 --- a/cpp/src/arrow/flight/sql/odbc/tests/errors_test.cc +++ b/cpp/src/arrow/flight/sql/odbc/tests/errors_test.cc @@ -309,15 +309,20 @@ TYPED_TEST(ErrorsTest, TestSQLGetDiagRecInputData) { EXPECT_EQ(SQL_NO_DATA, SQLGetDiagRec(SQL_HANDLE_DBC, conn, 1, nullptr, nullptr, nullptr, 0, nullptr)); - // Invalid handle #ifdef __APPLE__ // MacOS ODBC driver manager requires connection handle EXPECT_EQ(SQL_INVALID_HANDLE, SQLGetDiagRec(0, conn, 1, nullptr, nullptr, nullptr, 0, nullptr)); #else - EXPECT_EQ(SQL_INVALID_HANDLE, + // Linux & Windows driver managers have different expected return values +# ifdef __linux__ + SQLRETURN expected_rc = SQL_ERROR; +# else // Windows + SQLRETURN expected_rc = SQL_INVALID_HANDLE; +# endif + EXPECT_EQ(expected_rc, SQLGetDiagRec(0, nullptr, 0, nullptr, nullptr, nullptr, 0, nullptr)); -#endif // __APPLE__ +#endif } TYPED_TEST(ErrorsOdbcV2Test, TestSQLErrorInputData) { @@ -485,13 +490,13 @@ TYPED_TEST(ErrorsOdbcV2Test, TestSQLErrorEnvErrorFromDriverManager) { EXPECT_EQ(0, native_error); // Function sequence error state from driver manager -#ifdef _WIN32 - // Windows Driver Manager returns S1010 - EXPECT_EQ(kErrorStateS1010, SqlWcharToString(sql_state)); -#else - // unix Driver Manager returns HY010 +#ifdef __APPLE__ + // MacOS Driver Manager returns HY010 EXPECT_EQ(kErrorStateHY010, SqlWcharToString(sql_state)); -#endif // _WIN32 +#else // Linux & Windows + // Linux & Windows Driver Managers returns S1010 + EXPECT_EQ(kErrorStateS1010, SqlWcharToString(sql_state)); +#endif std::string msg = SqlWcharToString(message); EXPECT_FALSE(msg.empty()); diff --git a/cpp/src/arrow/flight/sql/odbc/tests/get_functions_test.cc b/cpp/src/arrow/flight/sql/odbc/tests/get_functions_test.cc index 6ae1178e5d28..cf94c20aa12b 100644 --- a/cpp/src/arrow/flight/sql/odbc/tests/get_functions_test.cc +++ b/cpp/src/arrow/flight/sql/odbc/tests/get_functions_test.cc @@ -40,8 +40,9 @@ using TestTypesOdbcV2 = ::testing::Types; TYPED_TEST_SUITE(GetFunctionsOdbcV2Test, TestTypesOdbcV2); -// MacOS driver manager iODBC does not support SQLGetFunctions for ODBC 3.x or 2.x driver -#ifndef __APPLE__ +// Unix driver managers iODBC and Unix-ODBC do not support SQLGetFunctions +// for ODBC 3.x or 2.x driver +#ifdef _WIN32 TYPED_TEST(GetFunctionsTest, TestSQLGetFunctionsAllFunctions) { // Verify driver manager return values for SQLGetFunctions @@ -217,6 +218,6 @@ TYPED_TEST(GetFunctionsOdbcV2Test, TestSQLGetFunctionsUnsupportedSingleAPI) { api_exists = -1; } } -#endif // __APPLE__ +#endif // _WIN32 } // namespace arrow::flight::sql::odbc diff --git a/cpp/src/arrow/flight/sql/odbc/tests/odbc_test_suite.cc b/cpp/src/arrow/flight/sql/odbc/tests/odbc_test_suite.cc index bc83ed1f785e..406483a1ce87 100644 --- a/cpp/src/arrow/flight/sql/odbc/tests/odbc_test_suite.cc +++ b/cpp/src/arrow/flight/sql/odbc/tests/odbc_test_suite.cc @@ -51,28 +51,64 @@ class MockServerEnvironment : public ::testing::Environment { } }; -::testing::Environment* mock_env = +bool RunningRemoteTests() { return !remote_test_connect_str.empty(); } + +class OdbcTestEnvironment : public ::testing::Environment { + public: + void SetUp() override { + remote_test_connect_str = ODBCTestBase::GetConnectionString(); + if (RunningRemoteTests()) { + ODBCTestBase::Connect(remote_test_connect_str, remote_odbcv3_handles.env, + remote_odbcv3_handles.conn, SQL_OV_ODBC3); + ODBCTestBase::Connect(remote_test_connect_str, remote_odbcv2_handles.env, + remote_odbcv2_handles.conn, SQL_OV_ODBC2); + } + + std::string mock_test_connect_str = ODBCMockTestBase::GetConnectionString(); + ODBCMockTestBase::Connect(mock_test_connect_str, mock_odbcv3_handles.env, + mock_odbcv3_handles.conn, SQL_OV_ODBC3); + ODBCMockTestBase::Connect(mock_test_connect_str, mock_odbcv2_handles.env, + mock_odbcv2_handles.conn, SQL_OV_ODBC2); + } + + void TearDown() override { + if (RunningRemoteTests()) { + ODBCTestBase::Disconnect(remote_odbcv3_handles.env, remote_odbcv3_handles.conn); + ODBCTestBase::Disconnect(remote_odbcv2_handles.env, remote_odbcv2_handles.conn); + } + + ODBCTestBase::Disconnect(mock_odbcv3_handles.env, mock_odbcv3_handles.conn); + ODBCTestBase::Disconnect(mock_odbcv2_handles.env, mock_odbcv2_handles.conn); + } +}; + +::testing::Environment* mock_server_env = ::testing::AddGlobalTestEnvironment(new MockServerEnvironment); -void ODBCTestBase::AllocEnvConnHandles(SQLINTEGER odbc_ver) { +::testing::Environment* odbc_test_env = + ::testing::AddGlobalTestEnvironment(new OdbcTestEnvironment); + +void ODBCTestBase::AllocEnvConnHandles(SQLHENV& env_handle, SQLHDBC& conn_handle, + SQLINTEGER odbc_ver) { // Allocate an environment handle - ASSERT_EQ(SQL_SUCCESS, SQLAllocEnv(&env)); + ASSERT_EQ(SQL_SUCCESS, SQLAllocEnv(&env_handle)); ASSERT_EQ( SQL_SUCCESS, - SQLSetEnvAttr(env, SQL_ATTR_ODBC_VERSION, + SQLSetEnvAttr(env_handle, SQL_ATTR_ODBC_VERSION, reinterpret_cast(static_cast(odbc_ver)), 0)); // Allocate a connection using alloc handle - ASSERT_EQ(SQL_SUCCESS, SQLAllocHandle(SQL_HANDLE_DBC, env, &conn)); + ASSERT_EQ(SQL_SUCCESS, SQLAllocHandle(SQL_HANDLE_DBC, env_handle, &conn_handle)); } -void ODBCTestBase::Connect(std::string connect_str, SQLINTEGER odbc_ver) { - ASSERT_NO_FATAL_FAILURE(AllocEnvConnHandles(odbc_ver)); - ASSERT_NO_FATAL_FAILURE(ConnectWithString(connect_str)); +void ODBCTestBase::Connect(std::string connect_str, SQLHENV& env_handle, + SQLHDBC& conn_handle, SQLINTEGER odbc_ver) { + ASSERT_NO_FATAL_FAILURE(AllocEnvConnHandles(env_handle, conn_handle, odbc_ver)); + ASSERT_NO_FATAL_FAILURE(ConnectWithString(connect_str, conn_handle)); } -void ODBCTestBase::ConnectWithString(std::string connect_str) { +void ODBCTestBase::ConnectWithString(std::string connect_str, SQLHDBC& conn_handle) { // Connect string std::vector connect_str0(connect_str.begin(), connect_str.end()); @@ -81,31 +117,39 @@ void ODBCTestBase::ConnectWithString(std::string connect_str) { // Connecting to ODBC server. ASSERT_EQ(SQL_SUCCESS, - SQLDriverConnect(conn, NULL, &connect_str0[0], + SQLDriverConnect(conn_handle, NULL, &connect_str0[0], static_cast(connect_str0.size()), out_str, kOdbcBufferSize, &out_str_len, SQL_DRIVER_NOPROMPT)) - << GetOdbcErrorMessage(SQL_HANDLE_DBC, conn); + << GetOdbcErrorMessage(SQL_HANDLE_DBC, conn_handle); } -void ODBCTestBase::Disconnect() { +void ODBCTestBase::Disconnect(SQLHENV& env_handle, SQLHDBC& conn_handle) { // Disconnect from ODBC - EXPECT_EQ(SQL_SUCCESS, SQLDisconnect(conn)) - << GetOdbcErrorMessage(SQL_HANDLE_DBC, conn); + if (conn_handle != SQL_NULL_HDBC) { + EXPECT_EQ(SQL_SUCCESS, SQLDisconnect(conn_handle)) + << GetOdbcErrorMessage(SQL_HANDLE_DBC, conn_handle); + } - FreeEnvConnHandles(); + FreeEnvConnHandles(env_handle, conn_handle); } -void ODBCTestBase::FreeEnvConnHandles() { +void ODBCTestBase::FreeEnvConnHandles(SQLHENV& env_handle, SQLHDBC& conn_handle) { // Free connection handle - EXPECT_EQ(SQL_SUCCESS, SQLFreeHandle(SQL_HANDLE_DBC, conn)); + if (conn_handle != SQL_NULL_HDBC) { + EXPECT_EQ(SQL_SUCCESS, SQLFreeHandle(SQL_HANDLE_DBC, conn_handle)); + conn_handle = SQL_NULL_HDBC; + } // Free environment handle - EXPECT_EQ(SQL_SUCCESS, SQLFreeHandle(SQL_HANDLE_ENV, env)); + if (env_handle != SQL_NULL_HENV) { + EXPECT_EQ(SQL_SUCCESS, SQLFreeHandle(SQL_HANDLE_ENV, env_handle)); + env_handle = SQL_NULL_HENV; + } } std::string ODBCTestBase::GetConnectionString() { std::string connect_str = - arrow::internal::GetEnvVar(kTestConnectStr.data()).ValueOrDie(); + arrow::internal::GetEnvVar(kTestConnectStr.data()).ValueOr(""); return connect_str; } @@ -168,68 +212,58 @@ std::wstring ODBCTestBase::GetQueryAllDataTypes() { } void ODBCTestBase::SetUp() { - if (connected) { - ASSERT_EQ(SQL_SUCCESS, SQLAllocHandle(SQL_HANDLE_STMT, conn, &stmt)); - } + ASSERT_EQ(SQL_SUCCESS, SQLAllocHandle(SQL_HANDLE_STMT, conn, &stmt)); } void ODBCTestBase::TearDown() { - if (connected) { - ASSERT_EQ(SQL_SUCCESS, SQLFreeHandle(SQL_HANDLE_STMT, stmt)); - } -} - -void ODBCTestBase::TearDownTestSuite() { - if (connected) { - Disconnect(); - connected = false; - } -} - -void FlightSQLODBCRemoteTestBase::CheckForRemoteTest() { - if (arrow::internal::GetEnvVar(kTestConnectStr.data()).ValueOr("").empty()) { - skipping_test = true; - GTEST_SKIP() << "Skipping test: kTestConnectStr not set"; - } + ASSERT_EQ(SQL_SUCCESS, SQLFreeHandle(SQL_HANDLE_STMT, stmt)); } void FlightSQLODBCRemoteTestBase::SetUpTestSuite() { - CheckForRemoteTest(); - if (skipping_test) { + if (!RunningRemoteTests()) { + GTEST_SKIP() << "Skipping Test Suite: Environment Variable " << kTestConnectStr.data() + << " is not set"; return; } - std::string connect_str = GetConnectionString(); - Connect(connect_str, SQL_OV_ODBC3); - connected = true; + env = remote_odbcv3_handles.env; + conn = remote_odbcv3_handles.conn; + stmt = remote_odbcv3_handles.stmt; } void FlightSQLOdbcV2RemoteTestBase::SetUpTestSuite() { - CheckForRemoteTest(); - if (skipping_test) { + if (!RunningRemoteTests()) { + GTEST_SKIP() << "Skipping Test Suite: Environment Variable " << kTestConnectStr.data() + << " is not set"; return; } - std::string connect_str = GetConnectionString(); - Connect(connect_str, SQL_OV_ODBC2); - connected = true; + env = remote_odbcv2_handles.env; + conn = remote_odbcv2_handles.conn; + stmt = remote_odbcv2_handles.stmt; } void FlightSQLOdbcEnvConnHandleRemoteTestBase::SetUpTestSuite() { - CheckForRemoteTest(); - if (skipping_test) { + if (!RunningRemoteTests()) { + GTEST_SKIP() << "Skipping Test Suite: Environment Variable " << kTestConnectStr.data() + << " is not set"; return; } - AllocEnvConnHandles(); + AllocEnvConnHandles(remote_non_connection_handles.env, + remote_non_connection_handles.conn); + env = remote_non_connection_handles.env; + conn = remote_non_connection_handles.conn; + stmt = remote_non_connection_handles.stmt; } void FlightSQLOdbcEnvConnHandleRemoteTestBase::TearDownTestSuite() { - if (skipping_test) { + if (!RunningRemoteTests()) { return; } - FreeEnvConnHandles(); + FreeEnvConnHandles(remote_non_connection_handles.env, + remote_non_connection_handles.conn); } std::string FindTokenInCallHeaders(const CallHeaders& incoming_headers) { @@ -400,20 +434,27 @@ void ODBCMockTestBase::DropUnicodeTable() { } void FlightSQLODBCMockTestBase::SetUpTestSuite() { - std::string connect_str = GetConnectionString(); - Connect(connect_str, SQL_OV_ODBC3); - connected = true; + env = mock_odbcv3_handles.env; + conn = mock_odbcv3_handles.conn; + stmt = mock_odbcv3_handles.stmt; } void FlightSQLOdbcV2MockTestBase::SetUpTestSuite() { - std::string connect_str = GetConnectionString(); - Connect(connect_str, SQL_OV_ODBC2); - connected = true; + env = mock_odbcv2_handles.env; + conn = mock_odbcv2_handles.conn; + stmt = mock_odbcv2_handles.stmt; } -void FlightSQLOdbcEnvConnHandleMockTestBase::SetUpTestSuite() { AllocEnvConnHandles(); } +void FlightSQLOdbcEnvConnHandleMockTestBase::SetUpTestSuite() { + AllocEnvConnHandles(mock_non_connection_handles.env, mock_non_connection_handles.conn); + env = mock_non_connection_handles.env; + conn = mock_non_connection_handles.conn; + stmt = mock_non_connection_handles.stmt; +} -void FlightSQLOdbcEnvConnHandleMockTestBase::TearDownTestSuite() { FreeEnvConnHandles(); } +void FlightSQLOdbcEnvConnHandleMockTestBase::TearDownTestSuite() { + FreeEnvConnHandles(mock_non_connection_handles.env, mock_non_connection_handles.conn); +} bool CompareConnPropertyMap(Connection::ConnPropertyMap map1, Connection::ConnPropertyMap map2) { @@ -490,7 +531,7 @@ bool WriteDSN(Connection::ConnPropertyMap properties) { } std::string driver = config.Get(FlightSqlConnection::DRIVER); - std::wstring w_driver = arrow::util::UTF8ToWideString(driver).ValueOr(L""); + CONVERT_SQLWCHAR_STR(w_driver, driver); return RegisterDsn(config, reinterpret_cast(w_driver.c_str())); } @@ -521,30 +562,34 @@ size_t SqlWCharArrLen(const SQLWCHAR* str_val) { return static_cast(p - str_val); } -std::wstring ConvertToWString(const SQLWCHAR* str_val) { +std::wstring ConvertToWString(const SQLWCHAR* str_val, SQLSMALLINT str_len, + SQLSMALLINT buffer_size) { + if (str_len == -1) { #ifdef __linux__ - size_t str_len = SqlWCharArrLen(str_val); -#else - size_t str_len = std::wcslen(str_val); + str_len = SqlWCharArrLen(str_val); +#else // Windows & Mac + str_len = std::wcslen(str_val); #endif + } std::wstring attr_str; if (str_len == 0) { attr_str = L""; } else { assert(str_val != nullptr); - assert(str_len > 0 && str_len <= static_cast(kOdbcBufferSize)); + assert(str_len > 0 && str_len <= buffer_size); attr_str.assign(str_val, str_val + str_len); } return attr_str; } -std::wstring ConvertToWString(const std::vector& str_val, SQLSMALLINT str_len) { +std::wstring ConvertToWString(const std::vector& str_val, SQLSMALLINT str_len, + SQLSMALLINT buffer_size) { std::wstring attr_str; if (str_len == 0) { attr_str = L""; } else { EXPECT_GT(str_len, 0); - EXPECT_LE(str_len, static_cast(kOdbcBufferSize)); + EXPECT_LE(str_len, buffer_size); attr_str = std::wstring(str_val.begin(), str_val.begin() + str_len / GetSqlWCharSize()); } diff --git a/cpp/src/arrow/flight/sql/odbc/tests/odbc_test_suite.h b/cpp/src/arrow/flight/sql/odbc/tests/odbc_test_suite.h index fb914ec19fd1..5271d2171c90 100644 --- a/cpp/src/arrow/flight/sql/odbc/tests/odbc_test_suite.h +++ b/cpp/src/arrow/flight/sql/odbc/tests/odbc_test_suite.h @@ -39,28 +39,45 @@ #include "arrow/flight/sql/odbc/odbc_impl/system_dsn.h" #ifdef __linux__ -# define ASSIGN_SQLWCHAR_ARR(name, wstring_var) \ - auto name##_vec = ODBC::ToSqlWCharVector(std::wstring(wstring_var)); \ +# define ASSIGN_SQLWCHAR_ARR(name, wstring_literal) \ + auto name##_vec = ODBC::ToSqlWCharVector(std::wstring(wstring_literal)); \ + if (name##_vec.empty() || name##_vec.back() != static_cast(0)) { \ + name##_vec.push_back(static_cast(0)); \ + } \ SQLWCHAR* name = name##_vec.data(); -# define ASSIGN_SQLWCHAR_ARR_AND_LEN(name, wstring_var) \ - ASSIGN_SQLWCHAR_ARR(name, wstring_var) \ - size_t name##_len = SqlWCharArrLen(name); -#else -# define ASSIGN_SQLWCHAR_ARR(name, wstring_var) SQLWCHAR name[] = wstring_var; -# define ASSIGN_SQLWCHAR_ARR_AND_LEN(name, wstring_var) \ - ASSIGN_SQLWCHAR_ARR(name, wstring_var) \ - size_t name##_len = std::wcslen(name); +# define ASSIGN_SQLWCHAR_ARR_AND_LEN(name, wstring_literal) \ + ASSIGN_SQLWCHAR_ARR(name, wstring_literal) \ + SQLSMALLINT name##_len = static_cast(name##_vec.size() - 1); +#else // Windows & Mac +# define ASSIGN_SQLWCHAR_ARR(name, wstring_literal) SQLWCHAR name[] = wstring_literal; +# define ASSIGN_SQLWCHAR_ARR_AND_LEN(name, wstring_literal) \ + ASSIGN_SQLWCHAR_ARR(name, wstring_literal) \ + SQLSMALLINT name##_len = static_cast(std::wcslen(name)); #endif static constexpr std::string_view kTestConnectStr = "ARROW_FLIGHT_SQL_ODBC_CONN"; static constexpr std::string_view kTestDsn = "Apache Arrow Flight SQL Test DSN"; -inline SQLHENV env = 0; -inline SQLHDBC conn = 0; -inline SQLHSTMT stmt = 0; +inline std::string remote_test_connect_str = ""; + +struct OdbcHandles { + SQLHENV env = SQL_NULL_HENV; + SQLHDBC conn = SQL_NULL_HDBC; + SQLHSTMT stmt = SQL_NULL_HSTMT; +}; + +inline OdbcHandles remote_odbcv3_handles; +inline OdbcHandles remote_odbcv2_handles; +inline OdbcHandles remote_non_connection_handles; +inline OdbcHandles mock_odbcv3_handles; +inline OdbcHandles mock_odbcv2_handles; +inline OdbcHandles mock_non_connection_handles; -inline bool skipping_test = false; -inline bool connected = false; +// These handles are meant to point to to the relevant handle above +// depending on the test fixture. +inline SQLHENV env = SQL_NULL_HENV; +inline SQLHDBC conn = SQL_NULL_HDBC; +inline SQLHSTMT stmt = SQL_NULL_HSTMT; inline std::shared_ptr mock_server; inline int mock_server_port = 0; @@ -75,17 +92,19 @@ namespace arrow::flight::sql::odbc { class ODBCTestBase : public ::testing::Test { public: /// \brief Allocate environment and connection handles - static void AllocEnvConnHandles(SQLINTEGER odbc_ver = SQL_OV_ODBC3); + static void AllocEnvConnHandles(SQLHENV& env_handle, SQLHDBC& conn_handle, + SQLINTEGER odbc_ver = SQL_OV_ODBC3); /// \brief Free environment and connection handles - static void FreeEnvConnHandles(); + static void FreeEnvConnHandles(SQLHENV& env_handle, SQLHDBC& conn_handle); /// \brief Connect to Arrow Flight SQL server using connection string defined in /// environment variable "ARROW_FLIGHT_SQL_ODBC_CONN", allocate statement handle. /// Connects using ODBC Ver 3 by default - static void Connect(std::string connect_str, SQLINTEGER odbc_ver = SQL_OV_ODBC3); + static void Connect(std::string connect_str, SQLHENV& env_handle, SQLHDBC& conn_handle, + SQLINTEGER odbc_ver = SQL_OV_ODBC3); /// \brief Connect to Arrow Flight SQL server using connection string - static void ConnectWithString(std::string connection_str); + static void ConnectWithString(std::string connect_str, SQLHDBC& conn_handle); /// \brief Disconnect from server - static void Disconnect(); + static void Disconnect(SQLHENV& env_handle, SQLHDBC& conn_handle); /// \brief Get connection string from environment variable "ARROW_FLIGHT_SQL_ODBC_CONN" static std::string GetConnectionString(); /// \brief Get invalid connection string based on connection string defined in @@ -97,7 +116,6 @@ class ODBCTestBase : public ::testing::Test { protected: void SetUp() override; void TearDown() override; - static void TearDownTestSuite(); }; /// \brief Base test fixture for running tests against a remote server. @@ -106,9 +124,6 @@ class ODBCTestBase : public ::testing::Test { /// The connection string for connecting to this server is defined /// in the ARROW_FLIGHT_SQL_ODBC_CONN environment variable. class FlightSQLODBCRemoteTestBase : public ODBCTestBase { - public: - static void CheckForRemoteTest(); - protected: static void SetUpTestSuite(); }; @@ -125,6 +140,8 @@ class FlightSQLOdbcEnvConnHandleRemoteTestBase : public FlightSQLODBCRemoteTestB protected: static void SetUpTestSuite(); static void TearDownTestSuite(); + void SetUp() override {} + void TearDown() override {} }; static constexpr std::string_view kAuthorizationHeader = "authorization"; @@ -214,6 +231,8 @@ class FlightSQLOdbcEnvConnHandleMockTestBase : public FlightSQLODBCMockTestBase protected: static void SetUpTestSuite(); static void TearDownTestSuite(); + void SetUp() override {} + void TearDown() override {} }; /** ODBC read buffer size. */ @@ -232,6 +251,7 @@ bool CompareConnPropertyMap(Connection::ConnPropertyMap map1, /// Get error message from ODBC driver using SQLGetDiagRec std::string GetOdbcErrorMessage(SQLSMALLINT handle_type, SQLHANDLE handle); +static constexpr std::string_view kErrorState00000 = "00000"; static constexpr std::string_view kErrorState01004 = "01004"; static constexpr std::string_view kErrorState01S02 = "01S02"; static constexpr std::string_view kErrorState01S07 = "01S07"; @@ -288,14 +308,19 @@ size_t SqlWCharArrLen(const SQLWCHAR* str_val); /// \brief Check wide char array and convert into wstring /// \param[in] str_val Array of SQLWCHAR. +/// \param[in] str_len length of string, in number of characters. +/// \param[in] buffer_size size of underlying buffer, in number of characters. /// \return wstring -std::wstring ConvertToWString(const SQLWCHAR* str_val); +std::wstring ConvertToWString(const SQLWCHAR* str_val, SQLSMALLINT str_len = -1, + SQLSMALLINT buffer_size = kOdbcBufferSize); /// \brief Check wide char vector and convert into wstring /// \param[in] str_val Vector of SQLWCHAR. /// \param[in] str_len length of string, in bytes. +/// \param[in] buffer_size size of underlying buffer, in number of characters. /// \return wstring -std::wstring ConvertToWString(const std::vector& str_val, SQLSMALLINT str_len); +std::wstring ConvertToWString(const std::vector& str_val, SQLSMALLINT str_len, + SQLSMALLINT buffer_size = kOdbcBufferSize); /// \brief Check wide string column. /// \param[in] stmt Statement. diff --git a/cpp/src/arrow/flight/sql/odbc/tests/statement_attr_test.cc b/cpp/src/arrow/flight/sql/odbc/tests/statement_attr_test.cc index 0855f3c9751c..d1d97fc3e115 100644 --- a/cpp/src/arrow/flight/sql/odbc/tests/statement_attr_test.cc +++ b/cpp/src/arrow/flight/sql/odbc/tests/statement_attr_test.cc @@ -424,7 +424,11 @@ TYPED_TEST(StatementAttributeTest, TestSQLSetStmtAttrAsyncEnableUnsupported) { TYPED_TEST(StatementAttributeTest, TestSQLSetStmtAttrAsyncStmtEventUnsupported) { // Driver does not support asynchronous notification ValidateSetStmtAttrErrorCode(stmt, SQL_ATTR_ASYNC_STMT_EVENT, 0, SQL_ERROR, +# ifdef __linux__ + kErrorStateHYC00); +# else // Windows & Mac kErrorStateHY118); +# endif } #endif diff --git a/cpp/src/arrow/flight/sql/odbc/tests/statement_test.cc b/cpp/src/arrow/flight/sql/odbc/tests/statement_test.cc index 084809861657..9f7c408bef64 100644 --- a/cpp/src/arrow/flight/sql/odbc/tests/statement_test.cc +++ b/cpp/src/arrow/flight/sql/odbc/tests/statement_test.cc @@ -385,9 +385,9 @@ TEST_F(StatementMockTest, TestSQLExecDirectVarbinaryQuery) { SQLLEN ind; ASSERT_EQ(SQL_SUCCESS, SQLGetData(stmt, 1, SQL_C_BINARY, &varbinary_val[0], buf_len, &ind)); - EXPECT_EQ('\xAB', varbinary_val[0]); - EXPECT_EQ('\xCD', varbinary_val[1]); - EXPECT_EQ('\xEF', varbinary_val[2]); + EXPECT_EQ(static_cast('\xAB'), static_cast(varbinary_val[0])); + EXPECT_EQ(static_cast('\xCD'), static_cast(varbinary_val[1])); + EXPECT_EQ(static_cast('\xEF'), static_cast(varbinary_val[2])); } // Tests with SQL_C_DEFAULT as the target type @@ -611,9 +611,9 @@ TEST_F(StatementRemoteTest, TestSQLExecDirectVarbinaryQueryDefaultType) { SQLLEN ind; ASSERT_EQ(SQL_SUCCESS, SQLGetData(stmt, 1, SQL_C_DEFAULT, &varbinary_val[0], buf_len, &ind)); - EXPECT_EQ('\xAB', varbinary_val[0]); - EXPECT_EQ('\xCD', varbinary_val[1]); - EXPECT_EQ('\xEF', varbinary_val[2]); + EXPECT_EQ(static_cast('\xAB'), static_cast(varbinary_val[0])); + EXPECT_EQ(static_cast('\xCD'), static_cast(varbinary_val[1])); + EXPECT_EQ(static_cast('\xEF'), static_cast(varbinary_val[2])); } // TODO(GH-48730): Enable this test when ARD/IRD descriptor support is fully implemented @@ -995,9 +995,9 @@ TEST_F(StatementMockTest, TestSQLExecDirectVarbinaryTruncation) { SQLGetData(stmt, 1, SQL_C_BINARY, &varbinary_val[0], buf_len, &ind)); // Verify binary truncation is reported VerifyOdbcErrorState(SQL_HANDLE_STMT, stmt, kErrorState01004); - EXPECT_EQ('\xAB', varbinary_val[0]); - EXPECT_EQ('\xCD', varbinary_val[1]); - EXPECT_EQ('\xEF', varbinary_val[2]); + EXPECT_EQ(static_cast('\xAB'), static_cast(varbinary_val[0])); + EXPECT_EQ(static_cast('\xCD'), static_cast(varbinary_val[1])); + EXPECT_EQ(static_cast('\xEF'), static_cast(varbinary_val[2])); EXPECT_EQ(4, ind); // Fetch same column 2nd time @@ -1008,7 +1008,7 @@ TEST_F(StatementMockTest, TestSQLExecDirectVarbinaryTruncation) { ASSERT_EQ(SQL_SUCCESS, SQLGetData(stmt, 1, SQL_C_BINARY, &varbinary_val2[0], buf_len, &ind)); - EXPECT_EQ('\xAB', varbinary_val[0]); + EXPECT_EQ(static_cast('\xAB'), static_cast(varbinary_val[0])); EXPECT_EQ(1, ind); // Attempt to fetch data 3rd time @@ -1131,9 +1131,9 @@ TEST_F(StatementRemoteTest, TestSQLExecDirectNullQueryNullIndicator) { VerifyOdbcErrorState(SQL_HANDLE_STMT, stmt, kErrorState22002); } -// MacOS Driver Manager iODBC returns SQL_ERROR when invalid buffer length is provided to -// SQLGetData -#ifndef __APPLE__ +// The MacOS and Linux Driver Managers return SQL_ERROR when invalid buffer length is +// provided to SQLGetData +#ifdef _WIN32 TYPED_TEST(StatementTest, TestSQLExecDirectIgnoreInvalidBufLen) { // Verify the driver ignores invalid buffer length for fixed data types @@ -1331,7 +1331,7 @@ TYPED_TEST(StatementTest, TestSQLExecDirectIgnoreInvalidBufLen) { EXPECT_EQ(59, timestamp_var.second); EXPECT_EQ(0, timestamp_var.fraction); } -#endif // __APPLE__ +#endif // _WIN32 TYPED_TEST(StatementTest, TestSQLBindColDataQuery) { // Numeric Types @@ -1656,9 +1656,9 @@ TEST_F(StatementMockTest, TestSQLBindColVarbinaryQuery) { ASSERT_EQ(SQL_SUCCESS, SQLFetch(stmt)); // Check varbinary values - EXPECT_EQ('\xAB', varbinary_val[0]); - EXPECT_EQ('\xCD', varbinary_val[1]); - EXPECT_EQ('\xEF', varbinary_val[2]); + EXPECT_EQ(static_cast('\xAB'), static_cast(varbinary_val[0])); + EXPECT_EQ(static_cast('\xCD'), static_cast(varbinary_val[1])); + EXPECT_EQ(static_cast('\xEF'), static_cast(varbinary_val[2])); } TEST_F(StatementRemoteTest, TestSQLBindColNullQuery) { @@ -1943,8 +1943,13 @@ TYPED_TEST(StatementTest, TestSQLMoreResultsNoData) { TYPED_TEST(StatementTest, TestSQLMoreResultsInvalidFunctionSequence) { // Verify function sequence error state is reported when SQLMoreResults is called // without executing any queries +#ifdef __linux__ + ASSERT_EQ(SQL_NO_DATA, SQLMoreResults(stmt)); + VerifyOdbcErrorState(SQL_HANDLE_STMT, stmt, kErrorState00000); +#else // Windows & Mac ASSERT_EQ(SQL_ERROR, SQLMoreResults(stmt)); VerifyOdbcErrorState(SQL_HANDLE_STMT, stmt, kErrorStateHY010); +#endif } TYPED_TEST(StatementTest, TestSQLNativeSqlReturnsInputString) { @@ -1961,7 +1966,7 @@ TYPED_TEST(StatementTest, TestSQLNativeSqlReturnsInputString) { // returned length is in characters std::wstring returned_string(buf, buf + output_char_len); - std::wstring input = ConvertToWString(input_str); + std::wstring input = ConvertToWString(input_str, input_str_len); EXPECT_EQ(input, returned_string); } @@ -1979,7 +1984,7 @@ TYPED_TEST(StatementTest, TestSQLNativeSqlReturnsNTSInputString) { // returned length is in characters std::wstring returned_string(buf, buf + output_char_len); - std::wstring expected_string = ConvertToWString(input_str); + std::wstring expected_string = ConvertToWString(input_str, input_str_len); EXPECT_EQ(expected_string, returned_string); } @@ -2007,8 +2012,7 @@ TYPED_TEST(StatementTest, TestSQLNativeSqlReturnsTruncatedString) { // Create expected return string based on buf size SQLWCHAR expected_string_buf[small_buf_size_in_char]; - wcsncpy(reinterpret_cast(expected_string_buf), - reinterpret_cast(input_str), 10); + std::copy(input_str, input_str + 10, expected_string_buf); expected_string_buf[10] = L'\0'; std::wstring expected_string(expected_string_buf, expected_string_buf + small_buf_size_in_char); @@ -2135,7 +2139,11 @@ TYPED_TEST(StatementTest, SQLRowCountReturnsSuccessOnNullptr) { TYPED_TEST(StatementTest, SQLRowCountFunctionSequenceErrorOnNoQuery) { SQLLEN row_count = 0; +#ifdef __linux__ + SQLLEN expected_value = -1; +#else // Windows & Mac SQLLEN expected_value = 0; +#endif ASSERT_EQ(SQL_ERROR, SQLRowCount(stmt, &row_count)); #ifdef __APPLE__ diff --git a/cpp/src/arrow/flight/sql/odbc/tests/tables_test.cc b/cpp/src/arrow/flight/sql/odbc/tests/tables_test.cc index 4fa4d4334990..ca3972a91d04 100644 --- a/cpp/src/arrow/flight/sql/odbc/tests/tables_test.cc +++ b/cpp/src/arrow/flight/sql/odbc/tests/tables_test.cc @@ -31,7 +31,7 @@ class TablesTest : public T {}; class TablesMockTest : public FlightSQLODBCMockTestBase {}; class TablesRemoteTest : public FlightSQLODBCRemoteTestBase {}; -using TestTypes = ::testing::Types; +using TestTypes = ::testing::Types; TYPED_TEST_SUITE(TablesTest, TestTypes); template @@ -77,6 +77,8 @@ TYPED_TEST(TablesTest, SQLTablesTestInputData) { ValidateFetch(stmt, SQL_SUCCESS); } +// GH-49702: TODO Disabled on Linux due to BlockingQueue issue +#ifndef __linux__ TEST_F(TablesMockTest, SQLTablesTestGetMetadataForAllCatalogs) { SQLWCHAR empty[] = {0}; ASSIGN_SQLWCHAR_ARR(SQL_ALL_CATALOGS_W, L"%"); @@ -96,6 +98,7 @@ TEST_F(TablesMockTest, SQLTablesTestGetMetadataForAllCatalogs) { ValidateFetch(stmt, SQL_NO_DATA); } +#endif // __linux__ TEST_F(TablesMockTest, SQLTablesTestGetMetadataForNamedCatalog) { CreateTestTable(); @@ -137,6 +140,8 @@ TEST_F(TablesMockTest, SQLTablesTestGetSchemaHasNoData) { ValidateFetch(stmt, SQL_NO_DATA); } +// GH-49702: TODO Disabled on Linux due to BlockingQueue issue +#ifndef __linux__ TEST_F(TablesRemoteTest, SQLTablesTestGetMetadataForAllSchemas) { SQLWCHAR empty[] = {0}; ASSIGN_SQLWCHAR_ARR(SQL_ALL_SCHEMAS_W, L"%"); @@ -341,6 +346,7 @@ TEST_F(TablesMockTest, SQLTablesTestGetMetadataForUnicodeTableByTableName) { DropUnicodeTable(); } +#endif // __linux__ TEST_F(TablesMockTest, SQLTablesTestGetMetadataForInvalidTableNameNoData) { CreateTestTable(); @@ -405,6 +411,8 @@ TEST_F(TablesMockTest, SQLTablesGetMetadataForTableType) { DropTestTable(); } +// GH-49702: TODO Disabled on Linux due to BlockingQueue issue +#ifndef __linux__ TEST_F(TablesRemoteTest, SQLTablesGetMetadataForTableTypeTable) { // Requires creation of user table named ODBCTest using schema $scratch in remote server @@ -433,6 +441,7 @@ TEST_F(TablesRemoteTest, SQLTablesGetMetadataForTableTypeTable) { ValidateFetch(stmt, SQL_NO_DATA); } } +#endif // __linux__ TEST_F(TablesRemoteTest, SQLTablesGetMetadataForTableTypeViewHasNoData) { SQLWCHAR empty[] = {0}; @@ -449,6 +458,8 @@ TEST_F(TablesRemoteTest, SQLTablesGetMetadataForTableTypeViewHasNoData) { ValidateFetch(stmt, SQL_NO_DATA); } +// GH-49702: TODO Disabled on Linux due to BlockingQueue issue +#ifndef __linux__ TEST_F(TablesMockTest, SQLTablesGetSupportedTableTypes) { SQLWCHAR empty[] = {0}; ASSIGN_SQLWCHAR_ARR(SQL_ALL_TABLE_TYPES_W, L"%"); @@ -489,6 +500,7 @@ TEST_F(TablesRemoteTest, SQLTablesGetSupportedTableTypes) { ValidateFetch(stmt, SQL_NO_DATA); } +#endif // __linux__ TYPED_TEST(TablesTest, SQLTablesGetMetadataBySQLDescribeCol) { SQLWCHAR column_name[1024]; diff --git a/cpp/src/arrow/flight/sql/odbc/tests/type_info_test.cc b/cpp/src/arrow/flight/sql/odbc/tests/type_info_test.cc index 702d9c8218d4..623f4aa3f500 100644 --- a/cpp/src/arrow/flight/sql/odbc/tests/type_info_test.cc +++ b/cpp/src/arrow/flight/sql/odbc/tests/type_info_test.cc @@ -1649,15 +1649,15 @@ TEST_F(TypeInfoOdbcV2MockTest, TestSQLGetTypeInfoDateODBCVer2) { } TEST_F(TypeInfoOdbcV2MockTest, TestSQLGetTypeInfoSQLTypeDate) { -#ifdef __APPLE__ - ASSERT_EQ(SQL_SUCCESS, SQLGetTypeInfo(stmt, SQL_TYPE_DATE)); -#else +#ifdef _WIN32 // Pass ODBC Ver 3 data type ASSERT_EQ(SQL_ERROR, SQLGetTypeInfo(stmt, SQL_TYPE_DATE)); // Driver manager returns SQL data type out of range error state VerifyOdbcErrorState(SQL_HANDLE_STMT, stmt, kErrorStateS1004); -#endif // __APPLE__ +#else // Mac & Linux + ASSERT_EQ(SQL_SUCCESS, SQLGetTypeInfo(stmt, SQL_TYPE_DATE)); +#endif } TEST_F(TypeInfoMockTest, TestSQLGetTypeInfoSQLTypeTime) { @@ -1762,15 +1762,15 @@ TEST_F(TypeInfoOdbcV2MockTest, TestSQLGetTypeInfoTimeODBCVer2) { } TEST_F(TypeInfoOdbcV2MockTest, TestSQLGetTypeInfoSQLTypeTime) { -#ifdef __APPLE__ - ASSERT_EQ(SQL_SUCCESS, SQLGetTypeInfo(stmt, SQL_TYPE_TIME)); -#else +#ifdef _WIN32 // Pass ODBC Ver 3 data type ASSERT_EQ(SQL_ERROR, SQLGetTypeInfo(stmt, SQL_TYPE_TIME)); // Driver manager returns SQL data type out of range error state VerifyOdbcErrorState(SQL_HANDLE_STMT, stmt, kErrorStateS1004); -#endif // __APPLE__ +#else // Mac & Linux + ASSERT_EQ(SQL_SUCCESS, SQLGetTypeInfo(stmt, SQL_TYPE_TIME)); +#endif } TEST_F(TypeInfoMockTest, TestSQLGetTypeInfoSQLTypeTimestamp) { @@ -1874,15 +1874,15 @@ TEST_F(TypeInfoOdbcV2MockTest, TestSQLGetTypeInfoSQLTimestampODBCVer2) { } TEST_F(TypeInfoOdbcV2MockTest, TestSQLGetTypeInfoSQLTypeTimestamp) { -#ifdef __APPLE__ - ASSERT_EQ(SQL_SUCCESS, SQLGetTypeInfo(stmt, SQL_TYPE_TIMESTAMP)); -#else +#ifdef _WIN32 // Pass ODBC Ver 3 data type ASSERT_EQ(SQL_ERROR, SQLGetTypeInfo(stmt, SQL_TYPE_TIMESTAMP)); // Driver manager returns SQL data type out of range error state VerifyOdbcErrorState(SQL_HANDLE_STMT, stmt, kErrorStateS1004); -#endif // __APPLE__ +#else // Mac & Linux + ASSERT_EQ(SQL_SUCCESS, SQLGetTypeInfo(stmt, SQL_TYPE_TIMESTAMP)); +#endif } TEST_F(TypeInfoMockTest, TestSQLGetTypeInfoInvalidDataType) {